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. The orthodox calendar and the Temporal.Orthodox namespace 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 treat Temporal.Orthodox as 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:

FieldExample ({ year: 2025, month: 2, day: 15 })
year2025
month2
day15
monthCodeM02
dayOfWeek5
dayOfYear46
daysInMonth28
daysInYear365
daysInWeek7
monthsInYear12
inLeapYearfalse
eraundefined
eraYearundefined
weekOfYearundefined
yearOfWeekundefined

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.

Month codes
M01M02M03M04M05M06M07M08M09M10M11M12

Supported Temporal types

Every Temporal type that takes a calendar accepts orthodox:

TypeAccepts calendar: "orthodox"
Temporal.PlainDateyes
Temporal.PlainDateTimeyes
Temporal.PlainYearMonthyes
Temporal.PlainMonthDayyes
Temporal.ZonedDateTimeyes
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):

Fixed
Nativity of the TheotokosExaltation of the CrossEntrance of the Theotokos into the TempleNativity of ChristTheophanyPresentation of Christ in the TempleAnnunciationTransfigurationDormition of the Theotokos
Movable
Entry of the Lord into JerusalemAscensionPentecost

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

MethodArgumentsReturns
paschayearcivil PlainDate of Orthodox Easter
greatFeastsyeararray of the twelve Great Feast records
feastname, yearone feast record (name must be a string)
feastsOndateLikearray of feasts on that civil date
fixedFeastyear, month, daya Julian date lifted to civil
movableFeastyear, offsetDaysa Pascha-relative civil date
nativityyearcivil PlainDate (25 December Julian)
theophanyyearcivil PlainDate
annunciationyearcivil PlainDate
ascensionyearcivil PlainDate (Pascha + 39)
pentecostyearcivil PlainDate (Pascha + 49)
annoMundidateLikeAnno Mundi year (number)
annoDominiLabeldateLike{ era, year }
toJuliandateLike{ year, month, day, calendar }
fromJulianyear, month, daycivil PlainDate

Limitations

  • No era on the calendar surface. era and eraYear are always undefined on an orthodox-calendar date, and a date cannot be constructed from { era, eraYear } (a numeric year is required). The Anno Mundi era is reachable only through Temporal.Orthodox.annoMundi and annoDominiLabel.
  • No era in formatted output. Intl.DateTimeFormat emits no era text for orthodox, even when the format options request one.
  • Display name is English-only. Intl.DisplayNames returns "Orthodox Calendar" in English; other locales fall back to the raw orthodox id.
  • No julian calendar id. withCalendar("julian") throws invalid calendar identifier. The Julian reckoning is reachable as orthodox, and the string "julian" appears only as a label inside toJulian's return record.
  • ISO-week fields are absent. weekOfYear and yearOfWeek are undefined, as for every non-ISO calendar.
  • Temporal.Orthodox is non-standard and non-enumerable. It is a Cruft extension, invisible to Object.keys and spreads, and discoverable only by name.