aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-18 16:21:02 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-18 16:21:02 +0200
commitc9bdca22b2c4492fcbb6cca696fe3a8c13713867 (patch)
tree404216d9adcfe196573848998db9fba43796631a
parent7657c4126339606c7c70d4ccd336ed14e78010bc (diff)
downloadlectio-c9bdca22b2c4492fcbb6cca696fe3a8c13713867.tar.gz
lectio-c9bdca22b2c4492fcbb6cca696fe3a8c13713867.zip
feat(ef): the votive Office of Our Lady on Saturday (RG 78)
RG 78, Caput IX "De sancta Maria in sabbato": "In sabbatis, in quibus occurrit Officium de feria IV classis, fit de sancta Maria in sabbato." A Saturday whose office would otherwise be a IV-class feria keeps Our Lady's office instead, and it is white -- RG 431(e) classes that Mass as a "Missa votiva IV classis ... de B. Maria Virg.", RG 121(a) gives a votive Mass the colour of the feast-type it answers to, and RG 120(b) makes feasts of the BVM white. Smaller than it sounds, because the protasis is already computed. "A IV-class feria" is exactly what rank still being RankClass4 means at that point: every branch above has promoted Advent, Lent, Passiontide, the Ember days, the privileged ferias and the Christmas octave out of it. The SLUG IS DELIBERATELY UNCHANGED. colitur, which has had this office since its v0.3.0, does the same: a bespoke slug would recur many times a year and break the one-slug-per-liturgical-year invariant, and the readings are selected by the Mass formulary rather than by the slug. Only the colour moves. It is also the temporal office only -- a sanctoral feast that outranks the Saturday still wins and brings its own colour, exactly as before. Result: lectio and colitur now agree on the colour of EVERY DAY of 2026. Zero mismatches, where this class alone was nine days that year and 439 rows across 2005-2050 -- the largest single divergence class between the two engines. Pinned in both directions, because the tempting wrong fix is to whiten every Saturday: Lent and Advent Saturdays are III-class violet ferias and must stay violet, and Holy Saturday must too. Mutation-tested -- dropping the IV-class guard reddens the test on the Lenten Saturday, not on the per-annum ones.
-rw-r--r--internal/calendar/temporal_ef.go26
-rw-r--r--internal/calendar/temporal_ef_test.go44
2 files changed, 70 insertions, 0 deletions
diff --git a/internal/calendar/temporal_ef.go b/internal/calendar/temporal_ef.go
index 26ad930..7d31b8b 100644
--- a/internal/calendar/temporal_ef.go
+++ b/internal/calendar/temporal_ef.go
@@ -276,6 +276,32 @@ func temporalEF(date time.Time) temporalDay {
// 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.
+ // RG 78 (Caput IX, "De sancta Maria in sabbato"): "In sabbatis, in quibus
+ // occurrit Officium de feria IV classis, fit de sancta Maria in sabbato."
+ // On a Saturday whose office would otherwise be a IV-class feria, the
+ // office is the votive Office of Our Lady, and it is white -- RG 431(e)
+ // classes that Mass as "Missa votiva IV classis ... de B. Maria Virg.",
+ // RG 121(a) gives a votive Mass the colour of the feast-type it answers
+ // to, and RG 120(b) makes feasts of the BVM white.
+ //
+ // The protasis is "a IV-class feria", which by this point in the function
+ // is exactly what rank still being RankClass4 means: every branch above
+ // has already promoted Advent, Lent, Passiontide, the Ember days, the
+ // privileged ferias and the Christmas octave out of it. Nothing further
+ // needs testing.
+ //
+ // The SLUG IS DELIBERATELY UNCHANGED. colitur, which has built this office
+ // since its v0.3.0, does the same and for a reason worth repeating: a
+ // bespoke slug would recur many times a year and break the "one slug per
+ // liturgical year" invariant, and the day's readings are selected by the
+ // Mass formulary rather than by the slug. Only the colour moves here.
+ //
+ // This is the temporal office only. If a sanctoral feast outranks the
+ // IV-class Saturday it wins as before and brings its own colour, exactly
+ // as it did when this day was a plain green feria.
+ if rank == RankClass4 && date.Weekday() == time.Saturday {
+ col = White
+ }
return efCel(season, "ef-"+string(slugSeason)+"-"+strconv.Itoa(week)+"-"+weekdayLower(date), col, rank, week)
}
diff --git a/internal/calendar/temporal_ef_test.go b/internal/calendar/temporal_ef_test.go
index a7fa8b0..1290a59 100644
--- a/internal/calendar/temporal_ef_test.go
+++ b/internal/calendar/temporal_ef_test.go
@@ -1,6 +1,7 @@
package calendar
import (
+ "strings"
"testing"
"time"
)
@@ -256,3 +257,46 @@ func TestTemporalEFEpiphanyWeekAnchor(t *testing.T) {
y, anchor.Format("2006-01-02"), got)
}
}
+
+// TestTemporalEFBVMSaturday pins RG 78, the votive Office of Our Lady on
+// Saturday: "In sabbatis, in quibus occurrit Officium de feria IV classis, fit
+// de sancta Maria in sabbato." A Saturday whose office would otherwise be a
+// IV-class feria keeps Our Lady's office instead, and it is white.
+//
+// Two things are asserted in opposite directions on purpose. The office
+// applies only where the feria really is IV class, so a Saturday in Lent or
+// Advent -- III class ferias, violet -- must NOT turn white; and the slug must
+// NOT change, because the day's readings are still the feria's and a bespoke
+// slug would recur many times a year.
+func TestTemporalEFBVMSaturday(t *testing.T) {
+ // Ordinary per-annum Saturdays: IV class, so Our Lady's office, white.
+ for _, day := range []string{"2026-06-20", "2026-06-27", "2026-07-04", "2026-10-31"} {
+ got := temporalEF(d(day))
+ if got.Cel.Colour != White {
+ t.Errorf("%s: colour = %s, want white (RG 78, Our Lady on Saturday)", day, got.Cel.Colour)
+ }
+ if got.Cel.Rank != RankClass4 {
+ t.Errorf("%s: rank = %v, want class-4 -- the office does not change the rank", day, got.Cel.Rank)
+ }
+ if !strings.HasPrefix(got.Cel.Slug, "ef-time-after-pentecost-") {
+ t.Errorf("%s: slug = %q, want the ferial slug unchanged -- the readings are "+
+ "still the feria's and a bespoke slug would recur many times a year", day, got.Cel.Slug)
+ }
+ }
+ // Saturdays whose feria is NOT IV class keep their own colour. Lent and
+ // Advent ferias are III class and violet; a fix that whitened every
+ // Saturday would fail here.
+ for _, c := range []struct {
+ day string
+ colour Colour
+ }{
+ {"2026-03-07", Violet}, // Saturday in Lent
+ {"2026-12-05", Violet}, // Saturday in Advent
+ {"2026-04-04", Violet}, // Holy Saturday -- I class, privileged
+ } {
+ if got := temporalEF(d(c.day)).Cel.Colour; got != c.colour {
+ t.Errorf("%s: colour = %s, want %s -- RG 78 applies only where the feria is IV class",
+ c.day, got, c.colour)
+ }
+ }
+}