From c6cceec6e934fd89d2b46461b351ccfd17a2db09 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 25 May 2026 13:21:39 +0200 Subject: [PATCH] feat(normalizer): Easter computus Co-Authored-By: Claude Opus 4.7 --- tools/import-normalizer/dates.py | 21 +++++++++++++++++++++ tools/import-normalizer/tests/test_dates.py | 9 +++++++++ 2 files changed, 30 insertions(+) create mode 100644 tools/import-normalizer/dates.py create mode 100644 tools/import-normalizer/tests/test_dates.py diff --git a/tools/import-normalizer/dates.py b/tools/import-normalizer/dates.py new file mode 100644 index 00000000..4df42f9f --- /dev/null +++ b/tools/import-normalizer/dates.py @@ -0,0 +1,21 @@ +"""Tolerant historical date parsing for the family archive.""" +import datetime + + +def easter(year: int) -> datetime.date: + """Easter Sunday (Gregorian) via the Anonymous Gregorian / Butcher algorithm.""" + a = year % 19 + b = year // 100 + c = year % 100 + d = b // 4 + e = b % 4 + f = (b + 8) // 25 + g = (b - f + 1) // 3 + h = (19 * a + b - d - g + 15) % 30 + i = c // 4 + k = c % 4 + l = (32 + 2 * e + 2 * i - h - k) % 7 + m = (a + 11 * h + 22 * l) // 451 + month = (h + l - 7 * m + 114) // 31 + day = ((h + l - 7 * m + 114) % 31) + 1 + return datetime.date(year, month, day) diff --git a/tools/import-normalizer/tests/test_dates.py b/tools/import-normalizer/tests/test_dates.py new file mode 100644 index 00000000..d46df4d0 --- /dev/null +++ b/tools/import-normalizer/tests/test_dates.py @@ -0,0 +1,9 @@ +import datetime +import dates + +def test_easter_known_years(): + # Anonymous Gregorian algorithm — verified against published tables + assert dates.easter(2024) == datetime.date(2024, 3, 31) + assert dates.easter(2000) == datetime.date(2000, 4, 23) + assert dates.easter(1922) == datetime.date(1922, 4, 16) + assert dates.easter(1888) == datetime.date(1888, 4, 1)