Skip to content

Calendars

zdate ships 18 calendar engines with conversion and arithmetic: solar, lunar, and lunisolar.

iso8601, gregory, buddhist, japanese, roc, indian, persian, coptic, ethiopic, ethioaa, hebrew, islamic-civil, islamic-tbla, islamic, islamic-rgsa, islamic-umalqura, chinese, dangi

A calendar is a view concern. with() returns a new instance carrying the same epoch with different view options:

ts
const d = zdate('2024-01-15T12:00:00Z')
const h = d.with({calendar: 'hebrew'})

h.utc.year                               // 5784
h.epoch === d.epoch                      // true — same instant, different projection
d.with({calendar: 'japanese'}).utc.era   // 'Reiwa'

Calendar-Aware Arithmetic

by() and to() operate in the view's calendar. Adding a month in the Hebrew calendar respects Hebrew month lengths and leap months; moving to 'start of year' in the Persian calendar lands on Nowruz.

ts
const h = zdate({year: 5784, month: 5, day: 5, calendar: 'hebrew'}, {calendar: 'source'})
h.by({months: 1})        // Hebrew month arithmetic
h.to('start of year')    // Hebrew new year

Construction from Calendar Fields

The calendar property inside the fields names the calendar the fields are expressed in. Without it, fields are ISO 8601:

ts
zdate({year: 1445, month: 7, day: 4, calendar: 'islamic-umalqura'})
zdate({year: 2024, month: 1, day: 15})   // iso8601

The fields' calendar and the view's calendar are separate concerns. The fields' calendar resolves the instant; the view option controls how the result is read. Pass {calendar: 'source'} as the view options to adopt the fields' calendar for the views.

The era field is required for gregory, japanese, roc, ethiopic, and coptic; it is ignored for all other calendars.

RFC 9557 Suffix Calendars

A [u-ca=...] suffix is captured on parsedSuffix and adopted as the view calendar only when requested with 'source' — the same rule as a suffix zone:

ts
const d = zdate('2024-01-15T12:00:00Z[u-ca=hebrew]', {calendar: 'source'})
d.utc.year               // 5784
d.parsedSuffix?.calendar // 'hebrew', with or without 'source'

An unknown non-critical suffix calendar is ignored; an unknown critical one ([!u-ca=...]) throws.

Engine Notes

The engines implement their own conversion and arithmetic. Intl participates where it is the right boundary — reference data for observational calendars — with zdate's math and caching around it.

Every engine is verified by round-trip: Gregorian/ISO date to calendar date and back to Gregorian/ISO, sampled every 3 days across the tested range. See Round-Trip Verification.

CalendarSupported rangeNotes
All calendars-267,000 to +267,000Hard limit; zdate rejects dates outside it
japanesefrom 645 CEThe calendar starts at Taika 1; zdate rejects earlier dates because pre-645 Taika years are semantically questionable
chinese / dangi-30,000 to +65,000ICU errors internally for years outside this range

For the Japanese start boundary: Gregorian 0645-01-01 is still year 0 in the Japanese calendar (offset by Julian-Gregorian drift). The Japanese range begins at Gregorian 0645-01-04, which is Taika 0001-01-01.