Import calendar-month/week Matins Nocturn 2 content for Aug-Nov
Deploy / deploy (push) Successful in 1m24s

The later post-Pentecost Sundays' real Nocturn 2 patristic content isn't
keyed by Pentecost offset in the reference engine — it's keyed by civil
calendar month/week (the old "Scriptura occurrens" cycle), which shifts
relative to post-pentecost-NN every year depending on Easter's date. Port
Date.pm's monthday() algorithm (pre-1955/Tridentine rubrics) as a new,
date-driven id, author all 20 August-November month-week files from the
reference engine's Tempora/0MN-0.txt sources, and pool them in
hours/matins.ts as a third, independent reading source alongside the
existing temporalId lookup.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWzE3kaSZCs79fPBrrE1aF
This commit is contained in:
2026-08-23 07:45:37 -04:00
parent 25e2eeb493
commit 72a0f8072e
25 changed files with 1587 additions and 10 deletions
+74
View File
@@ -0,0 +1,74 @@
// The reference engine's own "calendar-month/week" Matins-reading identity
// (e.g. "081" = 1st Sunday of August) — genuinely anchored to the civil
// calendar, NOT Easter-offset like every other id in temporal-id.ts. Real
// Nocturn 2 patristic content for the later post-Pentecost Sundays lives
// in the reference engine's Tempora/<MMN>-0.txt files (August-November),
// keyed this way, not by post-pentecost-NN — which month/week id governs a
// given date shifts every year with Easter's date, so there is no fixed
// post-pentecost-NN <-> month-week correspondence to hard-code. See
// hours/matins.ts's buildReadingPool, which pools this id's own authored
// content (data/propers/nocturn-readings/month-week-<id>.yml) alongside
// the ordinary temporalId-keyed pool, same dual-key precedent as
// propers/bible-plan.ts's Dec25-Jan13 calendar-date stretch.
//
// Ported from DivinumOfficium's own Date.pm::monthday() sub, Tridentine/
// pre-1955 rubrics (this app's baseline throughout — see
// calendar/temporal.ts's resolveTemporalCategory and its neighbors for the
// same choice made elsewhere). Concretely, "pre-1955" here means
// Date.pm's own $modernstyle flag is false: no 1960-rubric October
// week-III vanishing (that adjustment is entirely modernstyle-gated, so
// simply never applies here), and November's backward-from-Advent
// recount still applies for week > 0 (that part is NOT modernstyle-gated
// in the source) but its extra "collapse week 1 into week 0" step is
// skipped (that step *is* modernstyle-only).
import { addDays, dayOfWeek, daysBetween } from './date-math';
import { adventStart, sundayOnOrBefore } from './temporal';
/** The Sunday that "counts as" the 1st Sunday of `month` for this
* reckoning: the Sunday on-or-before the 1st, unless the 1st falls
* Thu-Sat (in which case that partial first week has fewer than 4 days in
* `month`, so it's skipped in favor of the following Sunday). */
function firstSundayOfLiturgicalMonth(year: number, month: number): string {
const firstOfMonth = `${year}-${String(month).padStart(2, '0')}-01`;
const sunday = sundayOnOrBefore(firstOfMonth);
return dayOfWeek(firstOfMonth) >= 4 ? addDays(sunday, 7) : sunday;
}
/**
* `"MMN"` (month, 2 digits; week, 1 digit — e.g. `"081"`), or `undefined`
* if `isoDate` falls outside the reckoning's range (before the 1st Sunday
* of August, or on/after Advent's start). Only ever non-undefined for
* August through the eve of Advent.
*/
export function monthWeekId(isoDate: string): string | undefined {
const year = Number(isoDate.slice(0, 4));
const month = Number(isoDate.slice(5, 7));
if (month < 7) return undefined;
let litMonth = 0;
const firstSundays: Partial<Record<number, string>> = {};
for (let m = 8; m <= 12; m++) {
const firstSunday = firstSundayOfLiturgicalMonth(year, m);
firstSundays[m] = firstSunday;
if (isoDate >= firstSunday) {
litMonth = m;
} else {
break;
}
}
if (litMonth === 0) return undefined;
const advent = adventStart(year);
if (litMonth > 10 && isoDate >= advent) return undefined;
let week = Math.floor(daysBetween(firstSundays[litMonth]!, isoDate) / 7);
// November: renumbered by counting backward from Advent rather than
// forward from November's own 1st Sunday, for every week after the
// first — real rubric, not modernstyle-gated (see header comment).
if (litMonth === 11 && week > 0) {
week = 4 - Math.floor((daysBetween(isoDate, advent) - 1) / 7);
}
return `${String(litMonth).padStart(2, '0')}${week + 1}`;
}