GreenCal 🌱🌙

CI

Agricultural sun & moon calendar for Elixir — pure arithmetic, zero dependencies, no time zone database required.

From a geolocation, a date (or a period) and optionally an elevation, GreenCal computes:

Rise/set times are found by numeric search (sampling + bisection) rather than the closed-form hour-angle formula, so moonrise is accurate too (the closed form can be 15+ minutes off for the Moon) and polar edge cases (midnight sun, polar night, "no moonrise today") are reported explicitly instead of producing garbage. ΔT (TT − UT) uses observed IERS values on 2000–2026, the Espenak & Meeus polynomials before that, and a documented hold-then-bridge extrapolation after — so results don't drift over the years.

Usage

loc = {48.8566, 2.3522} # Paris; {lat, lon}, East positive
day = GreenCal.day(loc, ~D[2026-06-21], elevation: 35.0)
day.sun.rise #=> ~U[2026-06-21 03:45:21Z]
day.sun.day_length_minutes #=> 974.1
day.twilight.dawn #=> ~U[2026-06-21 03:04:16Z]
day.moon.phase #=> :first_quarter
day.moon.trend #=> :descending (declination — sowing calendars)
day.moon.illumination_trend #=> :waxing (independent cycle!)
day.constellation #=> "Virgo"
day.organ #=> :root
# A whole month, with local times (needs a tz database, e.g. tzdata):
GreenCal.calendar(loc, Date.range(~D[2026-07-01], ~D[2026-07-31]),
time_zone: "Europe/Paris")
# A year in ~160 ms:
GreenCal.calendar(loc, Date.range(~D[2026-01-01], ~D[2026-12-31]), parallel: true)
# Exact instants of phases, apsides, nodes and standstills (geocentric):
GreenCal.lunar_events(Date.range(~D[2026-08-01], ~D[2026-08-31]))
# phases: [%{type: :new_moon, at: ~U[2026-08-12 17:36:40Z], eclipse: :likely}, ...]

The low-level astronomy is public too — GreenCal.Astro (UT-facing facade), GreenCal.Astro.Sun, GreenCal.Astro.Moon, GreenCal.Astro.RiseSet, GreenCal.Astro.Time — if you need positions, ephemerides or custom events.

An interactive tour lives in livebooks/green_cal.livemd.

Accuracy

QuantityMethodValidated against
Solar positionMeeus ch. 25 (low accuracy)Example 25.a
Lunar positionMeeus ch. 47, full 60+60 term tablesExample 47.a (0.13″ / 0.03 km)
NutationMeeus ch. 22 short series (~0.5″)Example 22.a
Sidereal timeMeeus ch. 12Examples 12.a / 12.b
ΔTObserved IERS 2000–2026, polynomials before, hold-then-bridge afterUSNO deltat.data
Sun rise/setnumeric searchNOAA-consistent, < 1 min for |lat| < 66°
Moon rise/setnumeric search with per-instant parallax h₀USNO API (Paris, Sydney, Edinburgh — ≤ 30 s)
Phase instantselongation crossings, bisectionPublished 2026 instants (≤ 3 min)
Eclipse screening|β| at syzygy (Meeus ch. 54 criterion)All 4 eclipses of 2026, no false positive
IAU constellationsboundary table (J2000) + precessionSolar entry dates, all 13 constellations

Honesty note. The astronomy above is science; elements, organs and "sowing days" are tradition. GreenCal computes the former rigorously and labels the latter for what it is.

Known limitations

Installation

def deps do
[
{:green_cal, "~> 0.1.1"}
]
end

Documentation: https://hexdocs.pm/green_cal.

License

MIT