aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/caldata/caldata_test.go48
-rw-r--r--internal/caldata/readings.go60
2 files changed, 108 insertions, 0 deletions
diff --git a/internal/caldata/caldata_test.go b/internal/caldata/caldata_test.go
index 95bb7dc..f993a12 100644
--- a/internal/caldata/caldata_test.go
+++ b/internal/caldata/caldata_test.go
@@ -5,6 +5,7 @@ import (
"testing"
"github.com/lukaszkasprzak/lectio/internal/calendar"
+ "time"
)
func TestUniversalLoads(t *testing.T) {
@@ -473,3 +474,50 @@ func TestTridentineMissalOverrides(t *testing.T) {
}
}
}
+
+// TestEFBVMSaturdayReadings pins RG 309(a)'s seasonal selection among the five
+// Missae de sancta Maria in sabbato. Without it the day was kept as Our Lady's
+// office (RG 78) but read the FERIA's Mass -- so a Saturday in Septuagesima
+// announced Our Lady and then read the parable of the labourers.
+//
+// Citations verified by colitur against both photographic scans of the 1962
+// Missal; see its lectionary_ef.ml for the per-formulary scan references.
+func TestEFBVMSaturdayReadings(t *testing.T) {
+ sel := calendar.Selection{Form: "old"}
+ for _, c := range []struct{ date, first string }{
+ {"2026-01-03", "Titus 3:4-7"}, // Christmas Time
+ {"2026-01-10", "Titus 3:4-7"}, // still Christmas Time
+ {"2026-02-14", "Ecclus 24:14-16"}, // after the Purification
+ {"2026-06-20", "Ecclus 24:14-16"}, // Time after Pentecost
+ {"2026-04-18", "Ecclus 24:14-16"}, // Paschaltide -- same first, DIFFERENT gospel
+ } {
+ day, _ := time.Parse("2006-01-02", c.date)
+ layers := []calendar.Layer{Tridentine()}
+ ld := calendar.Compute(day, sel, layers)
+ got := Readings(sel, layers, day, ld)
+ if len(got) == 0 || got[0].Citation != c.first {
+ first := "(none)"
+ if len(got) > 0 {
+ first = got[0].Citation
+ }
+ t.Errorf("%s: first reading = %q, want %q (RG 309(a), the Mass of Our Lady on Saturday)",
+ c.date, first, c.first)
+ }
+ }
+ // Paschaltide shares its first reading with the rest of the year but has
+ // its own gospel -- St John at the Cross rather than the woman in the
+ // crowd. Asserted separately because a first-reading-only test would pass
+ // with the wrong gospel.
+ day, _ := time.Parse("2006-01-02", "2026-04-18")
+ layers := []calendar.Layer{Tridentine()}
+ got := Readings(sel, layers, day, calendar.Compute(day, sel, layers))
+ var gospel string
+ for _, r := range got {
+ if r.Part == "gospel" {
+ gospel = r.Citation
+ }
+ }
+ if gospel != "John 19:25-27" {
+ t.Errorf("2026-04-18: gospel = %q, want %q (the Paschaltide formulary)", gospel, "John 19:25-27")
+ }
+}
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},
+ }
+}