--- Ecclesiastical Easter and related holiday calculations. -- Ported from "Calendrical Calculations" (4th edition) -- by Nachum Dershowitz and Edward M. Reingold. -- Original Lisp code (CALENDRICA 4.0) is Apache 2.0 licensed. -- @module calendrica-ecclesiastical -- @release 0.1 2026-07-19 local M = {} local basic = require("calendrica-basic") local gregorian = require("calendrica-gregorian") local julian = require("calendrica-julian") --- Fixed date of Orthodox Easter in Gregorian year `g_year`. -- @tparam number g_year Gregorian year. -- @treturn number Fixed date. function M.orthodox_easter(g_year) -- Age of moon for April 5. local shifted_epact = (14 + 11 * (g_year % 19)) % 30 -- Julian year number (no year zero). local j_year = g_year > 0 and g_year or g_year - 1 -- Day after full moon on or after March 21. local paschal_moon = julian.fixed_from_julian( julian.julian_date(j_year, julian.APRIL, 19) ) - shifted_epact -- Return the Sunday following the Paschal moon. return gregorian.kday_after(basic.SUNDAY, paschal_moon) end -- Alternative Orthodox Easter formula (not exported). local function alt_orthodox_easter(g_year) -- luacheck: ignore -- Day after full moon on or after March 21. local paschal_moon = 354 * g_year + 30 * basic.quotient(7 * g_year + 8, 19) + basic.quotient(g_year, 4) - basic.quotient(g_year, 19) - 273 + gregorian.GREGORIAN_EPOCH -- Return the Sunday following the Paschal moon. return gregorian.kday_after(basic.SUNDAY, paschal_moon) end --- Fixed date of Easter in Gregorian year `g_year`. -- @tparam number g_year Gregorian year. -- @treturn number Fixed date. function M.easter(g_year) local century = 1 + basic.quotient(g_year, 100) -- Age of moon for April 5, by Nicaean rule, -- corrected for the Gregorian century rule and -- Metonic cycle inaccuracy. local shifted_epact = (14 + 11 * (g_year % 19) - basic.quotient(3 * century, 4) + basic.quotient(5 + 8 * century, 25) ) % 30 -- Adjust for 29.5 day month. local adjusted_epact if shifted_epact == 0 or (shifted_epact == 1 and (g_year % 19) > 10) then adjusted_epact = shifted_epact + 1 else adjusted_epact = shifted_epact end -- Day after full moon on or after March 21. local paschal_moon = gregorian.fixed_from_gregorian( gregorian.gregorian_date(g_year, gregorian.APRIL, 19) ) - adjusted_epact -- Return the Sunday following the Paschal moon. return gregorian.kday_after(basic.SUNDAY, paschal_moon) end local easter = M.easter --- Fixed date of Pentecost in Gregorian year `g_year` (49 days after Easter). -- @tparam number g_year Gregorian year. -- @treturn number Fixed date. function M.pentecost(g_year) return easter(g_year) + 49 end return M