diff --git a/docs/src/developing.md b/docs/src/developing.md
index 48ff489cbc41846ef120ee972dcf3ef6941c6ccd..8049e8a2e2911b910eb0bcd9903e03255c7e7935 100644
--- a/docs/src/developing.md
+++ b/docs/src/developing.md
@@ -149,3 +149,10 @@ julia> 2km / 10m
 
 Within Persefone, the following units and dimensions have been imported for direct usage:
 `cm`, `m`, `km`, `m²`, `ha`, `km²`, `mg`, `g`, `kg`, `Length`, `Area`, `Mass`.
+
+### Dates
+
+Persefone expands the default [Dates](https://docs.julialang.org/en/v1/stdlib/Dates/) library
+with the [`AnnualDate`](@ref) type, which can be used to store dates that recur every year
+(e.g. migration or harvest). `AnnualDates` can be compared and added/subtracted just as normal
+dates. Use [`thisyear()`](@ref) to convert an `AnnualDate` to a `Date`.
diff --git a/docs/src/simulation.md b/docs/src/simulation.md
index 11ccea7c9ae9f5956f14fc0143f8741c410e170a..6bd8e0b4edb00ca4505a30f8d45b79500fa94826 100644
--- a/docs/src/simulation.md
+++ b/docs/src/simulation.md
@@ -9,7 +9,7 @@ This file defines the module, including all exported symbols and two high-level
 
 ```@autodocs
 Modules = [Persefone]
-Pages = ["Persefone.jl"]
+Pages = ["Persefone.jl", "core/utils.jl"]
 ```
 
 ## simulation.jl
diff --git a/src/core/utils.jl b/src/core/utils.jl
index f15df79f09d6d893bbd2478d7655404fb4c77d7b..d8077c38fcc0fa0f687625c877b8dd607a518fd5 100644
--- a/src/core/utils.jl
+++ b/src/core/utils.jl
@@ -8,10 +8,11 @@
 import Unitful: cm, m, km, ha, mg, g, kg, Length, Area, Mass
 const m² = m^2
 const km² = km^2
-import Base./ # enable division with different length/area unit types
-/(x::S,y::T) where {S<:Length, T<:Length} = (upreferred(x)/m) / (upreferred(y)/m)
-/(x::S,y::T) where {S<:Area, T<:Area} = (upreferred(x)/m²) / (upreferred(y)/m²)
-/(x::S,y::T) where {S<:Mass, T<:Mass} = (upreferred(x)/g) / (upreferred(y)/g)
+
+# enable division with different length/area unit types
+Base.:(/)(x::S,y::T) where {S<:Length, T<:Length} = (upreferred(x)/m) / (upreferred(y)/m)
+Base.:(/)(x::S,y::T) where {S<:Area, T<:Area} = (upreferred(x)/m²) / (upreferred(y)/m²)
+Base.:(/)(x::S,y::T) where {S<:Mass, T<:Mass} = (upreferred(x)/g) / (upreferred(y)/g)
 
 ## Utility type and function for working wth recurring dates
 
@@ -20,6 +21,8 @@ import Base./ # enable division with different length/area unit types
 
 A type to handle recurring dates (e.g. migration, harvest).
 Stores a month and a day, and can be compared against normal dates.
+To save typing, a Tuple{Int64,Int64} is automatically converted to an
+AnnualDate, allowing this syntax: `nestingend::AnnualDate = (August, 15)`.
 """
 mutable struct AnnualDate
     month::Int64
@@ -30,18 +33,31 @@ mutable struct AnnualDate
         if !(0 < month <= 12)
             Base.error("AnnualDate: month $month is out of range.") #TODO replace with exception
         elseif !(0 < day <= 31)
+            # not strictly accurate (AnnualDate(February, 30) is possible), but good enough
             Base.error("AnnualDate: day $day is out of range.") #TODO replace with exception
         else
             new(month, day)
         end
 end
 
+# (automatically) convert integer tuples to AnnualDate.
+# Allows writing `(August, 2)` instead of `AnnualDate(August, 2)` where an AnnualDate is expected.
+AnnualDate(ad::Tuple{Int64,Int64}) = AnnualDate(ad...)
+Base.convert(::Type{AnnualDate}, ad::Tuple{Int64,Int64}) = AnnualDate(ad)
+
 # Interface with Dates
 AnnualDate(date::Date) = AnnualDate(month(date), day(date))
 Dates.month(ad::AnnualDate) = ad.month
 Dates.day(ad::AnnualDate) = ad.day
 
 # Instantiate a recurring date for a given year
+"""
+    thisyear(annualdate, model)
+    nextyear(annualdate, model)
+    lastyear(annualdate, model)
+
+Convert an AnnualDate to a Date, using the current/next/previous year of the simulation run.
+"""
 thisyear(ad::AnnualDate, model::SimulationModel) = Date(year(model.date), ad.month, ad.day)
 nextyear(ad::AnnualDate, model::SimulationModel) = Date(year(model.date)+1, ad.month, ad.day)
 lastyear(ad::AnnualDate, model::SimulationModel) = Date(year(model.date)-1, ad.month, ad.day)
diff --git a/src/nature/macros.jl b/src/nature/macros.jl
index 54e3bef8ac70098a2d26f6552a3449ddf3605f5a..14a81d3f2546b0c9f217f010b0a603e48374dc58 100644
--- a/src/nature/macros.jl
+++ b/src/nature/macros.jl
@@ -495,8 +495,7 @@ end
 """
     @thisyear(annualdate)
 
-Construct a date object referring to the current model year from an AnnualDate
-(== Tuple{Int64,Int64}).
+Construct a date object referring to the current model year from an AnnualDate.
 """
 macro thisyear(annualdate)
     :(thisyear($(esc(annualdate)), $(esc(:model))))
@@ -505,9 +504,17 @@ end
 """
     @nextyear(annualdate)
 
-Construct a date object referring to the next year in the model from an AnnualDate
-(== Tuple{Int64,Int64}).
+Construct a date object referring to the next year in the model from an AnnualDate.
 """
 macro nextyear(annualdate)
     :(nextyear($(esc(annualdate)), $(esc(:model))))
 end
+
+"""
+    @lastyear(annualdate)
+
+Construct a date object referring to the last year in the model from an AnnualDate.
+"""
+macro lastyear(annualdate)
+    :(lastyear($(esc(annualdate)), $(esc(:model))))
+end