Generalize movable-feast anchors beyond Easter offsets, migrate Christ the King
Deploy / deploy (push) Successful in 1m18s
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:
@@ -8,24 +8,35 @@
|
||||
import type { FeastClass, OctaveConfig } from './types';
|
||||
import { easterOffsetOf } from './temporal';
|
||||
|
||||
/** How to compute a movable feast's own date each year, for a feast that
|
||||
* ISN'T itself a Sunday (or, for `unconditional` feasts like Christ the
|
||||
* King, one whose occurrence rule is simpler to express as "always wins
|
||||
* outright" than as an ordinary rank contest) -- see
|
||||
* calendar/movable-feasts.ts, which is the only consumer of this.
|
||||
* `resolveTemporalId`'s own Easter-offset resolution only ever finds one
|
||||
* of the 52 canonical *Sunday* ids (it works by finding the governing
|
||||
* Sunday on-or-before a date, then looking that Sunday's own offset up);
|
||||
* none of these three anchor kinds are reachable through that lookup no
|
||||
* matter what, so they need this separate mechanism instead. A feast
|
||||
* that *is* a Sunday itself (Pentecost) doesn't need this at all -- it's
|
||||
* already covered by the ordinary Sunday lookup, and by
|
||||
* EASTER_OFFSET_STARTS below if it also has an octave to start. */
|
||||
export type MovableAnchor =
|
||||
| { kind: 'easter'; offset: number }
|
||||
| { kind: 'nth-sunday-of-month'; month: number; nth: number } // nth: 1-4, or -1 for "last"
|
||||
| { kind: 'nth-sunday-of-advent'; nth: number }; // nth: 1-4 (Advent always has exactly 4 Sundays)
|
||||
|
||||
export interface TemporalFeastRecord {
|
||||
id: string;
|
||||
name: string;
|
||||
octave?: OctaveConfig;
|
||||
rank?: FeastClass;
|
||||
/** Days from Easter Sunday (0) this feast's own date falls on, for a
|
||||
* feast that ISN'T itself a Sunday -- see calendar/movable-feasts.ts.
|
||||
* `resolveTemporalId`'s own Easter-offset resolution only ever finds
|
||||
* one of the 52 canonical *Sunday* ids (it works by finding the
|
||||
* governing Sunday on-or-before a date, then looking that Sunday's own
|
||||
* offset up); a weekday-anchored feast like Immaculate Heart of Mary
|
||||
* (Easter+69, always a Saturday) is invisible to that lookup no matter
|
||||
* what, so it needs this separate field plus calendar/movable-feasts.ts's
|
||||
* own override pass instead. A feast that *is* a Sunday (Pentecost)
|
||||
* doesn't need this at all -- it's already covered by the ordinary
|
||||
* Sunday lookup, and by EASTER_OFFSET_STARTS below if it also has an
|
||||
* octave to start. */
|
||||
easterOffset?: number;
|
||||
anchor?: MovableAnchor;
|
||||
/** Christ the King's own rule: always wins the day outright, no rank
|
||||
* contest at all (see calendar/movable-feasts.ts's applyMovableFeasts) --
|
||||
* every other movable feast instead loses to an equal-or-higher-ranked
|
||||
* sanctoral winner and is commemorated alongside it instead. */
|
||||
unconditional?: boolean;
|
||||
}
|
||||
|
||||
const temporalFeastModules = import.meta.glob<{ default: TemporalFeastRecord }>(
|
||||
@@ -42,14 +53,14 @@ export function getTemporalFeastRecord(id: string): TemporalFeastRecord | undefi
|
||||
return temporalFeastsById.get(id);
|
||||
}
|
||||
|
||||
/** Every temporal feast record carrying its own `easterOffset` -- the
|
||||
* table calendar/movable-feasts.ts's applyMovableFeasts scans. Adding a
|
||||
* new weekday-anchored movable feast (an Ember/Rogation day, a Sacred-
|
||||
* Heart-family Marian feast, a Lenten Friday devotion, ...) is just a new
|
||||
* `data/calendar/temporal-feasts/<id>.yml` file with `rank` and
|
||||
* `easterOffset` set -- no new code. */
|
||||
/** Every temporal feast record carrying its own `anchor` -- the table
|
||||
* calendar/movable-feasts.ts's applyMovableFeasts scans. Adding a new
|
||||
* movable feast (an Ember/Rogation day, a Sacred-Heart-family Marian
|
||||
* feast, a Lenten Friday devotion, ...) is just a new
|
||||
* `data/calendar/temporal-feasts/<id>.yml` file with `rank` and `anchor`
|
||||
* set -- no new code. */
|
||||
export function movableTemporalFeasts(): TemporalFeastRecord[] {
|
||||
return [...temporalFeastsById.values()].filter((f) => f.easterOffset !== undefined);
|
||||
return [...temporalFeastsById.values()].filter((f) => f.anchor !== undefined);
|
||||
}
|
||||
|
||||
/** Fixed-calendar-date starts (MM-DD -> temporal feast id). Only Christmas
|
||||
|
||||
Reference in New Issue
Block a user