period.bend

bend-datetime@0.4.0.0 · 171 lines · 0 definitions

1import Base
2import bend-datetime@0.4.0.0/date.bend as D
3
4type Period.Error is Data:
5  OutOfRange{}
6
7type Period is Data:
8  Period{years: U32, months: U32, days: U32}
9
10def Period.from.y(
11  +years: U32, +extra: U32, +months: U32, +days: U32, ok: Bool
12) -> Result<&2, &2, Period.Error, Period>:
13  match ok:
14    case False{}:
15      Fail{OutOfRange{}}
16    case True{}:
17      Done{Period{U32.add(years, extra), months, days}}
18
19def Period.from(
20  +years: U32, +months: U32, +days: U32
21) -> Result<&2, &2, Period.Error, Period>:
22  +extra = U32.div(months, 12)
23  Period.from.y(
24    years, extra, U32.mod(months, 12), days,
25    U32.is_le(extra, U32.sub(U32.not(0), years))
26  )
27
28def Period.add_to.days(
29  r: Result<&2, &2, D.Date.Error, D.Date>, pd: U32
30) -> Result<&2, &2, D.Date.Error, D.Date>:
31  match r:
32    case Fail{e}:
33      Fail{e}
34    case Done{date}:
35      D.Date.add_days(date, pd)
36
37def Period.add_to.from(
38  +y: U32, +m: U32, +d: U32, +pd: U32
39) -> Result<&2, &2, D.Date.Error, D.Date>:
40  Period.add_to.days(D.Date.from(y, m, d), pd)
41
42def Period.add_to.day(
43  +y: U32, +m: U32, +d: U32, +pd: U32, +dim: U32, le: Bool
44) -> Result<&2, &2, D.Date.Error, D.Date>:
45  match le:
46    case True{}:
47      Period.add_to.from(y, m, d, pd)
48    case False{}:
49      Period.add_to.from(y, m, dim, pd)
50
51def Period.add_to.dim(
52  +y: U32, +m: U32, +d: U32, +pd: U32, dimr: Maybe<&2, U32>
53) -> Result<&2, &2, D.Date.Error, D.Date>:
54  match dimr:
55    case None{}:
56      Fail{D.OutOfRange{}}
57    case Some{+dim}:
58      Period.add_to.day(y, m, d, pd, dim, U32.is_le(d, dim))
59
60def Period.add_to.y3(
61  +y: U32, +m: U32, +d: U32, +pd: U32, ok: Bool
62) -> Result<&2, &2, D.Date.Error, D.Date>:
63  match ok:
64    case False{}:
65      Fail{D.OutOfRange{}}
66    case True{}:
67      Period.add_to.dim(y, m, d, pd, D.Date.dim(m, D.Date.leap(y)))
68
69def Period.add_to.y2(
70  +y: U32, +m: U32, +d: U32, +pd: U32
71) -> Result<&2, &2, D.Date.Error, D.Date>:
72  Period.add_to.y3(y, m, d, pd, Bool.and(U32.is_ge(y, 1), U32.is_le(y, 9999)))
73
74def Period.add_to.mc(
75  +y: U32, +m: U32, +d: U32, +pd: U32, ge: Bool
76) -> Result<&2, &2, D.Date.Error, D.Date>:
77  match ge:
78    case False{}:
79      Period.add_to.y2(y, m, d, pd)
80    case True{}:
81      Period.add_to.y2(U32.add(y, 1), U32.sub(m, 12), d, pd)
82
83def Period.add_to.ya(
84  +dy: U32, +py: U32, +dm: U32, +pm: U32, +dd: U32, +pd: U32, ok: Bool
85) -> Result<&2, &2, D.Date.Error, D.Date>:
86  match ok:
87    case False{}:
88      Fail{D.OutOfRange{}}
89    case True{}:
90      +y = U32.add(dy, py)
91      +m = U32.add(dm, pm)
92      Period.add_to.mc(y, m, dd, pd, U32.is_gt(m, 12))
93
94def Period.add_to.go(
95  date: D.Date, p: Period
96) -> Result<&2, &2, D.Date.Error, D.Date>:
97  match date p:
98    case D.Date{+dy, +dm, +dd} Period{+py, +pm, +pd}:
99      Period.add_to.ya(
100        dy, py, dm, pm, dd, pd, U32.is_le(py, U32.sub(U32.not(0), dy))
101      )
102
103def Period.add_to(
104  p: Period, date: D.Date
105) -> Result<&2, &2, D.Date.Error, D.Date>:
106  Period.add_to.go(date, p)
107
108def Period.emonth(+y: U32, +m: U32) -> U32:
109  U32.add(U32.mul(y, 12), m)
110
111def Period.between.ms.from(
112  +end: D.Date, +tm: U32, r: Result<&2, &2, D.Date.Error, D.Date>
113) -> Result<&2, &2, Period.Error, Period>:
114  match r:
115    case Fail{_}:
116      Fail{OutOfRange{}}
117    case Done{start2}:
118      Period.from(
119        U32.div(tm, 12),
120        U32.mod(tm, 12),
121        U32.sub(D.Date.to_days(end), D.Date.to_days(start2))
122      )
123
124def Period.between.ms.add(
125  +start: D.Date, +end: D.Date, +tm: U32,
126  rp: Result<&2, &2, Period.Error, Period>
127) -> Result<&2, &2, Period.Error, Period>:
128  match rp:
129    case Fail{_}:
130      Fail{OutOfRange{}}
131    case Done{pM}:
132      Period.between.ms.from(end, tm, Period.add_to(pM, start))
133
134def Period.between.ms(
135  +start: D.Date, +end: D.Date, +tm: U32
136) -> Result<&2, &2, Period.Error, Period>:
137  Period.between.ms.add(start, end, tm, Period.from(0, tm, 0))
138
139def Period.between.day(
140  start: D.Date, end: D.Date, sm: U32, em: U32, borrow: Bool
141) -> Result<&2, &2, Period.Error, Period>:
142  match borrow:
143    case True{}:
144      Period.between.ms(start, end, U32.sub(U32.sub(em, sm), 1))
145    case False{}:
146      Period.between.ms(start, end, U32.sub(em, sm))
147
148def Period.between.go(
149  +start: D.Date, +end: D.Date
150) -> Result<&2, &2, Period.Error, Period>:
151  match start end:
152    case D.Date{+sy, +sm, +sd} D.Date{+ey, +em, +ed}:
153      Period.between.day(
154        start, end,
155        Period.emonth(sy, sm), Period.emonth(ey, em),
156        U32.is_lt(ed, sd)
157      )
158
159def Period.between.ord(
160  start: D.Date, end: D.Date, lt: Bool
161) -> Result<&2, &2, Period.Error, Period>:
162  match lt:
163    case True{}:
164      Fail{OutOfRange{}}
165    case False{}:
166      Period.between.go(start, end)
167
168def Period.between(
169  +start: D.Date, +end: D.Date
170) -> Result<&2, &2, Period.Error, Period>:
171  Period.between.ord(start, end, D.Date.is_lt(end, start))