# `Calendrical.Julian`

Implements the proleptic Julian calendar.

The Julian calendar is the calendar introduced by Julius Caesar in
46 BCE and used throughout Europe until the Gregorian reform of
1582. It is identical in structure to `Calendrical.Gregorian` —
12 months, 365 or 366 days, the same month lengths — but uses a
simpler leap-year rule (every fourth year) and therefore drifts
about three days every 400 years against the tropical year.

This module is implemented as a macro module: `use Calendrical.Julian,
new_year_starting_month_and_day: {1, 1}` builds a Julian variant whose
year begins on the given month-and-day. The pre-built variants
`Calendrical.Julian.Jan1`, `Calendrical.Julian.March1`,
`Calendrical.Julian.March25`, `Calendrical.Julian.Sept1`, and
`Calendrical.Julian.Dec25` correspond to historical "year-style"
conventions used in different periods and regions.

The module itself is also a fully-functional calendar that can be
used directly (`~D[1500-03-15 Calendrical.Julian]`), in which case
the year begins on January 1.

# `day`

```elixir
@type day() :: 1..31
```

# `month`

```elixir
@type month() :: 1..12
```

# `year`

```elixir
@type year() :: -9999..-1 | 1..9999
```

# `calendar_base`

```elixir
@spec calendar_base() :: :month
```

Returns the CLDR calendar base for this calendar.

### Returns

* The atom `:month` (the Julian calendar is month-based).

### Examples

    iex> Calendrical.Julian.calendar_base()
    :month

# `calendar_year`

```elixir
@spec calendar_year(year(), month(), day()) :: Calendar.year()
```

Returns the calendar year as displayed on rendered calendars.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* The integer Julian year.

### Examples

    iex> Calendrical.Julian.calendar_year(2025, 1, 1)
    2025

# `cldr_calendar_type`

```elixir
@spec cldr_calendar_type() :: :gregorian
```

Returns the CLDR calendar type for this calendar.

This type is used in support of `Calendrical.localize/3`.

### Returns

* The atom `:gregorian` (the Julian calendar uses the Gregorian
  CLDR variant for month, era and other localised names).

### Examples

    iex> Calendrical.Julian.cldr_calendar_type()
    :gregorian

# `cyclic_year`

```elixir
@spec cyclic_year(year(), month(), day()) :: Calendar.year()
```

Returns the cyclic year as displayed on rendered calendars.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* The integer Julian year (the Julian calendar has no cyclic
  component).

### Examples

    iex> Calendrical.Julian.cyclic_year(2025, 1, 1)
    2025

# `date_from_iso_days`

```elixir
@spec date_from_iso_days(integer()) :: {year(), month(), day()}
```

Returns a Julian `{year, month, day}` tuple for the given ISO day
number.

### Arguments

* `iso_days` is an integer count of days since the proleptic
  ISO epoch.

### Returns

* A three-tuple `{year, month, day}` in the Julian calendar.

### Examples

    iex> Calendrical.Julian.date_from_iso_days(740_000)
    {2026, 1, 6}

# `date_to_iso_days`

```elixir
@spec date_to_iso_days(year(), month(), day()) :: integer()
```

Returns the number of ISO days for the given Julian `year`,
`month`, and `day`.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* An integer count of days since the proleptic ISO epoch.

### Examples

    iex> Calendrical.Julian.date_to_iso_days(2025, 1, 1)
    739630

# `day_of_era`

```elixir
@spec day_of_era(year(), month(), day()) :: {day :: pos_integer(), era :: 0..1}
```

Returns the day-of-era and era for the given Julian date.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* A two-tuple `{day_in_era, era}` where `era` is `0` or `1`.

### Examples

    iex> Calendrical.Julian.day_of_era(2025, 1, 1)
    {739267, 1}

    iex> Calendrical.Julian.day_of_era(1, 1, 1)
    {1, 1}

# `day_of_week`

```elixir
@spec day_of_week(year(), month(), day(), 1..7 | :default) ::
  {Calendar.day_of_week(), first_day_of_week :: non_neg_integer(),
   last_day_of_week :: non_neg_integer()}
```

Returns the day-of-week for the given Julian date.

The day-of-week is an integer from 1 to 7, where 1 is Monday and 7
is Sunday.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

* `starting_on` is `:default` for the calendar's natural week
  boundary (Monday).

### Returns

* A three-tuple `{day_of_week, first_day_of_week, last_day_of_week}`.

### Examples

    iex> Calendrical.Julian.day_of_week(2025, 1, 1, :default)
    {2, 1, 7}

# `day_of_year`

```elixir
@spec day_of_year(year(), month(), day()) :: 1..366
```

Returns the day of the year (1-based) for the given Julian date.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* An integer in the range `1..366`.

### Examples

    iex> Calendrical.Julian.day_of_year(2025, 3, 1)
    60

# `days_in_month`

```elixir
@spec days_in_month(month()) :: Calendar.day() | {:error, :unresolved}
```

Returns the number of days in the given month.

Because February's length depends on whether the year is a leap
year, this arity-1 form returns `{:error, :unresolved}` for month 2.

### Arguments

* `month` is a month in the range `1..12`.

### Returns

* An integer day count, or `{:error, :unresolved}` for February.

### Examples

    iex> Calendrical.Julian.days_in_month(4)
    30

    iex> Calendrical.Julian.days_in_month(2)
    {:error, :unresolved}

# `days_in_month`

```elixir
@spec days_in_month(year(), month()) :: 28..31
```

Returns the number of days in the given Julian `year` and `month`.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

### Returns

* An integer in the range `28..31`.

### Examples

    iex> Calendrical.Julian.days_in_month(2024, 2)
    29

    iex> Calendrical.Julian.days_in_month(2025, 2)
    28

# `days_in_week`

```elixir
@spec days_in_week() :: 7
```

Returns the number of days in a Julian week.

### Returns

* The integer `7`.

### Examples

    iex> Calendrical.Julian.days_in_week()
    7

# `days_in_year`

```elixir
@spec days_in_year(year()) :: 365 | 366
```

Returns the number of days in the given Julian `year`.

### Arguments

* `year` is any non-zero Julian year as an integer.

### Returns

* `365` for an ordinary year or `366` for a leap year.

### Examples

    iex> Calendrical.Julian.days_in_year(2024)
    366

    iex> Calendrical.Julian.days_in_year(2025)
    365

# `extended_year`

```elixir
@spec extended_year(year(), month(), day()) :: Calendar.year()
```

Returns the extended year as displayed on rendered calendars.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* The integer Julian year.

### Examples

    iex> Calendrical.Julian.extended_year(2025, 1, 1)
    2025

# `iso_week_of_year`

```elixir
@spec iso_week_of_year(year(), month(), day()) :: {:error, :not_defined}
```

Returns `{:error, :not_defined}` because ISO weeks are not defined
for the Julian calendar.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* `{:error, :not_defined}`.

### Examples

    iex> Calendrical.Julian.iso_week_of_year(2025, 1, 1)
    {:error, :not_defined}

# `leap_year?`

```elixir
@spec leap_year?(year :: Calendar.year()) :: boolean()
```

Returns whether the given Julian `year` is a leap year.

Julian leap years occur every four years (every year divisible
by 4 — unlike the Gregorian rule, the centurial exceptions do
not apply).

### Arguments

* `year` is any non-zero Julian year as an integer.

### Returns

* `true` if the year contains 366 days; otherwise `false`.

### Examples

    iex> Calendrical.Julian.leap_year?(2024)
    true

    iex> Calendrical.Julian.leap_year?(2025)
    false

    iex> Calendrical.Julian.leap_year?(2100)
    true

# `month`

```elixir
@spec month(year(), month()) :: Date.Range.t()
```

Returns a `t:Date.Range.t/0` representing a given Julian
year-and-month.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

### Returns

* A `t:Date.Range.t/0` spanning the requested month.

### Examples

    iex> Calendrical.Julian.month(2025, 2)
    Date.range(~D[2025-02-01 Calendrical.Julian], ~D[2025-02-28 Calendrical.Julian])

# `month_of_year`

```elixir
@spec month_of_year(year(), month(), day()) :: month()
```

Returns the month of the year for the given Julian date.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* The integer month.

### Examples

    iex> Calendrical.Julian.month_of_year(2025, 4, 1)
    4

# `naive_datetime_from_iso_days`

```elixir
@spec naive_datetime_from_iso_days(Calendar.iso_days()) ::
  {Calendar.year(), Calendar.month(), Calendar.day(), Calendar.hour(),
   Calendar.minute(), Calendar.second(), Calendar.microsecond()}
```

Returns the Julian naive-datetime tuple for the given
`t:Calendar.iso_days/0`.

### Arguments

* `iso_days` is a `t:Calendar.iso_days/0` `{days, day_fraction}`
  tuple.

### Returns

* A seven-tuple `{year, month, day, hour, minute, second, microsecond}`.

### Examples

    iex> Calendrical.Julian.naive_datetime_from_iso_days({739630, {43_200_000_000, 86_400_000_000}})
    {2025, 1, 1, 12, 0, 0, {0, 6}}

# `naive_datetime_to_iso_days`

```elixir
@spec naive_datetime_to_iso_days(
  Calendar.year(),
  Calendar.month(),
  Calendar.day(),
  Calendar.hour(),
  Calendar.minute(),
  Calendar.second(),
  Calendar.microsecond()
) :: Calendar.iso_days()
```

Returns the `t:Calendar.iso_days/0` form of the given Julian naive
datetime.

### Arguments

* `year`, `month`, `day`, `hour`, `minute`, `second`, `microsecond`
  form the Julian naive datetime.

### Returns

* A `t:Calendar.iso_days/0` `{days, day_fraction}` tuple.

### Examples

    iex> Calendrical.Julian.naive_datetime_to_iso_days(2025, 1, 1, 12, 0, 0, {0, 0})
    {739630, {43_200_000_000, 86_400_000_000}}

# `periods_in_year`

```elixir
@spec periods_in_year(year()) :: Calendar.month()
```

Returns the number of periods in the given Julian `year`.

A period corresponds to a month in month-based calendars and a
week in week-based calendars.

### Arguments

* `year` is any non-zero Julian year as an integer.

### Returns

* The integer `12` (months per Julian year).

### Examples

    iex> Calendrical.Julian.periods_in_year(2025)
    12

# `plus`

```elixir
@spec plus(
  year(),
  month(),
  day(),
  :years | :quarters | :months | :days,
  integer(),
  Keyword.t()
) ::
  {Calendar.year(), Calendar.month(), Calendar.day()}
```

Adds an `increment` of `date_part`s to a Julian `year-month-day`.

### Arguments

* `year`, `month`, `day` form the Julian date to shift.

* `date_part` is one of `:years`, `:quarters`, `:months`, or
  `:days`.

* `increment` is the integer number of `date_part`s to add (may
  be negative).

### Options

* `:coerce` (boolean, default `false`) — when `true`, clamps the
  resulting day-of-month to the last valid day of the new month
  if the original day overflows (so adding one month to 31 March
  yields 30 April rather than an invalid date).

### Returns

* A three-tuple `{year, month, day}` representing the shifted
  Julian date.

### Examples

    iex> Calendrical.Julian.plus(2025, 1, 1, :years, 1)
    {2026, 1, 1}

    iex> Calendrical.Julian.plus(2025, 1, 1, :months, 1)
    {2025, 2, 1}

    iex> Calendrical.Julian.plus(2025, 1, 1, :days, 1)
    {2025, 1, 2}

# `quarter`

```elixir
@spec quarter(year(), Calendrical.quarter()) :: Date.Range.t()
```

Returns a `t:Date.Range.t/0` representing a given quarter of a
Julian year.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `quarter` is an integer in the range `1..4`.

### Returns

* A `t:Date.Range.t/0` spanning the requested quarter.

### Examples

    iex> Calendrical.Julian.quarter(2025, 1)
    Date.range(~D[2025-01-01 Calendrical.Julian], ~D[2025-03-31 Calendrical.Julian])

# `quarter_of_year`

```elixir
@spec quarter_of_year(year(), month(), day()) :: 1..4
```

Returns the quarter of the year for the given Julian date.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* An integer in the range `1..4`.

### Examples

    iex> Calendrical.Julian.quarter_of_year(2025, 4, 1)
    2

# `related_gregorian_year`

```elixir
@spec related_gregorian_year(year(), month(), day()) :: Calendar.year()
```

Returns the proleptic Gregorian year in which the given Julian
year begins.

Per TR35 the related year is constant for every date of the
calendar year: Julian 2025 begins on Gregorian 2025-01-14, so
every Julian 2025 date — including 2025-12-25, which falls on
Gregorian 2026-01-07 — has related year 2025.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* An integer proleptic Gregorian year.

### Examples

    iex> Calendrical.Julian.related_gregorian_year(2025, 1, 1)
    2025

    iex> Calendrical.Julian.related_gregorian_year(2025, 12, 25)
    2025

# `shift_date`

```elixir
@spec shift_date(Calendar.year(), Calendar.month(), Calendar.day(), Duration.t()) ::
  {Calendar.year(), Calendar.month(), Calendar.day()}
```

Shifts a Julian date by the given `t:Duration.t/0`.

### Arguments

* `year`, `month`, `day` form the Julian date to shift.

* `duration` is a `t:Duration.t/0`.

### Returns

* A three-tuple `{year, month, day}` representing the shifted
  Julian date.

### Examples

    iex> Calendrical.Julian.shift_date(2025, 1, 1, Duration.new!(month: 1))
    {2025, 2, 1}

# `shift_naive_datetime`

```elixir
@spec shift_naive_datetime(
  Calendar.year(),
  Calendar.month(),
  Calendar.day(),
  Calendar.hour(),
  Calendar.minute(),
  Calendar.second(),
  Calendar.microsecond(),
  Duration.t()
) ::
  {Calendar.year(), Calendar.month(), Calendar.day(), Calendar.hour(),
   Calendar.minute(), Calendar.second(), Calendar.microsecond()}
```

Shifts a naive datetime by the given `t:Duration.t/0`.

### Arguments

* `year`, `month`, `day`, `hour`, `minute`, `second`, `microsecond`
  form the Julian naive datetime to shift.

* `duration` is a `t:Duration.t/0`.

### Returns

* A seven-tuple `{year, month, day, hour, minute, second, microsecond}`
  representing the shifted Julian naive datetime.

### Examples

    iex> Calendrical.Julian.shift_naive_datetime(2025, 1, 1, 12, 0, 0, {0, 0}, Duration.new!(day: 1))
    {2025, 1, 2, 12, 0, 0, {0, 0}}

# `shift_time`

```elixir
@spec shift_time(
  Calendar.hour(),
  Calendar.minute(),
  Calendar.second(),
  Calendar.microsecond(),
  Duration.t()
) ::
  {Calendar.hour(), Calendar.minute(), Calendar.second(),
   Calendar.microsecond()}
```

Shifts a time by the given `t:Duration.t/0`.

Time arithmetic is delegated to `Calendar.ISO.shift_time/5`.

### Arguments

* `hour`, `minute`, `second`, `microsecond` form the time to shift.

* `duration` is a `t:Duration.t/0`.

### Returns

* A four-tuple `{hour, minute, second, microsecond}` representing
  the shifted time.

### Examples

    iex> Calendrical.Julian.shift_time(12, 0, 0, {0, 0}, Duration.new!(hour: 1))
    {13, 0, 0, {0, 0}}

# `valid_date?`

```elixir
@spec valid_date?(Calendar.year(), Calendar.month(), Calendar.day()) :: boolean()
```

Returns whether the supplied `year`, `month`, and `day` is a valid
Julian date.

### Arguments

* `year` is any Julian year as an integer. The year `0` is not
  valid (the Julian calendar has no year zero).

* `month` is a month in the range `1..12`.

* `day` is a day-of-month appropriate for the month.

### Returns

* `true` if the date is valid; otherwise `false`.

### Examples

    iex> Calendrical.Julian.valid_date?(2024, 2, 29)
    true

    iex> Calendrical.Julian.valid_date?(2023, 2, 29)
    false

    iex> Calendrical.Julian.valid_date?(0, 1, 1)
    false

# `week`

```elixir
@spec week(year(), Calendrical.week()) :: {:error, :not_defined}
```

Returns `{:error, :not_defined}` because the Julian calendar does
not define a week-of-year scheme.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `week` is the requested week number.

### Returns

* `{:error, :not_defined}`.

### Examples

    iex> Calendrical.Julian.week(2025, 1)
    {:error, :not_defined}

# `week_of_month`

```elixir
@spec week_of_month(year(), month(), day()) ::
  {pos_integer(), pos_integer()} | {:error, :not_defined}
```

Returns `{:error, :not_defined}` because the Julian calendar does
not define a week-of-month scheme.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* `{:error, :not_defined}`.

### Examples

    iex> Calendrical.Julian.week_of_month(2025, 1, 1)
    {:error, :not_defined}

# `week_of_year`

```elixir
@spec week_of_year(year(), month(), day()) :: {:error, :not_defined}
```

Returns `{:error, :not_defined}` because the Julian calendar does
not define a week-of-year scheme of its own.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* `{:error, :not_defined}`.

### Examples

    iex> Calendrical.Julian.week_of_year(2025, 1, 1)
    {:error, :not_defined}

# `year`

```elixir
@spec year(year()) :: Date.Range.t()
```

Returns a `t:Date.Range.t/0` representing the given Julian `year`.

### Arguments

* `year` is any non-zero Julian year as an integer.

### Returns

* A `t:Date.Range.t/0` spanning all the days of the year.

### Examples

    iex> Calendrical.Julian.year(2025)
    Date.range(~D[2025-01-01 Calendrical.Julian], ~D[2025-12-31 Calendrical.Julian])

# `year_of_era`

```elixir
@spec year_of_era(year()) :: {year(), era :: 0..1}
```

Returns the year and era for the given Julian `year`.

The Julian calendar has two eras: the current era which starts in
year 1 and is defined as era `1`; and a second era for years less
than 1, defined as era `0`.

### Arguments

* `year` is any non-zero Julian year as an integer.

### Returns

* A two-tuple `{year_in_era, era}` where `era` is `0` or `1`.

### Examples

    iex> Calendrical.Julian.year_of_era(2025)
    {2025, 1}

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

# `year_of_era`

```elixir
@spec year_of_era(year(), month(), day()) :: {year :: Calendar.year(), era :: 0..1}
```

Returns the year and era for the Julian date given by `year`,
`month`, and `day`.

### Arguments

* `year` is any non-zero Julian year as an integer.

* `month` is a month in the range `1..12`.

* `day` is a day-of-month.

### Returns

* A two-tuple `{year_in_era, era}` where `era` is `0` or `1`.

### Examples

    iex> Calendrical.Julian.year_of_era(2025, 1, 1)
    {2025, 1}

---

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