Generalize movable-feast anchors beyond Easter offsets, migrate Christ the King
Deploy / deploy (push) Successful in 1m18s

Extends TemporalFeastRecord's date anchor from a single easterOffset
number into a MovableAnchor union: 'easter' (unchanged), plus
'nth-sunday-of-month' (nth: -1 for "last") and 'nth-sunday-of-advent' --
both new anchor kinds resolve to "Nth Sunday counting from an anchor
date," just with a different anchor (first-of-month vs. Advent's own
start, which is itself always a Sunday). Adds calendar/temporal.ts's
sundayOnOrAfter to support the month case.

Migrates christ-the-king.yml onto the new nth-sunday-of-month anchor
(month: 10, nth: -1) with a new `unconditional: true` flag replacing its
old always-wins-no-rank-check bespoke function -- retiring
applyChristTheKing and its lastSundayOfOctober helper from
calendar/index.ts entirely. Down to two bespoke override functions left
(marian-saturday's date-less fallback, christmas-octave-sunday's
fixed-date-range Sunday search), both structurally different from the
Nth-Sunday-from-anchor shape this table now covers.

No behavior change -- the full existing Christ the King test suite
passes unchanged through the new generic path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZSAgRi4QE4XRTqVto93zA
This commit is contained in:
2026-08-22 07:01:28 -04:00
parent 5e79fb4edf
commit 879d4d607c
6 changed files with 119 additions and 83 deletions
+9
View File
@@ -68,6 +68,15 @@ export function firstSundayStrictlyAfter(isoDate: string): string {
return addDays(isoDate, dow === 0 ? 7 : 7 - dow);
}
/** The Sunday on or after `isoDate` — `isoDate` itself, if it's already a
* Sunday. Used by calendar/movable-feasts.ts's "Nth Sunday of month"
* anchor (the 1st Sunday of a month counts the month's own 1st day if
* that's a Sunday, unlike firstSundayStrictlyAfter above). */
export function sundayOnOrAfter(isoDate: string): string {
const dow = dayOfWeek(isoDate);
return dow === 0 ? isoDate : addDays(isoDate, 7 - dow);
}
function seasonFromEasterOffset(offset: number): Season {
const dayOverride = easterOffsets.days?.[String(offset)];
if (dayOverride) {