An implementation of ECMAScript Temporal: calendar and time-zone aware dates, times, instants and durations. Ported from the Rust temporal_rs.
///|
test "the core types" {
// A calendar date: no time, no time zone.
let date = @temporal.PlainDate::try_new(2025, 3, 1)
inspect(date, content="2025-03-01")
// A wall-clock time: no date, no time zone.
let time = @temporal.PlainTime::try_new(hour=11, minute=16, second=10)
inspect(time, content="11:16:10")
// The two combined.
inspect(date.to_plain_date_time(time~), content="2025-03-01T11:16:10")
// An exact point on the timeline.
inspect(
@temporal.Instant::of_string("2025-03-01T11:16:10Z"),
content="2025-03-01T11:16:10Z",
)
// A span of time, with no anchor.
inspect(@temporal.Duration::of(hours=2, minutes=30), content="PT2H30M")
}moon add marianoguerra/temporalimport {
"marianoguerra/temporal",
}| Type | What it is | Calendar | Time zone |
|---|---|---|---|
| PlainDate | A calendar date | yes | no |
| PlainTime | A wall-clock time | no | no |
| PlainDateTime | A calendar date and wall-clock time | yes | no |
| PlainYearMonth | A year and month, no day | yes | no |
| PlainMonthDay | A month and day, no year | yes | no |
| Instant | An exact point on the timeline | no | no |
| ZonedDateTime | An exact point, read in a zone | yes | yes |
| Duration | A span of time | no | no |
///|
test "month arithmetic is calendar-aware" {
let one_month = @temporal.Duration::of(months=1)
// February has no 31st, so the day is constrained to the end of the month —
// and how far depends on whether it is a leap year.
inspect(
@temporal.PlainDate::try_new(2023, 1, 31).add(one_month),
content="2023-02-28",
)
inspect(
@temporal.PlainDate::try_new(2024, 1, 31).add(one_month),
content="2024-02-29",
)
// `reject` turns the constraint into an error instead.
let jan31 = @temporal.PlainDate::try_new(2023, 1, 31)
try jan31.add(one_month, overflow=Reject) |> ignore catch {
_ => ()
} noraise {
_ => fail("expected the constrained day to be rejected")
}
}///|
test "a difference is expressed in the units you ask for" {
let start = @temporal.PlainDate::try_new(2019, 1, 1)
let end = @temporal.PlainDate::try_new(2023, 9, 15)
let with_unit = (unit : @temporal.DateTimeUnit) => {
start.until(
end,
settings=@temporal.DifferenceSettings::new(largest_unit=unit),
)
}
inspect(with_unit(Year), content="P4Y8M14D")
inspect(with_unit(Month), content="P56M14D")
inspect(with_unit(Day), content="P1718D")
}///|
test "durations round and total against a reference point" {
let duration = @temporal.Duration::of_string("P1Y2M3DT4H5M6.789S")
inspect(duration.years(), content="1")
inspect(duration.months(), content="2")
// Time-only durations need no reference.
inspect(
@temporal.Duration::of(minutes=45).add(@temporal.Duration::of(hours=1)),
content="PT1H45M",
)
inspect(
@temporal.Duration::of(hours=1, minutes=30).total(
Minute,
@temporal.utc_only_provider,
),
content="90",
)
// Calendar units need one, because their length depends on where they start.
// From 2020-01-01, 45 days is one month and 14 days, and 14 is less than
// half of February's 29, so it rounds down.
let relative = @temporal.PlainDate::try_new(2020, 1, 1)
inspect(
@temporal.Duration::of(days=45).round(
@temporal.RoundingOptions::new(smallest_unit=Month),
@temporal.utc_only_provider,
relative_to=DateRelative(relative),
),
content="P1M",
)
}///|
test "rounding" {
let time = @temporal.PlainTime::try_new(hour=12, minute=34, second=56)
inspect(
time.round(@temporal.RoundingOptions::new(smallest_unit=Minute)),
content="12:35:00",
)
inspect(
time.round(
@temporal.RoundingOptions::new(smallest_unit=Minute, rounding_mode=Floor),
),
content="12:34:00",
)
// The increment must divide its unit evenly.
inspect(
time.round(
@temporal.RoundingOptions::new(
smallest_unit=Minute,
increment=@temporal.RoundingIncrement::try_new(15),
),
),
content="12:30:00",
)
}///|
test "RFC 9557 strings" {
inspect(
@temporal.PlainDateTime::of_string("2025-03-01T11:16:10.5[u-ca=iso8601]"),
content="2025-03-01T11:16:10.5",
)
// A `Z` designator asserts an exact instant, so a plain type rejects it.
try
@temporal.PlainDateTime::of_string("2025-03-01T11:16:10Z") |> ignore
catch {
err => assert_true(err is @temporal.RangeError(_))
} noraise {
_ => fail("expected the UTC designator to be rejected")
}
}///|
test "zoned date-times over a fixed offset" {
let zdt = @temporal.ZonedDateTime::of_string(
"2025-03-01T11:16:10Z[UTC]", @temporal.utc_only_provider,
)
inspect(zdt.hour(), content="11")
// Moving to another zone keeps the instant and changes the wall clock.
let chicago_offset = @temporal.TimeZone::OffsetZone(
@temporal.UtcOffset::from_minutes(-360),
)
let shifted = zdt.with_time_zone(chicago_offset, @temporal.utc_only_provider)
inspect(shifted.hour(), content="5")
assert_eq(shifted.epoch_nanoseconds(), zdt.epoch_nanoseconds())
}///|
test "errors carry an ECMAScript error kind" {
try @temporal.PlainDate::try_new(2023, 2, 30) |> ignore catch {
err => {
assert_true(err is @temporal.RangeError(_))
inspect(err.kind_name(), content="RangeError")
}
} noraise {
_ => fail("expected February 30th to be rejected")
}
}cargo build --release --manifest-path tools/oracle/Cargo.toml
# Refresh the frozen corpus.
python3 tools/gen_conformance.py
# Fuzz against the oracle; each round uses a fresh seed.
python3 tools/fuzz.py --rounds 20 --count 3000pub trait TimeZoneProvider {
fn offset_nanoseconds_for(Self, String, Int128) -> Int64 raise TemporalError
fn candidates_for_local_datetime(Self, String, IsoDateTime) -> CandidateEpochNanoseconds raise TemporalError
}pub(all) suberror TemporalError {
Generic(String)
TypeError(String)
RangeError(String)
SyntaxError(String)
AssertError(String)
} derive(Eq)impl Show for TemporalErrorimpl Debug for TemporalErrortest {
inspect(@temporal.Calendar::of_string("ISO8601"), content="iso8601")
}fn DateDuration::new(years : Int64, months : Int64, weeks : Int64, days : Int64) -> DateDuration raise TemporalErrortest {
let d = @temporal.DateDuration::new(1, 2, 3, 4)
inspect(d.years(), content="1")
inspect(d.days(), content="4")
}impl Show for DateTimeUnittest {
inspect(@temporal.DateTimeUnit::of_string("days"), content="day")
inspect(@temporal.DateTimeUnit::of_string("nanosecond"), content="nanosecond")
}pub struct DifferenceSettings {
largest_unit : DateTimeUnit?
smallest_unit : DateTimeUnit?
rounding_mode : RoundingMode?
increment : RoundingIncrement?
} derive(Default, Debug)fn DifferenceSettings::new(largest_unit? : DateTimeUnit, smallest_unit? : DateTimeUnit, rounding_mode? : RoundingMode, increment? : RoundingIncrement) -> DifferenceSettingsimpl Default for Disambiguationimpl Show for Disambiguationimpl Default for DisplayCalendarimpl Show for DisplayCalendarimpl Default for DisplayOffsetimpl Show for DisplayOffsetimpl Default for DisplayTimeZoneimpl Show for DisplayTimeZonetest {
let a = @temporal.Duration::of(minutes=45)
let b = @temporal.Duration::of(hours=1)
inspect(a.add(b), content="PT1H45M")
}test {
inspect(@temporal.Duration::of(days=1, hours=2).negated(), content="-P1DT2H")
}fn Duration::new(years : Int64, months : Int64, weeks : Int64, days : Int64, hours : Int64, minutes : Int64, seconds : Int64, milliseconds : Int64, microseconds : Int128, nanoseconds : Int128) -> Duration raise TemporalErrortest {
let d = @temporal.Duration::of(weeks=2, days=3)
inspect(d, content="P2W3D")
}fn Duration::of(years? : Int64, months? : Int64, weeks? : Int64, days? : Int64, hours? : Int64, minutes? : Int64, seconds? : Int64, milliseconds? : Int64, microseconds? : Int64, nanoseconds? : Int64) -> Duration raise TemporalErrortest {
let d = @temporal.Duration::of(hours=2, minutes=30)
inspect(d, content="PT2H30M")
}test {
let d = @temporal.Duration::of_string("P1Y2M3DT4H5M6.789S")
inspect(d, content="P1Y2M3DT4H5M6.789S")
inspect(@temporal.Duration::of_string("-P1D"), content="-P1D")
}fn[P : TimeZoneProvider] Duration::round(self : Duration, options : RoundingOptions, provider : P, relative_to? : RelativeTo) -> Duration raise TemporalErrortest {
let d = @temporal.Duration::of(hours=1, minutes=45)
let options = @temporal.RoundingOptions::new(smallest_unit=Hour)
inspect(d.round(options, @temporal.utc_only_provider), content="PT2H")
}test {
inspect(@temporal.Duration::of(days=-1).sign(), content="-1")
inspect(@temporal.duration_zero.sign(), content="0")
}test {
inspect(
@temporal.Duration::of(years=1, months=2, days=3).to_string(),
content="P1Y2M3D",
)
inspect(@temporal.duration_zero.to_string(), content="PT0S")
}fn Duration::to_string_with_options(self : Duration, options : ToStringRoundingOptions) -> String raise TemporalErrortest {
let d = @temporal.Duration::of(seconds=1, milliseconds=500)
inspect(
d.to_string_with_options(
@temporal.ToStringRoundingOptions::new(precision=Digit(1)),
),
content="PT1.5S",
)
}fn[P : TimeZoneProvider] Duration::total(self : Duration, unit : DateTimeUnit, provider : P, relative_to? : RelativeTo) -> Double raise TemporalErrortest {
let d = @temporal.Duration::of(hours=1, minutes=30)
inspect(d.total(Minute, @temporal.utc_only_provider), content="90")
}test {
let instant = @temporal.Instant::from_epoch_nanoseconds(
@int128.of_int64(1740827770000000000L),
)
inspect(instant, content="2025-03-01T11:16:10Z")
}test {
inspect(
@temporal.Instant::of_string("2025-03-01T11:16:10+01:00"),
content="2025-03-01T10:16:10Z",
)
}fn Instant::since(self : Instant, other : Instant, settings? : DifferenceSettings) -> Duration raise TemporalErrorfn[P : TimeZoneProvider] Instant::to_string_with_options(self : Instant, options : ToStringRoundingOptions, time_zone : TimeZone?, provider : P) -> String raise TemporalErrorfn[P : TimeZoneProvider] Instant::to_zoned_date_time(self : Instant, time_zone : TimeZone, provider : P, calendar? : Calendar) -> ZonedDateTime raise TemporalErrorfn Instant::until(self : Instant, other : Instant, settings? : DifferenceSettings) -> Duration raise TemporalErrorimpl Show for IsoDateTimeimpl Show for OffsetDisambiguationtest {
let jan31 = @temporal.PlainDate::try_new(2023, 1, 31)
let one_month = @temporal.Duration::of(months=1)
// February has no 31st, so `constrain` clamps to the end of the month.
inspect(jan31.add(one_month), content="2023-02-28")
}fn PlainDate::new_with_overflow(year : Int, month : Int, day : Int, overflow : Overflow, calendar : Calendar) -> PlainDate raise TemporalErrortest {
inspect(@temporal.PlainDate::of_string("2025-03-01"), content="2025-03-01")
inspect(
@temporal.PlainDate::of_string("2025-03-01T11:16:10[u-ca=iso8601]"),
content="2025-03-01",
)
}fn PlainDate::since(self : PlainDate, other : PlainDate, settings? : DifferenceSettings) -> Duration raise TemporalErrorfn PlainDate::to_plain_date_time(self : PlainDate, time? : PlainTime) -> PlainDateTime raise TemporalErrorfn PlainDate::to_string_with_options(self : PlainDate, display_calendar : DisplayCalendar) -> Stringtest {
inspect(@temporal.PlainDate::try_new(2024, 2, 29), content="2024-02-29")
}fn PlainDate::until(self : PlainDate, other : PlainDate, settings? : DifferenceSettings) -> Duration raise TemporalErrortest {
let a = @temporal.PlainDate::try_new(2020, 1, 1)
let b = @temporal.PlainDate::try_new(2024, 3, 15)
let settings = @temporal.DifferenceSettings::new(largest_unit=Year)
inspect(a.until(b, settings~), content="P4Y2M14D")
}fn PlainDate::with_fields(self : PlainDate, year? : Int, month? : Int, day? : Int, overflow? : Overflow) -> PlainDate raise TemporalErrortest {
let date = @temporal.PlainDate::try_new(2024, 3, 15)
inspect(date.with_fields(month=1), content="2024-01-15")
}impl Compare for PlainDateTimeimpl Show for PlainDateTimeimpl Debug for PlainDateTimefn PlainDateTime::add(self : PlainDateTime, duration : Duration, overflow? : Overflow) -> PlainDateTime raise TemporalErrortest {
let dt = @temporal.PlainDateTime::try_new(2024, 1, 31, hour=23)
// The time is added first: 23:00 + 2h rolls into the next day, so the
// date arithmetic sees one month and one day.
inspect(
dt.add(@temporal.Duration::of(months=1, hours=2)),
content="2024-03-01T01:00:00",
)
}fn PlainDateTime::new_with_overflow(year : Int, month : Int, day : Int, hour : Int, minute : Int, second : Int, millisecond : Int, microsecond : Int, nanosecond : Int, overflow : Overflow, calendar : Calendar) -> PlainDateTime raise TemporalErrortest {
inspect(
@temporal.PlainDateTime::of_string("2025-03-01T11:16:10"),
content="2025-03-01T11:16:10",
)
}fn PlainDateTime::round(self : PlainDateTime, options : RoundingOptions) -> PlainDateTime raise TemporalErrorfn PlainDateTime::since(self : PlainDateTime, other : PlainDateTime, settings? : DifferenceSettings) -> Duration raise TemporalErrorfn PlainDateTime::subtract(self : PlainDateTime, duration : Duration, overflow? : Overflow) -> PlainDateTime raise TemporalErrorfn PlainDateTime::to_string_with_options(self : PlainDateTime, options : ToStringRoundingOptions, display_calendar : DisplayCalendar) -> String raise TemporalErrorfn PlainDateTime::try_new(year : Int, month : Int, day : Int, hour? : Int, minute? : Int, second? : Int, millisecond? : Int, microsecond? : Int, nanosecond? : Int) -> PlainDateTime raise TemporalErrortest {
inspect(
@temporal.PlainDateTime::try_new(2025, 3, 1, hour=11, minute=16, second=10),
content="2025-03-01T11:16:10",
)
}fn PlainDateTime::until(self : PlainDateTime, other : PlainDateTime, settings? : DifferenceSettings) -> Duration raise TemporalErrorfn PlainDateTime::with_fields(self : PlainDateTime, year? : Int, month? : Int, day? : Int, hour? : Int, minute? : Int, second? : Int, millisecond? : Int, microsecond? : Int, nanosecond? : Int, overflow? : Overflow) -> PlainDateTime raise TemporalErrorimpl Show for PlainMonthDayimpl Debug for PlainMonthDaytest {
inspect(@temporal.PlainMonthDay::of_string("--02-29"), content="02-29")
inspect(@temporal.PlainMonthDay::of_string("2024-02-29"), content="02-29")
}fn PlainMonthDay::to_plain_date(self : PlainMonthDay, year : Int, overflow? : Overflow) -> PlainDate raise TemporalErrortest {
let leap_day = @temporal.PlainMonthDay::try_new(2, 29)
// 2023 is not a leap year, so the day is constrained to the 28th.
inspect(leap_day.to_plain_date(2023), content="2023-02-28")
}fn PlainMonthDay::to_string_with_options(self : PlainMonthDay, display_calendar : DisplayCalendar) -> Stringfn PlainMonthDay::try_new(month : Int, day : Int, reference_year? : Int, overflow? : Overflow, calendar? : Calendar) -> PlainMonthDay raise TemporalErrortest {
inspect(@temporal.PlainMonthDay::try_new(2, 29), content="02-29")
}fn PlainMonthDay::with_fields(self : PlainMonthDay, month? : Int, day? : Int, overflow? : Overflow) -> PlainMonthDay raise TemporalErrortest {
let t = @temporal.PlainTime::try_new(hour=23, minute=30)
inspect(t.add(@temporal.Duration::of(hours=1)), content="00:30:00")
}fn PlainTime::new_with_overflow(hour : Int, minute : Int, second : Int, millisecond : Int, microsecond : Int, nanosecond : Int, overflow : Overflow) -> PlainTime raise TemporalErrortest {
inspect(@temporal.PlainTime::of_string("12:30:45.5"), content="12:30:45.5")
}test {
let t = @temporal.PlainTime::try_new(hour=12, minute=34, second=56)
let options = @temporal.RoundingOptions::new(smallest_unit=Minute)
inspect(t.round(options), content="12:35:00")
}fn PlainTime::since(self : PlainTime, other : PlainTime, settings? : DifferenceSettings) -> Duration raise TemporalErrorfn PlainTime::to_string_with_options(self : PlainTime, options : ToStringRoundingOptions) -> String raise TemporalErrorfn PlainTime::try_new(hour? : Int, minute? : Int, second? : Int, millisecond? : Int, microsecond? : Int, nanosecond? : Int) -> PlainTime raise TemporalErrortest {
inspect(@temporal.PlainTime::try_new(hour=7, minute=30), content="07:30:00")
}fn PlainTime::until(self : PlainTime, other : PlainTime, settings? : DifferenceSettings) -> Duration raise TemporalErrorimpl Compare for PlainYearMonthimpl Show for PlainYearMonthimpl Debug for PlainYearMonthfn PlainYearMonth::add(self : PlainYearMonth, duration : Duration, overflow? : Overflow) -> PlainYearMonth raise TemporalErrortest {
let ym = @temporal.PlainYearMonth::try_new(2024, 1)
inspect(ym.add(@temporal.Duration::of(months=13)), content="2025-02")
}test {
inspect(@temporal.PlainYearMonth::of_string("2030-10"), content="2030-10")
}fn PlainYearMonth::since(self : PlainYearMonth, other : PlainYearMonth, settings? : DifferenceSettings) -> Duration raise TemporalErrorfn PlainYearMonth::subtract(self : PlainYearMonth, duration : Duration, overflow? : Overflow) -> PlainYearMonth raise TemporalErrorfn PlainYearMonth::to_string_with_options(self : PlainYearMonth, display_calendar : DisplayCalendar) -> Stringfn PlainYearMonth::try_new(year : Int, month : Int, reference_day? : Int, overflow? : Overflow, calendar? : Calendar) -> PlainYearMonth raise TemporalErrortest {
inspect(@temporal.PlainYearMonth::try_new(2030, 10), content="2030-10")
}fn PlainYearMonth::until(self : PlainYearMonth, other : PlainYearMonth, settings? : DifferenceSettings) -> Duration raise TemporalErrorfn PlainYearMonth::with_fields(self : PlainYearMonth, year? : Int, month? : Int, overflow? : Overflow) -> PlainYearMonth raise TemporalErrorfn[P : TimeZoneProvider] RelativeTo::of_string(source : String, provider : P) -> RelativeTo raise TemporalErrortest {
let relative = @temporal.RelativeTo::of_string(
"2020-01-01", @temporal.utc_only_provider,
)
inspect(relative is DateRelative(_), content="true")
}impl Default for RoundingIncrementimpl Show for RoundingIncrementtest {
inspect(@temporal.RoundingIncrement::try_new(30), content="30")
}impl Default for RoundingModeimpl Show for RoundingModepub struct RoundingOptions {
largest_unit : DateTimeUnit?
smallest_unit : DateTimeUnit?
rounding_mode : RoundingMode?
increment : RoundingIncrement?
} derive(Debug)impl Default for RoundingOptionsfn RoundingOptions::new(largest_unit? : DateTimeUnit, smallest_unit? : DateTimeUnit, rounding_mode? : RoundingMode, increment? : RoundingIncrement) -> RoundingOptionsfn[P : TimeZoneProvider] TimeZone::epoch_nanoseconds_for(self : TimeZone, local_iso : IsoDateTime, disambiguation : Disambiguation, provider : P) -> EpochNanosecondsAndOffset raise TemporalErrorfn[P : TimeZoneProvider] TimeZone::offset_nanoseconds_for(self : TimeZone, epoch_nanoseconds : Int128, provider : P) -> Int64 raise TemporalErrorfn[P : TimeZoneProvider] TimeZone::utc_offset_for(self : TimeZone, epoch_nanoseconds : Int128, provider : P) -> UtcOffset raise TemporalErrorpub struct ToStringRoundingOptions {
precision : Precision
smallest_unit : DateTimeUnit?
rounding_mode : RoundingMode?
} derive(Default, Debug)fn ToStringRoundingOptions::new(precision? : Precision, smallest_unit? : DateTimeUnit, rounding_mode? : RoundingMode) -> ToStringRoundingOptionstest {
inspect(@temporal.UtcOffset::from_minutes(-330), content="-05:30")
}test {
inspect(@temporal.UtcOffset::from_minutes(0), content="+00:00")
inspect(@temporal.UtcOffset::from_seconds(3661), content="+01:01:01")
}impl TimeZoneProvider for UtcOnlyProviderfn candidates_for_local_datetime(_self : UtcOnlyProvider, identifier : String, local_iso : IsoDateTime) -> CandidateEpochNanoseconds raise TemporalErrorfn offset_nanoseconds_for(_self : UtcOnlyProvider, identifier : String, _epoch : Int128) -> Int64 raise TemporalErrorimpl Show for ZonedDateTimeimpl Debug for ZonedDateTimefn[P : TimeZoneProvider] ZonedDateTime::add(self : ZonedDateTime, duration : Duration, provider : P, overflow? : Overflow) -> ZonedDateTime raise TemporalErrorfn[P : TimeZoneProvider] ZonedDateTime::hours_in_day(self : ZonedDateTime, provider : P) -> Double raise TemporalErrorfn[P : TimeZoneProvider] ZonedDateTime::of_string(source : String, provider : P, disambiguation? : Disambiguation, offset_option? : OffsetDisambiguation) -> ZonedDateTime raise TemporalErrortest {
let zdt = @temporal.ZonedDateTime::of_string(
"2025-03-01T11:16:10Z[UTC]", @temporal.utc_only_provider,
)
inspect(zdt.hour(), content="11")
}fn[P : TimeZoneProvider] ZonedDateTime::round(self : ZonedDateTime, options : RoundingOptions, provider : P) -> ZonedDateTime raise TemporalErrorfn[P : TimeZoneProvider] ZonedDateTime::since(self : ZonedDateTime, other : ZonedDateTime, provider : P, settings? : DifferenceSettings) -> Duration raise TemporalErrorfn[P : TimeZoneProvider] ZonedDateTime::start_of_day(self : ZonedDateTime, provider : P) -> ZonedDateTime raise TemporalErrorfn[P : TimeZoneProvider] ZonedDateTime::subtract(self : ZonedDateTime, duration : Duration, provider : P, overflow? : Overflow) -> ZonedDateTime raise TemporalErrorfn ZonedDateTime::to_string_with_options(self : ZonedDateTime, options : ToStringRoundingOptions, display_offset : DisplayOffset, display_time_zone : DisplayTimeZone, display_calendar : DisplayCalendar) -> String raise TemporalErrorfn[P : TimeZoneProvider] ZonedDateTime::try_new(epoch_nanoseconds : Int128, time_zone : TimeZone, provider : P, calendar? : Calendar) -> ZonedDateTime raise TemporalErrortest {
let zdt = @temporal.ZonedDateTime::try_new(
@int128.of_int64(1740827770000000000L),
@temporal.TimeZone::OffsetZone(@temporal.UtcOffset::from_minutes(-360)),
@temporal.utc_only_provider,
)
inspect(zdt.hour(), content="5")
}fn[P : TimeZoneProvider] ZonedDateTime::until(self : ZonedDateTime, other : ZonedDateTime, provider : P, settings? : DifferenceSettings) -> Duration raise TemporalErrorfn[P : TimeZoneProvider] ZonedDateTime::with_time_zone(self : ZonedDateTime, time_zone : TimeZone, provider : P) -> ZonedDateTime raise TemporalErrorlet MONTH_DAY_REFERENCE_YEAR : Intfn epoch_days_from_gregorian_date(year : Int, month : Int, day : Int) -> Int64test {
inspect(@temporal.epoch_days_from_gregorian_date(1970, 1, 1), content="0")
inspect(@temporal.epoch_days_from_gregorian_date(1969, 12, 31), content="-1")
inspect(
@temporal.epoch_days_from_gregorian_date(275760, 9, 14),
content="100000001",
)
}fn is_leap_year(year : Int) -> Booltest {
inspect(@temporal.is_leap_year(2024), content="true")
inspect(@temporal.is_leap_year(1900), content="false")
inspect(@temporal.is_leap_year(2000), content="true")
}fn is_valid_iso_date(year : Int, month : Int, day : Int) -> Boolfn is_valid_iso_time(hour : Int, minute : Int, second : Int, millisecond : Int, microsecond : Int, nanosecond : Int) -> Boolfn iso_day_of_week(year : Int, month : Int, day : Int) -> Inttest {
// 1970-01-01 was a Thursday.
inspect(@temporal.iso_day_of_week(1970, 1, 1), content="4")
}fn iso_day_of_year(year : Int, month : Int, day : Int) -> Inttest {
inspect(@temporal.iso_day_of_year(2024, 3, 1), content="61")
}fn iso_days_in_month(year : Int, month : Int) -> Inttest {
inspect(@temporal.iso_days_in_month(2024, 2), content="29")
inspect(@temporal.iso_days_in_month(2023, 2), content="28")
inspect(@temporal.iso_days_in_month(2023, 12), content="31")
}fn iso_days_in_year(year : Int) -> Intfn iso_week_of_year(year : Int, month : Int, day : Int) -> (Int, Int)test {
// 2021-01-01 was a Friday, falling in week 53 of 2020.
debug_inspect(@temporal.iso_week_of_year(2021, 1, 1), content="(53, 2020)")
debug_inspect(@temporal.iso_week_of_year(2024, 1, 1), content="(1, 2024)")
}fn ymd_from_epoch_days(epoch_days : Int64) -> (Int, Int, Int)test {
debug_inspect(@temporal.ymd_from_epoch_days(0), content="(1970, 1, 1)")
debug_inspect(
@temporal.ymd_from_epoch_days(-100000001),
content="(-271821, 4, 19)",
)
}fn ymd_from_epoch_milliseconds(epoch_ms : Int64) -> (Int, Int, Int)An implementation of ECMAScript Temporal: calendar and time-zone aware dates, times, instants and durations. Ported from the Rust temporal_rs.