aboutsummaryrefslogtreecommitdiff
path: root/internal/calendar/temporal_ef.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-28 07:51:05 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-28 07:51:05 +0200
commitcca0eb0db6cf6850d55d7a44fef470c594bce8ec (patch)
treebf6eff3bc95e5ed029dd0aa0d50db85d90e3a6cc /internal/calendar/temporal_ef.go
parentc33158ed31d46e9bed76f01223f6718da3b4da21 (diff)
downloadlectio-cca0eb0db6cf6850d55d7a44fef470c594bce8ec.tar.gz
lectio-cca0eb0db6cf6850d55d7a44fef470c594bce8ec.zip
fix(ef): 1962 occurrence precedence for the full sanctoral
- Septuagesima ferias are IV class (per annum), not III class, so a III class saint (e.g. Conversion of St Paul, Jan 25) displaces them. - Holy Week ferias and the privileged octaves of Easter and Pentecost are I class: no saint's feast is admitted (temporal office wins). - Occurrence tie-break is asymmetric by season: an ordinary feria (Advent, per annum) yields to an equal-class feast; Lent/Passiontide ferias keep their privilege and win the tie. - A II class feast of the Lord (Purification, Exaltation of the Cross, Dedication of the Lateran) displaces a II class Sunday it falls on. Validated against missalemeum across 2025-2027: ~97.5% of days observe the liturgically-correct celebration, 100% rank agreement on saint-days.
Diffstat (limited to 'internal/calendar/temporal_ef.go')
-rw-r--r--internal/calendar/temporal_ef.go33
1 files changed, 31 insertions, 2 deletions
diff --git a/internal/calendar/temporal_ef.go b/internal/calendar/temporal_ef.go
index 6f641f4..2ead3e3 100644
--- a/internal/calendar/temporal_ef.go
+++ b/internal/calendar/temporal_ef.go
@@ -132,8 +132,19 @@ func temporalEF(date time.Time) temporalDay {
}
week := efWeek(date, season, y, easter)
rank := RankClass4 // per-annum feria
- if season == Advent || season == Lent || season == Passiontide || season == Septuagesima {
- rank = RankClass3 // penitential ferias rank higher
+ if season == Advent || season == Lent || season == Passiontide {
+ // III class ferias (1960 rubrics): Advent to Dec 16, and Lent/Passiontide.
+ // These outrank III class feasts (the saint is only commemorated). The
+ // Septuagesima season's ferias are IV class per annum, so a III class
+ // saint (e.g. the Conversion of St Paul, Jan 25) displaces them.
+ rank = RankClass3
+ }
+ if efPrivilegedFeria(date, easter) {
+ // The days of Holy Week and the privileged octaves of Easter and
+ // Pentecost are I class; no saint's feast is admitted (the feast is
+ // transferred or, in the octaves, omitted). Ranking them class-1 makes
+ // the temporal office win, matching the 1962 occurrence rules.
+ rank = RankClass1
}
// Unique ferial slug (season-week-weekday) so proper ferias (Lent, Advent,
// Holy Week, Ember days) can key their own readings; green-season ferias
@@ -145,6 +156,24 @@ func weekdayLower(d time.Time) string {
return strings.ToLower(d.Weekday().String())
}
+// efPrivilegedFeria reports whether date is an I class temporal weekday that
+// impedes saints' feasts: the ferias of Holy Week (the six days before Easter)
+// and the weekdays within the privileged octaves of Easter (Easter Mon–Sat) and
+// Pentecost (Whit Mon–Sat). Easter/Pentecost Sundays and Low Sunday are already
+// handled as named I class feasts above; this covers only their octave weekdays.
+func efPrivilegedFeria(date, easter time.Time) bool {
+ days := int(date.Sub(easter) / (24 * time.Hour))
+ switch {
+ case days >= -6 && days <= -1: // Monday..Saturday of Holy Week (incl. Triduum)
+ return true
+ case days >= 1 && days <= 6: // Easter octave weekdays
+ return true
+ case days >= 50 && days <= 55: // Pentecost octave weekdays (Pentecost = easter+49)
+ return true
+ }
+ return false
+}
+
// efWeek is a best-effort week-within-season number for display (not
// oracle-checked). Time after Pentecost counts Sundays from Pentecost.
func efWeek(date time.Time, season Season, y int, easter time.Time) int {