aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/calendar/precedence_ef.go27
-rw-r--r--internal/calendar/temporal_ef.go33
2 files changed, 53 insertions, 7 deletions
diff --git a/internal/calendar/precedence_ef.go b/internal/calendar/precedence_ef.go
index 81862b7..4fda626 100644
--- a/internal/calendar/precedence_ef.go
+++ b/internal/calendar/precedence_ef.go
@@ -22,13 +22,30 @@ func efRankOrder(r Rank) int {
}
// precedenceEF ranks an EF candidate for occurrence (lower = higher precedence):
-// by class first, then — at equal class — the temporal office before the
-// sanctoral (a first-cut of the 1960 occurrence table; the missalemeum oracle
-// drives refinement).
+// by class first, then a tie-break at equal class (1960 occurrence table).
+//
+// The tie-break is asymmetric by season. An ORDINARY feria (per annum, Advent,
+// Septuagesima) yields to an equal-class feast — the feast is celebrated and the
+// feria commemorated (e.g. St Francis Xavier, Dec 3, on an Advent feria). The
+// ferias of Lent and Passiontide are privileged: they outrank an equal-class
+// feast, which is only commemorated. Sundays and named temporal feasts likewise
+// win their ties. The *2 class spacing means the ±1 tie-break never crosses a
+// class boundary.
func precedenceEF(c candidate) int {
p := (6 - efRankOrder(c.Cel.Rank)) * 2 // class-1 -> 2, ferial -> 12
- if !c.Temporal {
- p++ // sanctoral yields to a temporal office of equal class
+ if c.Temporal {
+ ordinaryFeria := (c.Cel.Rank == RankClass3 || c.Cel.Rank == RankClass4) &&
+ c.Season != Lent && c.Season != Passiontide
+ if ordinaryFeria {
+ p++ // an ordinary feria yields to an equal-class feast
+ } else {
+ p-- // Sundays, named feasts, and penitential ferias win their ties
+ }
+ } else if c.Cel.Class == ClassLord && c.Cel.Rank == RankClass2 {
+ // A II class feast of the Lord takes the place of a II class Sunday it
+ // falls on (unlike a saint's feast, which is only commemorated). Give it
+ // the edge over the Sunday's tie-break bonus.
+ p -= 2
}
return p
}
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 {