aboutsummaryrefslogtreecommitdiff
path: root/internal/calendar/temporal_ef.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-28 08:49:57 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-28 08:49:57 +0200
commit70b805018f0ae0d8424c3c7de8f1169476385a35 (patch)
tree82de4bf52e588ab0c1f31ce0fc68d4b97b3d0de6 /internal/calendar/temporal_ef.go
parentd3b7f41c7a823bda6b7bafae3a156348c4ce687f (diff)
downloadlectio-70b805018f0ae0d8424c3c7de8f1169476385a35.tar.gz
lectio-70b805018f0ae0d8424c3c7de8f1169476385a35.zip
feat: complete OF+EF calendar coverage (movable feasts, vigils, ember days, transfers)
Ordinary Form: - Add the movable Marian memorials that have no fixed date: Mary, Mother of the Church (Monday after Pentecost) and the Immaculate Heart (Saturday after the Sacred Heart), computed in computeOF. - General Norms 60: two coinciding obligatory memorials both become optional and the ferial is observed -- except Mary, Mother of the Church, which keeps precedence per its 2018 decree. Extraordinary Form: - Capture fixed-date vigils in the sanctoral harvest (Christmas, the Nativity of St John the Baptist, Sts Peter & Paul, St Lawrence, the Assumption, All Saints, the Immaculate Conception) -- previously filtered out. - temporalEF: the movable vigils of the Ascension (I class eve) and Pentecost (Whitsun Eve), and the September/Advent Ember days (II class violet ferias). - transferIfImpededEF: the Annunciation transfers past Holy Week/the Easter octave to the Monday after Low Sunday; All Souls moves to Nov 3 when Nov 2 is a Sunday. - genlect harvests proper Masses for the new Ember/vigil days. Adds TestEFCoverage + TestOFMovableMemorials. Bumps to 0.39.0. Verified per-day: OF 0 real errors vs calapi (2026-2028, 100% correct-observed); EF 0 real errors vs missalemeum (2025-2027), residual = deep-tail vigil-occurrence and St Joseph Passiontide-transfer edges (~1-2 days/yr, documented).
Diffstat (limited to 'internal/calendar/temporal_ef.go')
-rw-r--r--internal/calendar/temporal_ef.go44
1 files changed, 44 insertions, 0 deletions
diff --git a/internal/calendar/temporal_ef.go b/internal/calendar/temporal_ef.go
index 2ead3e3..877545c 100644
--- a/internal/calendar/temporal_ef.go
+++ b/internal/calendar/temporal_ef.go
@@ -114,6 +114,10 @@ func temporalEF(date time.Time) temporalDay {
return efCel(TimeAfterPentecost, "ef-sacred-heart", White, RankClass1, 0)
case sameDay(date, efChristTheKing(y)):
return efCel(TimeAfterPentecost, "ef-christ-the-king", White, RankClass1, 0)
+ case sameDay(date, easter.AddDate(0, 0, 38)): // Wednesday: Vigil of the Ascension (II class)
+ return efCel(Easter_, "ef-ascension-vigil", White, RankClass2, 0)
+ case sameDay(date, easter.AddDate(0, 0, 48)): // Saturday: Vigil of Pentecost / Whitsun Eve (I class)
+ return efCel(Easter_, "ef-pentecost-vigil", Red, RankClass1, 0)
}
// Sundays vs ferias of the current season.
@@ -146,12 +150,52 @@ func temporalEF(date time.Time) temporalDay {
// the temporal office win, matching the 1962 occurrence rules.
rank = RankClass1
}
+ // The Ember Days of September and Advent (Wednesday, Friday, Saturday after
+ // the third Sunday of the month/season) are II class privileged ferias in
+ // violet; they displace a III class saint (only commemorated). The Lenten
+ // Ember days are already III class Lenten ferias, and the Whitsun Ember days
+ // fall inside the I class Pentecost octave handled above.
+ if es, ok := efEmberSlug(date, y); ok {
+ return efCel(season, es, Violet, RankClass2, week)
+ }
// Unique ferial slug (season-week-weekday) so proper ferias (Lent, Advent,
// Holy Week, Ember days) can key their own readings; green-season ferias
// simply have no lectionary entry and fall back to the Sunday.
return efCel(season, "ef-"+string(season)+"-"+strconv.Itoa(week)+"-"+weekdayLower(date), col, rank, week)
}
+// thirdSundayOfSeptember returns the third Sunday in September of year y.
+func thirdSundayOfSeptember(y int) time.Time {
+ d := time.Date(y, time.September, 1, 0, 0, 0, 0, time.UTC)
+ for d.Weekday() != time.Sunday {
+ d = d.AddDate(0, 0, 1)
+ }
+ return d.AddDate(0, 0, 14)
+}
+
+// efEmberSlug returns the Ember-day slug for date, if it is an Ember Wednesday,
+// Friday or Saturday of September or Advent (the day after the third Sunday +
+// 3/5/6). Returns ("", false) otherwise.
+func efEmberSlug(date time.Time, y int) (string, bool) {
+ for _, e := range []struct {
+ third time.Time
+ season string
+ }{
+ {thirdSundayOfSeptember(y), "september"},
+ {adventStart(y).AddDate(0, 0, 14), "advent"}, // 3rd Sunday of Advent
+ } {
+ switch {
+ case sameDay(date, e.third.AddDate(0, 0, 3)):
+ return "ef-" + e.season + "-ember-wed", true
+ case sameDay(date, e.third.AddDate(0, 0, 5)):
+ return "ef-" + e.season + "-ember-fri", true
+ case sameDay(date, e.third.AddDate(0, 0, 6)):
+ return "ef-" + e.season + "-ember-sat", true
+ }
+ }
+ return "", false
+}
+
func weekdayLower(d time.Time) string {
return strings.ToLower(d.Weekday().String())
}