# `Calendrical.Era`

Era arithmetic for CLDR calendars.

Era definitions come from the [era data in CLDR](https://github.com/unicode-org/cldr/blob/main/common/supplemental/supplementalData.xml) 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.

# `calendar`

```elixir
@type calendar() :: module()
```

A calendar module.

# `cldr_calendar_type`

```elixir
@type cldr_calendar_type() :: atom()
```

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

# `cyclic_year`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@spec era_year(cldr_calendar_type(), integer(), Calendar.year(), boolean()) ::
  {Calendar.year(), Calendar.era()}
```

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`

```elixir
@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

# `related_gregorian_year`

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

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.

### 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.related_gregorian_year(Calendrical.Hebrew, 5786, 1, 1)
    2025

    iex> Calendrical.Era.related_gregorian_year(Calendrical.Gregorian, 2026, 6, 1)
    2026

# `year_of_era`

```elixir
@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`

```elixir
@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`

```elixir
@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}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
