aboutsummaryrefslogtreecommitdiff
path: root/internal/calendar
diff options
context:
space:
mode:
Diffstat (limited to 'internal/calendar')
-rw-r--r--internal/calendar/oracle_ef_test.go20
-rw-r--r--internal/calendar/temporal_ef.go25
-rw-r--r--internal/calendar/temporal_ef_test.go34
3 files changed, 79 insertions, 0 deletions
diff --git a/internal/calendar/oracle_ef_test.go b/internal/calendar/oracle_ef_test.go
index b765a34..79b31a5 100644
--- a/internal/calendar/oracle_ef_test.go
+++ b/internal/calendar/oracle_ef_test.go
@@ -128,6 +128,26 @@ type efAllow struct {
var efAllowList = []efAllow{
{
+ // RG 87: "Litaniae minores seu Rogationes, per se, assignantur feriis
+ // II, III et IV ante festum Ascensionis Domini." The Monday and
+ // Tuesday before the Ascension are Rogation days, and RG 128(d) gives
+ // violet to "the procession and Mass of the Greater and Lesser
+ // Litanies" -- a penitential note inside white Paschaltide.
+ //
+ // missalemeum builds no Rogation days and shows a plain white
+ // paschaltide feria. It is the outlier here, not lectio: colitur has
+ // had these since its own v0.1 and the two engines now agree day for
+ // day. Rare in practice, because a III-class saint displaces them --
+ // in 2026 all three are impeded and none appears.
+ match: func(date string, day time.Time, o efOracleDay) bool {
+ e := calendar.Easter(day.Year())
+ off := int(day.Sub(e).Hours() / 24)
+ return off == 36 || off == 37
+ },
+ field: "colour",
+ reason: "RG 87 + RG 128(d): the Rogation Monday and Tuesday before the Ascension are violet; missalemeum builds no Rogation days (colitur C8)",
+ },
+ {
// RG 124, "De coloribus paramentorum", section C "De colore rubro",
// both photographic scans word for word: 124(b) gives red to
// "Sanctorum Apostolorum et Evangelistarum, in eorum die natalicio,
diff --git a/internal/calendar/temporal_ef.go b/internal/calendar/temporal_ef.go
index 7d31b8b..a043179 100644
--- a/internal/calendar/temporal_ef.go
+++ b/internal/calendar/temporal_ef.go
@@ -276,6 +276,31 @@ 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 87: "Litaniae minores seu Rogationes, per se, assignantur feriis II,
+ // III et IV ante festum Ascensionis Domini" -- the Minor Litanies are
+ // assigned to the Monday, Tuesday and Wednesday before the Ascension.
+ // Easter+36 and +37 are that Monday and Tuesday; their colour is violet,
+ // RG 128(d) giving violet "for the procession and Mass of the Greater and
+ // Lesser Litanies", a penitential note inside white Paschaltide.
+ //
+ // The WEDNESDAY is deliberately absent. Easter+38 is by construction the
+ // Vigil of the Ascension, which is II class and wins the day outright, so
+ // there is no Rogation OFFICE to build there -- what the rubric asks for on
+ // that day is a commemoration, and that needs a channel this function does
+ // not have (it returns one office per day). colitur reaches it through a
+ // movable-date entry in its own sanctoral overlay; here it stays unbuilt
+ // rather than half-built, which is why this comment says so.
+ //
+ // The rank is left as the feria's: these are IV-class days per annum and
+ // any III-class saint displaces them, which is what makes the office rare
+ // -- in 2026 all three are impeded and none appears at all.
+ if rog := daysBetween(easter, date); rog == 36 || rog == 37 {
+ slug := "ef-rogation-monday"
+ if rog == 37 {
+ slug = "ef-rogation-tuesday"
+ }
+ return efCel(season, slug, Violet, rank, week)
+ }
// 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
diff --git a/internal/calendar/temporal_ef_test.go b/internal/calendar/temporal_ef_test.go
index 1290a59..e8ef391 100644
--- a/internal/calendar/temporal_ef_test.go
+++ b/internal/calendar/temporal_ef_test.go
@@ -300,3 +300,37 @@ func TestTemporalEFBVMSaturday(t *testing.T) {
}
}
}
+
+// TestTemporalEFRogationDays pins RG 87's Minor Litanies: "Litaniae minores seu
+// Rogationes, per se, assignantur feriis II, III et IV ante festum Ascensionis
+// Domini." Easter+36 and +37 are that Monday and Tuesday; RG 128(d) makes them
+// violet, "for the procession and Mass of the Greater and Lesser Litanies" --
+// a penitential note inside white Paschaltide.
+//
+// The Wednesday is asserted in the opposite direction on purpose. Easter+38 is
+// by construction the Vigil of the Ascension, II class, which wins the day; the
+// rubric asks for a COMMEMORATION there, which this function cannot express
+// since it returns one office per day. A change that extended the Rogation
+// branch to +38 to "complete" the rubric would silently displace the Vigil, and
+// this test is what stops it.
+func TestTemporalEFRogationDays(t *testing.T) {
+ // 2027: Easter is 28 March, so the Rogation days are 3-5 May and the
+ // Monday is unimpeded that year.
+ easter := d("2027-03-28")
+ for off, want := range map[int]string{36: "ef-rogation-monday", 37: "ef-rogation-tuesday"} {
+ day := easter.AddDate(0, 0, off)
+ got := temporalEF(day)
+ if got.Cel.Slug != want {
+ t.Errorf("Easter+%d (%s): slug = %q, want %q", off, day.Format("2006-01-02"), got.Cel.Slug, want)
+ }
+ if got.Cel.Colour != Violet {
+ t.Errorf("Easter+%d (%s): colour = %s, want violet (RG 128(d))",
+ off, day.Format("2006-01-02"), got.Cel.Colour)
+ }
+ }
+ // Easter+38 is the Ascension's Vigil and must stay so.
+ if got := temporalEF(easter.AddDate(0, 0, 38)); got.Cel.Slug != "ef-ascension-vigil" {
+ t.Errorf("Easter+38: slug = %q, want ef-ascension-vigil -- the Rogation Wednesday's "+
+ "commemoration cannot be built here without displacing the Vigil", got.Cel.Slug)
+ }
+}