Calendar/Date Math Package?

Thankfully for the basics we don’t need to consider all that complicated stuff like leap days and leap seconds—Typst’s datetime handles this correctly (hopefully) by being based on a proper calendar library.

Adding 3 months is “hard” exactly because it is not an exact duration like duration(days: 90), but in reverse that means that we only need to handle the number of months “naively”. Here’s one way to offset dates by a number of years and months:

#let date-offset(date, years: 0, months: 0) = {
  let date = (year: date.year(), month: date.month(), day: date.day())

  // months overflow into years
  // months are 1-based, factor that in when dividing
  let month = months + date.month
  years += calc.div-euclid(month - 1, 12)
  date.month = calc.rem-euclid(month - 1, 12) + 1

  date.year += years

  // conert back into datetime -- won't work if a month is too short
  datetime(..date)
}

(I purposefully didn’t add a days parameter since it is ambiguous: if you increase 2026-02-28 by one month and one day, do you get 2026-03-29 (one day after 2026-03-28) or 2026-04-01 (one month after 2026-03-01)? For days and weeks, just use duration)

Here’s a test:

#let d = datetime(year: 2025, month: 10, day: 31)

#d.display()\
// #date-offset(d, months: 1).display()  // 2025-11-31 doesn't exist!
#date-offset(d, months: 2).display()\
#date-offset(d, months: 3).display()     // year overflows

#date-offset(d, months: -2).display()\   // negative offset
#date-offset(d, months: -9).display()\
#date-offset(d, months: -10).display()   // year overflows

#let d = datetime(year: 2024, month: 2, day: 29)
// #date-offset(d, years: 1).display()   // 2025-02-29 doesn't exist!

And here’s an implementation for leap year detection:

#let is-leap-year(year) = {
  let a = datetime(year: year, month: 2, day: 28)
  let b = datetime(year: year, month: 3, day: 1)
  b - a == duration(days: 2)
}

Weekdays are built into Typst, and you can determine the length of a month similarly to how I detected a leap year.


@Steve_Moore if your code is significantly more complex than this, then I hope this helps. Otherwise, hopefully others can benefit at least. Handling work days and holidays requires more than Typst built-in types provide, so that code will end up more complex…

Finally—not really related to writing a calendar math package, but maybe interesting for this thread: a while ago, I created a calendar for this thread: Is anyone working on something like the LaTeX tikz Kalender? - #2 by SillyFreak. It demonstrates a bit how working with datetime and duration can look like.