Zoned
An instant. Has an epoch, and can be read in another zone.
Parse and format RFC 3339, HTTP and ASN.1 timestamps. Keep the four kinds of moment apart. Ask a cron expression when it fires next — every answer comes from the calendar's arithmetic, not a lookup table.
moon add moonbitstack/moondate
TOML names exactly these, and the difference is not decoration. Only Zoned is an instant; the other three need a zone before they can name one.
An instant. Has an epoch, and can be read in another zone.
An alarm, an opening time. No zone, so no epoch yet.
A date with no time of day.
A time with no date.
The calendar is the proleptic Gregorian one, exact over the whole range an integer holds. Everything else is about reading and writing it in the forms the wire expects.
Read a timestamp into one of four shapes, and write it back in the shape it was. A round trip through a document keeps every field.
Days, weekdays, year days, ISO week dates and ordinals. Add days or months and carry across boundaries without a table.
Write the one form a Date header may take, and read the two obsolete forms a recipient must still accept.
UTCTime and GeneralizedTime, with the two-digit-year window a certificate uses — which is not the window HTTP uses.
The C89 directives, in both directions, so a format string reads back exactly what it wrote.
Five fields, or six with seconds. Names, lists, ranges, steps and the shorthands — plus the answer to when it next fires.
| Form | Where it is used |
|---|---|
| RFC 3339 / ISO 8601 | JSON, TOML, and most of the wire |
| IMF-fixdate · RFC 850 · asctime | HTTP's Date header — all three read, IMF-fixdate written |
| UTCTime · GeneralizedTime | ASN.1, and so an X.509 certificate's validity |
| strftime / strptime | The C89 directives, both ways |
Five fields as crontab(5) defines them, or six with seconds in front. Both 0 and 7 are Sunday. Day-of-month and day-of-week are or-ed when both are restricted.
let now = @moondate.parse("2026-09-22T14:30:00Z")
@cron.parse("0 9 * * MON-FRI").next(now) // Some(2026-09-23T09:00:00Z)
@cron.parse("@daily").next(now) // Some(2026-09-23T00:00:00Z)