summaryrefslogtreecommitdiff
path: root/lib/kernel/date_spec.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-11 11:37:32 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-11 11:37:32 +0200
commit57322e766a0ece579dafcddcfe2572676a98fca8 (patch)
tree0e6ca11d29000b956a2c4bb65e436b0eb9251809 /lib/kernel/date_spec.ml
parent270be2561ae440cea814566b74712750352e56e4 (diff)
downloadcolitur-57322e766a0ece579dafcddcfe2572676a98fca8.tar.gz
colitur-57322e766a0ece579dafcddcfe2572676a98fca8.zip
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.
Diffstat (limited to 'lib/kernel/date_spec.ml')
-rw-r--r--lib/kernel/date_spec.ml53
1 files changed, 53 insertions, 0 deletions
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