Skip to content

Calendar Domain

The Calendar domain gives you robust tools for handling Dates and Human Time.

Handling time is deceptively hard. Ideally, a day is 24 hours. In reality, months have 28-31 days, years have 365 or 366 days, and timezones shift clocks back and forth.

The Calendar domain hides this chaos. Writing timestamp + 86400 works until a leap second deletes your data. This domain ensures that when you say "Next Month," it handles the math correctly—whether it's February or July—making your scheduling logic reliable and legible.

Overview

aivi
use aivi.calendar (Date, DateTime)

now = DateTime.now()

// Instantiation using sigils
birthday = ~d(1990-12-31)
event    = ~dt(2025-02-08T12:34:56Z)
lunch    = ~t(12:30:00)

// "Human" math: Add 7 days, regardless of seconds
next_week = now + 7d

Features

aivi
Date     = { year: Int, month: Int, day: Int }
Time     = { hour: Int, min: Int, sec: Int, nanos: Int }
DateTime = { date: Date, time: Time }

EndOfMonth = EndOfMonth

Domain Definition

aivi
domain Calendar over Date = {
  type Delta = Day Int | Month Int | Year Int | End EndOfMonth
  
  // Add delta to date
  (+) : Date -> Delta -> Date
  (+) date (Day n)   = addDays date n
  (+) date (Month n) = addMonths date n
  (+) date (Year n)  = addYears date n
  (+) date End       = endOfMonth date
  
  // Subtract delta from date
  (-) : Date -> Delta -> Date
  (-) date delta = date + (negateDelta delta)
  
  // Delta literals
  1d = Day 1
  1m = Month 1
  1y = Year 1
  eom = End
}

Helper Functions

FunctionExplanation
isLeapYear date
Date -> Bool
Returns whether date.year is a leap year.
daysInMonth date
Date -> Int
Returns the number of days in date.month.
endOfMonth date
Date -> Date
Returns the last day of the month for date.
addDays date n
Date -> Int -> Date
Applies a day delta with calendar normalization.
addMonths date n
Date -> Int -> Date
Applies a month delta with normalization and day clamping.
addYears date n
Date -> Int -> Date
Applies a year delta.
negateDelta delta
Delta -> Delta
Returns the inverse delta (except End, which is idempotent).

Usage Examples

aivi
use aivi.calendar

today = { year: 2025, month: 2, day: 8 }

tomorrow = today + 1d
nextMonth = today + 1m
lastYear = today - 1y
monthEnd = today + eom