From 57322e766a0ece579dafcddcfe2572676a98fca8 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Tue, 11 Aug 2026 11:37:32 +0200 Subject: kernel: Names, Citation and Date_spec Names is an open language-keyed assoc kept in canonical order so equal name sets serialise identically. Citation carries references only, never text. Date_spec ships the one form the EF sanctoral needs; 29 February is constructible and resolves to None in common years. --- lib/kernel/date_spec.ml | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 lib/kernel/date_spec.ml (limited to 'lib/kernel/date_spec.ml') diff --git a/lib/kernel/date_spec.ml b/lib/kernel/date_spec.ml new file mode 100644 index 0000000..1d0da00 --- /dev/null +++ b/lib/kernel/date_spec.ml @@ -0,0 +1,53 @@ +(* How a sanctoral entry expresses its date. Plan 2 ships the only form the EF + sanctoral needs -- a fixed calendar date -- because EF movable feasts come + from the rite's temporal code, not from data. Sunday-relative and + Easter-relative forms arrive with the OF sanctoral. *) +type t = Fixed of { month : int; day : int } + +(* Leap-year maximum, so 29 February is constructible; it simply does not + resolve in a common year. *) +let max_day = function + | 1 | 3 | 5 | 7 | 8 | 10 | 12 -> 31 + | 4 | 6 | 9 | 11 -> 30 + | 2 -> 29 + | _ -> 0 + +let fixed ~month ~day = + if month < 1 || month > 12 then Error (Printf.sprintf "date_spec: month %d out of range 1..12" month) + else if day < 1 || day > max_day month then + Error (Printf.sprintf "date_spec: day %d out of range for month %d" day month) + else Ok (Fixed { month; day }) + +let resolve t ~year = + match t with + | Fixed { month; day } -> Result.to_option (Date.make ~year ~month ~day) + +let sexp_of_t = function + | Fixed { month; day } -> + Sexplib0.Sexp.List [ + Sexplib0.Sexp_conv.sexp_of_string "Fixed"; + Sexplib0.Sexp.List [ + Sexplib0.Sexp.List [ + Sexplib0.Sexp_conv.sexp_of_string "month"; + Sexplib0.Sexp_conv.sexp_of_int month + ]; + Sexplib0.Sexp.List [ + Sexplib0.Sexp_conv.sexp_of_string "day"; + Sexplib0.Sexp_conv.sexp_of_int day + ] + ] + ] + +let t_of_sexp sexp = + match sexp with + | Sexplib0.Sexp.List [ + Sexplib0.Sexp.Atom "Fixed"; + Sexplib0.Sexp.List [ + Sexplib0.Sexp.List [ Sexplib0.Sexp.Atom "month"; month_sexp ]; + Sexplib0.Sexp.List [ Sexplib0.Sexp.Atom "day"; day_sexp ] + ] + ] -> + let month = Sexplib0.Sexp_conv.int_of_sexp month_sexp in + let day = Sexplib0.Sexp_conv.int_of_sexp day_sexp in + Fixed { month; day } + | _ -> Sexplib0.Sexp_conv.of_sexp_error "Date_spec.t_of_sexp: expected Fixed record format" sexp -- cgit v1.3