// The session clock (spec session-clock ยง5).
///|
/// Where "now" comes from. The library never reads a wall clock: the
/// composition root (the CLI's `main`) builds the real clock, and tests and
/// embedders pass fixed or fake ones. `now` may raise; the evaluator turns a
/// raise into a data error.
pub(all) struct Clock {
now : () -> @time.ZonedDateTime raise
}
///|
/// A clock that always gives `at`.
pub fn Clock::fixed(at : @time.ZonedDateTime) -> Clock {
{ now: () => at, }
}
///|
/// The instant `seconds` (since 1970-01-01T00:00Z) and `nanos` seen at `zone`:
/// a timestamp with the zone's offset and `precision` fraction digits, cut,
/// never rounded. A date outside 0001-9999 is a datetime field overflow.
pub fn clock_timestamp(
seconds : Int64,
nanos : Int32,
zone : ZoneOffset,
precision : Int32,
) -> @value.Timestamp raise OpFailure {
let shifted = big64(seconds) * nanos_per_second +
big64(nanos.to_int64()) +
minutes_nanos(zone.minutes)
let (days, rest) = floor_split(shifted, nanos_per_day)
if !fits_int64(days) {
raise DataFailure("datetime field overflow: the clock is out of range")
}
let date = date_of_days(days.to_int64())
let of_day = rest.to_int64()
let second_of_day = (of_day / 1000000000L).to_int()
let fraction = (of_day % 1000000000L).to_int()
let unit = fraction_unit(precision)
let time = @value.Time::Time(
hour=second_of_day / 3600,
minute=second_of_day % 3600 / 60,
second=second_of_day % 60,
nanos=fraction - fraction % unit,
precision~,
offset=Some(zone.minutes),
) catch {
_ => raise DataFailure("a time out of range")
}
@value.Timestamp::Timestamp(date, time)
}