Calendrical.Era (Calendrical v2.2.0)

Copy Markdown

Era arithmetic for CLDR calendars.

Era definitions come from the era data in CLDR as Localize publishes it, where every era boundary is a proleptic Gregorian date. CLDR 49 carries the Japanese eras from Meiji onwards only; Localize supplies the earlier ones from its curated set, converted from their lunisolar proclamation dates. On first access the boundaries for a calendar type are resolved to ISO day counts and cached in :persistent_term, so lookups are lock-free and copy-free with no compile-time code generation.

Two year-numbering styles cover all CLDR calendars:

  • Most calendars number their years from the epoch of their primary era, so the year of era is the calendar year itself ({year, era}). Years at or before zero belong to a "before" era when CLDR defines one (BCE, before ROC, before Hijra) and count backwards: year 0 is year 1 of that era.

  • The Japanese calendar numbers years in Gregorian years while eras begin mid-year, so the era is selected by the date and the year of era is counted from each era's first Gregorian year.

A before era's days count back from its last day, as Calendar.ISO counts the days before the common era. A date before a calendar's first era, which TR35 gives no era, belongs to the first era counted back from its start: the year before its first is year 0, as ICU numbers the Japanese years before Taika, and the day before its first is day 0.

A calendar of one era with no before era, such as the Buddhist, Persian and Indian calendars, numbers the years before its year 1 as 0, -1 and so on in that era, as ICU does, where Calendar.ISO counts them in a second era.

Summary

Types

A calendar module.

A CLDR calendar type, such as :gregorian or :persian.

Functions

Returns the cyclic year of a date: its place in the cycle its calendar's info/0 names, reckoned as the East Asian sexagenary cycle is from the related Gregorian year, or its year in a calendar whose years run in no cycle.

Returns the day of era and era number for a date.

Returns the day of era and era of a date of a calendar, as the calendar answers them: by its own day_of_era/3 where it defines one, and otherwise by the era calendar its info/0 names (the member's, in a composite calendar).

Returns the resolved era data for a CLDR calendar type.

Returns the year of era and era number for a date, by a calendar's year-zero rule: year_of_era/3 with the rule of a calendar that has a year 0.

Returns the extended year of a date: its year, numbered without a gap at 0 in a calendar that has no year 0, so 1 BC is 0 and 2 BC -1.

Returns the related Gregorian year of a date: the Gregorian year its calendar year begins in, as TR35 defines it, the same for every date of the year.

Returns the year of era and era of a year of a calendar: of its first day, by the era calendar its info/0 names and its year-zero rule.

Returns the year of era and era number for a date.

Returns the year of era and era of a date of a calendar, as the calendar answers them: by its own year_of_era/3 where it defines one, and otherwise by the era calendar its info/0 names (the member's, in a composite calendar) and its year-zero rule. A date with no month or day is taken on the first day of its year.

Types

calendar()

@type calendar() :: module()

A calendar module.

cldr_calendar_type()

@type cldr_calendar_type() :: atom()

A CLDR calendar type, such as :gregorian or :persian.

Functions

cyclic_year(calendar, year, month, day)

@spec cyclic_year(calendar(), Calendar.year(), Calendar.month(), Calendar.day()) ::
  integer() | {:error, Exception.t()}

Returns the cyclic year of a date: its place in the cycle its calendar's info/0 names, reckoned as the East Asian sexagenary cycle is from the related Gregorian year, or its year in a calendar whose years run in no cycle.

Arguments

  • calendar is a calendar module.

  • year, month and day are a date of the calendar.

Returns

  • An integer, or {:error, %Calendrical.InvalidDateError{}} for a year the calendar does not have, or {:error, Calendrical.MissingFieldsError.t()} for no year.

Examples

iex> Calendrical.Era.cyclic_year(Calendrical.Chinese, 4662, 1, 1)
42

iex> Calendrical.Era.cyclic_year(Calendrical.Gregorian, 2026, 1, 1)
2026

day_of_era(cldr_calendar_type, iso_days)

@spec day_of_era(cldr_calendar_type(), integer()) :: {integer(), Calendar.era()}

Returns the day of era and era number for a date.

Arguments

  • cldr_calendar_type is the CLDR calendar type of the calendar, such as :persian or :japanese.

  • iso_days is the date as a count of days since the proleptic ISO epoch.

Returns

  • A two-tuple {day_of_era, era} where day_of_era counts from the era's first day, or back from a before era's last day. A date before the first era counts back from its start, so its day_of_era is 0 or less.

Examples

iex> iso_days = Date.to_gregorian_days(~D[2019-05-01])
iex> Calendrical.Era.day_of_era(:japanese, iso_days)
{1, 236}

iex> iso_days = Date.to_gregorian_days(~D[1911-12-31])
iex> Calendrical.Era.day_of_era(:roc, iso_days)
{1, 0}

day_of_era(calendar, year, month, day)

@spec day_of_era(calendar(), Calendar.year(), Calendar.month(), Calendar.day()) ::
  {integer(), Calendar.era()} | {:error, Exception.t()}

Returns the day of era and era of a date of a calendar, as the calendar answers them: by its own day_of_era/3 where it defines one, and otherwise by the era calendar its info/0 names (the member's, in a composite calendar).

Arguments

  • calendar is a calendar module.

  • year, month and day are a date of the calendar.

Returns

  • {day_of_era, era}, or {:error, %Calendrical.InvalidDateError{}} for a date the calendar does not have, or {:error, Calendrical.MissingFieldsError.t()} for a missing field.

Examples

iex> Calendrical.Era.day_of_era(Calendrical.Gregorian, 1, 1, 1)
{1, 1}

iex> Calendrical.Era.day_of_era(Calendrical.Japanese, 2019, 5, 1)
{1, 236}

era_data(cldr_calendar_type)

@spec era_data(cldr_calendar_type()) :: map()

Returns the resolved era data for a CLDR calendar type.

The data is computed on first access and cached in :persistent_term.

Arguments

  • cldr_calendar_type is the CLDR calendar type of the calendar, such as :persian or :japanese.

Returns

  • A map with the era records (most recent era first), the year-numbering mode and the forward/before era numbers.

Raises Localize.UnknownCalendarError for a type CLDR does not define.

Examples

iex> era_data = Calendrical.Era.era_data(:japanese)
iex> era_data.year_mode
:gregorian_offset

era_year(cldr_calendar_type, iso_days, year, year_zero?)

Returns the year of era and era number for a date, by a calendar's year-zero rule: year_of_era/3 with the rule of a calendar that has a year 0.

Arguments

  • cldr_calendar_type is the CLDR calendar type of the calendar, such as :persian or :japanese.

  • iso_days is the date as a count of days since the proleptic ISO epoch.

  • year is the year of the date in the calendar's own numbering.

  • year_zero? is whether the calendar has a year 0: where it has, year 0 is year 1 of the era before, and where it has none, year -1 is.

Returns

  • A two-tuple {year_of_era, era}.

Examples

iex> Calendrical.Era.era_year(:gregorian, 0, -1, true)
{2, 0}

iex> Calendrical.Era.era_year(:gregorian, 0, -1, false)
{1, 0}

extended_year(calendar, year, month, day)

@spec extended_year(calendar(), Calendar.year(), Calendar.month(), Calendar.day()) ::
  integer() | {:error, Exception.t()}

Returns the extended year of a date: its year, numbered without a gap at 0 in a calendar that has no year 0, so 1 BC is 0 and 2 BC -1.

Arguments

  • calendar is a calendar module.

  • year, month and day are a date of the calendar.

Returns

  • An integer, or

  • {:error, %Calendrical.InvalidDateError{}} for a date the calendar does not have, or

  • {:error, %Calendrical.MissingFieldsError{}} for no year.

Examples

iex> Calendrical.Era.extended_year(Calendrical.Gregorian, -1, 1, 1)
-1

iex> Calendrical.Era.extended_year(Calendrical.Julian, -1, 1, 1)
0

year_of_era(calendar, year)

@spec year_of_era(calendar(), Calendar.year()) ::
  {Calendar.year(), Calendar.era()} | {:error, Exception.t()}

Returns the year of era and era of a year of a calendar: of its first day, by the era calendar its info/0 names and its year-zero rule.

Arguments

  • calendar is a calendar module.

  • year is a year of the calendar.

Returns

  • {year_of_era, era}, or {:error, %Calendrical.InvalidDateError{}} for a year the calendar does not have, or {:error, Calendrical.MissingFieldsError.t()} for no year.

Examples

iex> Calendrical.Era.year_of_era(Calendrical.Gregorian, -1)
{2, 0}

iex> Calendrical.Era.year_of_era(Calendrical.Julian, -1)
{1, 0}

year_of_era(cldr_calendar_type, iso_days, year)

@spec year_of_era(cldr_calendar_type(), integer(), Calendar.year()) ::
  {Calendar.year(), Calendar.era()}

Returns the year of era and era number for a date.

Arguments

  • cldr_calendar_type is the CLDR calendar type of the calendar, such as :persian or :japanese.

  • iso_days is the date as a count of days since the proleptic ISO epoch.

  • year is the year of the date in the calendar's own numbering.

Returns

  • A two-tuple {year_of_era, era}.

Examples

iex> iso_days = Date.to_gregorian_days(~D[2019-05-01])
iex> Calendrical.Era.year_of_era(:japanese, iso_days, 2019)
{1, 236}

iex> Calendrical.Era.year_of_era(:persian, 0, 1405)
{1405, 0}

# Taika, the first Japanese era, began in 645
iex> iso_days = Date.to_gregorian_days(~D[0600-01-01])
iex> Calendrical.Era.year_of_era(:japanese, iso_days, 600)
{-44, 0}

year_of_era(calendar, year, month, day)

@spec year_of_era(
  calendar(),
  Calendar.year(),
  Calendar.month() | nil,
  Calendar.day() | nil
) ::
  {Calendar.year(), Calendar.era()} | {:error, Exception.t()}

Returns the year of era and era of a date of a calendar, as the calendar answers them: by its own year_of_era/3 where it defines one, and otherwise by the era calendar its info/0 names (the member's, in a composite calendar) and its year-zero rule. A date with no month or day is taken on the first day of its year.

Arguments

  • calendar is a calendar module.

  • year, month and day are a date of the calendar.

Returns

  • {year_of_era, era}, or {:error, %Calendrical.InvalidDateError{}} for a date the calendar does not have, or {:error, Calendrical.MissingFieldsError.t()} for no year.

Examples

iex> Calendrical.Era.year_of_era(Calendrical.Japanese, 2025, 6, 1)
{7, 236}

iex> Calendrical.Era.year_of_era(Calendrical.Gregorian, 2025, nil, nil)
{2025, 1}