How Temporal and Intl are built

How Cruft builds Temporal and Intl without bundling ICU. The calendar math and locale formatting are computed in its own Rust, and only the data that cannot be computed, locale tables, the time-zone database, and astronomical coefficients, is sourced from pinned, checksummed releases. The page walks the calendars, the eras, and that sourced data.

Most engines get calendars, time zones, and locale formatting the same way: they bundle ICU, a large C++ library that carries both the formatting algorithms and the reference data together. Cruft does not embed ICU. It implements the algorithms in its own Rust and sources only the data that cannot be computed, the locale tables, the time-zone database, and the astronomical coefficients, from authoritative origins, generating each into a pinned, verifiable table.

That split is the whole shape of how Temporal and Intl are made here. Everything that is an algorithm is computed in-tree. Everything that is a given, a value that exists in the world rather than falling out of a rule, is taken from its source and nothing more. There are two kinds of given, and they are treated the same way.

The astronomical calendars start with the physics

Some calendars are defined by the sky. A Chinese or Dangi (Korean) month begins at a new moon; its solar terms fall at fixed points of the sun's apparent longitude. Cruft does not ship a lookup table of those dates. It computes them, by determining when the new moon actually occurred and where the sun actually was.

Underneath that sits a real celestial-mechanics calculation: the sun's and moon's positions as Fourier sums of periodic terms, evaluated against the J2000 epoch and the mean tropical year, with the ΔT time-scale correction and the aberration/nutation adjustments an accurate position needs. All of that is algorithm, and all of it is implemented.

The only things taken as given are the periodic-term coefficients, the amplitude, phase, and frequency triples of the solar series (49 terms), the new-moon series, and the lunar longitude, latitude, and distance series. Those come from celestial mechanics, from the gravitationally-integrated ephemeris that no rule can reproduce, and Cruft sources them following the standard Reingold-Dershowitz calendrical algorithms (after Meeus). So a Chinese-calendar date computation starts with the physics coefficients and derives everything else.

Not every non-Gregorian calendar is astronomical. The Hebrew calendar shipped here is the arithmetic one, defined by fixed rules rather than observation, so it computes its months without touching the astronomy. The sky is consulted only for the calendars the sky actually defines.

The Julian calendar and the era of creation

Surfaced as the orthodox calendar. The Julian reckoning this section describes ships as the orthodox Temporal calendar, and the paschalion, the Twelve Great Feasts, and the Anno Mundi era are reachable through the Temporal.Orthodox namespace. The Orthodox calendar reference enumerates that surface. Two limits carry over from the model below: the calendar itself exposes no era (era and eraYear are undefined, and the Anno Mundi count lives on Temporal.Orthodox instead), and there is no separate julian or byzantine calendar id (withCalendar("julian") throws invalid calendar identifier). The rest of this section describes the model and the internal representation those surfaces are built on.

Eastern Orthodox reckoning runs on the Julian calendar (the "Old Calendar") and counts its years from the creation of the world. The intended model reckons both, and the part worth explaining is how those anchors are pinned, because the whole scheme rests on one representational choice.

Every date here is expressed as a rata die, a plain running count of days from a fixed origin, independent of any calendar's year numbering. A calendar is then just a rule for turning a rata die into a year-month-day and back. The Julian rule is the simple one, a leap year every fourth year, and it is the base the Julian family (Gregorian, Coptic, Ethiopic, and the rest) is defined against. Because the anchors are rata dies, they survive whatever numbering convention sits on top.

The one convention Cruft does commit to is astronomical year numbering, in which year zero exists (1 BC is year 0). That single choice is what makes the era anchors line up cleanly:

EpochAnchored at
Creation (Anno Mundi)1 September 5509 BC, Julian, the year turning each 1 September
Incarnation (Anno Domini)1 January AD 1, Julian (the Dionysian definition)
Modern civil1 January AD 1, proleptic Gregorian

The creation is pinned to 1 September 5509 BC on the Julian calendar and anchored at year zero, so the Anno Mundi count is a true zero origin. This is only clean because of astronomical numbering: 5509 BC is Julian year −5508, and the count runs continuously across the BC/AD boundary with no gap. The traditional no-year-zero convention would put a one-year discontinuity right at the origin; admitting year zero removes it. Anno Domini is then the second epoch marked within that same timeline, the Dionysian AD 1 falling in Anno Mundi 5509, a fixed offset of 5508 years, so any day can be read from the creation or from the Incarnation and the two differ only by that constant.

Pascha (Orthodox Easter) is computed, not tabulated: the Julian paschalion runs a 19-year lunar cycle and the rule "the first Sunday after the ecclesiastical full moon after the Julian March-21 equinox," producing a Julian-calendar date that is then lifted through its rata die to the civil (Gregorian) date you would read off a wall calendar. Fixed feasts work the same way, which is why the Nativity on 25 December (Julian) lands on 7 January civil, the thirteen-day Julian gap made concrete. The computed dates match the published Orthodox dates for the coming decades. The paschalion and the Hebrew calendar are, underneath, two settings of the same 19-year lunisolar cycle, so the same kernel that places Pascha also places the Hebrew molad.

The clock and the calendars answer to different origins

None of this replaces the epoch the industry counts from. Cruft's observable clock is the Unix epoch, midnight on 1 January 1970 UTC, exactly as the specification requires: a Temporal.Instant reports its epochMilliseconds and epochNanoseconds from 1970, and Date behaves identically. The Julian calendar, the rata die, and the eras above sit underneath that, and are bridged to 1970 at the boundary. The era of creation is never the runtime's clock; it is one way to name a day, offered alongside the industry epoch, not a substitute for it.

The two origins exist because they answer different questions. A timekeeping epoch answers "how long since a fixed instant," and 1970 is as good an instant as any. A calendar has to answer something else: "what year, month, and day is this in this calendar, and what is the same day in another." That needs a single calendar-neutral running day count that every calendar's rules, its leap years, month lengths, and era boundaries, can be defined against, and that extends by rule across all of recorded history, including the centuries before 1970 and before AD

  1. The rata die is that day count. Its origin sits near AD 1 for arithmetic convenience rather than for meaning, and 1970 is simply one more day on it.

Given that day count, three choices follow, and each is made for a plain reason:

  • The Julian calendar is the base because it is the common ancestor of a whole family. The Gregorian calendar is the Julian calendar minus the century-leap correction; the Coptic and Ethiopic calendars are Julian-family siblings. Defining the Julian rule once and deriving the others is the smallest correct construction. It is also the historically accurate one: every date before the 1582 Gregorian reform was Julian, and Orthodox liturgical dates still are, so a runtime that wants those dates right needs the real Julian calendar, not Gregorian reaching into a period it never governed.
  • Year zero exists (astronomical numbering) so the eras run continuously across the BC/AD boundary instead of jumping from 1 BC straight to AD 1. That removes a class of off-by-one era bugs, and it is what lets the era of creation be a genuine zero origin rather than one with a one-year seam at the start.
  • The era of creation is included because it is the native era of the Orthodox/Byzantine reckoning, the calendar system the paschalion belongs to. Once the day count and year-zero numbering are in place, expressing that era costs almost nothing and reads cleanly, so it is offered alongside Anno Domini as another address on the same day line.

Intl's locale data is sourced and pinned

Intl's givens are reference data rather than physics, and they get the same treatment. The formatting logic, NumberFormat, DateTimeFormat, DisplayNames, and the rest, is Cruft's own; the tables those formatters read are generated from the canonical data projects:

CLDR (currencies, display names, supported values)
cldr-core 48.2
IANA time-zone database (canonical zones)
tzdata 2026c

Each generated table records the exact source it came from and a checksum of both the input and the emitted output. Because the source version is pinned and the output is hashed, the tables are reproducible and provably match the standard they came from: regenerating from the same CLDR or tzdata release produces byte-for-byte the same Rust. The data is never hand-transcribed, so it cannot quietly drift from the standard, and you can always name exactly which release the runtime tracks.

Why compute instead of bundle

Bundling ICU would hand you the algorithms and the data in one dependency, at the cost of a large library carrying far more than any one program uses, and a data surface no one on the project could fully account for. Computing the algorithms and sourcing only the irreducible data keeps the runtime small and keeps the story whole: the calendar math and the formatting logic are code Cruft maintains, and the data is a small set of tables pinned to named, checksummed releases. It is the same principle as the rest of owning the stack, and the same instinct as Nativo, source the one thing you cannot derive, and derive the rest.

Limitations

  • Intl is a selected slice of ECMA-402, not full ICU. The locale coverage is the currencies, time zones, and display names generated from the pinned CLDR and tzdata releases, not the entire CLDR surface an ICU build carries. Locales and fields outside that slice are not present.
  • The pinned data must be refreshed to stay current. The tables track specific releases (CLDR 48.2, tzdata 2026c). New currencies, renamed zones, and DST-rule changes land only when those sources are updated and the tables regenerated.
  • The astronomical calendars are civil-grade, not an ephemeris. The periodic series are the standard truncated sets for calendrical use. They place calendar dates correctly, but this is not an astronomy-grade ephemeris for high-precision positional work.
  • Astronomical coverage is the lunisolar set. The sky-driven computation backs the Chinese and Dangi calendars; other non-Gregorian calendars here are arithmetic (rule-based), and the astronomy is not exposed as a general API.
  • Cruft is 0.0.10. Both surfaces are young; treat coverage as expanding rather than complete, and check the Temporal and Intl pages for what is actually present.