Sign in

    tempo

    UTC date/time library — RFC 3339 parsing, Unix timestamps, arithmetic

    datetime
    time
    date
    rfc3339
    iso8601
    duration
    Download zip
    Author
    Version
    0.8.1
    License
    Apache-2.0
    Last updated
    2 months ago
    Downloads
    589

    No dependencies

    #tempo

    UTC date/time library for MoonBit. RFC 3339 parsing, Unix timestamp conversion, calendar and duration arithmetic. No external dependencies.

    In your moon.pkg:

    import { "brickfrog/tempo/src" @tempo, }

    #Quick start

    ///|
    test {
    let dt = @tempo.DateTime::parse("2026-03-28T14:31:43Z")
    inspect(dt.date.year, content="2026")
    inspect(dt.date.month, content="3")
    inspect(dt.time.hour, content="14")
    inspect(dt.format(), content="2026-03-28T14:31:43Z")
    }

    #Types

    TypeDescription
    Dateyear, month (1–12), day (1–31)
    Timehour, minute, second, nanosecond
    DateTimeCombined UTC date and time
    DurationSigned duration, stored as nanoseconds

    All types implement Eq, Compare, and Show.

    #Constructing values

    ///|
    test {
    let d = @tempo.Date::new(2026, 3, 28)
    let t = @tempo.Time::new(14, 31, 43, 0)
    let dt = @tempo.DateTime::new(d, t)
    inspect(dt.format(), content="2026-03-28T14:31:43Z")
    }

    #Unix timestamps

    ///|
    test {
    let dt = @tempo.DateTime::from_unix_seconds(0L)
    inspect(dt.format(), content="1970-01-01T00:00:00Z")
    inspect(dt.to_unix_seconds(), content="0")

    let dt2 = @tempo.DateTime::from_unix_nanos(1_000_000_000L)
    inspect(dt2.format(), content="1970-01-01T00:00:01Z")
    inspect(dt2.to_unix_nanos(), content="1000000000")

    let ms = @tempo.DateTime::from_unix_millis(1500L)
    inspect(ms.format(), content="1970-01-01T00:00:01.5Z")
    inspect(ms.to_unix_millis(), content="1500")

    let us = @tempo.DateTime::from_unix_micros(1_500_250L)
    inspect(us.format(), content="1970-01-01T00:00:01.50025Z")
    inspect(us.to_unix_micros(), content="1500250")
    }

    #Parsing

    Accepts RFC 3339 / ISO 8601. UTC markers (Z, +00:00, -00:00) and fixed numeric offsets are accepted. Parsed values are stored as UTC DateTimes; the original offset is not retained. Out-of-range offsets raise TempoError.

    ///|
    test {
    let dt = @tempo.DateTime::parse("2026-03-28T14:31:43.125Z")
    inspect(dt.time.nanosecond, content="125000000")
    }

    ///|
    test {
    let dt = @tempo.DateTime::parse("2024-07-21T17:11:00-04:00")
    inspect(dt.format(), content="2024-07-21T21:11:00Z")
    }

    #Formatting

    DateTime::format produces RFC 3339 with a Z suffix. Fractional seconds are included only when nanosecond ≠ 0, trailing zeros trimmed.

    ///|
    test {
    let dt = @tempo.DateTime::from_unix_nanos(1_711_630_303_100_000_000L)
    inspect(dt.format(), content="2024-03-28T14:31:43.1Z")
    }

    #Arithmetic

    ///|
    test {
    let dt = @tempo.DateTime::parse("2026-03-28T12:00:00Z")
    let dt2 = dt.add(@tempo.Duration::hours(2L))
    inspect(dt2.time.hour, content="14")

    let dt3 = dt.sub(@tempo.Duration::minutes(30L))
    inspect(dt3.time.minute, content="30")

    let gap = dt2.diff(dt)
    inspect(gap.as_hours(), content="2")
    }

    ///|
    test {
    let a = @tempo.Duration::hours(1L)
    let b = @tempo.Duration::minutes(30L)
    inspect((a + b).as_minutes(), content="90")
    inspect((-a).as_nanoseconds(), content="-3600000000000")
    }

    #Duration constructors

    ///|
    test {
    inspect(@tempo.Duration::weeks(1L).as_days(), content="7")
    inspect(@tempo.Duration::days(1L).as_hours(), content="24")
    inspect(@tempo.Duration::hours(1L).as_minutes(), content="60")
    inspect(@tempo.Duration::minutes(1L).as_seconds(), content="60")
    inspect(@tempo.Duration::seconds(1L).as_milliseconds(), content="1000")
    inspect(@tempo.Duration::milliseconds(1L).as_microseconds(), content="1000")
    inspect(@tempo.Duration::microseconds(1L).as_nanoseconds(), content="1000")
    }

    #Duration accessors

    All accessors truncate toward zero (e.g. 90 minutes → 1 hour).

    ///|
    test {
    inspect(@tempo.Duration::days(3L).as_days(), content="3")
    inspect(@tempo.Duration::weeks(1L).as_weeks(), content="1")
    inspect(@tempo.Duration::days(7L).as_weeks(), content="1")
    }

    #Duration predicates

    ///|
    test {
    assert_eq(@tempo.Duration::seconds(0L).is_zero(), true)
    assert_eq(@tempo.Duration::seconds(1L).is_zero(), false)
    assert_eq(@tempo.Duration::seconds(-1L).is_negative(), true)
    assert_eq(@tempo.Duration::seconds(1L).is_negative(), false)
    assert_eq(@tempo.Duration::seconds(1L).is_positive(), true)
    }

    #Duration arithmetic

    Duration::abs, is_positive, signum, multiply, checked_multiply, and divide cover common signed-duration operations. Duration::divide raises on division by zero and on the Int64::min_value / -1 overflow.

    ///|
    test {
    let d = @tempo.Duration::seconds(-3L)
    inspect(d.abs().as_seconds(), content="3")
    assert_eq(d.signum(), -1)

    let doubled = @tempo.Duration::seconds(2L).multiply(3L)
    inspect(doubled.as_seconds(), content="6")
    assert_eq(
    @tempo.Duration::seconds(2L).checked_multiply(3L),
    Some(@tempo.Duration::seconds(6L)),
    )

    let half = @tempo.Duration::seconds(7L).divide(2L)
    inspect(half.as_seconds(), content="3")
    }

    #Calendar arithmetic

    Date::add_months and Date::add_years clamp to the end of the target month when needed. Date and DateTime both provide start_of_month, end_of_month, start_of_year, and end_of_year; the DateTime variants adjust the date and preserve the time of day.

    ///|
    test {
    let d = @tempo.Date::new(2024, 3, 15)

    // Add/remove days
    let d2 = d.add_days(10)
    inspect(d2.day, content="25")

    // ISO weekday: Monday = 1 … Sunday = 7
    assert_eq(d.day_of_week(), 5) // Friday

    // Day of year (1-based)
    assert_eq(d.day_of_year(), 75)

    // Days between two dates
    let other = @tempo.Date::new(2024, 3, 1)
    assert_eq(d.days_until(other), -14)
    }

    ///|
    test {
    let end = @tempo.Date::new(2024, 1, 31)
    inspect(end.add_months(1).format(), content="2024-02-29")

    let leap = @tempo.Date::new(2024, 2, 29)
    inspect(leap.add_years(1).format(), content="2025-02-28")

    let mid = @tempo.Date::new(2024, 3, 15)
    inspect(mid.start_of_month().format(), content="2024-03-01")
    inspect(mid.end_of_month().format(), content="2024-03-31")
    inspect(mid.start_of_year().format(), content="2024-01-01")
    inspect(mid.end_of_year().format(), content="2024-12-31")

    let dt = @tempo.DateTime::parse("2024-03-15T14:31:43Z")
    inspect(dt.start_of_month().format(), content="2024-03-01T14:31:43Z")
    inspect(dt.end_of_year().format(), content="2024-12-31T14:31:43Z")
    }

    #Field updaters

    DateTime::to_date and DateTime::to_time split a timestamp into its parts. Date has with_year/with_month/with_day; Time has with_hour through with_nanosecond; DateTime has with_date/with_time plus with_year through with_nanosecond. Fallible updaters raise on invalid values rather than clamping.

    ///|
    test {
    let d = @tempo.Date::new(2024, 3, 15)
    inspect(d.with_day(1).format(), content="2024-03-01")

    let t = @tempo.Time::new(14, 31, 43, 125_000_000)
    inspect(t.with_nanosecond(0).format(), content="14:31:43")

    let dt = @tempo.DateTime::new(d, t)
    inspect(dt.to_date().format(), content="2024-03-15")
    inspect(dt.to_time().format(), content="14:31:43.125")
    inspect(
    dt.with_year(2025).with_hour(9).format(),
    content="2025-03-15T09:31:43.125Z",
    )
    }

    #Weekday and Month

    Date::weekday and Date::month_enum return enums. Weekday and Month use 1-based numbers for to_int/from_int; next and previous wrap around.

    ///|
    test {
    let d = @tempo.Date::new(2024, 3, 15)
    assert_eq(d.weekday(), @tempo.Friday)
    assert_eq(d.month_enum(), @tempo.March)

    assert_eq(@tempo.Friday.to_int(), 5)
    assert_eq(@tempo.Weekday::from_int(5), Some(@tempo.Friday))
    assert_eq(@tempo.Friday.next(), @tempo.Saturday)
    assert_eq(@tempo.Monday.previous(), @tempo.Sunday)

    assert_eq(@tempo.February.to_int(), 2)
    assert_eq(@tempo.Month::from_int(2), Some(@tempo.February))
    assert_eq(@tempo.February.days_in(2024), 29)
    }

    #Rounding and truncation

    start_of_day/end_of_day, truncate_to(unit) (floor to a TimeUnit), and round_to(unit, mode) snap a DateTime to a boundary. round_to takes a RoundMode of Floor, Ceil, or HalfExpand (ties round up).

    ///|
    test {
    let dt = @tempo.DateTime::parse("2024-03-15T14:31:43.125Z")
    inspect(dt.start_of_day().format(), content="2024-03-15T00:00:00Z")
    inspect(dt.truncate_to(@tempo.Hour).format(), content="2024-03-15T14:00:00Z")

    let half = @tempo.DateTime::parse("2024-03-15T14:30:00Z")
    inspect(
    half.round_to(@tempo.Hour, @tempo.HalfExpand).format(),
    content="2024-03-15T15:00:00Z",
    )
    }

    #Comparison

    Named helpers over the derived ordering: is_before/is_after, min/max, and clamp for Date/Time/DateTime, plus wall-clock DateTime::since/ until (over diff).

    ///|
    test {
    let a = @tempo.Date::new(2024, 1, 1)
    let b = @tempo.Date::new(2024, 2, 1)
    assert_eq(a.is_before(b), true)
    assert_eq(a.max(b), b)

    let dt = @tempo.DateTime::parse("2024-03-15T12:00:00Z")
    let later = dt.add(@tempo.Duration::hours(2L))
    inspect(later.since(dt).as_hours(), content="2")
    }

    #Intervals

    DateInterval is an inclusive [start, end] range of dates; Interval is a half-open [start, end) range of datetimes. Both provide contains, overlaps, and intersection.

    ///|
    test {
    let span = @tempo.DateInterval::{
    start: @tempo.Date::new(2024, 1, 1),
    end: @tempo.Date::new(2024, 1, 10),
    }
    assert_eq(span.contains(@tempo.Date::new(2024, 1, 5)), true)
    inspect(span.length_in_days(), content="10")
    }

    #Weekday navigation

    Date::next/previous find the nearest date on a weekday (strictly after/before); next_or_same/previous_or_same include the date itself; and nth_weekday_of_month finds the nth occurrence (a negative n counts from the end).

    ///|
    test {
    let fri = @tempo.Date::new(2024, 3, 15) // a Friday
    inspect(fri.next(@tempo.Monday).format(), content="2024-03-18")
    inspect(fri.previous(@tempo.Monday).format(), content="2024-03-11")

    // 3rd Tuesday of March 2024
    let t = @tempo.Date::nth_weekday_of_month(2024, 3, @tempo.Tuesday, 3)
    inspect(t.format(), content="2024-03-19")
    }

    #Hashing

    Date, Time, DateTime, and Duration derive Hash, so they work as keys in the stdlib hash Map/Set.

    ///|
    test {
    let counts : Map[@tempo.Date, Int] = {}
    counts.set(@tempo.Date::new(2024, 3, 15), 3)
    assert_eq(counts.get(@tempo.Date::new(2024, 3, 15)), Some(3))
    }

    #ISO 8601 durations

    Duration::format_iso/parse_iso round-trip the PnDTnHnMnS subset (days, hours, minutes, seconds with fractions). Years and months are rejected as calendar units.

    ///|
    test {
    let d = @tempo.Duration::hours(2L) + @tempo.Duration::minutes(30L)
    inspect(d.format_iso(), content="PT2H30M")
    assert_eq(@tempo.Duration::parse_iso("PT2H30M"), d)
    }

    #JSON

    Date, Time, DateTime, and Duration serialize to their canonical strings (RFC 3339 / ISO 8601) via moonbitlang/core/json — not field objects.

    ///|
    test {
    let dt = @tempo.DateTime::parse("2024-07-21T17:11:00Z")
    inspect(dt.to_json().stringify(), content="\"2024-07-21T17:11:00Z\"")
    }

    #Overflow safety

    to_unix_nanos and the add/sub/diff operators wrap silently outside the Int64-nanosecond range (~1677–2262). The checked variants return None instead: to_unix_nanos_checked, DateTime::checked_add/checked_sub/checked_diff, and Duration::checked_add/checked_sub. min_unix_nanos/max_unix_nanos document the bounds.

    ///|
    test {
    let far = @tempo.DateTime::parse("3000-01-01T00:00:00Z")
    assert_eq(far.to_unix_nanos_checked(), None)

    match @tempo.DateTime::epoch().checked_add(@tempo.Duration::hours(1L)) {
    Some(dt) => inspect(dt.format(), content="1970-01-01T01:00:00Z")
    None => ()
    }
    }

    #Date and Time parsing/formatting

    ///|
    test {
    // Date-only parse/format
    let d = @tempo.Date::parse("2024-03-15")
    inspect(d.format(), content="2024-03-15")

    // Time-only parse/format
    let t = @tempo.Time::parse("14:31:43.500")
    inspect(t.format(), content="14:31:43.5")
    }

    #Current time

    ///|
    test {
    // Millisecond precision on js/wasm-gc, whole seconds on native.
    let now = @tempo.DateTime::now()
    assert_eq(now > @tempo.DateTime::epoch(), true)
    }

    #Calendar helpers

    ///|
    test {
    assert_eq(@tempo.is_leap_year(2000), true)
    assert_eq(@tempo.is_leap_year(1900), false)
    assert_eq(@tempo.is_leap_year(2024), true)
    assert_eq(@tempo.days_in_month(2024, 2), 29)
    assert_eq(@tempo.days_in_month(2023, 2), 28)
    }

    #Year-month

    YearMonth is a year-and-month value with no day — useful for billing periods and month-granular keys. length is leap-aware; plus_months / plus_years carry across years; Compare orders chronologically.

    ///|
    test {
    let ym = @tempo.YearMonth::new(2024, 2)
    inspect(ym.format(), content="2024-02")
    inspect(ym.length(), content="29") // February 2024 is a leap year
    inspect(ym.at_day(15).format(), content="2024-02-15")
    inspect(ym.at_end_of_month().format(), content="2024-02-29")
    inspect(ym.plus_months(11).format(), content="2025-01")
    inspect(@tempo.YearMonth::parse("2025-01").format(), content="2025-01")
    }

    #Periods

    Period is a calendar-aware { years, months, days } value — the calendar counterpart to the elapsed-time Duration. It stores its fields exactly and is never auto-normalized (15 months stays 15 months), because applying a period is anchor-dependent: adding one month clamps to the end of a short month, while adding 30 days does not. normalized rolls months into years only. Date::until returns the period between two dates and round-trips with add_period. Operations that would overflow the Int fields raise.

    ///|
    test {
    let p = @tempo.Period::of(1, 2, 10)
    inspect(p.format(), content="P1Y2M10D")
    inspect(@tempo.Period::of(0, 15, 0).normalized().format(), content="P1Y3M")

    // Anchor-dependent: months adjust (with clamping) before days.
    let d = @tempo.Date::new(2024, 1, 31)
    inspect(d.add_period(@tempo.Period::of(0, 1, 0)).format(), content="2024-02-29")
    inspect(d.add_period(@tempo.Period::of(0, 0, 30)).format(), content="2024-03-01")

    // until round-trips with add_period.
    let start = @tempo.Date::new(2024, 1, 15)
    let end = @tempo.Date::new(2024, 3, 20)
    let span = start.until(end)
    inspect(span.format(), content="P2M5D")
    assert_eq(start.add_period(span), end)
    }

    #ISO week and ordinal dates

    Date exposes ISO 8601 week dates and ordinal (day-of-year) dates. The ISO week-numbering year can differ from the calendar year for dates near January 1 and December 31.

    ///|
    test {
    let d = @tempo.Date::new(2021, 1, 1)
    assert_eq(d.iso_week_year(), 2020) // belongs to ISO week-year 2020
    assert_eq(d.iso_week(), 53)
    inspect(d.format_iso_week(), content="2020-W53-5")
    inspect(@tempo.Date::from_iso_week(2020, 53, 5).format(), content="2021-01-01")

    // Ordinal (day-of-year) dates, leap-aware.
    inspect(@tempo.Date::from_ordinal(2024, 60).format(), content="2024-02-29")
    inspect(@tempo.Date::new(2024, 12, 31).format_ordinal(), content="2024-366")

    // Parse the string forms (inverses of the format_* methods).
    inspect(
    @tempo.Date::parse_iso_week("2020-W53-5").format(),
    content="2021-01-01",
    )
    inspect(@tempo.Date::parse_ordinal("2024-060").format(), content="2024-02-29")
    }

    #Quarters

    ///|
    test {
    let d = @tempo.Date::new(2024, 5, 15)
    assert_eq(d.quarter(), 2)
    inspect(d.start_of_quarter().format(), content="2024-04-01")
    inspect(d.end_of_quarter().format(), content="2024-06-30")
    assert_eq(d.days_in_year(), 366)
    assert_eq(d.is_leap(), true)
    }

    #Fractional and human-readable durations

    as_seconds_f64 / as_minutes_f64 / as_hours_f64 give fractional totals as Double. humanize renders an English elapsed string (days/hours/minutes/ seconds, non-zero components only, - prefix for negatives).

    ///|
    test {
    inspect(@tempo.Duration::minutes(90).as_hours_f64(), content="1.5")
    inspect(@tempo.Duration::milliseconds(1500).as_seconds_f64(), content="1.5")

    let d = @tempo.Duration::hours(2) + @tempo.Duration::minutes(30)
    inspect(d.humanize(), content="2 hours 30 minutes")
    inspect(@tempo.Duration::seconds(1).humanize(), content="1 second")
    inspect(@tempo.Duration::minutes(-90).humanize(), content="-1 hour 30 minutes")
    }

    #Custom formatting

    format_fixed produces a fixed-width timestamp (always nine fractional digits) whose byte order matches chronological order for years 0–9999 — handy for log lines and database keys. format_with is a brace-token DSL: the tokens {YYYY}, {MM}, {DD}, {HH}, {mm}, {ss}, {fff}, and {nnnnnnnnn} substitute, everything else is literal, {{ and }} emit literal braces, and an unknown token or unmatched brace raises.

    ///|
    test {
    let dt = @tempo.DateTime::parse("2024-07-21T17:11:00.5Z")
    inspect(dt.format_fixed(), content="2024-07-21T17:11:00.500000000Z")
    inspect(dt.format_with("{YYYY}/{MM}/{DD} {HH}:{mm}"), content="2024/07/21 17:11")
    }

    #Not included

    • Timezones / DST — planned as a separate tempo-tz package
    • Locale-aware formatting — strftime patterns, localized names
    • Leap seconds — POSIX ignores them, so does tempo

    TempoError

    pub(all) suberror TempoError {
    TempoError(String)
    }

    Error type for all tempo operations.
    impl Show for TempoError

    Date

    pub struct Date {
    year : Int
    month : Int
    day : Int
    } derive(Compare, Eq, Hash,
    Debug
    )

    A calendar date in the proleptic Gregorian calendar (UTC).
    impl Show for Date
    impl ToJson for Date

    Date::add_days

    fn Date::add_days(self : Date, days : Int) -> Date

    Add a signed number of calendar days to this date (proleptic Gregorian).

    This infallible operation wraps if the resulting year falls outside the Int year envelope. Use Date::add_days_checked to get None instead.

    Date::add_days_checked

    fn Date::add_days_checked(self : Date, days : Int) -> Date?

    Add a signed number of calendar days, returning None if the resulting year cannot be represented by Date's Int year field.

    Date::add_months

    fn Date::add_months(self : Date, months : Int) -> Date

    Add a signed number of calendar months to this date.

    If the original day does not exist in the target month, the result is clamped to that month's last day. Clamping is non-sticky: 2024-02-28 plus one month is 2024-03-28, not 2024-03-31.

    This infallible operation wraps if the resulting year falls outside the Int year envelope. Use Date::add_months_checked to get None instead.

    Date::add_months_checked

    fn Date::add_months_checked(self : Date, months : Int) -> Date?

    Add a signed number of calendar months, returning None if the resulting year cannot be represented by Date's Int year field.

    Date::add_period

    fn Date::add_period(self : Date, period : Period) -> Date raise TempoError

    Add a calendar period to this date.

    Years and months are combined into one month adjustment first, using the same end-of-month clamping as Date::add_months; days are added afterward.

    Date::add_years

    fn Date::add_years(self : Date, years : Int) -> Date

    Add a signed number of calendar years to this date.

    If the original day does not exist in the target year/month, the result is clamped to that month's last day, so leap day moves to February 28 in non-leap target years.

    This infallible operation wraps if the resulting year falls outside the Int year envelope. Use Date::add_years_checked to get None instead.

    Date::add_years_checked

    fn Date::add_years_checked(self : Date, years : Int) -> Date?

    Add a signed number of calendar years, returning None if the resulting year cannot be represented by Date's Int year field.

    Date::clamp

    fn Date::clamp(self : Date, lo : Date, hi : Date) -> Date

    Clamp this date to the inclusive range [lo, hi]. Assumes lo <= hi.

    Date::day_of_week

    fn Date::day_of_week(self : Date) -> Int

    ISO 8601 weekday: Monday = 1 through Sunday = 7.

    Date::day_of_year

    fn Date::day_of_year(self : Date) -> Int

    Day of year in 1..366 (1 = January 1).

    Date::days_in_year

    fn Date::days_in_year(self : Date) -> Int

    Number of days in this date's calendar year.

    Date::days_until

    fn Date::days_until(self : Date, other : Date) -> Int

    Calendar days from self to other (other minus self). Negative if other is earlier.

    Date::end_of_month

    fn Date::end_of_month(self : Date) -> Date

    Return the last day of this date's calendar month.

    Date::end_of_quarter

    fn Date::end_of_quarter(self : Date) -> Date

    Return the last day of this date's calendar quarter.

    Date::end_of_year

    fn Date::end_of_year(self : Date) -> Date

    Return the last day of this date's calendar year.

    Date::format

    fn Date::format(self : Date) -> String

    Format this date as 'YYYY-MM-DD'.

    Date::format_iso_week

    fn Date::format_iso_week(self : Date) -> String

    Format this date as an ISO 8601 week date: YYYY-Www-D.

    The ISO week-year is computed with Int64 intermediates, so at the absolute Int year boundary the formatted week-year may be the expanded year just outside Date's representable calendar envelope.

    Date::format_ordinal

    fn Date::format_ordinal(self : Date) -> String

    Format this date as an ordinal date using ISO 8601 expanded-year output (see pad4_year), followed by day-of-year in 001..366.

    Date::from_iso_week

    fn Date::from_iso_week(week_year : Int, week : Int, weekday : Int) -> Date raise TempoError

    Create a date from an ISO 8601 week date.

    week_year is the ISO week-numbering year, week is 1..52 or 1..53 depending on that year, and weekday is Monday = 1 through Sunday = 7. Raises TempoError if the ISO week fields are invalid or if the computed calendar date falls outside Date's representable Int year envelope.

    Date::from_ordinal

    fn Date::from_ordinal(year : Int, day_of_year : Int) -> Date raise TempoError

    Create a Date from year and day-of-year in 1..365, or 1..366 for a leap year.

    Date::is_after

    fn Date::is_after(self : Date, other : Date) -> Bool

    true if this date is later than other.

    Date::is_before

    fn Date::is_before(self : Date, other : Date) -> Bool

    true if this date is earlier than other.

    Date::is_leap

    fn Date::is_leap(self : Date) -> Bool

    true if this date's year is a leap year.

    Date::iso_week

    fn Date::iso_week(self : Date) -> Int

    ISO 8601 week number in 1..53 for this date.

    Date::iso_week_year

    fn Date::iso_week_year(self : Date) -> Int

    ISO 8601 week-numbering year for this date.

    This can differ from the calendar year near New Year; for example, 2021-01-01 belongs to ISO week year 2020.

    At the absolute Int year boundary, the true ISO week-year may be one year outside the representable Int range. This infallible accessor wraps in that residual case; Date::format_iso_week still formats the true expanded ISO week-year text.

    Date::max

    fn Date::max(self : Date, other : Date) -> Date

    Return the later of this date and other.

    Date::min

    fn Date::min(self : Date, other : Date) -> Date

    Return the earlier of this date and other.

    Date::month_enum

    fn Date::month_enum(self : Date) -> Month

    Calendar month for this date.

    Date::new

    fn Date::new(year : Int, month : Int, day : Int) -> Date raise TempoError

    Create a Date, validating that month is 1–12 and day fits the calendar month (accounting for leap years in February).

    Date::next

    fn Date::next(self : Date, wd : Weekday) -> Date

    Nearest date strictly after this date that falls on wd.

    Date::next_or_same

    fn Date::next_or_same(self : Date, wd : Weekday) -> Date

    This date if it falls on wd, otherwise the next such date.

    Date::nth_weekday_of_month

    fn Date::nth_weekday_of_month(year : Int, month : Int, wd : Weekday, n : Int) -> Date raise TempoError

    The nth occurrence of wd in year/month.

    Positive n counts from the start of the month; negative n counts from the end, so n == -1 is the last occurrence.

    Date::parse

    fn Date::parse(s : String) -> Date raise TempoError

    Parse a calendar date in 'YYYY-MM-DD' format.

    Date::parse_iso_week

    fn Date::parse_iso_week(s : String) -> Date raise TempoError

    Parse an ISO 8601 week date in 'YYYY-Www-D' format.

    The leading year is the ISO week-numbering year, which can differ from the resulting calendar year near New Year. This parser is strict: it accepts only an unsigned 4-digit week-year, a literal W, a 2-digit week, a 1-digit weekday, and end-of-input. Expanded-year output from Date::format_iso_week is not accepted.

    Date::parse_ordinal

    fn Date::parse_ordinal(s : String) -> Date raise TempoError

    Parse an ISO 8601 ordinal date in 'YYYY-DDD' format.

    This parser is strict: it accepts only an unsigned 4-digit year, a hyphen, exactly 3 digits for the day-of-year, and end-of-input. Expanded-year output from Date::format_ordinal is not accepted.

    Date::previous

    fn Date::previous(self : Date, wd : Weekday) -> Date

    Nearest date strictly before this date that falls on wd.

    Date::previous_or_same

    fn Date::previous_or_same(self : Date, wd : Weekday) -> Date

    This date if it falls on wd, otherwise the previous such date.

    Date::quarter

    fn Date::quarter(self : Date) -> Int

    Calendar quarter number in 1..4.

    Date::start_of_month

    fn Date::start_of_month(self : Date) -> Date

    Return the first day of this date's calendar month.

    Date::start_of_quarter

    fn Date::start_of_quarter(self : Date) -> Date

    Return the first day of this date's calendar quarter.

    Date::start_of_year

    fn Date::start_of_year(self : Date) -> Date

    Return the first day of this date's calendar year.

    Date::until

    fn Date::until(self : Date, other : Date) -> Period raise TempoError

    Calendar period from this date until other.

    The result round-trips through Date::add_period, including month-end clamping and the day-borrow behavior used by java.time.LocalDate.until.

    Date::weekday

    fn Date::weekday(self : Date) -> Weekday

    ISO 8601 weekday for this date.

    Date::with_day

    fn Date::with_day(self : Date, day : Int) -> Date raise TempoError

    Return a copy of this date with day replaced, validating the result.

    This does not clamp. Chaining with with_month can raise on an intermediate invalid date before a later update would make it valid.

    Date::with_month

    fn Date::with_month(self : Date, month : Int) -> Date raise TempoError

    Return a copy of this date with month replaced, validating the result.

    This does not clamp. Chaining with with_day can raise on an intermediate invalid date before a later update would make it valid.

    Date::with_year

    fn Date::with_year(self : Date, year : Int) -> Date raise TempoError

    Return a copy of this date with year replaced, validating the result.

    DateInterval

    pub(all) struct DateInterval {
    start : Date
    end : Date
    } derive(Eq,
    Debug
    )

    A calendar date interval with an inclusive end date: [start, end].

    This mirrors NodaTime's DateInterval-vs-Interval convention split: DateInterval is inclusive-end for whole calendar dates, while Interval is half-open for instants. Assumes start <= end; methods do not validate inverted intervals.

    DateInterval::contains

    fn DateInterval::contains(self : DateInterval, d : Date) -> Bool

    true if d is in this inclusive-end date interval [start, end].

    DateInterval::intersection

    fn DateInterval::intersection(self : DateInterval, other : DateInterval) -> DateInterval?

    Return the inclusive-end overlap between this date interval and other.

    DateInterval::length_in_days

    fn DateInterval::length_in_days(self : DateInterval) -> Int

    Inclusive day count for this date interval.

    DateInterval::overlaps

    fn DateInterval::overlaps(self : DateInterval, other : DateInterval) -> Bool

    true if this inclusive-end date interval shares at least one date with other.

    DateTime

    pub struct DateTime {
    date : Date
    time : Time
    } derive(Compare, Eq, Hash,
    Debug
    )

    A combined UTC date and time.
    impl Show for DateTime
    impl ToJson for DateTime

    DateTime::add

    fn DateTime::add(self : DateTime, d : Duration) -> DateTime

    Add a Duration to this DateTime.

    This wraps on Int64 overflow. Use checked_add when overflow or a DateTime outside the representable Unix-nanoseconds range should return None.

    DateTime::checked_add

    fn DateTime::checked_add(self : DateTime, d : Duration) -> DateTime?

    Add a Duration to this DateTime, returning None on Int64 overflow or when this DateTime is outside the representable Unix-nanoseconds range.

    DateTime::checked_diff

    fn DateTime::checked_diff(self : DateTime, other : DateTime) -> Duration?

    Compute self - other as a Duration, returning None on Int64 overflow or when either DateTime is outside the representable Unix-nanoseconds range.

    DateTime::checked_sub

    fn DateTime::checked_sub(self : DateTime, d : Duration) -> DateTime?

    Subtract a Duration from this DateTime, returning None on Int64 overflow or when this DateTime is outside the representable Unix-nanoseconds range.

    DateTime::clamp

    fn DateTime::clamp(self : DateTime, lo : DateTime, hi : DateTime) -> DateTime

    Clamp this DateTime to the inclusive range [lo, hi]. Assumes lo <= hi.

    DateTime::diff

    fn DateTime::diff(self : DateTime, other : DateTime) -> Duration

    Compute self - other as a Duration (may be negative).

    This wraps on Int64 overflow. Use checked_diff when overflow or a DateTime outside the representable Unix-nanoseconds range should return None.

    DateTime::end_of_day

    fn DateTime::end_of_day(self : DateTime) -> DateTime

    Return this DateTime at the end of its calendar day.

    DateTime::end_of_month

    fn DateTime::end_of_month(self : DateTime) -> DateTime

    Return this DateTime at the end of its calendar month. Preserves the time-of-day; rebuild with DateTime::new and a zero Time if midnight is desired.

    DateTime::end_of_year

    fn DateTime::end_of_year(self : DateTime) -> DateTime

    Return this DateTime at the end of its calendar year. Preserves the time-of-day; rebuild with DateTime::new and a zero Time if midnight is desired.

    DateTime::epoch

    fn DateTime::epoch() -> DateTime

    The Unix epoch: 1970-01-01T00:00:00Z.

    DateTime::format

    fn DateTime::format(self : DateTime) -> String

    Format this DateTime as an RFC 3339-style string (UTC, Z suffix) when the year is in the usual four-digit range; negative years and years ≥ 10000 use ISO 8601 expanded-year conventions (see pad4_year). Sub-second precision is included only when nanoseconds ≠ 0. Note: expanded-year output (years outside 0000–9999) cannot be round-tripped through DateTime::parse, which accepts only 4-digit positive years (RFC 3339).

    DateTime::format_fixed

    fn DateTime::format_fixed(self : DateTime) -> String

    Format this DateTime as a UTC timestamp with a fixed-width fractional second; for years 0..9999, the whole output is fixed-width and lexicographically sortable: YYYY-MM-DDTHH:MM:SS.nnnnnnnnnZ.

    The fractional-second field is always present with exactly 9 nanosecond digits. For years 0..9999, byte-for-byte string comparison of two format_fixed outputs matches chronological order. Outside that year range, expanded or negative year text (especially a leading -) does not preserve that lexicographic ordering guarantee.

    DateTime::format_with

    fn DateTime::format_with(self : DateTime, pattern : String) -> String raise TempoError

    Format this DateTime with a brace-token pattern.

    Supported tokens are {YYYY}, {MM}, {DD}, {HH}, {mm}, {ss}, {fff}, and {nnnnnnnnn}. Non-brace text outside tokens is copied literally. Use {{ and }} to emit literal braces. Unknown tokens and unmatched braces raise TempoError.

    DateTime::from_unix_micros

    fn DateTime::from_unix_micros(us : Int64) -> DateTime

    Convert a Unix timestamp in microseconds to a DateTime. Negative values represent dates before the Unix epoch.

    DateTime::from_unix_millis

    fn DateTime::from_unix_millis(ms : Int64) -> DateTime

    Convert a Unix timestamp in milliseconds to a DateTime. Negative values represent dates before the Unix epoch.

    DateTime::from_unix_nanos

    fn DateTime::from_unix_nanos(ns : Int64) -> DateTime

    Convert a Unix timestamp in nanoseconds to a DateTime. Nanosecond resolution is preserved. Negative values represent dates before the Unix epoch.

    DateTime::from_unix_seconds

    fn DateTime::from_unix_seconds(ts : Int64) -> DateTime

    Convert a Unix timestamp (seconds since 1970-01-01T00:00:00Z) to a DateTime. Negative values represent dates before the Unix epoch. The year range is limited by what Int64 can represent as days (~±100 billion years).

    DateTime::is_after

    fn DateTime::is_after(self : DateTime, other : DateTime) -> Bool

    true if this DateTime is later than other.

    DateTime::is_before

    fn DateTime::is_before(self : DateTime, other : DateTime) -> Bool

    true if this DateTime is earlier than other.

    DateTime::max

    fn DateTime::max(self : DateTime, other : DateTime) -> DateTime

    Return the later of this DateTime and other.

    DateTime::max_unix_nanos

    fn DateTime::max_unix_nanos() -> Int64

    Maximum Unix timestamp in nanoseconds representable by an Int64.

    DateTime::min

    fn DateTime::min(self : DateTime, other : DateTime) -> DateTime

    Return the earlier of this DateTime and other.

    DateTime::min_unix_nanos

    fn DateTime::min_unix_nanos() -> Int64

    Minimum Unix timestamp in nanoseconds representable by an Int64.

    DateTime::new

    fn DateTime::new(date : Date, time : Time) -> DateTime

    Create a DateTime from a validated Date and Time. Both arguments should be constructed via Date::new / Time::new (which validate). This constructor performs no additional checks.

    DateTime::now

    fn DateTime::now() -> DateTime

    Returns the current UTC time. Precision: whole seconds on native, milliseconds on js/wasm-gc.

    DateTime::parse

    fn DateTime::parse(s : String) -> DateTime raise TempoError

    Parse an RFC 3339 / ISO 8601 datetime string. Fixed numeric offsets are accepted and normalized to UTC.

    DateTime::round_to

    fn DateTime::round_to(self : DateTime, unit : TimeUnit, mode : RoundMode) -> DateTime

    Round this DateTime to the requested unit boundary.

    Floor is equivalent to truncate_to(unit). Ceil rounds to the next boundary only when there is a remainder. HalfExpand rounds to the nearest boundary, with exact half-unit ties rounded up toward the later boundary.

    Rounding is anchored to the calendar day and carries with Date::add_days, avoiding Unix-nanosecond conversion for dates outside the Int64 nanosecond timestamp range.

    DateTime::since

    fn DateTime::since(self : DateTime, earlier : DateTime) -> Duration

    Wall-clock duration elapsed since earlier (self - earlier).

    DateTime::start_of_day

    fn DateTime::start_of_day(self : DateTime) -> DateTime

    Return this DateTime at the start of its calendar day.

    DateTime::start_of_month

    fn DateTime::start_of_month(self : DateTime) -> DateTime

    Return this DateTime at the start of its calendar month. Preserves the time-of-day; rebuild with DateTime::new and a zero Time if midnight is desired.

    DateTime::start_of_year

    fn DateTime::start_of_year(self : DateTime) -> DateTime

    Return this DateTime at the start of its calendar year. Preserves the time-of-day; rebuild with DateTime::new and a zero Time if midnight is desired.

    DateTime::sub

    fn DateTime::sub(self : DateTime, d : Duration) -> DateTime

    Subtract a Duration from this DateTime.

    This wraps on Int64 overflow. Use checked_sub when overflow or a DateTime outside the representable Unix-nanoseconds range should return None.

    DateTime::to_date

    fn DateTime::to_date(self : DateTime) -> Date

    Return the date component of this DateTime.

    DateTime::to_time

    fn DateTime::to_time(self : DateTime) -> Time

    Return the time component of this DateTime.

    DateTime::to_unix_micros

    fn DateTime::to_unix_micros(self : DateTime) -> Int64

    Convert this DateTime to a Unix timestamp in microseconds.

    DateTime::to_unix_millis

    fn DateTime::to_unix_millis(self : DateTime) -> Int64

    Convert this DateTime to a Unix timestamp in milliseconds.

    DateTime::to_unix_nanos

    fn DateTime::to_unix_nanos(self : DateTime) -> Int64

    Convert this DateTime to a Unix timestamp in nanoseconds.

    Overflow note: this method silently wraps on Int64 overflow. Use to_unix_nanos_checked when dates outside the representable nanosecond range should return None; use to_unix_seconds for wider ranges.

    DateTime::to_unix_nanos_checked

    fn DateTime::to_unix_nanos_checked(self : DateTime) -> Int64?

    Convert this DateTime to a Unix timestamp in nanoseconds.

    Returns None when the instant is outside the representable Int64 nanosecond range.

    DateTime::to_unix_seconds

    fn DateTime::to_unix_seconds(self : DateTime) -> Int64

    Convert this DateTime to a Unix timestamp in seconds (nanoseconds truncated).

    DateTime::truncate_to

    fn DateTime::truncate_to(self : DateTime, unit : TimeUnit) -> DateTime

    Floor this DateTime to the requested unit boundary.

    Day truncation is anchored to the calendar day: it returns midnight UTC on the same date, rather than truncating a duration since the Unix epoch.

    DateTime::until

    fn DateTime::until(self : DateTime, later : DateTime) -> Duration

    Wall-clock duration until later (later - self).

    DateTime::with_date

    fn DateTime::with_date(self : DateTime, date : Date) -> DateTime

    Return a copy of this DateTime with date replaced.

    DateTime::with_day

    fn DateTime::with_day(self : DateTime, day : Int) -> DateTime raise TempoError

    Return a copy of this DateTime with its date day replaced.

    DateTime::with_hour

    fn DateTime::with_hour(self : DateTime, hour : Int) -> DateTime raise TempoError

    Return a copy of this DateTime with its time hour replaced.

    DateTime::with_minute

    fn DateTime::with_minute(self : DateTime, minute : Int) -> DateTime raise TempoError

    Return a copy of this DateTime with its time minute replaced.

    DateTime::with_month

    fn DateTime::with_month(self : DateTime, month : Int) -> DateTime raise TempoError

    Return a copy of this DateTime with its date month replaced.

    DateTime::with_nanosecond

    fn DateTime::with_nanosecond(self : DateTime, nanosecond : Int) -> DateTime raise TempoError

    Return a copy of this DateTime with its time nanosecond replaced.

    DateTime::with_second

    fn DateTime::with_second(self : DateTime, second : Int) -> DateTime raise TempoError

    Return a copy of this DateTime with its time second replaced.

    DateTime::with_time

    fn DateTime::with_time(self : DateTime, time : Time) -> DateTime

    Return a copy of this DateTime with time replaced.

    DateTime::with_year

    fn DateTime::with_year(self : DateTime, year : Int) -> DateTime raise TempoError

    Return a copy of this DateTime with its date year replaced.

    Duration

    pub struct Duration {
    nanoseconds : Int64
    } derive(Compare, Eq, Hash,
    Debug
    )

    A signed duration stored as total nanoseconds.
    impl Add for Duration
    impl Neg for Duration
    impl Show for Duration
    impl Sub for Duration
    impl ToJson for Duration

    Duration::abs

    fn Duration::abs(self : Duration) -> Duration

    Absolute value of this duration.

    Int64::min_value cannot be negated as an Int64, so that edge saturates to Int64::max_value instead of raising or wrapping.

    Duration::as_days

    fn Duration::as_days(self : Duration) -> Int64

    Total whole days in this duration (truncated toward zero).

    Duration::as_hours

    fn Duration::as_hours(self : Duration) -> Int64

    Total whole hours in this duration (truncated toward zero).

    Duration::as_hours_f64

    fn Duration::as_hours_f64(self : Duration) -> Double

    Total hours in this duration as a Double.

    Double has a ~53-bit mantissa, so magnitudes beyond ~2^53 nanoseconds (~104 days) lose sub-unit precision.

    Duration::as_microseconds

    fn Duration::as_microseconds(self : Duration) -> Int64

    Total whole microseconds in this duration (truncated toward zero).

    Duration::as_milliseconds

    fn Duration::as_milliseconds(self : Duration) -> Int64

    Total whole milliseconds in this duration (truncated toward zero).

    Duration::as_minutes

    fn Duration::as_minutes(self : Duration) -> Int64

    Total whole minutes in this duration (truncated toward zero).

    Duration::as_minutes_f64

    fn Duration::as_minutes_f64(self : Duration) -> Double

    Total minutes in this duration as a Double.

    Double has a ~53-bit mantissa, so magnitudes beyond ~2^53 nanoseconds (~104 days) lose sub-unit precision.

    Duration::as_nanoseconds

    fn Duration::as_nanoseconds(self : Duration) -> Int64

    Total nanoseconds in this duration.

    Duration::as_seconds

    fn Duration::as_seconds(self : Duration) -> Int64

    Total whole seconds in this duration (truncated toward zero).

    Duration::as_seconds_f64

    fn Duration::as_seconds_f64(self : Duration) -> Double

    Total seconds in this duration as a Double.

    Double has a ~53-bit mantissa, so magnitudes beyond ~2^53 nanoseconds (~104 days) lose sub-unit precision.

    Duration::as_weeks

    fn Duration::as_weeks(self : Duration) -> Int64

    Duration::checked_add

    fn Duration::checked_add(self : Duration, other : Duration) -> Duration?

    Add two durations, returning None on Int64 overflow.

    Duration::checked_multiply

    fn Duration::checked_multiply(self : Duration, n : Int64) -> Duration?

    Multiply this duration by an Int64 scalar, returning None on overflow.

    Duration::checked_sub

    fn Duration::checked_sub(self : Duration, other : Duration) -> Duration?

    Subtract one duration from another, returning None on Int64 overflow.

    Duration::days

    fn Duration::days(d : Int64) -> Duration

    Create a Duration representing the given number of days (86 400 seconds each).

    Duration::divide

    fn Duration::divide(self : Duration, n : Int64) -> Duration raise TempoError

    Divide this duration by an Int64 scalar, truncating toward zero.

    Division by zero raises TempoError. The Int64::min_value / -1 overflow edge also raises TempoError instead of trapping on backends where signed division overflow is a runtime error.

    Duration::format_iso

    fn Duration::format_iso(self : Duration) -> String

    Format this fixed-length duration as a canonical ISO 8601 duration string.

    Calendar units (years and months) are not representable as a fixed nanosecond duration and are therefore never emitted.

    Duration::hours

    fn Duration::hours(h : Int64) -> Duration

    Create a Duration representing the given number of hours.

    Duration::humanize

    fn Duration::humanize(self : Duration) -> String

    Format this duration as English elapsed time using day/hour/minute/second units. Sub-second remainder is dropped.

    Duration::is_negative

    fn Duration::is_negative(self : Duration) -> Bool

    true when this duration is negative.

    Duration::is_positive

    fn Duration::is_positive(self : Duration) -> Bool

    true when this duration is greater than zero.

    Duration::is_zero

    fn Duration::is_zero(self : Duration) -> Bool

    true when this duration is exactly zero.

    Duration::microseconds

    fn Duration::microseconds(us : Int64) -> Duration

    Create a Duration representing the given number of microseconds.

    Duration::milliseconds

    fn Duration::milliseconds(ms : Int64) -> Duration

    Create a Duration representing the given number of milliseconds.

    Duration::minutes

    fn Duration::minutes(m : Int64) -> Duration

    Create a Duration representing the given number of minutes.

    Duration::multiply

    fn Duration::multiply(self : Duration, n : Int64) -> Duration

    Multiply this duration by an Int64 scalar.

    This wraps on Int64 overflow. Use checked_multiply when overflow should return None.

    Duration::nanoseconds

    fn Duration::nanoseconds(ns : Int64) -> Duration

    Create a Duration from a number of nanoseconds.

    Duration::parse_iso

    fn Duration::parse_iso(s : String) -> Duration raise TempoError

    Parse an ISO 8601 duration string in the fixed-nanosecond subset: PnDTnHnMnS, with optional fractional seconds and leading sign.

    Calendar units (Y years or date-part M months) raise TempoError; use a calendar-period representation for those units.

    Duration::seconds

    fn Duration::seconds(s : Int64) -> Duration

    Create a Duration representing the given number of seconds.

    Duration::signum

    fn Duration::signum(self : Duration) -> Int

    Sign of this duration: -1 for negative, 0 for zero, 1 for positive.

    Duration::weeks

    fn Duration::weeks(w : Int64) -> Duration

    Create a Duration representing the given number of weeks (7 × 86 400 seconds).

    FixedOffsetDateTime

    pub struct FixedOffsetDateTime {
    // private fields
    } derive(Compare, Eq, Hash,
    Debug
    )

    A UTC instant paired with a fixed numeric display offset.

    DateTime is tempo's primary timestamp type. Use FixedOffsetDateTime only for wire formats and interop surfaces where a timestamp carries an explicit numeric offset but no IANA time zone. The stored instant is normalized UTC; the offset is retained only as a display hint.

    FixedOffsetDateTime::format

    fn FixedOffsetDateTime::format(self : FixedOffsetDateTime) -> String

    Format the local wall-clock time with the retained fixed offset.

    The local wall-clock is computed as the stored UTC instant plus offset_seconds; the fixed offset suffix is Z for zero and +HH:MM / -HH:MM otherwise. This is for RFC 3339-style interop only; DateTime remains the primary UTC timestamp type.

    Note: formatting uses the same infallible day arithmetic as Date; if the local-time projection carries past the absolute Int year envelope, the year wraps. pad4_year itself handles the full Int range and does not abort.

    FixedOffsetDateTime::from_datetime_and_offset

    fn FixedOffsetDateTime::from_datetime_and_offset(utc : DateTime, offset_seconds : Int) -> FixedOffsetDateTime raise TempoError

    Bundle a UTC instant with a fixed numeric display offset in seconds.

    The utc argument is the UTC instant and is stored as-is. DateTime remains tempo's primary timestamp type; this wrapper is for wire-format interop when a timestamp carries an explicit numeric offset but no IANA time zone.

    Raises TempoError when offset_seconds is not whole-minute granularity or is outside the RFC 3339 fixed-offset range ±23:59.

    FixedOffsetDateTime::offset_seconds

    fn FixedOffsetDateTime::offset_seconds(self : FixedOffsetDateTime) -> Int

    Return the retained fixed display offset in seconds.

    FixedOffsetDateTime::parse

    fn FixedOffsetDateTime::parse(s : String) -> FixedOffsetDateTime raise TempoError

    Parse an RFC 3339 datetime string, retaining the explicit fixed offset.

    The stored instant is normalized UTC. The parsed numeric offset is retained only for formatting/interoperability; it is not an IANA time zone.

    FixedOffsetDateTime::to_utc

    Return the stored UTC instant.

    Interval

    pub(all) struct Interval {
    start : DateTime
    end : DateTime
    } derive(Eq,
    Debug
    )

    A DateTime interval with a half-open end instant: [start, end).

    This mirrors NodaTime's DateInterval-vs-Interval convention split: DateInterval is inclusive-end for calendar dates, while Interval is half-open for instants. Assumes start <= end; methods do not validate inverted intervals.

    Interval::contains

    fn Interval::contains(self : Interval, dt : DateTime) -> Bool

    true if dt is in this half-open DateTime interval [start, end).

    Interval::intersection

    fn Interval::intersection(self : Interval, other : Interval) -> Interval?

    Return the half-open overlap between this DateTime interval and other.

    Interval::overlaps

    fn Interval::overlaps(self : Interval, other : Interval) -> Bool

    true if this half-open DateTime interval shares at least one instant with other.

    Interval::to_duration

    fn Interval::to_duration(self : Interval) -> Duration

    Duration of this half-open DateTime interval (end - start).

    Month

    pub(all) enum Month {
    January
    February
    March
    April
    May
    June
    July
    August
    September
    October
    November
    December
    } derive(Compare, Eq, Hash,
    Debug
    )

    Calendar month, ordered January through December.
    impl Show for Month

    Month::days_in

    fn Month::days_in(self : Month, year : Int) -> Int

    Number of days in this month for year.

    Month::from_int

    fn Month::from_int(n : Int) -> Month?

    Convert a calendar month number (1..12) to a Month.

    Month::next

    fn Month::next(self : Month) -> Month

    Next month, wrapping December to January.

    Month::previous

    fn Month::previous(self : Month) -> Month

    Previous month, wrapping January to December.

    Month::to_int

    fn Month::to_int(self : Month) -> Int

    Calendar month number: January = 1 through December = 12.

    Period

    pub(all) struct Period {
    years : Int
    months : Int
    days : Int
    } derive(Eq, Hash,
    Debug
    )

    A calendar-aware date period stored as exact year, month, and day fields.

    This is a calendar-field vector, not an elapsed-time scalar. It is never auto-canonicalized: fifteen months stays fifteen months, and thirty days never becomes one month.
    impl Show for Period

    Period::days

    fn Period::days(self : Period) -> Int

    Day field accessor.

    Period::format

    fn Period::format(self : Period) -> String

    Format this calendar period as ISO 8601 date-period components.

    Period::is_zero

    fn Period::is_zero(self : Period) -> Bool

    true when all three fields are zero.

    Period::minus

    fn Period::minus(self : Period, other : Period) -> Period raise TempoError

    Subtract two periods field-wise, without normalization.

    Period::months

    fn Period::months(self : Period) -> Int

    Month field accessor.

    Period::negated

    fn Period::negated(self : Period) -> Period raise TempoError

    Negate each period field, without normalization.

    Period::normalized

    fn Period::normalized(self : Period) -> Period raise TempoError

    Normalize the month field into years. Days are left untouched.

    Period::of

    fn Period::of(years : Int, months : Int, days : Int) -> Period

    Create a Period, storing the year, month, and day fields exactly.

    Period::of_days

    fn Period::of_days(days : Int) -> Period

    Create a Period with only a day field.

    Period::of_months

    fn Period::of_months(months : Int) -> Period

    Create a Period with only a month field.

    Period::of_weeks

    fn Period::of_weeks(weeks : Int) -> Period raise TempoError

    Create a Period from whole weeks, stored as days.

    Period::of_years

    fn Period::of_years(years : Int) -> Period

    Create a Period with only a year field.

    Period::parse

    fn Period::parse(s : String) -> Period raise TempoError

    Parse an ISO 8601 date-period string in the PnYnMnD subset.

    Week notation (PnW) is accepted and stored as days. Time components and any T separator are rejected.

    Period::plus

    fn Period::plus(self : Period, other : Period) -> Period raise TempoError

    Add two periods field-wise, without normalization.

    Period::to_total_months

    fn Period::to_total_months(self : Period) -> Int64

    Total months represented by the year and month fields. Days are ignored.

    Period::years

    fn Period::years(self : Period) -> Int

    Year field accessor.

    Period::zero

    fn Period::zero() -> Period

    The zero period.

    RoundMode

    pub(all) enum RoundMode {
    Floor
    Ceil
    HalfExpand
    } derive(Compare, Eq, Hash,
    Debug
    )

    Rounding modes for DateTime::round_to, ordered from earlier-boundary preference to nearest-boundary behavior.
    impl Show for RoundMode

    Time

    pub struct Time {
    hour : Int
    minute : Int
    second : Int
    nanosecond : Int
    } derive(Compare, Eq, Hash,
    Debug
    )

    A time of day with nanosecond precision (UTC).
    impl Show for Time
    impl ToJson for Time

    Time::clamp

    fn Time::clamp(self : Time, lo : Time, hi : Time) -> Time

    Clamp this time to the inclusive range [lo, hi]. Assumes lo <= hi.

    Time::format

    fn Time::format(self : Time) -> String

    Format this time as HH:MM:SS or HH:MM:SS.fraction (nanoseconds trimmed, consistent with DateTime::format).

    Time::is_after

    fn Time::is_after(self : Time, other : Time) -> Bool

    true if this time is later than other.

    Time::is_before

    fn Time::is_before(self : Time, other : Time) -> Bool

    true if this time is earlier than other.

    Time::max

    fn Time::max(self : Time, other : Time) -> Time

    Return the later of this time and other.

    Time::min

    fn Time::min(self : Time, other : Time) -> Time

    Return the earlier of this time and other.

    Time::new

    fn Time::new(hour : Int, minute : Int, second : Int, nanosecond : Int) -> Time raise TempoError

    Create a Time, validating that each field is within its canonical range: hour 0–23, minute 0–59, second 0–59, nanosecond 0–999_999_999.

    Time::parse

    fn Time::parse(s : String) -> Time raise TempoError

    Parse a time-of-day string: HH:MM:SS or HH:MM:SS.fraction.

    Time::with_hour

    fn Time::with_hour(self : Time, hour : Int) -> Time raise TempoError

    Return a copy of this time with hour replaced, validating the result.

    Time::with_minute

    fn Time::with_minute(self : Time, minute : Int) -> Time raise TempoError

    Return a copy of this time with minute replaced, validating the result.

    Time::with_nanosecond

    fn Time::with_nanosecond(self : Time, nanosecond : Int) -> Time raise TempoError

    Return a copy of this time with nanosecond replaced, validating the result.

    Time::with_second

    fn Time::with_second(self : Time, second : Int) -> Time raise TempoError

    Return a copy of this time with second replaced, validating the result.

    TimeUnit

    pub(all) enum TimeUnit {
    Second
    Minute
    Hour
    Day
    } derive(Compare, Eq, Hash,
    Debug
    )

    Units supported by DateTime::truncate_to and DateTime::round_to, ordered smallest to largest.
    impl Show for TimeUnit

    Weekday

    pub(all) enum Weekday {
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
    Sunday
    } derive(Compare, Eq, Hash,
    Debug
    )

    ISO 8601 weekday, ordered Monday through Sunday.
    impl Show for Weekday

    Weekday::from_int

    fn Weekday::from_int(n : Int) -> Weekday?

    Convert an ISO weekday number (1..7) to a Weekday.

    Weekday::next

    fn Weekday::next(self : Weekday) -> Weekday

    Next weekday, wrapping Sunday to Monday.

    Weekday::number_from_monday

    fn Weekday::number_from_monday(self : Weekday) -> Int

    ISO 8601 weekday number: Monday = 1 through Sunday = 7.

    Weekday::number_from_sunday

    fn Weekday::number_from_sunday(self : Weekday) -> Int

    Sunday-based weekday number: Sunday = 1 through Saturday = 7.

    Weekday::previous

    fn Weekday::previous(self : Weekday) -> Weekday

    Previous weekday, wrapping Monday to Sunday.

    Weekday::to_int

    fn Weekday::to_int(self : Weekday) -> Int

    ISO 8601 weekday number: Monday = 1 through Sunday = 7.

    YearMonth

    pub(all) struct YearMonth {
    year : Int
    month : Int
    } derive(Compare, Eq, Hash,
    Debug
    )

    A calendar year and month in the proleptic Gregorian calendar.
    impl Show for YearMonth

    YearMonth::at_day

    fn YearMonth::at_day(self : YearMonth, day : Int) -> Date raise TempoError

    Return the date in this year-month at day, validating the day.

    YearMonth::at_end_of_month

    fn YearMonth::at_end_of_month(self : YearMonth) -> Date

    Return the last date in this year-month.

    YearMonth::format

    fn YearMonth::format(self : YearMonth) -> String

    Format this year-month using the same year text as Date::format, followed by - and a zero-padded month. Years in 0..9999 produce YYYY-MM; negative years and years >= 10000 produce expanded-year strings that cannot be round-tripped through YearMonth::parse.

    YearMonth::length

    fn YearMonth::length(self : YearMonth) -> Int

    Number of days in this year-month.

    YearMonth::month_enum

    fn YearMonth::month_enum(self : YearMonth) -> Month

    Calendar month for this year-month.

    YearMonth::new

    fn YearMonth::new(year : Int, month : Int) -> YearMonth raise TempoError

    Create a YearMonth, validating that month is 1–12.

    YearMonth::parse

    fn YearMonth::parse(s : String) -> YearMonth raise TempoError

    Parse a calendar year-month in 'YYYY-MM' format.

    YearMonth::plus_months

    fn YearMonth::plus_months(self : YearMonth, months : Int) -> YearMonth

    Add a signed number of calendar months to this year-month.

    YearMonth::plus_years

    fn YearMonth::plus_years(self : YearMonth, years : Int) -> YearMonth

    Add a signed number of calendar years to this year-month.

    days_in_month

    fn days_in_month(year : Int, month : Int) -> Int

    Number of days in the given month (1–12). Returns 0 for invalid month values outside 1–12.

    is_leap_year

    fn is_leap_year(year : Int) -> Bool

    true if year is a leap year in the proleptic Gregorian calendar.