hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 623c7eebf41c9d50aae67d5afc14530d56f60c78
parent 2af3a7751ccc9f453066718cf9096103e41574e3
Author: Byron Torres <b@torresjrjr.com>
Date:   Tue,  2 Aug 2022 01:12:16 +0100

time::chrono,datetime: embed instant into moment

This commit facilitates a system of unification of all temporal types.
The time::chrono::moment type now:

* Embeds the time::instant struct type.
* Uses voidable .date, .time, and .zone fields.

Datetimes and moments are now intrinsically instants and inherit the
same properties of precision and ubiquity. Pointers to such objects may
be passed around seemlessly.

	*(&dt: *time::chrono::moment)
	*(&dt: *time::instant)

The .date and .time fields now hold observed values instead of
normalized values, and are to be evalutated and accessed with the new
exported functions listed below. The dangerous transform() function is
no longer needed.

New exports:

* fn time::chrono::getzone()
* fn time::chrono::getdate()
* fn time::chrono::gettime()
* fn time::chrono::from_datetime()

Removed exports:

* fn time::chrono::from_instant() // use chrono::new()
* fn time::chrono::to_instant()   // use *(&m: *time::instant)
* fn datetime::to_instant()       // use *(&dt: *time::instant)
* fn datetime::to_moment()        // use *(&dt: *time::chrono::moment)
* fn datetime::lookupzone()       // use chrono::getzone()
* fn time::chrono::transform()    // time::chrono now handles this
* fn datetime::transform()        // time::chrono now handles this

Diffstat:
Mdatetime/arithmetic.ha | 83+++++++++----------------------------------------------------------------------
Mdatetime/chronology.ha | 29++++++++++-------------------
Mdatetime/datetime.ha | 70+++++++++++++++++++++++++---------------------------------------------
Mdatetime/format.ha | 8++++----
Mdatetime/parse.ha | 9+++++----
Mdatetime/timezone.ha | 17+----------------
Mtime/chrono/chronology.ha | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mtime/chrono/timezone.ha | 35++++++++---------------------------
8 files changed, 168 insertions(+), 224 deletions(-)

diff --git a/datetime/arithmetic.ha b/datetime/arithmetic.ha @@ -53,7 +53,7 @@ export type unit = enum int { // Equivalence means they represent the same moment in time, regardless of their // locality or observed chronological values. export fn eq(a: datetime, b: datetime) bool = { - return a.date == b.date && a.time == b.time; + return time::compare(*(&a: *time::instant), *(&b: *time::instant)) == 0; }; // Returns true if [[datetime]] "a" succeeds [[datetime]] "b". @@ -61,8 +61,7 @@ export fn eq(a: datetime, b: datetime) bool = { // Temporal order is evaluated in a universal frame of reference, regardless of // their locality or observed chronological values. export fn after(a: datetime, b: datetime) bool = { - return !eq(a, b) && - (a.date > b.date || a.date == b.date && a.time > b.time); + return time::compare(*(&a: *time::instant), *(&b: *time::instant)) == +1; }; // Returns true if [[datetime]] "a" precedes [[datetime]] "b". @@ -70,7 +69,7 @@ export fn after(a: datetime, b: datetime) bool = { // Temporal order is evaluated in a universal frame of reference, regardless of // their locality or observed chronological values. export fn before(a: datetime, b: datetime) bool = { - return !eq(a, b) && !after(a, b); + return time::compare(*(&a: *time::instant), *(&b: *time::instant)) == -1; }; // Calculates the [[period]] between two [[datetime]]s. @@ -148,7 +147,7 @@ export fn unitdiff(a: datetime, b: datetime, u: unit) i64 = { case unit::WEEK => yield unitdiff(a, b, unit::DAY) / 7; case unit::DAY => - yield math::absi(a.date - b.date): int; + yield math::absi(chrono::getdate(&a) - chrono::getdate(&b)): int; case unit::HOUR => const full_diff = diff(a, b); yield (unitdiff(a, b, unit::DAY) * 24) + full_diff.hours; @@ -202,7 +201,7 @@ export fn truncate(dt: datetime, u: unit) datetime = { 0, 0, 0, 0, )!; case unit::WEEK => - const date = dt.date - (weekday(&dt) - 1); + const date = chrono::getdate(&dt) - (weekday(&dt) - 1); const ymd = calc_ymd(date); yield new(dt.loc, 0, ymd.0, ymd.1, ymd.2, @@ -322,7 +321,7 @@ export fn add(dt: datetime, flag: calculus, pp: period...) datetime = { for (let i = 0z; i < len(pp); i += 1) { const p = pp[i]; - let latest_date = dt.date; + let latest_date = chrono::getdate(&dt); if (p.years != 0) { d_year += p.years; @@ -346,8 +345,7 @@ export fn add(dt: datetime, flag: calculus, pp: period...) datetime = { if (p.weeks != 0) { p.days += p.weeks * 7; }; - latest_date = calc_date_from_ymd( - d_year, d_month, d_day)!; + latest_date = calc_date_from_ymd(d_year, d_month, d_day)!; if (p.days != 0) { const new_ymd = calc_ymd(latest_date + p.days); d_year = new_ymd.0; @@ -376,7 +374,7 @@ export fn add(dt: datetime, flag: calculus, pp: period...) datetime = { p.nanoseconds %= ns_in_day; }; - let new_time = dt.time + p.nanoseconds; + let new_time = chrono::gettime(&dt) + p.nanoseconds; if (new_time >= ns_in_day) { overflowed_days += 1; @@ -387,8 +385,7 @@ export fn add(dt: datetime, flag: calculus, pp: period...) datetime = { }; if (overflowed_days != 0) { - const new_date = latest_date + - overflowed_days; + const new_date = latest_date + overflowed_days; const new_ymd = calc_ymd(new_date); d_year = new_ymd.0; d_month = new_ymd.1; @@ -432,68 +429,6 @@ export fn sub(dt: datetime, flag: calculus, pp: period...) datetime = { return add(dt, flag, pp...); }; -@test fn eq() void = { - const dt = new(chrono::UTC, 0, 2022, 2, 4, 3, 14, 7, 0)!; - const cases = [ - ((-768, 1, 1, 3, 14, 7, 0), false), - (( 1, 1, 1, 14, 0, 0, 1234), false), - ((2022, 2, 4, 3, 14, 7, 0), true), - ((2022, 2, 4, 3, 14, 7, 1), false), - ((2038, 1, 19, 3, 14, 7, 0), false), - ((5555, 5, 5, 5, 55, 55, 5555), false), - ]; - for (let i = 0z; i < len(cases); i += 1) { - const c = cases[i].0; - const expected = cases[i].1; - const case_dt = new(chrono::UTC, 0, - c.0, c.1, c.2, c.3, c.4, c.5, c.6)!; - assert(eq(dt, case_dt) == expected, - "equality comparison failed"); - }; -}; - -@test fn after() void = { - const dt = new(chrono::UTC, 0, 2022, 2, 4, 3, 14, 7, 0)!; - const cases = [ - ((-768, 1, 1, 3, 14, 7, 0), false), - (( 1, 1, 1, 14, 0, 0, 1234), false), - ((2020, 2, 4, 3, 14, 7, 1), false), - ((2022, 2, 4, 3, 14, 7, 0), false), - ((2022, 2, 4, 4, 1, 1, 0), true), - ((2038, 1, 19, 3, 14, 7, 0), true), - ((5555, 5, 5, 5, 55, 55, 5555), true), - ]; - for (let i = 0z; i < len(cases); i += 1) { - const c = cases[i].0; - const expected = cases[i].1; - const case_dt = new(chrono::UTC, 0, - c.0, c.1, c.2, c.3, c.4, c.5, c.6)!; - assert(after(case_dt, dt) == expected, - "incorrect date ordering in after()"); - }; -}; - -@test fn before() void = { - const dt = new(chrono::UTC, 0, 2022, 2, 4, 3, 14, 7, 0)!; - const cases = [ - ((-768, 1, 1, 3, 14, 7, 0), true), - (( 1, 1, 1, 14, 0, 0, 1234), true), - ((2020, 2, 4, 3, 14, 7, 1), true), - ((2022, 2, 4, 3, 14, 7, 0), false), - ((2022, 2, 4, 4, 1, 1, 0), false), - ((2038, 1, 19, 3, 14, 7, 0), false), - ((5555, 5, 5, 5, 55, 55, 5555), false), - ]; - for (let i = 0z; i < len(cases); i += 1) { - const c = cases[i].0; - const expected = cases[i].1; - const case_dt = new(chrono::UTC, 0, - c.0, c.1, c.2, c.3, c.4, c.5, c.6)!; - assert(before(case_dt, dt) == expected, - "incorrect date ordering in before()"); - }; -}; - @test fn diff() void = { const cases = [ ( diff --git a/datetime/chronology.ha b/datetime/chronology.ha @@ -59,12 +59,11 @@ export fn second(dt: *datetime) int = _second(dt); export fn nanosecond(dt: *datetime) int = _nanosecond(dt); fn _epochal(dt: *datetime) chrono::date = { - const ldt = transform(*dt, dt.zone.zoffset); - return ldt.date - EPOCHAL_GREGORIAN; + return chrono::getdate(dt) - EPOCHAL_GREGORIAN; }; fn _epochunix(dt: *datetime) int = { - return to_instant(*dt).sec: int; + return time::unix(*(dt: *time::instant)): int; }; fn _era(dt: *datetime) int = { @@ -81,10 +80,9 @@ fn _era(dt: *datetime) int = { }; fn _year(dt: *datetime) int = { - const ldt = transform(*dt, dt.zone.zoffset); match (dt.year) { case void => - const ymd = calc_ymd(ldt.date: chrono::date); + const ymd = calc_ymd(chrono::getdate(dt)); dt.year = ymd.0; dt.month = ymd.1; dt.day = ymd.2; @@ -95,10 +93,9 @@ fn _year(dt: *datetime) int = { }; fn _month(dt: *datetime) int = { - const ldt = transform(*dt, dt.zone.zoffset); match (dt.month) { case void => - const ymd = calc_ymd(ldt.date: chrono::date); + const ymd = calc_ymd(chrono::getdate(dt)); dt.year = ymd.0; dt.month = ymd.1; dt.day = ymd.2; @@ -109,10 +106,9 @@ fn _month(dt: *datetime) int = { }; fn _day(dt: *datetime) int = { - const ldt = transform(*dt, dt.zone.zoffset); match (dt.day) { case void => - const ymd = calc_ymd(ldt.date: chrono::date); + const ymd = calc_ymd(chrono::getdate(dt)); dt.year = ymd.0; dt.month = ymd.1; dt.day = ymd.2; @@ -123,10 +119,9 @@ fn _day(dt: *datetime) int = { }; fn _weekday(dt: *datetime) int = { - const ldt = transform(*dt, dt.zone.zoffset); match (dt.weekday) { case void => - dt.weekday = calc_weekday(ldt.date: chrono::date); + dt.weekday = calc_weekday(chrono::getdate(dt)); return dt.weekday: int; case let y: int => return y; @@ -241,10 +236,9 @@ fn _isoweek(dt: *datetime) int = { }; fn _hour(dt: *datetime) int = { - const ldt = transform(*dt, dt.zone.zoffset); match (dt.hour) { case void => - const hmsn = calc_hmsn(ldt.time: time::duration); + const hmsn = calc_hmsn(chrono::gettime(dt)); dt.hour = hmsn.0; dt.minute = hmsn.1; dt.second = hmsn.2; @@ -256,10 +250,9 @@ fn _hour(dt: *datetime) int = { }; fn _minute(dt: *datetime) int = { - const ldt = transform(*dt, dt.zone.zoffset); match (dt.minute) { case void => - const hmsn = calc_hmsn(ldt.time: time::duration); + const hmsn = calc_hmsn(chrono::gettime(dt)); dt.hour = hmsn.0; dt.minute = hmsn.1; dt.second = hmsn.2; @@ -271,10 +264,9 @@ fn _minute(dt: *datetime) int = { }; fn _second(dt: *datetime) int = { - const ldt = transform(*dt, dt.zone.zoffset); match (dt.second) { case void => - const hmsn = calc_hmsn(ldt.time: time::duration); + const hmsn = calc_hmsn(chrono::gettime(dt)); dt.hour = hmsn.0; dt.minute = hmsn.1; dt.second = hmsn.2; @@ -286,10 +278,9 @@ fn _second(dt: *datetime) int = { }; fn _nanosecond(dt: *datetime) int = { - const ldt = transform(*dt, dt.zone.zoffset); match (dt.nanosecond) { case void => - const hmsn = calc_hmsn(ldt.time: time::duration); + const hmsn = calc_hmsn(chrono::gettime(dt)); dt.hour = hmsn.0; dt.minute = hmsn.1; dt.second = hmsn.2; diff --git a/datetime/datetime.ha b/datetime/datetime.ha @@ -29,10 +29,12 @@ export type datetime = struct { }; fn init() datetime = datetime { - date = 0, - time = 0, loc = chrono::LOCAL, - zone = chrono::zone { ... }, + sec = 0, + nsec = 0, + date = void, + time = void, + zone = void, era = void, year = void, @@ -85,7 +87,7 @@ fn init() datetime = datetime { // as the clock jumped back 1 hour from 03:00 CEST to 02:00 CET. export fn new( loc: chrono::locality, - offs: (time::duration | void), + zo: (time::duration | void), fields: int... ) (datetime | invalid) = { // TODO: @@ -115,29 +117,20 @@ export fn new( const sec = defaults[5]; const nsec = defaults[6]; - let m = chrono::moment { - date = calc_date_from_ymd(year, month, day)?, - time = calc_time_from_hmsn(hour, min, sec, nsec)?, - loc = loc, - zone = chrono::zone { ... }, - }; + const mdate = calc_date_from_ymd(year, month, day)?; + const mtime = calc_time_from_hmsn(hour, min, sec, nsec)?; - // TODO: Set the correct values according to the given zo and - // locality/timezone. - // - // figuring out what zone this moment observes - if (offs is time::duration) { - // Transform inversely to the moment that would transform back - // to the current moment, then perform a zone lookup. - m = chrono::transform(m, -(offs as time::duration)); - chrono::lookupzone(&m); - } else { - // Just perform a zone lookup, then try that zone and the - // zones that are observed before and after. This requires - // knowlegde of the transition index. - //const z0 = chrono::lookupzone(*m); - //m = chrono::transform(m, -z0.zoffset); - abort("TODO"); // TODO + // create the moment + const m = match (zo) { + case let zo: time::duration => + yield chrono::from_datetime(loc, zo, mdate, mtime); + case void => + // TODO: Deduce the zone offset + // + // perform a zone lookup, then try that zone and the zones that + // are observed before and after. This requires knowlegde of the + // transition index. + abort("TODO: datetime::new(zo=void)"); }; const dt = from_moment(m); @@ -166,29 +159,26 @@ export fn now() datetime = { // // https://todo.sr.ht/~sircmpwn/hare/645 const i = time::now(time::clock::REALTIME); - const m = chrono::from_instant(i, chrono::LOCAL); + const m = chrono::new(chrono::LOCAL, i); return from_moment(m); }; // Creates a [[datetime]] from a [[time::chrono::moment]]. export fn from_moment(m: chrono::moment) datetime = { const dt = init(); + dt.loc = m.loc; + dt.sec = m.sec; + dt.nsec = m.nsec; dt.date = m.date; dt.time = m.time; - dt.loc = m.loc; dt.zone = m.zone; return dt; }; // Creates a [[datetime]] from a [[time::instant]] // in a [[time::chrono::locality]]. -export fn from_instant(i: time::instant, loc: chrono::locality) datetime = { - return from_moment(chrono::from_instant(i, loc)); -}; - -// Creates a [[time::instant]] from a [[datetime]]. -export fn to_instant(dt: datetime) time::instant = { - return chrono::to_instant(to_moment(dt)); +export fn from_instant(loc: chrono::locality, i: time::instant) datetime = { + return from_moment(chrono::new(loc, i)); }; // Creates a [[datetime]] from a string, parsed according to a layout, @@ -200,16 +190,6 @@ export fn from_str(layout: str, s: str) (datetime | insufficient | invalid) = { return finish(&b)?; }; -// Creates a [[time::chrono::moment]] from a [[datetime]]. -export fn to_moment(dt: datetime) chrono::moment = { - return chrono::moment { - date = dt.date, - time = dt.time, - loc = dt.loc, - zone = dt.zone, - }; -}; - // A [[builder]] has insufficient information and cannot create a valid datetime. export type insufficient = !void; diff --git a/datetime/format.ha b/datetime/format.ha @@ -155,15 +155,15 @@ fn fmtout(out: io::handle, r: rune, dt: *datetime) (size | io::error) = { case 'z' => // TODO: test me let pm = '+'; - const z = if (dt.zone.zoffset >= 0) { - yield calc_hmsn(dt.zone.zoffset); + const z = if (chrono::getzone(dt).zoffset >= 0) { + yield calc_hmsn(chrono::getzone(dt).zoffset); } else { pm = '-'; - yield calc_hmsn(-dt.zone.zoffset); + yield calc_hmsn(-chrono::getzone(dt).zoffset); }; return fmt::fprintf(out, "{}{:02}{:02}", pm, z.0, z.1); case 'Z' => - return fmt::fprint(out, dt.zone.abbr); + return fmt::fprint(out, chrono::getzone(dt).abbr); case '%' => return fmt::fprint(out, "%"); case => diff --git a/datetime/parse.ha b/datetime/parse.ha @@ -5,6 +5,7 @@ use errors; use strings; use time; +use time::chrono; // Parses a date/time string into a [[builder]], according to a layout format // string with specifiers as documented under [[format]]. Partial, sequential, @@ -147,20 +148,20 @@ export fn parse(build: *builder, layout: str, s: str) (void | invalid) = { case 'z' => const rest = strings::iterstr(&s_iter); if(strings::hasprefix(rest, 'Z') || strings::hasprefix(rest, 'z')) { - build.zone.zoffset = 0; + (build.zone: chrono::zone).zoffset = 0; } else { const prefix = strings::next(&s_iter); - build.zone.zoffset = get_max_n_digits(&s_iter, 2)? * time::HOUR; + (build.zone: chrono::zone).zoffset = get_max_n_digits(&s_iter, 2)? * time::HOUR; const rest = strings::iterstr(&s_iter); if(strings::hasprefix(rest, ":")) { strings::next(&s_iter); }; - build.zone.zoffset += get_max_n_digits(&s_iter, 2)? * time::MINUTE; + (build.zone: chrono::zone).zoffset += get_max_n_digits(&s_iter, 2)? * time::MINUTE; if(prefix == '-') { - build.zone.zoffset *= -1; + (build.zone: chrono::zone).zoffset *= -1; }; }; case '%' => diff --git a/datetime/timezone.ha b/datetime/timezone.ha @@ -7,20 +7,5 @@ use time::chrono; // Creates an equivalent [[datetime]] with a different // [[time::chrono::locality]]. export fn in(loc: chrono::locality, dt: datetime) datetime = { - const old = to_moment(dt); - const new = chrono::in(loc, old); - const new_dt = from_moment(new); - return new_dt; -}; - -// Finds, sets and returns a [[datetime]]'s currently observed zone. -export fn lookupzone(dt: *datetime) chrono::zone = { - const m = to_moment(*dt); - const z = chrono::lookupzone(&m); - dt.zone = z; - return z; -}; - -export fn transform(dt: datetime, zo: time::duration) datetime = { - return from_moment(chrono::transform(to_moment(dt), zo)); + return from_moment(chrono::in(loc, *(&dt: *chrono::moment))); }; diff --git a/time/chrono/chronology.ha b/time/chrono/chronology.ha @@ -1,24 +1,40 @@ // License: MPL-2.0 // (c) 2021-2022 Byron Torres <b@torresjrjr.com> use time; +use math; // Invalid [[moment]]. export type invalid = !void; -// A moment in time, within a [[locality]], interpreted via a chronology. +// A moment in time within a [[locality]]. Create one with [[new]]. +// +// Moments extend the [[time::instant]] type and couples it with a [[timescale]] +// via the .loc field. +// +// Moments observe a [[date]], time-of-day, and [[zone]], which are evaluated +// and accessed by the [[getdate]], [[gettime]], and [[getzone]] functions. +// +// The [[time::chrono]] modules implements a small chronology of dates & times. +// Higher level modules like [[datetime]] expand upon this with more complex +// chronological values (years, hours, etc.). The [[datetime::datetime]] type +// embeds this type, and other modules implementing other chronologies may +// interoperate by passing pointers. export type moment = struct { - // The ordinal day (on Earth or otherwise) - // since the Hare epoch (zeroth day) 1970-01-01 - date: date, + // The embedded [[time::instant]] of this moment + time::instant, - // The time since the start of the day - time: time::duration, - - // The timezone used for interpreting a moment's date and time + // The [[locality]] with which to interpret this moment loc: locality, - // The current [[zone]] this moment observes - zone: zone, + // The observed ordinal day (on Earth or otherwise) + // since an abitrary epoch, like the Hare epoch 1970-01-01 + date: (date | void), + + // The observed time since the start of the day + time: (time::duration | void), + + // The observed [[zone]] + zone: (zone | void), }; // An ordinal day since an epoch. The Hare epoch (zeroth day) 1970 Jan 1st is @@ -26,40 +42,95 @@ export type moment = struct { export type date = i64; // Creates a new [[moment]]. -export fn new( +export fn new(loc: locality, inst: time::instant) moment = { + return moment { + loc = loc, + sec = inst.sec, + nsec = inst.nsec, + date = void, + time = void, + zone = void, + }; +}; + +// Evalutes, caches, and returns a [[moment]]'s observed [[zone]]. +export fn getzone(m: *moment) zone = { + match (m.zone) { + case let z: zone => + return z; + case void => + return lookupzone(m); + }; +}; + +// Evaluates, caches, and returns a [[moment]]'s observed epochal date. +export fn getdate(m: *moment) date = { + match (m.date) { + case let d: date => + return d; + case void => + return eval_datetime(m).0; + }; +}; + +// Evaluates, caches, and returns a [[moment]]'s observed time-of-day as a +// [[time::duration]] since the start of a day. +export fn gettime(m: *moment) time::duration = { + match (m.time) { + case let t: time::duration => + return t; + case void => + return eval_datetime(m).1; + }; +}; + +// Evaluates, caches, and returns a [[moment]]'s observed date & time. +fn eval_datetime(m: *moment) (date, time::duration) = { + const i = time::add(*(m: *time::instant), getzone(m).zoffset); + const day = m.loc.daylength; + const daysec = day / time::SECOND; + const d = if (i.sec >= 0) i.sec / daysec else (i.sec + 1) / daysec - 1; + const t = ((i.sec % daysec + daysec) * time::SECOND + i.nsec) % day; + m.time = t; + m.date = d; + return (d, t); +}; + +// Creates a [[moment]] from a given [[locality]], zone offset, [[date]] and +// time-of-day. +export fn from_datetime( loc: locality, + zo: time::duration, d: date, t: time::duration, -) (moment | invalid) = { - if (t > loc.daylength) { - return invalid; - }; - const m = moment { +) moment = { + const inst = calc_instant(loc.daylength, zo, d, t); + return moment { + loc = loc, + sec = inst.sec, + nsec = inst.nsec, date = d, time = t, - loc = loc, - zone = zone { ... }, + zone = void }; - lookupzone(&m); - return m; -}; - -// Creates a new [[moment]] from a [[time::instant]] in a [[locality]]. -export fn from_instant(i: time::instant, loc: locality) moment = { - const daysec = (loc.daylength / time::SECOND); - const d = i.sec / daysec; - const t = (i.sec % daysec) * time::SECOND + i.nsec * time::NANOSECOND; - assert(t < loc.daylength, "Internal error: time excedes daylength"); - return new(loc, d, t)!; }; -// Creates a new [[time::instant]] from a [[moment]]. -export fn to_instant(m: moment) time::instant = { - const daysec = (m.loc.daylength / time::SECOND); - const i = time::instant { - sec = (m.date: i64 * daysec) + (m.time / time::SECOND), - nsec = m.time % time::SECOND, +fn calc_instant( + day: time::duration, // length of a day + zo: time::duration, // zone offset + d: date, // date since epoch + t: time::duration, // time since start of day +) time::instant = { + // TODO: make sure this works across transitions + const daysec = (day / time::SECOND): i64; + const dayrem = day % time::SECOND; + let i = time::instant { + sec = d * daysec, + nsec = 0, }; + i = time::add(i, d: i64 * dayrem); + i = time::add(i, t); + i = time::add(i, -zo); return i; }; diff --git a/time/chrono/timezone.ha b/time/chrono/timezone.ha @@ -78,7 +78,7 @@ type tzname = struct { // timescales. export fn in(loc: locality, m: moment) moment = { if (m.loc.timescale != loc.timescale) { - const i = to_instant(m); + const i = *(&m: *time::instant); const i = match (m.loc.timescale.to_tai(i)) { case let i: time::instant => yield i; @@ -91,26 +91,9 @@ export fn in(loc: locality, m: moment) moment = { case time::error => abort("time::chrono::in(): direct timescale conversion failed"); }; - const m = from_instant(i, loc); - return m; + return new(loc, i); }; - - assert(m.time < loc.daylength, "Internal error: time excedes daylength"); - return new(loc, m.date, m.time)!; // resets .zone -}; - -export fn transform(m: moment, zo: time::duration) moment = { - const daylen = m.loc.daylength; - - const t = m.time + zo; - const mtime = (if (t >= 0) t else t + daylen) % daylen; - - const d = (t / daylen): int; - const mdate = m.date + (if (t >= 0) d else d - 1); - - m.time = mtime; - m.date = mdate; - return m; + return new(loc, *(&m: *time::instant)); }; // Finds, sets and returns a [[moment]]'s currently observed zone. @@ -118,19 +101,17 @@ export fn lookupzone(m: *moment) zone = { // TODO: https://todo.sr.ht/~sircmpwn/hare/643 if (len(m.loc.zones) == 0) { // TODO: what to do? not ideal to assume UTC - abort("lookup(): timezones should have at least one zone"); + abort("lookupzone(): timezones should have at least one zone"); }; if (len(m.loc.zones) == 1) { m.zone = m.loc.zones[0]; - return m.zone; + return m.zone as zone; }; - const inst = to_instant(*m); - if ( len(m.loc.transitions) == 0 - || time::compare(inst, m.loc.transitions[0].when) == -1 + || time::compare(*(m: *time::instant), m.loc.transitions[0].when) == -1 ) { // TODO: special case abort("lookupzone(): time is before known transitions"); @@ -141,7 +122,7 @@ export fn lookupzone(m: *moment) zone = { for (hi - lo > 1) { const mid = lo + (hi - lo) / 2; const middle = m.loc.transitions[mid].when; - switch (time::compare(inst, middle)) { + switch (time::compare(*(m: *time::instant), middle)) { case -1 => hi = mid; case 0 => @@ -163,7 +144,7 @@ export fn lookupzone(m: *moment) zone = { void; }; - return m.zone; + return m.zone as zone; }; // Creates a [[timezone]] with a single [[zone]]. Useful for fixed offsets.