# lua-calendrica 0.2 2026-10-13

Lua port of the calendrical algorithms from
[Calendrical Calculations (4th edition)](https://www.cambridge.org/9781107683167)
by Nachum Dershowitz and Edward M. Reingold.

Covers a wide range of calendars including
Gregorian, Hebrew, Islamic, Persian, Chinese,
Hindu (modern and old), Tibetan, Balinese, Mayan, Julian, Coptic,
Ethiopic, French Revolutionary, Icelandic, Akan, Bahá'í, and more,
along with astronomical calculations for sunrise, sunset, lunar phases, and solar terms.

## Requirements

- Lua 5.3 or later

## Usage

### High-level API

#### Dates

Dates are immutable value objects, tied to a calendar: a built-in one by name
(see `calendrica.calendars()` for the full list), or a calendar object, e.g. one of
your own made with `calendrica.calendar{...}`. The built-in names are fixed; nothing
is registered under a name, so they cannot clash.

```lua
local calendrica = require("calendrica")

-- Create a date on one calendar and convert to another
local d = calendrica.date({year=2024, month=1, day=1}, "gregorian")
print(d)                       -- gregorian(year=2024, month=1, day=1, rd=738886)

-- Convert with date(...) or the :to / :as / :date methods (all equivalent)
local h = calendrica.date(d, "hebrew")
local h2 = d:to("hebrew")
print(h)                       -- hebrew(year=5784, month=10, day=20, rd=738886)
print(h.year, h.month, h.day)  -- 5784   10   20

-- Every date exposes .rd (its fixed Rata Die integer) and .calendar (an object)
print(d.rd, d.calendar.name)   -- 738886   gregorian

-- A date can also be built directly from an RD integer (the inverse of .rd)
print(calendrica.date(738886, "gregorian"))
```

#### Holidays

`holiday` and `holidays` take an optional trailing calendar (name or object) for
the returned dates (default `"gregorian"`).

```lua
-- Occurrences of a named holiday in a Gregorian year
print(calendrica.holiday("shavuot", 2026)[1])            -- gregorian(year=2026, month=5, day=22, rd=739758)

-- ...expressed in another calendar (same underlying day)
print(calendrica.holiday("shavuot", 2026, "hebrew")[1])  -- hebrew(year=5786, month=3, day=6, rd=739758)

-- All holidays in a year, sorted by date
for _, hol in ipairs(calendrica.holidays(2026)) do
  print(hol.name, hol.dates[1])
end
```

#### Astronomy

A location takes its time zone in hours from UTC. `sunrise`, `sunset`,
`moonrise` and `moonset` return a **moment**: a date object that also carries a
time-of-day (`.hour`, `.minute`, `.second`, `.zone`, and the fractional `.moment`).

```lua
local jerusalem = calendrica.location(31.78, 35.24, 754, 2)  -- lat, lon, elevation (m), UTC+2
print(jerusalem)               -- location(latitude=31.78, longitude=35.24, elevation=754, zone=UTC+2)

local sr = calendrica.sunrise(d, jerusalem)
print(sr)                      -- gregorian(year=2024, month=1, day=1, rd=738886, 06:33:32 UTC+2)
print(sr.hour, sr.minute)      -- 6   33
print(sr:to("hebrew"))         -- same moment, Hebrew calendar (time preserved)

-- These (and lunar_phase) also accept a plain RD integer
print(calendrica.sunrise(738886, jerusalem))
print(calendrica.lunar_phase(738886))   -- lunar phase in degrees (0 new, 180 full)

-- Dates of a lunar phase across a Gregorian year; optional calendar for the result
local full_moons = calendrica.lunar_phases(180, 2026)              -- Gregorian
local full_moons_hijri = calendrica.lunar_phases(180, 2026, "islamic")
```

### Low-level API

Each calendar is also available as a standalone module:

```lua
local gregorian = require("calendrica-gregorian")
local hebrew    = require("calendrica-hebrew")

local fixed = gregorian.fixed_from_gregorian({2024, 1, 1})
local h     = hebrew.hebrew_from_fixed(fixed)
print(h[1], h[2], h[3])  -- year, month, day
```
The standalone modules sometimes have more functionality, such as
leap year predicate.

## Modules

Each module name links to its page in the API documentation.

<!-- BEGIN MODULES (generated by build.lua) -->
| Module | Description |
|--------|-------------|
| [`calendrica`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica.html) | High-level API: date objects, calendar conversion, calendar objects, holidays |
| [`calendrica-akan`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-akan.html) | Akan day-name calendar |
| [`calendrica-astro`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-astro.html) | Astronomical calculations (solar, lunar, location) |
| [`calendrica-astronomical-lunar`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-astronomical-lunar.html) | Observational lunar calendars (Babylonian, Samaritan, observational Islamic and Hebrew) |
| [`calendrica-bahai`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-bahai.html) | Bahá'í calendar (arithmetic and astronomical) |
| [`calendrica-balinese`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-balinese.html) | Balinese Pawukon calendar |
| [`calendrica-basic`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-basic.html) | Core arithmetic and utility functions |
| [`calendrica-chinese`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-chinese.html) | Chinese, Japanese, Korean, and Vietnamese calendars |
| [`calendrica-coptic-ethiopic`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-coptic-ethiopic.html) | Coptic and Ethiopic calendars |
| [`calendrica-ecclesiastical`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-ecclesiastical.html) | Easter and Christian holidays |
| [`calendrica-egyptian-armenian`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-egyptian-armenian.html) | Egyptian and Armenian calendars |
| [`calendrica-french-revolutionary`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-french-revolutionary.html) | French Revolutionary calendar |
| [`calendrica-gregorian`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-gregorian.html) | Gregorian calendar |
| [`calendrica-hebrew`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-hebrew.html) | Hebrew calendar |
| [`calendrica-icelandic`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-icelandic.html) | Icelandic calendar |
| [`calendrica-islamic`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-islamic.html) | Arithmetic Islamic calendars and tabular variants (TBLA, Umm al-Qura, Turkish/Diyanet) |
| [`calendrica-iso`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-iso.html) | ISO week calendar |
| [`calendrica-julian`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-julian.html) | Julian calendar and Roman dates |
| [`calendrica-mayan`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-mayan.html) | Mayan and Aztec calendars |
| [`calendrica-modern-civil`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-modern-civil.html) | Modern civil calendars not in the book: Indian National (reformed Śaka), Minguo/ROC, Thai solar (Buddhist Era), revised Bengali, Nanakshahi, Amazigh (Berber), Japanese era (nengō) |
| [`calendrica-modern-hindu`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-modern-hindu.html) | Modern Hindu solar and lunar calendars |
| [`calendrica-old-hindu`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-old-hindu.html) | Old Hindu solar and lunar calendars |
| [`calendrica-persian`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-persian.html) | Astronomical and arithmetic Persian calendars |
| [`calendrica-tibetan`](https://polyglossia.codeberg.page/lua-calendrica/modules/calendrica-tibetan.html) | Tibetan calendar |
<!-- END MODULES -->

## Testing

The tests run with [l3build](https://ctan.org/pkg/l3build) in plain LuaTeX
and compare the results with values computed by the book's own code, the
authors' `calendar.l` (from
[calendar-code2](https://github.com/EdReingold/calendar-code2)), with the
corrections of the book's errata list applied. These values are not in the
repository; compute them first (needs [SBCL](https://www.sbcl.org/) and
curl):

```sh
l3build reference   # writes testfiles/support/reference.lua
l3build check
```

`l3build reference` downloads `calendar.l` into `build/` and runs
`reference.lisp` on it; run it again only after changing `reference.lisp`.

## Documentation

API documentation is available at https://polyglossia.codeberg.page/lua-calendrica/topics/manual.md.html#

The site also has Calendars and Holidays pages listing all built-in
calendars and holidays.

## Repository

- Source code: https://codeberg.org/polyglossia/lua-calendrica
- Issue tracker: https://codeberg.org/polyglossia/lua-calendrica/issues

## Maintainer

Udi Fogiel <udifogiel@proton.me>

## License

The algorithms are ported from CALENDRICA 4.0, which is licensed under the Apache License 2.0.  
See the source files for details.
