moondate

    Dates, times, durations and cron expressions for MoonBit.

    date
    time
    duration
    cron
    moonbit
    Download zip
    Version
    0.1.2
    License
    Apache-2.0
    Last updated
    18 hours ago
    Downloads
    4K

    No dependencies

    #moondate

    Dates, times, instants, durations and cron schedules.

    Homepage · Guide · API · Playground · mooncakes


    A calendar with the arithmetic that goes with it, the five ways to write a moment that C, Python, Rust, Go and JavaScript each speak, and a second package that answers when a schedule fires next.

    moon add moonbitstack/moondate

    fn main raise {
    let stamp = @moondate.parse("2026-09-22T14:30:05.123456+08:00")
    println(stamp.strftime("%A %d %B %Y at %H:%M"))
    println(stamp.epoch())
    }

    Tuesday 22 September 2026 at 14:30 1790058605

    #The four shapes

    A moment is one of four things, and the difference is not decoration. Each constructor answers a different question, and each one refuses the questions it cannot.

    What it isWrittenepoch
    ZonedAn instant. Sits on the timeline.2026-09-22T14:30:00+08:00Some
    PlainA wall clock. An alarm, an opening time.2026-09-22T14:30:00None
    DayA date with no time.2026-09-22None
    ClockA time with no date.14:30:00None

    Turning a Plain into an instant needs a zone nobody supplied, so epoch and astimezone answer None for it rather than guessing. parse keeps whatever shape the text had; nothing is padded with a hidden midnight.

    #What it reads and writes

    FormWhere it is used
    RFC 3339 / ISO 8601TOML, JSON Schema's date-time, most of the wire
    IMF-fixdate, RFC 850, asctimeHTTP's Date header (RFC 9110 §5.6.7): all three read, the first written
    UTCTime, GeneralizedTimeASN.1, so X.509 validity (ITU-T X.680 §47)
    strftime / strptimeThe % directives of C23, POSIX, glibc, Python, chrono and C++20
    layout / parse_layoutGo's time reference layouts (RFC3339, RFC1123, …)
    pattern / parse_patternUnicode LDML patterns, what ICU, Intl and Java use
    isoformat, to_iso_string, to_utc_stringECMA-262's Date Time String Format and Temporal

    The two-digit-year windows differ between HTTP and X.509: 69/70 against 49/50. Each is applied where its own specification says, because a certificate read by HTTP's rule expires a century early.

    #Five ways to write a moment

    The same instant can be written five ways, and moondate speaks all of them in both directions: whatever it writes, strptime, parse_layout or parse_pattern reads back to the same moment.

    StyleWriteReadWhere it is from
    % directivesstrftimestrptimeC23, POSIX, glibc, Python, chrono, C++20
    Reference-time layoutlayoutparse_layoutGo's time package
    LDML patternpatternparse_patternUnicode UTS #35, ICU, Intl, Java
    ECMAScript ISOisoformat, to_iso_string, to_utc_stringparseECMA-262 and Temporal

    let m = @moondate.parse("2026-09-22T14:30:05.123456+08:00")

    m.strftime("%A %d %B %Y, %I:%M %p") // Tuesday 22 September 2026, 02:30 PM
    m.layout(@moondate.RFC1123) // Tue, 22 Sep 2026 14:30:05 +08:00
    m.pattern("EEEE, MMMM d, yyyy 'at' HH:mm") // Tuesday, September 22, 2026 at 14:30
    m.to_iso_string() // 2026-09-22T06:30:05.123Z
    m.isoformat(fraction=3, offset=false) // 2026-09-22T14:30:05.123

    The % set is the union of the six lineages, with the conflicts settled in the table: %f is Python's six digits (chrono's nine are %9f or %N), %Z writes UTC±HH:MM for a fixed offset, and %+ is chrono's form. The Go layout and the LDML pattern work against a fixed offset rather than a zone database, so their zone letters fall back to the offset (GMT±HH:MM in LDML), which is what ICU and babel do for a zone they only know by its offset.

    The vectors come from each language's own implementation — CPython for the % directives, a Rust program for chrono's additions, Go for the layout, Babel for LDML, and Node for the ECMAScript strings — and are committed under src/; CI only replays them. The generators are in scripts/.

    #Interface

    Types are listed with the calls that matter most; the API reference has every method and every parameter.

    TypeBuilt withAsked
    DateDate::new, of_days, of_ordinal, of_isocalendarweekday, yearday, isocalendar, ordinal, days, diff, plus_days, plus_months, replace
    TimeTime::new, midnight, end_of_daynanos, replace
    Momentparse, of_isoformat, http, asn1, strftime, strptime, layout, parse_layout, pattern, parse_pattern, combine, of_epochepoch, timestamp, date, time, zone, utcoffset, astimezone, isoformat, to_iso_string, to_utc_string, strftime, layout, pattern, http_text, asn1_text, ctime, timetuple, weekday, isocalendar, ordinal, diff, plus, replace
    SpanSpan::new, of, nanosecond … dayadd, sub, seconds, total_seconds, parts, neg, abs
    ZoneUtc, Offset(minutes)—
    Cron@cron.parsematches, next

    Free functions: parse, of_isoformat, strptime, parse_layout, parse_pattern, http, asn1, leap, length. Constants: min_date, max_date, min_year, max_year, resolution, midnight, end_of_day, the Go layout constants (RFC3339, RFC1123, ANSIC, …), and one span per unit from nanosecond to day.

    Errors are one type with three reasons: Range carries the field, the value it got and the range it wanted; Malformed carries what was expected; Truncated carries the name of the thing that ran out of text.

    #Examples

    Every file under src/examples/ is a complete program with the output it prints. Run one with moon run src/examples/hello.

    What it shows
    helloThe smallest complete program: read, look inside, write back.
    shapesZoned, Plain, Day and Clock side by side, and what each one can answer.
    calendarWeekdays, ISO week dates, ordinals, month lengths, adding days and months.
    zonesOne instant written three ways, astimezone, the epoch both directions, spans.
    wireHTTP dates in three formats, ASN.1 time in two, and the refusals.
    formatOne moment through all five styles: % directives, Go layout, LDML pattern, ECMAScript.
    scheduleCron expressions: what they mean, the next few firings, the or-rule, the refusals.

    #cron

    let every_quarter = @cron.parse("*/15 9-17 * * MON-FRI")
    let next = every_quarter.next(now)

    Five fields as crontab(5) defines them, or six with seconds in front. *, n, a-b, */n, a-b/n, lists of those, month and day names, ? for a day field that is not in use, and the @ shorthands. Both 0 and 7 are Sunday.

    Day-of-month and day-of-week are or-ed when both are restricted, which is Vixie cron's rule: 0 0 13 * 5 fires on the thirteenth or on any Friday, not only on Friday the thirteenth. When one of them is * they are and-ed. This surprises everyone once, so it has a test that says so.

    An expression naming a day that does not exist — 0 0 30 2 * — answers None instead of searching forever.

    #Reading the code

    The calendar is date.mbt, the wire formats are text.mbt, the % directives are strftime.mbt, the Go layout is layout.mbt, the LDML pattern is pattern.mbt, the rest of the surface is python.mbt, and the schedule is cron/. Each public item has a doc comment saying what it does and, where there is a rule behind it, which specification the rule comes from.

    There is no now(). Reading the clock is I/O, this package has none, so a caller passes what its own clock said to Moment::of_epoch.

    Named zones — daylight saving, the IANA database — are not here. Zone is a fixed offset. A zone database is a different thing with a different update cadence, and %Z writes UTC, or UTC±HH:MM for a fixed offset, rather than inventing a name it cannot recover.

    #Licence

    Apache-2.0. See LICENSE.

    Refused

    pub(all) suberror Refused {
    Range(field~ : String, got~ : Int, low~ : Int, high~ : Int)
    Malformed(String)
    Truncated(String)
    } derive(Eq,
    Debug
    )

    Why a date, a time or a text form was refused.

    A calendar that accepted the thirty-first of February would be no use as a check, so every constructor here validates and says what was wrong.
    impl Show for Refused

    Refused::equal

    fn Refused::equal(Refused, Refused) -> Bool

    Refused::not_equal

    fn Refused::not_equal(x : Refused, y : Refused) -> Bool

    Refused::output

    fn Refused::output(self : Refused, logger : &Logger) -> Unit

    Refused::to_repr

    Refused::to_string

    fn Refused::to_string(self : Refused) -> String

    Date

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

    A day on the proleptic Gregorian calendar.

    Proleptic means the Gregorian rules are applied before 1582 as well, which is what every interchange format in reach assumes and what makes arithmetic over the whole range uniform.
    impl Show for Date

    Date::compare

    fn Date::compare(Date, Date) -> Int

    Date::days

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

    Days since 1970-01-01, negative before it.

    Howard Hinnant's days_from_civil: shift the year to start in March so the leap day falls at the end, then the month lengths follow a pattern with no table.

    Date::diff

    fn Date::diff(self : Date, other : Date) -> Span

    One date less another, as a span of whole days.

    Date::equal

    fn Date::equal(Date, Date) -> Bool

    Date::isocalendar

    fn Date::isocalendar(self : Date) -> (Int, Int, Int)

    The ISO 8601 week date: the year the week belongs to, the week number, and the day of the week with Monday as 1.

    The year is not always the calendar year. A week belongs to the year that holds its Thursday, so the first of January can fall in the last week of the year before — which is exactly the case a hand-rolled week number gets wrong.

    Date::isoweekday

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

    The day of the week with Monday as 1 and Sunday as 7.

    [Date::weekday] counts from Sunday as 0, which is what cron and C's tm_wday use; ISO counts from Monday as 1. Both are here because both are asked for, and picking one silently is how an off-by-one gets into a schedule.

    Date::new

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

    A date, checked against the calendar.

    Date::not_equal

    fn Date::not_equal(x : Date, y : Date) -> Bool

    Date::of_days

    fn Date::of_days(n : Int) -> Date

    The date n days after 1970-01-01, the inverse of [Date::days].

    Date::of_isocalendar

    fn Date::of_isocalendar(year : Int, week : Int, weekday : Int) -> Date raise Refused

    The date named by an ISO week date.

    Date::of_ordinal

    fn Date::of_ordinal(n : Int) -> Date

    The date with that ordinal.

    Date::op_ge

    fn Date::op_ge(x : Date, y : Date) -> Bool

    Date::op_gt

    fn Date::op_gt(x : Date, y : Date) -> Bool

    Date::op_le

    fn Date::op_le(x : Date, y : Date) -> Bool

    Date::op_lt

    fn Date::op_lt(x : Date, y : Date) -> Bool

    Date::ordinal

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

    Days since 0001-01-01, that day being 1.

    The proleptic Gregorian ordinal, which is what makes two dates subtractable without going through an epoch.

    Date::output

    fn Date::output(self : Date, logger : &Logger) -> Unit

    Date::plus_days

    fn Date::plus_days(self : Date, n : Int) -> Date

    The date n days later, or earlier when n is negative.

    Date::plus_months

    fn Date::plus_months(self : Date, n : Int) -> Date

    The date n months later, clamped to the end of the month it lands in.

    The thirty-first of January plus one month is the twenty-eighth or twenty-ninth of February, because there is no thirty-first. Every calendar library makes this choice and they all make the same one.

    Date::replace

    fn Date::replace(self : Date, year? : Int, month? : Int, day? : Int) -> Date raise Refused

    The same date with some parts replaced.

    Date::to_repr

    Date::to_string

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

    Date::weekday

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

    The day of the week, Sunday being 0.

    1970-01-01 was a Thursday, which is where the 4 comes from.

    Date::yearday

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

    The day of the year, the first of January being 1.

    Moment

    pub(all) enum Moment {
    Zoned(date~ : Date, time~ : Time, zone~ : Zone)
    Plain(date~ : Date, time~ : Time)
    Day(date~ : Date)
    Clock(time~ : Time)
    } derive(Eq,
    Debug
    )

    A point in time, in one of the four shapes a format names.

    TOML 1.0.0 has exactly these four and gives them these meanings; RFC 3339 defines only Zoned. The distinction is not decoration: Plain is a wall clock, which is what an alarm or an opening time is, and turning it into an instant needs a zone nobody has supplied.
    impl Show for Moment

    Moment::asn1_text

    fn Moment::asn1_text(self : Moment) -> String raise Refused

    A moment as an ASN.1 GeneralizedTime, which is the form DER uses from 2050 on and the one that needs no windowing rule to read back.

    Moment::astimezone

    fn Moment::astimezone(self : Moment, zone : Zone) -> Moment?

    The same instant read in another zone.

    None for the three shapes that are not instants: moving a wall clock to another zone would be inventing the zone it was written in.

    Moment::combine

    fn Moment::combine(date : Date, time : Time, zone? : Zone) -> Moment

    A date and a time as one wall-clock moment.

    zone makes it an instant instead; leaving it off keeps it a wall clock, which is what an opening time or an alarm is.

    Moment::ctime

    fn Moment::ctime(self : Moment) -> String

    The moment in C's ctime form: Sun Nov 6 08:49:37 1994.

    The day is space-padded rather than zero-padded, which is the one thing that distinguishes this form from every other.

    Moment::date

    fn Moment::date(self : Moment) -> Date?

    The date out of a moment.

    None for a bare time, which has no date to give.

    Moment::diff

    fn Moment::diff(self : Moment, other : Moment) -> Span?

    One moment less another, for the two shapes that can be subtracted.

    None when they are not comparable: an instant and a wall clock differ by an amount that depends on a zone nobody supplied, and a bare time and a bare date are not the same kind of thing.

    Moment::epoch

    fn Moment::epoch(self : Moment) -> Int64?

    Seconds since 1970-01-01T00:00:00Z, for a moment that names one.

    None for the three shapes that do not: a wall clock, a bare date and a bare time are not instants until someone supplies the zone they are read in.

    Moment::equal

    fn Moment::equal(Moment, Moment) -> Bool

    Moment::http_text

    fn Moment::http_text(self : Moment) -> String raise Refused

    A moment as an HTTP date (RFC 9110 §5.6.7): Sun, 06 Nov 1994 08:49:37 GMT.

    This is the one form a Date header may take. The two obsolete forms are read by [http] and never written, which is what §5.6.7 asks for.

    Moment::isocalendar

    fn Moment::isocalendar(self : Moment) -> (Int, Int, Int)?

    The ISO week date of the moment's date.

    Moment::isoformat

    fn Moment::isoformat(self : Moment, sep? : Char, fraction? : Int, offset? : Bool) -> String

    The moment as ISO 8601 text, which is what [Show] writes.

    sep is the character between the date and the time, T by default; any character works, and a space is what a log line wants.

    fraction is the number of fractional-second digits, 0 to 9, truncated. Left out, it writes as many as the value needs and none for a whole second, which is Temporal's auto.

    offset writes the zone the moment carries, Z for UTC and ±HH:MM for an offset; false leaves the zone off, which is Temporal's offset: "never".

    Moment::layout

    fn Moment::layout(self : Moment, layout : StringView) -> String

    A moment written by a Go reference-time layout.

    Moment::layout(RFC3339) is 2006-01-02T15:04:05Z07:00 with the fields filled in, which is the same text Go's time.Format would write for the same instant. A moment with no zone writes its offset forms as if it sat at UTC, and a bare time or date is padded with 1 January 1900 as strftime does.

    Moment::not_equal

    fn Moment::not_equal(x : Moment, y : Moment) -> Bool

    Moment::of_epoch

    fn Moment::of_epoch(seconds : Int64, nano? : Int, zone? : Zone) -> Moment

    The moment seconds after 1970-01-01T00:00:00Z, the inverse of [Moment::timestamp].

    Reading the clock is not this package's job — it has no I/O — so a caller that wants "now" passes what its own clock said.

    Moment::ordinal

    fn Moment::ordinal(self : Moment) -> Int?

    Days since 0001-01-01 of the moment's date.

    Moment::output

    fn Moment::output(self : Moment, logger : &Logger) -> Unit

    Moment::pattern

    fn Moment::pattern(self : Moment, pattern : StringView) -> String

    A moment written by an LDML date pattern.

    pattern("yyyy-MM-dd") is the calendar date, pattern("EEEE, MMMM d, yyyy") is Tuesday, September 22, 2026, and text in single quotes is literal, so pattern("yyyy-MM-dd'T'HH:mm:ss") writes an ISO-style timestamp.

    Moment::plus

    fn Moment::plus(self : Moment, span : Span) -> Moment

    The moment span later.

    A bare date moves by whole days and a bare time wraps within the day, because that is all each of them can mean.

    Moment::replace

    fn Moment::replace(self : Moment, year? : Int, month? : Int, day? : Int, hour? : Int, minute? : Int, second? : Int, nano? : Int, zone? : Zone) -> Moment raise Refused

    The same moment with some parts replaced.

    Moment::strftime

    fn Moment::strftime(self : Moment, format : StringView) -> String

    A moment written by a format string of % directives.

    The directives are the ones every Unix knows plus the ones chrono and C++ add; the table below is the whole set. Literal text between directives is copied through untouched, and %% is a single per cent sign.

    %Y %C %yyear of four digits, century, two-digit year
    %G %gISO week year, two-digit
    %m %B %b %hmonth, long and short name
    %d %eday of month, zero- and space-padded
    %jday of year, 001 to 366
    %U %W %Vweek of year, Sunday-, Monday-, and ISO-based
    %u %wweekday, 1 to 7 and 0 to 6
    %a %Ashort and long weekday name
    %qquarter of the year, 1 to `4 (chrono)
    %H %k %I %lhour, 00 and 0, twelve-hour 01 and 1
    %M %Sminute, second
    %p %PAM/PM and am/pm
    %f %.f %.Nf %Nf %Nfractional second, dot or not, N digits or auto
    %sseconds since the epoch
    %z %:z %::z %:::z %#z %Ez %Ozoffset, with and without colons
    %ZUTC, UTC±HH:MM for a fixed offset, or nothing when there is no zone
    %c %x %X %D %F %T %R %r %v %+the composite forms
    %n %t %%a newline, a tab, a per cent sign

    A moment with no date writes its date directives as 1 January 1900, as Python's time.strftime and C's struct tm do, so a bare time still formats.

    Moment::time

    fn Moment::time(self : Moment) -> Time

    The time out of a moment.

    Midnight for a bare date, which is the time it names.

    Moment::timestamp

    fn Moment::timestamp(self : Moment) -> Double?

    Seconds since the epoch as a Double, fraction included.

    Moment::timetuple

    fn Moment::timetuple(self : Moment) -> (Int, Int, Int, Int, Int, Int, Int, Int, Int)

    The nine fields of a broken-down time, in the usual order: year, month, day, hour, minute, second, weekday counted from Monday as 0, day of the year, and whether daylight saving applies — always -1 here, because that needs a zone database this package does not carry.

    Moment::to_iso_string

    fn Moment::to_iso_string(self : Moment) -> String

    The Date.prototype.toISOString shape: always the UTC instant, always three fractional digits, always a trailing Z.

    A moment with no zone has no instant of its own, so its wall clock is taken as UTC — the same convention [Moment::strftime] uses for a bare time.

    Moment::to_repr

    Moment::to_string

    fn Moment::to_string(self : Moment) -> String

    Moment::to_utc_string

    fn Moment::to_utc_string(self : Moment) -> String raise Refused

    The Date.prototype.toUTCString shape: Tue, 22 Sep 2026 05:30:05 GMT.

    This is the HTTP date form [Moment::http_text] already writes, on the UTC instant, which is exactly what ECMAScript's toUTCString produces.

    Moment::utcoffset

    fn Moment::utcoffset(self : Moment) -> Span?

    How far the moment's clock is from UTC.

    Moment::weekday

    fn Moment::weekday(self : Moment) -> Int?

    The day of the week of the moment's date, Sunday being 0.

    Moment::zone

    fn Moment::zone(self : Moment) -> Zone?

    The zone a moment carries, or None when it carries none.

    Span

    pub(all) struct Span {
    nanos : Int64
    } derive(Compare, Eq,
    Debug
    )

    A length of time, to the nanosecond, with no calendar attached.

    A span is not a number of months: a month is not a fixed length, so adding one is a calendar operation (Date::plus_months) rather than arithmetic.

    Span::abs

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

    The span with its sign dropped.

    Span::add

    fn Span::add(self : Span, other : Span) -> Span

    Two spans added.

    Span::compare

    fn Span::compare(Span, Span) -> Int

    Span::equal

    fn Span::equal(Span, Span) -> Bool

    Span::neg

    fn Span::neg(self : Span) -> Span

    The span negated.

    Span::new

    fn Span::new(days? : Int64, hours? : Int64, minutes? : Int64, seconds? : Int64, millis? : Int64, micros? : Int64, nanos? : Int64) -> Span

    A span made of the usual parts: days, hours, minutes, seconds and fractions.

    Span::not_equal

    fn Span::not_equal(x : Span, y : Span) -> Bool

    Span::of

    fn Span::of(unit : Span, n : Int64) -> Span

    A span of n of these.

    Span::op_ge

    fn Span::op_ge(x : Span, y : Span) -> Bool

    Span::op_gt

    fn Span::op_gt(x : Span, y : Span) -> Bool

    Span::op_le

    fn Span::op_le(x : Span, y : Span) -> Bool

    Span::op_lt

    fn Span::op_lt(x : Span, y : Span) -> Bool

    Span::parts

    fn Span::parts(self : Span) -> (Int, Int, Int)

    The whole days in a span, and the seconds and nanoseconds left over — the three fields a duration is usually stored in.

    Span::seconds

    fn Span::seconds(self : Span) -> Int64

    The span as whole seconds, rounded towards zero.

    Span::sub

    fn Span::sub(self : Span, other : Span) -> Span

    One span less another.

    Span::to_repr

    Span::total_seconds

    fn Span::total_seconds(self : Span) -> Double

    The span as seconds, fraction included.

    Time

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

    A time on a twenty-four hour clock, to the nanosecond.

    A second of 60 is allowed: RFC 3339 §5.6 permits it for a leap second, and a timestamp that records one is a real timestamp that a reader should not throw away.
    impl Show for Time

    Time::compare

    fn Time::compare(Time, Time) -> Int

    Time::equal

    fn Time::equal(Time, Time) -> Bool

    Time::nanos

    fn Time::nanos(self : Time) -> Int64

    Nanoseconds since midnight.

    Time::new

    fn Time::new(hour : Int, minute : Int, second? : Int, nano? : Int) -> Time raise Refused

    A time, checked against the clock.

    Time::not_equal

    fn Time::not_equal(x : Time, y : Time) -> Bool

    Time::op_ge

    fn Time::op_ge(x : Time, y : Time) -> Bool

    Time::op_gt

    fn Time::op_gt(x : Time, y : Time) -> Bool

    Time::op_le

    fn Time::op_le(x : Time, y : Time) -> Bool

    Time::op_lt

    fn Time::op_lt(x : Time, y : Time) -> Bool

    Time::output

    fn Time::output(self : Time, logger : &Logger) -> Unit

    Time::replace

    fn Time::replace(self : Time, hour? : Int, minute? : Int, second? : Int, nano? : Int) -> Time raise Refused

    The same time with some parts replaced.

    Time::to_repr

    Time::to_string

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

    Zone

    pub(all) enum Zone {
    Utc
    Offset(Int)
    } derive(Eq,
    Debug
    )

    How far the clock is from UTC, in minutes east of it.

    Utc and Offset(0) are both midnight-at-Greenwich, but they are not the same thing to write down: RFC 3339 §4.3 gives -00:00 to a timestamp whose offset is unknown, and Z to one that is genuinely UTC.

    Zone::equal

    fn Zone::equal(Zone, Zone) -> Bool

    Zone::not_equal

    fn Zone::not_equal(x : Zone, y : Zone) -> Bool

    Zone::to_repr

    ANSIC

    let ANSIC : String

    The predefined layouts Go's time package ships with, so a caller need not spell the reference time out by hand.

    DateOnly

    let DateOnly : String

    DateTime

    let DateTime : String

    Kitchen

    let Kitchen : String

    RFC1123

    let RFC1123 : String

    RFC1123Z

    let RFC1123Z : String

    RFC3339

    let RFC3339 : String

    RFC3339Nano

    let RFC3339Nano : String

    RFC822

    let RFC822 : String

    RFC822Z

    let RFC822Z : String

    RFC850

    let RFC850 : String

    RubyDate

    let RubyDate : String

    Stamp

    let Stamp : String

    StampMicro

    let StampMicro : String

    StampMilli

    let StampMilli : String

    StampNano

    let StampNano : String

    TimeOnly

    let TimeOnly : String

    UnixDate

    let UnixDate : String

    asn1

    fn asn1(src : StringView) -> Moment raise Refused

    An ASN.1 UTCTime or GeneralizedTime (ITU-T X.680 §47), as DER writes them.

    This is how a certificate spells its validity. DER requires the Z and whole seconds; UTCTime's two-digit year is read as RFC 5280 §4.1.2.5.1 says — 49 and under are 2000s, 50 and over are 1900s, which is not the same rule HTTP uses.

    day

    let day : Span

    end_of_day

    let end_of_day : Time

    hour

    let hour : Span

    http

    fn http(src : StringView) -> Moment raise Refused

    An HTTP date read (RFC 9110 §5.6.7).

    The preferred IMF-fixdate, and the two obsolete forms a recipient must still accept: RFC 850's Sunday, 06-Nov-94 08:49:37 GMT and asctime's Sun Nov 6 08:49:37 1994. A two-digit year is read as RFC 6265 §5.1.1 says: 69 and under are 2000s, 70 and over are 1900s.

    leap

    fn leap(year : Int) -> Bool

    Whether year has a twenty-ninth of February.

    Every fourth year, except every hundredth, except every four-hundredth — the rule that makes the calendar year 365.2425 days long.

    length

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

    How many days month has in year.

    max_date

    let max_date : Date

    max_year

    let max_year : Int

    midnight

    let midnight : Time

    Midnight, and the last representable moment of a day.

    min_date

    let min_date : Date

    The earliest and latest dates this calendar writes with a four-digit year.

    They are 1-01-01 and 9999-12-31, which is as far as a four-digit year writes; the arithmetic here works outside that range, so these are a convention for writing rather than a limit on computing.

    min_year

    let min_year : Int

    The years this calendar spans.

    minute

    let minute : Span

    nanosecond

    let nanosecond : Span

    Nanoseconds in a second, a minute, an hour and a day.

    of_isoformat

    fn of_isoformat(src : StringView) -> Moment raise Refused

    A moment read from ISO 8601 text, which is what [parse] does.

    parse

    fn parse(src : StringView) -> Moment raise Refused

    A moment read from RFC 3339 text, in whichever of the four shapes it is written.

    2026-09-22T14:30:00Z and …+08:00 are Zoned; without an offset it is Plain; a bare 2026-09-22 is Day and a bare 14:30:00 is Clock. TOML 1.0.0 names these four and allows a space in place of the T, which is what a person writes; RFC 3339 §5.6 allows it too, under NOTE.

    parse_layout

    fn parse_layout(src : StringView, layout : StringView) -> Moment raise Refused

    A moment read by a Go reference-time layout.

    The layout is the pattern the text must follow: each field token pulls its value out, and literal characters — including a layout space, which swallows any run of whitespace — must match. A layout that names an offset yields a Zoned moment; one that does not yields Plain, Day or Clock, as [parse] does.

    parse_pattern

    fn parse_pattern(src : StringView, pattern : StringView) -> Moment raise Refused

    A moment read by an LDML date pattern.

    The inverse of [Moment::pattern] for the field letters that carry a value: years, months (numeric or named), days, hours, minutes, seconds, fractions and zone offsets. A pattern that names an offset yields Zoned; one that does not yields Plain, Day or Clock.

    resolution

    let resolution : Span

    The smallest difference two moments can have.

    A nanosecond rather than a microsecond: the clock here is finer, which is what a protocol timestamp and a benchmark both want.

    second

    let second : Span

    strptime

    fn strptime(src : StringView, format : StringView) -> Moment raise Refused

    A moment read by a format string of % directives.

    The directives are the ones [Moment::strftime] writes. Literal characters in the format must match the text exactly, except that whitespace in the format matches any run of whitespace, which is what C's strptime does and what makes a format survive a double space.

    The result is Zoned when the text carried an offset or a raw %s, Plain when it carried a date and a time, Day when only a date, and Clock when only a time — the same four shapes [parse] answers with.

    Powered by MoonBit

    Site sourceReport issueModulesSkillsBuild queueStatistics

    © 2026 mooncakes.io