1 import Base 2 import ./date.bend as D 3 4 type Period.Error is Data: 5 OutOfRange{} 6 7 type Period is Data: 8 Period{years: U32, months: U32, days: U32} 9 10 def 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 19 def 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 28 def 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 37 def 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 42 def 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 51 def 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 60 def 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 69 def 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 74 def 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 83 def 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 94 def 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 103 def 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 108 def Period.emonth(+y: U32, +m: U32) -> U32: 109 U32.add(U32.mul(y, 12), m) 110 111 def 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 124 def 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 134 def 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 139 def 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 148 def 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 159 def 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 168 def 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))