datetime.bend source
datetime.bend on the hub · documented module
import Baseimport ./date.bend as Dimport ./duration.bend as Durimport ./instant.bend as Iimport ./period.bend as Pertype TimeFrac is Data: FracNone{} Frac{digits: Nat, value: U32}type TimeOfDay.Error is Data: InvalidTime{hour: U32, minute: U32, second: U32}type TimeOfDay is Data: TimeOfDay{hour: U32, minute: U32, second: U32, frac: TimeFrac}type UtcOffset.Error is Data: InvalidOffset{hour: U32, minute: U32} NegativeZero{}type UtcOffset is Data: OffsetZero{} OffsetEast{hour: U32, minute: U32} OffsetWest{hour: U32, minute: U32}type OffsetDateTime is Data: OffsetDateTime{date: D.Date, time: TimeOfDay, offset: UtcOffset}type LocalDateTime is Data: LocalDateTime{date: D.Date, time: TimeOfDay}def TimeOfDay.from.frac(+h: U32, +mi: U32, +s: U32, frac: TimeFrac, ok: Bool) -> Result<&2, &2, TimeOfDay.Error, TimeOfDay>: match ok: case False{}: Fail{InvalidTime{h, mi, s}} case True{}: Done{TimeOfDay{h, mi, s, frac}}def TimeOfDay.from( +h: U32, +mi: U32, +s: U32, frac: TimeFrac) -> Result<&2, &2, TimeOfDay.Error, TimeOfDay>: TimeOfDay.from.frac( h, mi, s, frac, Bool.and( U32.is_le(h, 23), Bool.and(U32.is_le(mi, 59), U32.is_le(s, 59)) ) )def UtcOffset.from.zero( z: Bool, west: Bool, +h: U32, +mi: U32) -> Result<&2, &2, UtcOffset.Error, UtcOffset>: match z west: case True{} True{}: Fail{NegativeZero{}} case True{} False{}: Done{OffsetZero{}} case False{} True{}: Done{OffsetWest{h, mi}} case False{} False{}: Done{OffsetEast{h, mi}}def UtcOffset.from.range( west: Bool, +h: U32, +mi: U32, ok: Bool) -> Result<&2, &2, UtcOffset.Error, UtcOffset>: match ok: case False{}: Fail{InvalidOffset{h, mi}} case True{}: UtcOffset.from.zero( Bool.and(U32.is_eq(h, 0), U32.is_eq(mi, 0)), west, h, mi )def UtcOffset.from( west: Bool, +h: U32, +mi: U32) -> Result<&2, &2, UtcOffset.Error, UtcOffset>: UtcOffset.from.range( west, h, mi, Bool.and(U32.is_le(h, 23), U32.is_le(mi, 59)) )def UtcOffset.zero() -> UtcOffset: OffsetZero{}def Datetime.sod(+h: U32, +mi: U32, +s: U32) -> U32: U32.add(U32.add(U32.mul(h, 3600), U32.mul(mi, 60)), s)def Datetime.off_sec(+h: U32, +mi: U32) -> U32: U32.add(U32.mul(h, 3600), U32.mul(mi, 60))def Datetime.frac_nanos(frac: TimeFrac) -> U32: match frac: case FracNone{}: 0 case Frac{digits, value}: U32.mul(value, U32.pow(10, Nat.sub(9n, digits)))def Datetime.utc_east2( days: U32, +local: U32, +off: U32, ge: Bool) -> U32 & U32: match ge: case True{}: (days, U32.sub(local, off)) case False{}: (U32.sub(days, 1), U32.sub(U32.add(local, 86400), off))def Datetime.utc_east(days: U32, +local: U32, +off: U32) -> U32 & U32: Datetime.utc_east2(days, local, off, U32.is_ge(local, off))def Datetime.utc_west2( days: U32, +local: U32, +off: U32, ge: Bool) -> U32 & U32: match ge: case True{}: (U32.add(days, 1), U32.sub(U32.add(local, off), 86400)) case False{}: (days, U32.add(local, off))def Datetime.utc_west(days: U32, +local: U32, +off: U32) -> U32 & U32: Datetime.utc_west2( days, local, off, U32.is_ge(U32.add(local, off), 86400) )def Datetime.apply_off( days: U32, local: U32, offset: UtcOffset) -> U32 & U32: match offset: case OffsetZero{}: (days, local) case OffsetEast{h, mi}: Datetime.utc_east(days, local, Datetime.off_sec(h, mi)) case OffsetWest{h, mi}: Datetime.utc_west(days, local, Datetime.off_sec(h, mi))def Datetime.unapply_off( days: U32, utc: U32, offset: UtcOffset) -> U32 & U32: match offset: case OffsetZero{}: (days, utc) case OffsetEast{h, mi}: Datetime.utc_west(days, utc, Datetime.off_sec(h, mi)) case OffsetWest{h, mi}: Datetime.utc_east(days, utc, Datetime.off_sec(h, mi))def Datetime.inst_of2(ds: U32 & U32, nanos: U32) -> I.Instant: (days, sod) = ds I.Instant{days, sod, nanos}def Datetime.inst_of( date: D.Date, time: TimeOfDay, offset: UtcOffset) -> I.Instant: match time: case TimeOfDay{h, mi, s, frac}: Datetime.inst_of2( Datetime.apply_off(D.Date.to_days(date), Datetime.sod(h, mi, s), offset), Datetime.frac_nanos(frac) )def OffsetDateTime.to_instant(odt: OffsetDateTime) -> I.Instant: match odt: case OffsetDateTime{date, time, offset}: Datetime.inst_of(date, time, offset)def OffsetDateTime.same_instant( +a: OffsetDateTime, +b: OffsetDateTime) -> Bool: I.Instant.is_eq(OffsetDateTime.to_instant(a), OffsetDateTime.to_instant(b))def OffsetDateTime.from( date: D.Date, time: TimeOfDay, offset: UtcOffset) -> OffsetDateTime: OffsetDateTime{date, time, offset}def OffsetDateTime.to_unix.map( r: Result<&2, &2, I.Instant.Error, U32>) -> Result<&2, &2, D.Date.Error, U32>: match r: case Fail{_}: Fail{D.OutOfRange{}} case Done{n}: Done{n}def OffsetDateTime.to_unix( odt: OffsetDateTime) -> Result<&2, &2, D.Date.Error, U32>: OffsetDateTime.to_unix.map(I.Instant.to_unix(OffsetDateTime.to_instant(odt)))def OffsetDateTime.from_instant.clock( offset: UtcOffset, date: D.Date, r: Result<&2, &2, TimeOfDay.Error, TimeOfDay>) -> Result<&2, &2, D.Date.Error, OffsetDateTime>: match r: case Fail{_}: Fail{D.OutOfRange{}} case Done{t}: Done{OffsetDateTime.from(date, t, offset)}def OffsetDateTime.from_instant.date( +sod: U32, offset: UtcOffset, r: Result<&2, &2, D.Date.Error, D.Date>) -> Result<&2, &2, D.Date.Error, OffsetDateTime>: match r: case Fail{e}: Fail{e} case Done{date}: OffsetDateTime.from_instant.clock( offset, date, TimeOfDay.from( U32.div(sod, 3600), U32.div(U32.mod(sod, 3600), 60), U32.mod(sod, 60), FracNone{} ) )def OffsetDateTime.from_instant.pair( ds: U32 & U32, offset: UtcOffset) -> Result<&2, &2, D.Date.Error, OffsetDateTime>: (days, sod) = ds OffsetDateTime.from_instant.date(sod, offset, D.Date.from_days(days))def OffsetDateTime.from_instant( i: I.Instant, +offset: UtcOffset) -> Result<&2, &2, D.Date.Error, OffsetDateTime>: match i: case I.Instant{days, sod, _}: OffsetDateTime.from_instant.pair( Datetime.unapply_off(days, sod, offset), offset )def OffsetDateTime.from_instant_utc( i: I.Instant) -> Result<&2, &2, D.Date.Error, OffsetDateTime>: OffsetDateTime.from_instant(i, OffsetZero{})def OffsetDateTime.add.odt( r: Result<&2, &2, D.Date.Error, OffsetDateTime>) -> Result<&2, &2, I.Instant.Error, OffsetDateTime>: match r: case Fail{_}: Fail{I.OutOfRange{}} case Done{o}: Done{o}def OffsetDateTime.add.map( offset: UtcOffset, r: Result<&2, &2, I.Instant.Error, I.Instant>) -> Result<&2, &2, I.Instant.Error, OffsetDateTime>: match r: case Fail{e}: Fail{e} case Done{i}: OffsetDateTime.add.odt(OffsetDateTime.from_instant(i, offset))def OffsetDateTime.add( +odt: OffsetDateTime, d: Dur.Duration) -> Result<&2, &2, I.Instant.Error, OffsetDateTime>: match odt: case OffsetDateTime{_, _, +offset}: OffsetDateTime.add.map( offset, I.Instant.add(OffsetDateTime.to_instant(odt), d) )def OffsetDateTime.sub( +odt: OffsetDateTime, d: Dur.Duration) -> Result<&2, &2, I.Instant.Error, OffsetDateTime>: match odt: case OffsetDateTime{_, _, +offset}: OffsetDateTime.add.map( offset, I.Instant.sub(OffsetDateTime.to_instant(odt), d) )def LocalDateTime.from(date: D.Date, time: TimeOfDay) -> LocalDateTime: LocalDateTime{date, time}def LocalDateTime.at_offset( local: LocalDateTime, offset: UtcOffset) -> OffsetDateTime: match local: case LocalDateTime{date, time}: OffsetDateTime.from(date, time, offset)def OffsetDateTime.to_local(odt: OffsetDateTime) -> LocalDateTime: match odt: case OffsetDateTime{date, time, _}: LocalDateTime.from(date, time)def OffsetDateTime.from_unix( secs: U32) -> Result<&2, &2, D.Date.Error, OffsetDateTime>: OffsetDateTime.from_instant_utc(I.Instant.from_unix(secs))def Datetime.ldt_inst(ldt: LocalDateTime) -> I.Instant: match ldt: case LocalDateTime{date, TimeOfDay{+h, +mi, +s, frac}}: I.Instant{ D.Date.to_days(date), Datetime.sod(h, mi, s), Datetime.frac_nanos(frac) }def Datetime.frac_of(+nanos: U32, z: Bool) -> TimeFrac: match z: case True{}: FracNone{} case False{}: Frac{9n, nanos}def Datetime.inst_ldt.tod( date: D.Date, r: Result<&2, &2, TimeOfDay.Error, TimeOfDay>) -> Result<&2, &2, I.Instant.Error, LocalDateTime>: match r: case Fail{_}: Fail{I.OutOfRange{}} case Done{tod}: Done{LocalDateTime{date, tod}}def Datetime.inst_ldt.date( +sod: U32, +nanos: U32, r: Result<&2, &2, D.Date.Error, D.Date>) -> Result<&2, &2, I.Instant.Error, LocalDateTime>: match r: case Fail{_}: Fail{I.OutOfRange{}} case Done{date}: Datetime.inst_ldt.tod( date, TimeOfDay.from( U32.div(sod, 3600), U32.div(U32.mod(sod, 3600), 60), U32.mod(sod, 60), Datetime.frac_of(nanos, U32.is_eq(nanos, 0)) ) )def Datetime.inst_ldt( i: I.Instant) -> Result<&2, &2, I.Instant.Error, LocalDateTime>: match i: case I.Instant{+days, +sod, +nanos}: Datetime.inst_ldt.date(sod, nanos, D.Date.from_days(days))def LocalDateTime.add.map( r: Result<&2, &2, I.Instant.Error, I.Instant>) -> Result<&2, &2, I.Instant.Error, LocalDateTime>: match r: case Fail{e}: Fail{e} case Done{i}: Datetime.inst_ldt(i)def LocalDateTime.add( ldt: LocalDateTime, d: Dur.Duration) -> Result<&2, &2, I.Instant.Error, LocalDateTime>: LocalDateTime.add.map(I.Instant.add(Datetime.ldt_inst(ldt), d))def LocalDateTime.sub( ldt: LocalDateTime, d: Dur.Duration) -> Result<&2, &2, I.Instant.Error, LocalDateTime>: LocalDateTime.add.map(I.Instant.sub(Datetime.ldt_inst(ldt), d))def LocalDateTime.until( start: LocalDateTime, end: LocalDateTime) -> Result<&2, &2, I.Instant.Error, Dur.Duration>: I.Instant.until(Datetime.ldt_inst(start), Datetime.ldt_inst(end))def LocalDateTime.add_period.map( time: TimeOfDay, r: Result<&2, &2, D.Date.Error, D.Date>) -> Result<&2, &2, D.Date.Error, LocalDateTime>: match r: case Fail{e}: Fail{e} case Done{date2}: Done{LocalDateTime{date2, time}}def LocalDateTime.add_period( ldt: LocalDateTime, p: Per.Period) -> Result<&2, &2, D.Date.Error, LocalDateTime>: match ldt: case LocalDateTime{+date, +time}: LocalDateTime.add_period.map(time, Per.Period.add_to(p, date))def OffsetDateTime.add_period.map( offset: UtcOffset, r: Result<&2, &2, D.Date.Error, LocalDateTime>) -> Result<&2, &2, D.Date.Error, OffsetDateTime>: match r: case Fail{e}: Fail{e} case Done{ldt}: Done{LocalDateTime.at_offset(ldt, offset)}def OffsetDateTime.add_period( +odt: OffsetDateTime, p: Per.Period) -> Result<&2, &2, D.Date.Error, OffsetDateTime>: match odt: case OffsetDateTime{_, _, +offset}: OffsetDateTime.add_period.map( offset, LocalDateTime.add_period(OffsetDateTime.to_local(odt), p) )