# `Calendrical.Lunisolar`

Shared base implementation for Calendrical's lunisolar calendars.

A lunisolar calendar approximates the tropical year using lunar months,
inserting an intercalary (leap) month roughly every three years to keep the
calendar aligned with the seasons. Calendrical implements three lunisolar
calendars on top of this module:

* `Calendrical.Chinese` — observation point Beijing.

* `Calendrical.Korean` (Dangi) — observation point Seoul.

* `Calendrical.LunarJapanese` — observation point Tokyo.

Each implementation is a thin wrapper that supplies its epoch and an
observation-location function (latitude, longitude, altitude, time-zone
offset). All of the astronomical heavy lifting — winter solstice, mean and
true new moon, leap-month detection, sexagesimal cycle calculations — is
delegated to `Astro` and lives in this module.

This module is **not** intended to be called directly from application code.
Use one of the lunisolar calendar modules above and the standard `Date` and
`Calendar` APIs.

## Naming conventions

Several "year" and "month" concepts coexist in a lunisolar calendar. To keep
the code unambiguous, this module uses the following names consistently:

* `year` is the calendar year, counted as the number of years since the
  calendar's epoch.

* `cyclical_year` is the position within the 60-year sexagesimal cycle
  (1..60).

* `cycle` is the number of completed sexagesimal cycles since the epoch.

* `month` is the *ordinal* month of the calendar year — 1..12 in an ordinary
  year and 1..13 in a leap year. This is the value the `Calendar` behaviour
  expects.

* `lunar_month` is the *traditional* month number, 1..12 in all years, with
  leap months represented as `{month, :leap}`. This is the value users see
  in cultural contexts and the form returned by `Calendrical.localize/3`.

## References

* Reingold and Dershowitz, *Calendrical Calculations: The Ultimate Edition*,
  4th ed., chapters on the Chinese, Korean and Japanese calendars.

* The accompanying Common Lisp source distributed with the same book.

# `cycle`

```elixir
@type cycle() :: pos_integer()
```

A sexigesimal cycle number

# `lunar_month`

```elixir
@type lunar_month() :: Calendar.month() | {Calendar.month(), :leap}
```

A lunar month

# `chinese_new_year_for_gregorian_year`

Return iso_days of Lunar New Year at `location` for a
Gregorian year.

# `current_major_solar_term`

Return last Chinese major solar term (zhongqi) before
`iso_days`.

# `current_minor_solar_term`

Return last minor solar term (jieqi) before `iso_days`.

# `cyclical_date_to_iso_days`

# `date_from_iso_days`

# `date_to_iso_days`

# `day_name`

Return sexagesimal name for date, date.

# `day_name_on_or_before`

Return iso_days of latest date on or before iso_days, that
has Chinese name, name.

# `december_solstice_on_or_before`

Return iso_days, in the `location` zone, of winter solstice
on or before `iso_days`.

# `elapsed_years`

# `gregorian_date_for_lunar`

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

# `is_prior_leap_month?`

Return `true` if there is a Lunar leap month on or after lunar
month starting on `m_prime` and at or before
lunar month starting at `m`.

# `leap_month`

Returns the leap month number for a given year
or nil if its not a leap year.

# `leap_month?`

Approximately every three years (7 times in 19 years),
a leap month is added to the Chinese calendar.

To determine when, find the number of new moons between
the 11th month in one year and the 11th month in the
following year.

A leap month is inserted 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 next year.

The Chinese calendar uses a solar term system
that has 12 principal terms to indicate when the Sun's
longitude is a multiple of 30 degrees.

Unlike all other months, the leap month does not
contain a principal term (Zhongqi).

# `leap_month?`

# `leap_year?`

Returns 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.

# `location`

# `major_solar_term_on_or_after`

Return moment at `location` of the first major
solar term (zhongqi) on or after `iso_days`.  The
major terms begin when the sun's longitude is a
multiple of 30 degrees.

# `midnight_in_location`

Return Universal time of (clock) midnight at start of `iso_days`,
at `location`.

# `minor_solar_term_on_or_after`

Return moment at `location` of the first minor solar
term (jieqi) on or after `iso_days`.  The minor terms
begin when the sun's longitude is an odd multiple of 15 degrees.

# `month_name`

Return sexagesimal name for month, month, of Chinese year, year.

# `name_difference`

Return the number of names from Lunar name c_name1 to the
next occurrence of Lunar name c_name2.

# `new`

Create a new date in the lunisolar calendar.

### Arguments

* `year` is a year in the lunisolar calendar.

* `month` is a month in the lunisolar calendar
as either a positive integer or a tuple of the
form `{month, :leap}` representing the leap month.

* `day` is a day of month.

* `epoch` is the epoch in iso days for the lunisolar
  calendar.

* `location_fun` is a 1-arity function that returns
  the tuple of the form `{latitude, longitude, altitude, hour_offset}`
  for  the lunisolar calendar.

### Returns

* `iso_days` begin the iso_days for the given date in
  based upon the given location.

# `new_moon_before`

Return `iso_day` at `location` of first new moon
before `iso_days`.

# `new_moon_on_or_after`

Return `iso_day` at `location` of first new moon on or after
`iso_days`.

# `new_year_in_sui`

Return `iso_day` of Lunar New Year in sui
(period from solstice to solstice)
containing `iso_days`.

# `new_year_on_or_before`

Return `iso_day` of Lunar New Year on or
before `iso_days` at `location`.

# `no_major_solar_term?`

Return `true` if lunar month starting on `iso_days`
at `location` has no major solar term.

# `solar_longitude_on_or_after`

```elixir
@spec solar_longitude_on_or_after(Astro.angle(), number(), function()) ::
  Astro.Time.time()
```

Return moment at `location` of the first date on or after
`iso_days` when the solar longitude
will be 'lambda' degrees.

# `stem_and_branch`

Return the name of the Lunar
sexagesimal cycle.

---

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