aboutsummaryrefslogtreecommitdiff
path: root/lib/kernel/date_spec.mli
blob: 70a303020fba14abc18eaae05a85ee8a2c1a91ff (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
(** A sanctoral entry's date expression.

    [Fixed] is a civil (month, day) that recurs every year. The other two move:
    [Easter_offset] is a signed day count from the rite's own Easter, and
    [Nth_weekday] is the nth (or nth-from-last) given weekday of a month.

    Sunday-relative forms ("the Sunday on or after 2 November") are
    deliberately absent: nothing in this repository needs one yet, and the same
    mechanism admits them when something does. *)
type t =
  | Fixed of { month : int; day : int }
  | Easter_offset of int
  | Nth_weekday of { month : int; nth : int; weekday : Date.weekday }
[@@deriving sexp]

(** Validates against the leap-year maximum, so 29 February is constructible. *)
val fixed : month:int -> day:int -> (t, string) result

(** Rejects an offset further than a year from Easter in either direction --
    past that, a spec is a data error rather than a calendar. Deliberately
    generous: Septuagesima is Easter-63 and Time after Pentecost runs well past
    Easter+180. *)
val easter_offset : int -> (t, string) result

(** [nth] is 1-based forward, or negative from the end ([-1] is the last).
    Rejects [nth = 0] (meaningless), [|nth| > 5] (no month has six of any
    weekday), and a month outside 1..12. *)
val nth_weekday : month:int -> nth:int -> weekday:Date.weekday -> (t, string) result

(** [resolve spec ~year ~easter] is the civil date [spec] denotes in [year],
    where [easter] is that year's Easter as the RITE reckons it -- supplied by
    the caller, never computed here, since {!Computus} ships both Gregorian and
    Julian and choosing one in the kernel would be silently wrong for a
    Julian-reckoning rite.

    [None] means "this spec does not occur in that year", per variant:
    - [Fixed]: 29 February in a common year, or a date outside 1583..9999.
    - [Easter_offset]: the resolved date falls outside 1583..9999. This is
      reachable at the domain edges -- {!Date.add_days} is unbounded total
      arithmetic and will happily denote a date outside the domain.
    - [Nth_weekday]: the month has no such occurrence (a fifth Sunday it
      lacks).

    Total: never raises on any in-range year, for any constructible spec. *)
val resolve : t -> year:int -> easter:Date.t -> Date.t option