hare

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

commit d25645eef01a73b83e77bfb2f3d777833676140e
parent 928f2c7ad3e8daa66e513293567f2e7490d01290
Author: Byron Torres <b@torresjrjr.com>
Date:   Tue,  9 Apr 2024 20:48:42 +0100

time::date: implement zflag in realize()

Diffstat:
Mtime/date/virtual.ha | 199+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 186 insertions(+), 13 deletions(-)

diff --git a/time/date/virtual.ha b/time/date/virtual.ha @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 // (c) Hare authors <https://harelang.org> +use sort; use time; use time::chrono; @@ -131,13 +132,13 @@ export type lack = enum u8 { // by direct field assignments and/or with [[parse]]. Finish with [[realize]]. // // let v = date::newvirtual(); -// v.vloc = time::chrono::UTC; -// v.zoff = 0; -// date::parse(&v, "Date: %Y-%m-%d", "Date: 2038-01-19")!; -// v.hour = 03; -// v.minute = 14; -// v.second = 07; -// v.nanosecond = 0; +// v.vloc = chrono::tz("Europe/Amsterdam")!; +// v.zoff = date::zflag::LAP_EARLY | date::zflag::GAP_END; +// date::parse(&v, "Date: %Y-%m-%d", "Date: 2000-01-02")!; +// v.hour = 15; +// v.minute = 4; +// v.second = 5; +// v.nanosecond = 600000000; // let d = date::realize(v)!; // export type virtual = struct { @@ -151,7 +152,7 @@ export type virtual = struct { // locality name locname: (void | str), // zone offset - zoff: (void | time::duration), + zoff: (void | time::duration | zflag), // zone abbreviation zabbr: (void | str), // all but the last two digits of the year @@ -209,7 +210,7 @@ export fn newvirtual() virtual = virtual { // // let v = date::newvirtual(); // v.locname = "Europe/Amsterdam"; -// v.zoff = 1 * time::HOUR; +// v.zoff = date::zflag::LAP_EARLY | date::zflag::GAP_END; // date::parse(&v, // fills-in .year .month .day // "Date: %Y-%m-%d", "Date: 2038-01-19")!; // v.hour = 4; @@ -259,9 +260,58 @@ export fn newvirtual() virtual = virtual { // // If not enough information was provided, [[insufficient]] is returned. // If invalid information was provided, [[invalid]] is returned. +// Any [[zflag]]s assigned to the .zoff field affect the final result. export fn realize( v: virtual, locs: chrono::locality... +) (date | insufficient | invalid | zfunresolved) = { + match (v.zoff) { + case void => + return lack::ZOFF; + case time::duration => + return realize_validzoff(v, locs...); + case let zf: zflag => + let valid_dates = realize_validzoffs(v, locs...)?; + switch (len(valid_dates)) { + case 0 => + if (0 != zf & zflag::GAP_END) { + return realize_gapbounds(v).1; + } else if (0 != zf & zflag::GAP_START) { + return realize_gapbounds(v).0; + } else { + return false: zfunresolved; + }; + case 1 => + return valid_dates[0]; + case => + if (0 != zf & zflag::LAP_LATE) { + return valid_dates[len(valid_dates) - 1]; + } else if (0 != zf & zflag::LAP_EARLY) { + return valid_dates[0]; + } else { + return true: zfunresolved; + }; + }; + }; +}; + +fn realize_validzoff( + v: virtual, + locs: chrono::locality... +) (date | insufficient | invalid) = { + let d = realize_datetimezoff(v, locs...)?; + + // verify zone offset + if (chrono::ozone(&d).zoff != v.zoff as time::duration) { + return invalid; + }; + + return d; +}; + +fn realize_datetimezoff( + v: virtual, + locs: chrono::locality... ) (date | insufficient | invalid) = { let lacking = 0u8; @@ -399,11 +449,134 @@ export fn realize( v.daytime as i64, )); - // verify zone offset - const z = chrono::ozone(&d); - if (z.zoff != v.zoff as time::duration) { + return d; +}; + +fn realize_validzoffs( + v: virtual, + locs: chrono::locality... +) ([]date | insufficient | invalid) = { + // check if only zoff is missing + v.zoff = 0o0; + match (realize_validzoff(v, locs...)) { + case (date | invalid) => + void; + case let ins: insufficient => + return ins; + }; + v.zoff = void; + + let dates: []date = []; + + // determine .loc + if (v.vloc is chrono::locality) { + v.loc = v.vloc as chrono::locality; + } else if (v.locname is str) { + for (let loc .. locs) { + if (loc.name == v.locname as str) { + v.loc = loc; + v.vloc = loc; + break; + }; + }; + } else { + return insufficient::LOCALITY; + }; + + // try matching zone abbreviation + if (v.zabbr is str) { + for (let zone .. v.loc.zones) { + if (v.zabbr as str == zone.abbr) { + v.zoff = zone.zoff; + match (realize_validzoff(v, locs...)) { + case let d: date => + match (sort::search( + dates, size(date), &d, &cmpdates, + )) { + case size => + void; + case void => + append(dates, d); + sort::sort(dates, size(date), &cmpdates); + }; + case invalid => + continue; + case => + abort(); + }; + }; + }; + return invalid; }; - return d; + // try zone offsets from locality + for (let zone .. v.loc.zones) { + v.zoff = zone.zoff; + match (realize_validzoff(v, locs...)) { + case let d: date => + match (sort::search(dates, size(date), &d, &cmpdates)) { + case size => + void; + case void => + append(dates, d); + sort::sort(dates, size(date), &cmpdates); + }; + case invalid => + continue; + case => + abort(); + }; + }; + + return dates; +}; + +fn cmpdates(a: const *opaque, b: const *opaque) int = { + let a = a: *date; + let b = b: *date; + return chrono::compare(a, b)!: int; +}; + +fn realize_gapbounds(v: virtual) (date, date) = { + let loc = v.vloc as chrono::locality; + + let zlo: time::duration = 48 * time::HOUR; + let zhi: time::duration = -48 * time::HOUR; + for (let zone .. loc.zones) { + if (zone.zoff > zhi) { + zhi = zone.zoff; + }; + if (zone.zoff < zlo) { + zlo = zone.zoff; + }; + }; + + v.zoff = zhi; + let earliest = realize_datetimezoff(v)!; + let earliest = *(&earliest: *time::instant); + + v.zoff = zlo; + let latest = realize_datetimezoff(v)!; + let latest = *(&latest: *time::instant); + + let t = time::instant{ ... }; + for (let tr .. loc.transitions) { + let is_within_bounds = ( + time::compare(earliest, tr.when) < 0 + && time::compare(latest, tr.when) > 0 + ); + + if (is_within_bounds) { + t = tr.when; + break; + }; + }; + + let gapstart = from_instant(loc, time::add(t, -time::NANOSECOND)); + let gapend = from_instant(loc, t); + + // TODO: check if original v falls within gapstart & gapend? + + return (gapstart, gapend); };