From 2dc44fbbc581430e2038e2ff6d67265fe716791c Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Tue, 18 Aug 2026 16:27:05 +0200 Subject: feat(ef): the Mass of Our Lady on Saturday (RG 309(a)) RG 78 gave the day Our Lady's office and its white colour; this gives it her Mass. RG 309(a): "Missae de sancta Maria in sabbato ... iuxta temporum diversitatem" -- the Missal prints five formularies and the season selects among them. Without it the day was kept as Our Lady's and then read the FERIA's Mass, so a Saturday in Septuagesima announced Our Lady and read the parable of the labourers. colitur hit and fixed the identical bug in its own v0.3.0. The five, with the two seams that are easy to get wrong: Advent Isa 7:10-15 / Luke 1:26-38 Christmas Time Titus 3:4-7 / Luke 2:15-20 ...and on until the Purification (2 February), not merely to the end of Christmas Time thereafter Ecclus 24:14-16 / Luke 11:27-28 Paschaltide Ecclus 24:14-16 / John 19:25-27 -- same first reading, different gospel, which is why the test asserts the gospel separately Citations are colitur's, verified there against both photographic scans. The Advent formulary is unreachable as the Saturday office -- Advent has no IV-class ferias -- and is kept anyway, because RG 309's subject is the Masses themselves and a missing branch would fall through silently if the rank rules ever changed. Guarded on the office rather than the day: a sanctoral feast that outranks the Saturday keeps its own readings, so the observed slug must still be the temporal one. All twelve BVM Saturdays of 2026 now agree with colitur on readings, where none did before. --- internal/caldata/readings.go | 60 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'internal/caldata/readings.go') diff --git a/internal/caldata/readings.go b/internal/caldata/readings.go index 3e46ccd..62ef1fc 100644 --- a/internal/caldata/readings.go +++ b/internal/caldata/readings.go @@ -2,6 +2,7 @@ package caldata import ( "sort" + "strings" "time" "github.com/lukaszkasprzak/lectio/internal/calendar" @@ -56,6 +57,9 @@ func Readings(sel calendar.Selection, layers []calendar.Layer, date time.Time, d // weekday cycle (I/II). Sunday-slugs and weekday-slugs are disjoint, so // trying both cycle suffixes is unambiguous. if sel.Form == "old" { + if r := efBVMSaturdayReadings(day); len(r) > 0 { + return r + } if r := TemporalReadings("old", day.Observed.Slug); len(r) > 0 { return r } @@ -85,3 +89,59 @@ func Readings(sel calendar.Selection, layers []calendar.Layer, date time.Time, d } return nil } + +// efBVMSaturdayReadings returns the Mass of Our Lady on Saturday, when that is +// the day's office. +// +// RG 78 keeps the office on a Saturday whose feria is IV class (see +// temporal_ef.go, which gives the day its white colour). RG 309(a) then says +// which Mass is said: "Missae de sancta Maria in sabbato ... iuxta temporum +// diversitatem" -- the Missal prints five formularies and the season selects +// among them. Without this the day was kept as Our Lady's but read the feria's +// Mass, so a Saturday in Septuagesima announced Our Lady and then read the +// parable of the labourers. +// +// Citations are colitur's, which verified each against both photographic scans +// of the 1962 Missal; see its lib/rites/rite_ef/lectionary_ef.ml, +// bvm_saturday_citations, for the per-formulary scan references. +// +// The Advent formulary is included and is STRUCTURALLY UNREACHABLE as the +// Saturday office: Advent has no IV-class ferias, so the office never falls +// there. It is kept because RG 309's subject is the Masses themselves, not +// only the days they land on -- the Mass exists -- and because a missing +// branch would silently fall through if the rank rules ever changed. +func efBVMSaturdayReadings(day calendar.LiturgicalDay) []calendar.Reading { + // The office, not merely the day: a Saturday IV-class feria that nothing + // outranked. A sanctoral feast that won the day keeps its own readings, so + // the observed slug must still be the temporal one -- which the office + // deliberately reuses rather than replacing (see temporal_ef.go). + if day.Weekday != time.Saturday || day.Observed.Rank != calendar.RankClass4 { + return nil + } + if !strings.HasPrefix(day.Observed.Slug, "ef-") || !strings.HasSuffix(day.Observed.Slug, "-saturday") { + return nil + } + first, gospel := "", "" + switch day.Season { + case calendar.Advent: + first, gospel = "Isa 7:10-15", "Luke 1:26-38" + case calendar.Christmas: + first, gospel = "Titus 3:4-7", "Luke 2:15-20" + case calendar.TimeAfterEpiphany: + // The Christmastide formulary runs on until the Purification (2 + // February), not merely to the end of Christmas Time. + if m := day.Date.Month(); m == time.January || (m == time.February && day.Date.Day() <= 2) { + first, gospel = "Titus 3:4-7", "Luke 2:15-20" + } else { + first, gospel = "Ecclus 24:14-16", "Luke 11:27-28" + } + case calendar.Easter_: + first, gospel = "Ecclus 24:14-16", "John 19:25-27" + default: // Septuagesima, Lent, Passiontide, Time after Pentecost + first, gospel = "Ecclus 24:14-16", "Luke 11:27-28" + } + return []calendar.Reading{ + {Part: "first", Citation: first}, + {Part: "gospel", Citation: gospel}, + } +} -- cgit v1.3