The Orthodox calendar
Cruft ships Eastern Orthodox reckoning as two separate surfaces. The `orthodox` Temporal calendar gives Julian ("Old Calendar") civil dates, and `Temporal.Orthodox` is a namespace that computes Pascha, the Twelve Great Feasts, and the Anno Mundi era. This page enumerates both.
Under development. Theorthodoxcalendar and theTemporal.Orthodoxnamespace are young and still moving. The surface described here can change between releases, so pin your Cruft version if you depend on it, and do not treatTemporal.Orthodoxas a stable or portable API.
Cruft exposes Eastern Orthodox reckoning through two surfaces that do not overlap. The first is a calendar: orthodox is a Temporal calendar id that reports civil dates on the Julian ("Old Calendar") reckoning, the thirteen-day-shifted system Orthodox liturgical life still runs on. The second is Temporal.Orthodox, a namespace that computes the things a calendar alone cannot: Pascha (Orthodox Easter), the Twelve Great Feasts, the Anno Mundi era, and conversion to and from raw Julian dates.
The split matters, because each surface answers a different question. The calendar answers "what Julian date is this civil day"; the namespace answers "when is Pascha this year, and what feasts fall on this day." Nothing flows between them: the orthodox calendar carries no era and no feast data, and Temporal.Orthodox returns ordinary civil Temporal.PlainDates, not orthodox-calendar dates.
The orthodox calendar
orthodox is a first-class Temporal calendar id. It appears in the supported-calendar list and has an English display name:
Intl.supportedValuesOf("calendar").includes("orthodox"); // true
new Intl.DisplayNames("en", { type: "calendar" }).of("orthodox"); // "Orthodox Calendar"
Under that id is the proleptic Julian calendar. A civil (Gregorian) day read in the orthodox calendar is its Julian equivalent, thirteen days earlier in the modern era:
const d = Temporal.PlainDate.from("2025-01-07").withCalendar("orthodox");
[d.year, d.month, d.day]; // [2024, 12, 25] — civil 7 January is Julian 25 December, the Nativity
(d.toString() reports 2025-01-07[u-ca=orthodox]: Temporal always shows the ISO day with the calendar annotation, and the Julian year-month-day lives in the fields.)
The leap rule is the pure Julian one, a leap year every fourth year with no century correction. That is what separates it from Gregorian: both 1900 and 2100 are leap years here, though neither is in the civil calendar.
Temporal.PlainDate.from({ year: 2100, month: 2, day: 29, calendar: "orthodox" }).daysInMonth; // 29
Temporal.PlainDate.from({ year: 2100, month: 1, day: 1, calendar: "orthodox" }).inLeapYear; // true
Because the correction is dropped, the gap between the two calendars widens over time: it is thirteen days now and becomes fourteen after the 2100 Julian-only leap day.
Fields
A Temporal.PlainDate in the orthodox calendar reports the standard field set, with the era and ISO-week fields absent:
| Field | Example ({ year: 2025, month: 2, day: 15 }) |
|---|---|
year | 2025 |
month | 2 |
day | 15 |
monthCode | M02 |
dayOfWeek | 5 |
dayOfYear | 46 |
daysInMonth | 28 |
daysInYear | 365 |
daysInWeek | 7 |
monthsInYear | 12 |
inLeapYear | false |
era | undefined |
eraYear | undefined |
weekOfYear | undefined |
yearOfWeek | undefined |
Year numbering is astronomical: year zero exists, and years run continuously into the negatives with no gap at the proleptic boundary. era and eraYear are always undefined; the Anno Mundi era lives on Temporal.Orthodox instead (below).
Months
Twelve months every year, with Gregorian month lengths. Only February differs, by the Julian leap rule. There are no leap months, so monthsInYear is always 12 and no M??L codes appear.
Supported Temporal types
Every Temporal type that takes a calendar accepts orthodox:
| Type | Accepts calendar: "orthodox" |
|---|---|
Temporal.PlainDate | yes |
Temporal.PlainDateTime | yes |
Temporal.PlainYearMonth | yes |
Temporal.PlainMonthDay | yes |
Temporal.ZonedDateTime | yes |
Temporal.Now.* (via .withCalendar) | yes |
Arithmetic and conversion
Date arithmetic and durations work as on any calendar, following the Julian leap rule at the boundaries:
const d = Temporal.PlainDate.from({ year: 2025, month: 1, day: 1, calendar: "orthodox" });
d.until(Temporal.PlainDate.from({ year: 2026, month: 3, day: 15, calendar: "orthodox" }),
{ largestUnit: "years" }); // P1Y2M14D
Converting to another calendar is lossless in both directions. Converting to gregory is where the missing era becomes visible, because gregory does carry one:
const o = Temporal.PlainDate.from({ year: 2025, month: 1, day: 1, calendar: "orthodox" });
const g = o.withCalendar("gregory");
[g.year, g.month, g.day, g.era, g.eraYear]; // [2025, 1, 14, "ce", 2025]
Formatting
Intl.DateTimeFormat accepts calendar: "orthodox" and formats the Julian civil fields:
const d = Temporal.PlainDate.from({ year: 2025, month: 1, day: 1, calendar: "orthodox" });
new Intl.DateTimeFormat("en", { calendar: "orthodox", dateStyle: "full" }).format(d);
// "Tuesday, January 1, 2025"
resolvedOptions().calendar round-trips as "orthodox". Requesting an era in the format options produces no era text, because the calendar carries none.
Temporal.Orthodox
Temporal.Orthodox is a Cruft extension, not part of the ECMAScript Temporal proposal. Code that uses it runs on Cruft and will not run on other engines.
Temporal.Orthodox is a namespace object on Temporal. It is non-enumerable, so it does not appear in Object.keys(Temporal) or an object spread; reach it by name:
"Orthodox" in Temporal; // true
Its methods compute the Orthodox liturgical year and the Byzantine era. Every method that returns a date returns a civil (Gregorian) Temporal.PlainDate, the date you would read off a wall calendar, not an orthodox-calendar date.
Pascha
pascha(year) computes Orthodox Easter for a civil year, using the Julian paschalion (a nineteen-year lunar cycle and the rule "the first Sunday after the ecclesiastical full moon after the Julian March-21 equinox"), lifted to the civil date:
Temporal.Orthodox.pascha(2024).toString(); // "2024-05-05"
Temporal.Orthodox.pascha(2025).toString(); // "2025-04-20"
Temporal.Orthodox.pascha(2026).toString(); // "2026-04-12"
The Twelve Great Feasts
greatFeasts(year) returns the twelve principal feasts of the liturgical year, each as a record carrying its rank, whether it is a fixed Julian date or reckoned from Pascha, and both its Julian and civil dates:
Temporal.Orthodox.greatFeasts(2025)[0];
// {
// id: "nativity-theotokos",
// name: "Nativity of the Theotokos",
// rank: "great-feast",
// kind: "fixed-julian",
// julian: { year: 2025, month: 9, day: 8, calendar: "julian" },
// date: <Temporal.PlainDate 2025-09-21>
// }
The twelve, seven fixed on the Julian calendar and three reckoned from Pascha (with Transfiguration and Dormition also fixed):
Individual feasts
Named helpers return single feasts. The fixed feasts are Julian dates lifted to civil; the movable ones are offsets from Pascha:
Temporal.Orthodox.nativity(2025).toString(); // "2026-01-07" (25 December Julian)
Temporal.Orthodox.theophany(2025).toString(); // "2025-01-19"
Temporal.Orthodox.annunciation(2025).toString();// "2025-04-07"
Temporal.Orthodox.ascension(2025).toString(); // "2025-05-29" (Pascha + 39)
Temporal.Orthodox.pentecost(2025).toString(); // "2025-06-08" (Pascha + 49)
feast(name, year) looks a feast up by id, feastsOn(dateLike) returns the feasts that fall on a civil date, fixedFeast(year, month, day) lifts an arbitrary Julian date to civil, and movableFeast(year, offsetDays) returns a Pascha-relative date.
Temporal.Orthodox.feastsOn("2025-01-07").map(f => f.name); // ["Nativity of Christ"]
The Anno Mundi era
The Byzantine "era of creation" is anchored at 1 September 5509 BC on the Julian calendar, with the era year turning each 1 September. annoMundi(dateLike) takes a date and returns its Anno Mundi year; annoDominiLabel(dateLike) returns the Anno Domini label:
Temporal.Orthodox.annoMundi("2025-01-07"); // 7533
Temporal.Orthodox.annoMundi("2025-09-21"); // 7534 (crosses the 1 September boundary)
Temporal.Orthodox.annoDominiLabel("2025-01-07"); // { era: "ad", year: 2024 }
Julian conversion
toJulian and fromJulian cross between a civil Temporal.PlainDate and a plain Julian year-month-day record:
Temporal.Orthodox.toJulian(Temporal.PlainDate.from("2025-01-07"));
// { year: 2024, month: 12, day: 25, calendar: "julian" }
Temporal.Orthodox.fromJulian(2024, 12, 25).toString(); // "2025-01-07"
The calendar: "julian" label in that record is a plain data field; there is no julian calendar id to construct with (see Limitations).
Method reference
| Method | Arguments | Returns |
|---|---|---|
pascha | year | civil PlainDate of Orthodox Easter |
greatFeasts | year | array of the twelve Great Feast records |
feast | name, year | one feast record (name must be a string) |
feastsOn | dateLike | array of feasts on that civil date |
fixedFeast | year, month, day | a Julian date lifted to civil |
movableFeast | year, offsetDays | a Pascha-relative civil date |
nativity | year | civil PlainDate (25 December Julian) |
theophany | year | civil PlainDate |
annunciation | year | civil PlainDate |
ascension | year | civil PlainDate (Pascha + 39) |
pentecost | year | civil PlainDate (Pascha + 49) |
annoMundi | dateLike | Anno Mundi year (number) |
annoDominiLabel | dateLike | { era, year } |
toJulian | dateLike | { year, month, day, calendar } |
fromJulian | year, month, day | civil PlainDate |
Limitations
- No era on the calendar surface.
eraanderaYearare alwaysundefinedon anorthodox-calendar date, and a date cannot be constructed from{ era, eraYear }(a numericyearis required). The Anno Mundi era is reachable only throughTemporal.Orthodox.annoMundiandannoDominiLabel. - No era in formatted output.
Intl.DateTimeFormatemits no era text fororthodox, even when the format options request one. - Display name is English-only.
Intl.DisplayNamesreturns "Orthodox Calendar" in English; other locales fall back to the raworthodoxid. - No
juliancalendar id.withCalendar("julian")throwsinvalid calendar identifier. The Julian reckoning is reachable asorthodox, and the string"julian"appears only as a label insidetoJulian's return record. - ISO-week fields are absent.
weekOfYearandyearOfWeekareundefined, as for every non-ISO calendar. Temporal.Orthodoxis non-standard and non-enumerable. It is a Cruft extension, invisible toObject.keysand spreads, and discoverable only by name.