# `Calendrical.Korean`

Implementation of the Korean lunisolar calendar.

In a ‘regular’ Korean lunisolar calendar, one year
is divided into 12 months, with one month corresponding
to the time between two full moons.

Since the cycle of the moon is not
an even number of days, a month in the lunar calendar
can vary between 29 and 30 days and a normal year can
have 353, 354, or 355 days.

The default epoch for the Korean lunisolar calendar
is `~D[-2332-02-15]` which is the traditional year of
the founding of the first Korean nation. The epoch can
be changed by setting the `:korean_epoch` configuration
key in `config.exs`:

    config :calendrical,
      korean_epoch: ~D[-2332-02-15]

## Two month numbering conventions

The Korean lunisolar (Dangi) calendar (like the Japanese and Chinese
ones) has **two distinct month numbering conventions**. Choosing the
wrong one silently produces dates that are off by one full lunar month
after the intercalary month in leap years. The conventions are:

* **Ordinal** — months counted monotonically 1..12 in ordinary years
  and 1..13 in leap years. The intercalary appears at whatever position
  the astronomical no-zhongqi rule places it; it is not separately
  labelled. This is the convention `Date.new/4` accepts, the `Date.t`
  struct stores, and `Date.convert/2` returns. It is what the standard
  `Calendar` behaviour callbacks expect.

* **Traditional** — months always numbered 1..12, with the intercalary
  expressed as `{month, :leap}` (read 윤N월 in Korean — "intercalary Nth
  month") where `month` is the *preceding* traditional month number.
  This is the convention used by cultural references and primary
  sources. `Calendrical.Korean.new/3` and the return value of
  `lunar_month_of_year/1` use this convention.

Converting between the two: if a year is a leap year, traditional
numbers below the leap-month position equal the ordinal numbers, and
traditional numbers at or above the leap-month position equal
ordinal-minus-one. `leap_month/1` returns the **ordinal** position of
the intercalary; `traditional_leap_month/1` returns the **traditional**
number that the intercalary repeats.

# `calendar_base`

Identifies whether this calendar is month
or week based.

# `calendar_year`

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

Returns the calendar year as displayed
on rendered calendars.

# `cldr_calendar_type`

Defines the CLDR calendar type for this calendar.

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

# `cyclic_year`

```elixir
@spec cyclic_year(date :: Date.t()) :: Calendrical.Lunisolar.cycle()
```

Returns the year in the lunisolar sexigesimal 60-year
cycle.

Traditionally years are numbered only within the cycle
however in this implementation the year is an offset from
the epoch date. It can be converted to the current year in
the current cycle with this function.

The cycle year is commonly shown on lunisolar calendars and
it forms part of the traditional Chinese zodiac.

### Arguments

* `date` which is any `t:Calendar.date/0` in the `Calendrical.Korean`
  calendar, *or*

*`year` and `month` representing the calendar year and month.

### Returns

* the integer year within the sexigesimal cycle of 60 years.

### Examples

    iex> Calendrical.Korean.cyclic_year(~D[4356-04-01 Calendrical.Korean])
    36

    iex> Calendrical.Korean.cyclic_year(~D[4357-01-01 Calendrical.Korean])
    37

    iex> Calendrical.Korean.cyclic_year(~D[4321-01-01 Calendrical.Korean])
    1

    iex> Calendrical.Korean.cyclic_year(~D[4320-01-01 Calendrical.Korean])
    60

# `cyclic_year`

# `cyclic_year`

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

Returns the cyclic year as displayed
on rendered calendars.

# `day_of_era`

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

Calculates the day and era from the given
`year`, `month`, and `day`.

By default we consider on two eras: before the epoch
and on-or-after the epoch.

# `day_of_year`

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

Calculates the day of the year from the given
`year`, `month`, and `day`.

# `days_in_month`

```elixir
@spec days_in_month(Calendar.month()) ::
  Calendar.month()
  | {:ambiguous, Range.t() | [pos_integer()]}
  | {:error, :undefined}
```

Returns how many days there are in the given month.

Must be implemented in derived calendars because
we cannot know what the calendar format is.

# `days_in_month`

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

Returns how many days there are in the given year
and month.

# `days_in_week`

Returns the number days in a a week.

# `days_in_year`

Returns the number days in a given year.

The year is the number of years since the
epoch.

# `epoch`

# `epoch_day_of_week`

# `extended_year`

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

Returns the extended year as displayed
on rendered calendars.

# `first_day_of_week`

# `gregorian_date_for_lunar`

Returns the Gregorian date for the given gregorian year
and lunar month and day.

### Arguments

* `gregorian_year` is any year in the Gregorian calendar.

* `lunar_month` is either a cardinal month number between 1 and 12 or
  for a leap month the 2-tuple in the format `{month, :leap}`.

* `day` is any day number valid for `year` and `lunar_month`.

### Returns

* A gregorian date `t:Date.t()`.

# `iso_week_of_year`

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

Calculates the ISO week of the year from the
given `year`, `month`, and `day`.

By default this function always returns
`{:error, :not_defined}`.

# `last_day_of_week`

# `leap_month`

```elixir
@spec leap_month(date_or_year :: Date.t() | Calendar.year()) :: Calendar.month() | nil
```

Returns the **ordinal** position (1..13) of the leap month for a year,
or `nil` if the year is not a leap year.

This is the position of the intercalary in the monotonic 1..13 ordinal
sequence used by `Date.t` structs and the standard `Calendar` callbacks.
See `traditional_leap_month/1` for the traditional notation (the
preceding-month number that the intercalary repeats).

### Arguments

* `date_or_year` is either an integer year number
  or a `t:Calendar.date/0` in the `Calendrical.Korean`
  calendar.

### Returns

* the ordinal position of the leap month (1..13), or

* `nil` if there is no leap month in the given year.

### Examples

    # Y4356 is a leap year; the intercalary is at ordinal m3
    # (= traditional {2, :leap})
    iex> Calendrical.Korean.leap_month(4356)
    3

    iex> Calendrical.Korean.leap_month(~D[4356-13-29 Calendrical.Korean])
    3

    iex> Calendrical.Korean.leap_month(4357)
    nil

# `leap_month?`

```elixir
@spec leap_month?(date :: Date.t()) :: boolean()
```

Returns a boolean indicating if the given year and month
is a leap month.

### Arguements

* `date` is any `t:Calendar.date/0` in the `Calendrical.Korean` calendar.

### Returns

* A booelan indicating if the given year and month is a leap
  month.

### Examples

    iex> Calendrical.Korean.leap_month?(~D[4356-01-01 Calendrical.Korean])
    false

    iex> Calendrical.Korean.leap_month?(~D[4356-03-29 Calendrical.Korean])
    true

# `leap_month?`

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

Returns a boolean indicating if the given year and month
is a leap month.

### Arguements

* `year` is any year in the `Calendrical.Korean` calendar.

* `month` is any ordinal month number in the `Calendrical.Korean`
  calendar.

### Returns

* A booelan indicating if the given year and month is a leap
  month.

### Examples

    iex> Calendrical.Korean.leap_month?(4356, 1)
    false

    iex> Calendrical.Korean.leap_month?(4356, 3)
    true

# `leap_year?`

```elixir
@spec leap_year?(date_or_year :: Calendar.year() | Date.t()) :: boolean()
```

Returns a boolean indicating if the given year is a leap
year.

Leap years have 13 months. To determine if a year
is a leap year, calculate the number of new moons
between the 11th month in one year (i.e., the month
containing the Winter Solstice) and the 11th month
in the following year.

If there are 13 new moons from the start of the 11th
month in the first year to the start of the 11th
month in the second year, a leap month must be inserted.

In leap years, at least one month does not contain a
Principal Term. The first such month is the leap month.

The additional complexity is that a leap year is
calculated for the solar year, but the calendar
is managed in lunar years and months. Therefore when
a leap year is detected, the leap month could be in
the current lunar year or the next lunar year.

### Arguments

* `date_or_year` is either an integer year number
  or a `t:Calendar.date/0` in the `Calendrical.Korean`
  calendar.

### Returns

* A booelan indicating if the given year is a leap
  year.

### Examples

    iex> Calendrical.Korean.leap_year?(4356)
    true

    iex> Calendrical.Korean.leap_year?(4355)
    false

# `lunar_month_of_year`

```elixir
@spec lunar_month_of_year(date :: Date.t()) :: Calendrical.Lunisolar.lunar_month()
```

Returns the lunar month of the year for a given date or
year and month.

The lunar month number in the traditional lunisolar calendar is
between 1 and 12 with a leap month added when there are 13 new moons
between Winter solstices. This intercalary leap month is not
representable in its traditional form in the `t:Calendar.date/0` struct.

This function takes a date, or year and month, and returns either the
month number between 1 and 12 or a 2-tuple representing the leap month.
This 2-tuple looks like `{month_number, :leap}`.

The value returned from this function can be passed to `Calendrical.Korean.new/3` to
define a date using traditional lunar months.

### Arguments

* `date` which is any `t:Calendar.date/0` in the `Calendrical.Korean`
  calendar, *or*

*`year` and `month` representing the calendar year and month.

### Returns

* the lunar month as either an integer between 1 and 12 or a
tuple of the form `{lunar_month, :leap}`.

### Examples

    iex> Calendrical.Korean.lunar_month_of_year(~D[4356-02-01 Calendrical.Korean])
    2

    iex> Calendrical.Korean.lunar_month_of_year(~D[4356-03-01 Calendrical.Korean])
    {2, :leap}

    iex> Calendrical.Korean.lunar_month_of_year(~D[4356-04-01 Calendrical.Korean])
    3

# `lunar_month_of_year`

```elixir
@spec lunar_month_of_year(year :: Calendar.year(), month :: Calendar.month()) ::
  Calendrical.Lunisolar.lunar_month()
```

# `month`

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

# `months_in_leap_year`

Returns the number of months in a leap year.

# `months_in_ordinary_year`

Returns the number of months in a normal year.

# `months_in_year`

Returns the number of months in a
given `year`.

# `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()}
```

Converts the `t:Calendar.iso_days` format to the
datetime format specified by this calendar.

# `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` format of
the specified date.

# `new`

```elixir
@spec new(
  year :: Calendar.year(),
  month :: Calendrical.Lunisolar.lunar_month(),
  day :: Calendar.day()
) ::
  {:ok, Date.t()} | {:error, atom()}
```

Returns a `t:Calendar.date/0` in the `Calendrical.Korean` calendar
formed by a calendar year, a **traditional** lunar month number, and
a day number.

The lunar month is that used in traditional lunisolar calendar
notation. It is either a number between 1 and 12 (the number of months
in an ordinary year) or a leap month specified by the 2-tuple
`{month, :leap}` where `month` is the preceding traditional month
number that the intercalary repeats (read 윤N월 in Korean —
"intercalary Nth month"). See the moduledoc for the full
ordinal-vs-traditional discussion.

This function is the right entry point for creating dates from
cultural references, holidays defined in the lunisolar calendar, or
any other source that reports a lunisolar date in its native form. To
build a date from an ordinal (1..13) month number — the form stored on
the `Date.t` struct itself — use `Date.new/4` instead.

### Arguments

* `year` is any year in the `Calendrical.Korean` calendar.

* `lunar_month` is either a traditional month number between 1 and 12,
  or for an intercalary month the 2-tuple `{month, :leap}` where `month`
  is the preceding traditional month number.

* `day` is any day number valid for `year` and `lunar_month`.

### Returns

* `{:ok, date}` where `date.month` is the **ordinal** position of the
  given lunar month within the year (1..12 in ordinary years, 1..13 in
  leap years), or

* `{:error, reason}`.

### Examples

    # Buddha's birthday is the 8th day of traditional lunar month 4.
    # In Y4356 (a leap year with the intercalary at ordinal m3),
    # traditional M4 is at ordinal m5.
    iex> Calendrical.Korean.new(4356, 4, 8)
    {:ok, ~D[4356-05-08 Calendrical.Korean]}

    # In an ordinary year, traditional M4 = ordinal m4.
    iex> Calendrical.Korean.new(4355, 4, 8)
    {:ok, ~D[4355-04-08 Calendrical.Korean]}

    # First day of the intercalary 2nd month of Y4356
    iex> Calendrical.Korean.new(4356, {2, :leap}, 1)
    {:ok, ~D[4356-03-01 Calendrical.Korean]}

# `new!`

```elixir
@spec new!(
  year :: Calendar.year(),
  month :: Calendrical.Lunisolar.lunar_month(),
  day :: Calendar.day()
) ::
  Date.t()
```

Raising variant of `new/3`.

Raises `ArgumentError` if the date is not valid in this calendar.

# `new_year_for_gregorian_year`

```elixir
@spec new_year_for_gregorian_year(Calendar.year()) :: Date.t()
```

Returns the gregorian date of the
Luanr New Year for a given gregorian year.

### Arguments

* `gregorian_year` is any year in the Gregorian calendar.

### Returns

* a `t:Calendar.date/0` representing the Gregorian date of
  the lunar year of the given Gregorian year.

### Example

    iex> Calendrical.Chinese.new_year_for_gregorian_year(2021)
    ~D[2021-02-12]

    iex> Calendrical.Chinese.new_year_for_gregorian_year(2022)
    ~D[2022-02-01]

    iex> Calendrical.Chinese.new_year_for_gregorian_year(2023)
    ~D[2023-01-22]

# `periods_in_year`

Returns the number of periods in a given
`year`. A period corresponds to a month
in month-based calendars and a week in
week-based calendars.

# `plus`

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

`date_part` can be `:months` only.

# `quarter`

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

# `quarter_of_year`

```elixir
@spec quarter_of_year(Calendar.year(), Calendar.month(), Calendar.day()) ::
  Calendrical.quarter()
```

Returns the quarter of the year from the given
`year`, `month`, and `day`.

# `related_gregorian_year`

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

Returns the related gregorain year as displayed
on rendered calendars.

# `thanksgiving_for_gregorian_year`

```elixir
@spec thanksgiving_for_gregorian_year(Calendar.year()) :: Date.t()
```

Returns the Gregorian date of Korean thanksgiving
(15th day of 8th lunar month) for a given Gregorian year.

### Arguments

* `year` is any year in the Gregorian calendar.

### Returns

* The Gregorian date of the Korean thanksgiving date for
  the given year.

### Example

    iex> Calendrical.Korean.thanksgiving_for_gregorian_year(2021)
    ~D[2021-09-21]

    iex> Calendrical.Korean.thanksgiving_for_gregorian_year(2022)
    ~D[2022-09-10]

    iex> Calendrical.Korean.thanksgiving_for_gregorian_year(2023)
    ~D[2023-09-29]

# `traditional_leap_month`

```elixir
@spec traditional_leap_month(date_or_year :: Date.t() | Calendar.year()) ::
  Calendar.month() | nil
```

Returns the **traditional** number (1..12) of the leap month for a year,
or `nil` if the year is not a leap year.

The intercalary month in lunisolar tradition repeats the number of the
preceding non-leap month. For example, the intercalary that appears at
ordinal position 3 of Y4356 is written 윤2월 in traditional notation
and used as `{2, :leap}` in this module's API.

### Arguments

* `date_or_year` is either an integer year number
  or a `t:Calendar.date/0` in the `Calendrical.Korean`
  calendar.

### Returns

* the traditional number of the leap month (1..12), or

* `nil` if there is no leap month in the given year.

### Examples

    iex> Calendrical.Korean.traditional_leap_month(4356)
    2

    iex> Calendrical.Korean.traditional_leap_month(4357)
    nil

# `valid_date?`

Determines if the `date` given is valid according to
this calendar.

# `week`

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

# `week_of_month`

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

Calculates the week of the year from the given
`year`, `month`, and `day`.

By default this function always returns
`{:error, :not_defined}`.

# `week_of_year`

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

Calculates the week of the year from the given
`year`, `month`, and `day`.

By default this function always returns
`{:error, :not_defined}`.

# `weeks_in_year`

Returns the number of weeks in a
given `year`.

# `year`

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

# `year_of_era`

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

Calculates the year and era from the given `year`.

# `year_of_era`

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

Calculates the year and era from the given `date`.

---

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