Byron Torres: 10 time::chrono: rename _lookupzone to lookupzone time::chrono: improve LOCAL, tz() time::chrono: moment.daytime: i64, not duration time::chrono: rename mzone to ozone time::chrono: rename path constant exports time::date: rename some calc functions time::date: make type insufficient an enum time::date: change type parsefail, add byteindex time::date: change, add layout constants time::date: virtual: add .vsec, .vnsec 33 files changed, 301 insertions(+), 222 deletions(-)
Thanks! To git@git.sr.ht:~sircmpwn/hare 8f42a75a..0dbc18b1 master -> master
hare/patches: SUCCESS in 1m9s [time::chrono: rename _lookupzone to lookupzone][0] v2 from [Byron Torres][1] [0]: https://lists.sr.ht/~sircmpwn/hare-dev/patches/49755 [1]: mailto:b@torresjrjr.com ✓ #1155871 SUCCESS hare/patches/freebsd.yml https://builds.sr.ht/~sircmpwn/job/1155871 ✓ #1155870 SUCCESS hare/patches/alpine.yml https://builds.sr.ht/~sircmpwn/job/1155870 ✓ #1155872 SUCCESS hare/patches/openbsd.yml https://builds.sr.ht/~sircmpwn/job/1155872
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~sircmpwn/hare-dev/patches/49755/mbox | git am -3Learn more about email & git
Signed-off-by: Byron Torres <b@torresjrjr.com> --- time/chrono/chronology.ha | 2 +- time/chrono/timezone.ha | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/time/chrono/chronology.ha b/time/chrono/chronology.ha index 885eb7c5..8361f599 100644 --- a/time/chrono/chronology.ha +++ b/time/chrono/chronology.ha @@ -56,7 +56,7 @@ export fn mzone(m: *moment) zone = { case let z: *zone => return *z; case null => - const z = _lookupzone(m.loc, *(m: *time::instant)); + const z = lookupzone(m.loc, *(m: *time::instant)); m.zone = z; return *z; }; diff --git a/time/chrono/timezone.ha b/time/chrono/timezone.ha index 8371f795..3a1ac096 100644 --- a/time/chrono/timezone.ha +++ b/time/chrono/timezone.ha @@ -110,7 +110,7 @@ export fn in(loc: locality, m: moment) (moment | discontinuity) = { }; // Finds and returns a [[moment]]'s currently observed [[zone]]. -fn _lookupzone(loc: locality, inst: time::instant) *zone = { +fn lookupzone(loc: locality, inst: time::instant) *zone = { // TODO: https://todo.sr.ht/~sircmpwn/hare/643 if (len(loc.zones) == 0) { abort("time::chrono: Timezone has no zones"); -- 2.43.2
Make [[LOCAL]] default to [[UTC]] proper. Accept the POSIX $TZ environment variable form with a ':' prefix. Allow [[tz]] to accept full filepaths as in POSIX. Signed-off-by: Byron Torres <b@torresjrjr.com> --- hare/module/format.ha | 2 +- time/chrono/timezone.ha | 78 +++++++++++------------------------------ time/chrono/tzdb.ha | 20 +++++++---- 3 files changed, 35 insertions(+), 65 deletions(-) diff --git a/hare/module/format.ha b/hare/module/format.ha index 8c54f3c7..a8788f75 100644 --- a/hare/module/format.ha +++ b/hare/module/format.ha @@ -35,7 +35,7 @@ export fn format_srcset(out: io::handle, srcs: *srcset) (size | io::error) = { n += fmt::fprint(out, "relevant tags: ")?; n += format_tags(out, srcs.seentags)?; n += fmt::fprintln(out)?; - const dt = date::from_instant(time::chrono::LOCAL, srcs.mtime); + const dt = date::from_instant(chrono::LOCAL, srcs.mtime); n += date::format(out, "last change to source list: %F %T\n", &dt)?; n += fmt::fprintln(out, "hare sources:")?; for (let i = 0z; i < len(srcs.ha); i += 1) { diff --git a/time/chrono/timezone.ha b/time/chrono/timezone.ha index 3a1ac096..97bd5e4d 100644 --- a/time/chrono/timezone.ha +++ b/time/chrono/timezone.ha @@ -181,77 +181,41 @@ export fn fixedzone(ts: *timescale, daylen: time::duration, z: zone) timezone = }; }; -// The system's [[locality]]; the system's local [[timezone]]. +// The local [[locality]]; the system or environment configured [[timezone]]. // // This is set during a program's initialisation, where the TZ environment -// variable is tried, otherwise the /etc/localtime file is tried, otherwise a -// default is used. -// -// The default timezone is equivalent to that of [[UTC]], with "Local" being the -// name of both the timezone and its single zero-offset zone. -export const LOCAL: locality = &TZ_LOCAL; - -def TZ_LOCAL_NAME: str = "Local"; - -let TZ_LOCAL: timezone = timezone { - name = TZ_LOCAL_NAME, - timescale = &utc, - daylength = EARTH_DAY, - zones = [ - zone { - zoff = 0 * time::SECOND, - name = TZ_LOCAL_NAME, - abbr = "", - dst = false, - }, - ], - transitions = [], - posix_extend = "", -}; +// variable is tried, otherwise the /etc/localtime file is tried, otherwise it +// defaults to [[UTC]]. +export const LOCAL: locality = &TZ_UTC; @init fn init_tz_local() void = { - match (os::getenv("TZ")) { - case let timezone: str => - match (tz(timezone)) { - case let loc: locality => - TZ_LOCAL = *loc; - case => - return; + let path = match (os::getenv("TZ")) { + case let path: str => + // remove POSIX prefix ':' + yield if (strings::hasprefix(path, ':')) { + yield strings::sub(path, 1, strings::end); + } else { + yield path; }; case void => - const filepath = match (os::readlink(LOCALTIME_PATH)) { - case let fp: str => - yield fp; - case => - yield LOCALTIME_PATH; - }; - - const file = match (os::open(filepath)) { - case let f: io::file => - yield f; + yield match (os::realpath(LOCALTIME_PATH)) { + case let path: str => + yield path; case => return; }; - defer io::close(file)!; - - if (strings::hasprefix(filepath, ZONEINFO_PREFIX)) { - TZ_LOCAL.name = strings::trimprefix( - filepath, ZONEINFO_PREFIX, - ); - }; + }; - static let buf: [os::BUFSZ]u8 = [0...]; - const file = bufio::init(file, buf, []); - load_tzif(&file, &TZ_LOCAL): void; + match (tz(path)) { + case => void; + case let loc: locality => + LOCAL = loc; }; }; @fini fn free_tz_local() void = { - free(TZ_LOCAL.transitions); - switch(TZ_LOCAL.name) { - case TZ_LOCAL_NAME => void; - case => - free(TZ_LOCAL.zones); + if (LOCAL != UTC) { + timezone_free(LOCAL); }; }; diff --git a/time/chrono/tzdb.ha b/time/chrono/tzdb.ha index b27edf43..437cd2d8 100644 --- a/time/chrono/tzdb.ha +++ b/time/chrono/tzdb.ha @@ -19,16 +19,22 @@ export type tzdberror = !(invalidtzif | fs::error | io::error); export type invalidtzif = !void; // Finds, loads, and allocates a [[timezone]] from the system's Timezone -// database, normally located at /usr/share/zoneinfo, and returns it as a -// [[locality]]. Each call returns a new instance. The caller must free the -// return value. +// database (TZDB), and returns it as a [[locality]]. Each call returns a new +// instance. The caller must free the return value; see [[timezone_free]]. // -// All localities provided default to the [[utc]] [[timescale]] and +// The system TZDB is normally located at [[ZONEINFO_PREFIX]]. The timezone +// filepath is resolved by appending the name argument to this prefix path. +// If [name] is a full filepath (begins with '/'), it is used directly instead. +// +// All localities returned default to the [[utc]] [[timescale]] and // [[EARTH_DAY]] day-length. export fn tz(name: str) (locality | tzdberror) = { - const filepath = path::init(ZONEINFO_PREFIX, name)!; - const fpath = path::string(&filepath); - const file = os::open(fpath)?; + const filepath = + if (!strings::hasprefix(name, ZONEINFO_PREFIX)) + path::init(ZONEINFO_PREFIX, name)! + else + path::init(name)!; + const file = os::open(path::string(&filepath))?; static let buf: [os::BUFSZ]u8 = [0...]; const bufstrm = bufio::init(file, buf, []); -- 2.43.2
Semantic correction. Signed-off-by: Byron Torres <b@torresjrjr.com> --- time/chrono/chronology.ha | 15 ++++++++------- time/date/daytime.ha | 4 ++-- time/date/virtual.ha | 6 +++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/time/chrono/chronology.ha b/time/chrono/chronology.ha index 8361f599..ea8f9cfc 100644 --- a/time/chrono/chronology.ha +++ b/time/chrono/chronology.ha @@ -33,8 +33,9 @@ export type moment = struct { // since an abitrary epoch (e.g. the Unix epoch 1970-01-01). daydate: (void | i64), - // The observed time-of-day (amount of daytime progressed in a day). - daytime: (void | time::duration), + // The observed time-of-day (amount of daytime progressed in a day) + // as nanoseconds. + daytime: (void | i64), }; // Creates a new [[moment]]. Uses a given [[time::instant]] with a [[timescale]] @@ -83,10 +84,10 @@ export fn daydate(m: *moment) i64 = { }; // Observes a [[moment]]'s observed time-of-day (amount of daytime progressed in -// a day) as a [[time::duration]]. -export fn daytime(m: *moment) time::duration = { +// a day) as nanoseconds. +export fn daytime(m: *moment) i64 = { match (m.daytime) { - case let dt: time::duration => + case let dt: i64 => return dt; case void => const (dd, dt) = calc_datetime( @@ -119,7 +120,7 @@ export fn from_datetime( loc: locality, zo: time::duration, dd: i64, - dt: time::duration, + dt: i64, ) moment = { const inst = calc_instant(loc.daylength, zo, dd, dt); return moment { @@ -136,7 +137,7 @@ fn calc_instant( day: time::duration, // length of a day zo: time::duration, // zone offset dd: i64, // date since epoch - dt: time::duration, // time since start of day + dt: i64, // time since start of day (ns) ) time::instant = { const daysec = (day / time::SECOND): i64; const dayrem = day % time::SECOND; diff --git a/time/date/daytime.ha b/time/date/daytime.ha index 493f42de..ee4b2483 100644 --- a/time/date/daytime.ha +++ b/time/date/daytime.ha @@ -5,7 +5,7 @@ use time; // Calculates the wall clock (hour, minute, second, nanosecond), // given a time-of-day (amount of daytime progressed in a day). -fn calc_hmsn(t: time::duration) (int, int, int, int) = { +fn calc_hmsn(t: i64) (int, int, int, int) = { // TODO: Special case for leap seconds, 61st second? const hour = (t / time::HOUR): int; const min = ((t / time::MINUTE) % 60): int; @@ -21,7 +21,7 @@ fn calc_daytime__hmsn( min: int, sec: int, nsec: int, -) (time::duration | invalid) = { +) (i64 | invalid) = { const t = ( (hour * time::HOUR) + (min * time::MINUTE) + diff --git a/time/date/virtual.ha b/time/date/virtual.ha index 651e602c..f330ed26 100644 --- a/time/date/virtual.ha +++ b/time/date/virtual.ha @@ -158,7 +158,7 @@ export fn realize( }; // determine .daytime - if (v.daytime is time::duration) { + if (v.daytime is i64) { void; } else { const hour = if (v.hour is int) { @@ -188,7 +188,7 @@ export fn realize( }; // determine zone offset - if (v.zoff is time::duration) { + if (v.zoff is i64) { void; } else { return insufficient; @@ -213,7 +213,7 @@ export fn realize( v.loc, v.zoff as time::duration, v.daydate as i64, - v.daytime as time::duration, + v.daytime as i64, )); // verify zone offset -- 2.43.2
Rename export [[time::chrono::mzone]] to [[time::chrono::ozone]]. Braking-Change: 0.24.0 Signed-off-by: Byron Torres <b@torresjrjr.com> --- It's "observe zone". time/chrono/chronology.ha | 8 ++++---- time/date/date.ha | 2 +- time/date/format.ha | 8 ++++---- time/date/parithm.ha | 16 ++++++++-------- time/date/reckon.ha | 2 +- time/date/virtual.ha | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/time/chrono/chronology.ha b/time/chrono/chronology.ha index ea8f9cfc..78741b29 100644 --- a/time/chrono/chronology.ha +++ b/time/chrono/chronology.ha @@ -17,7 +17,7 @@ export type invalid = !void; // // Moments observe a daydate, time-of-day, and [[zone]], which are evaluated, // cached and obtained with the observer functions [[daydate]], [[daytime]], and -// [[mzone]]. These values are derived from the embedded instant and locality +// [[ozone]]. These values are derived from the embedded instant and locality // information, and thus are guaranteed to be valid. export type moment = struct { // The embedded [[time::instant]]. @@ -52,7 +52,7 @@ export fn new(loc: locality, i: time::instant) moment = { }; // Observes a [[moment]]'s observed [[zone]]. -export fn mzone(m: *moment) zone = { +export fn ozone(m: *moment) zone = { match (m.zone) { case let z: *zone => return *z; @@ -75,7 +75,7 @@ export fn daydate(m: *moment) i64 = { return dd; case void => const (dd, dt) = calc_datetime( - m.loc, *(m: *time::instant), mzone(m).zoff, + m.loc, *(m: *time::instant), ozone(m).zoff, ); m.daytime = dt; m.daydate = dd; @@ -91,7 +91,7 @@ export fn daytime(m: *moment) i64 = { return dt; case void => const (dd, dt) = calc_datetime( - m.loc, *(m: *time::instant), mzone(m).zoff, + m.loc, *(m: *time::instant), ozone(m).zoff, ); m.daytime = dt; m.daydate = dd; diff --git a/time/date/date.ha b/time/date/date.ha index 87266200..4f46fe1f 100644 --- a/time/date/date.ha +++ b/time/date/date.ha @@ -164,7 +164,7 @@ export fn new( // check if input values are actually observed if ( - zoff != chrono::mzone(&d).zoff + zoff != chrono::ozone(&d).zoff || _fields[0] != _year(&d) || _fields[1] != _month(&d) || _fields[2] != _day(&d) diff --git a/time/date/format.ha b/time/date/format.ha index 25db9c2d..592461b4 100644 --- a/time/date/format.ha +++ b/time/date/format.ha @@ -148,15 +148,15 @@ fn fmtout(out: io::handle, r: rune, d: *date) (size | io::error) = { case 'Y' => return fmt::fprintf(out, "{:.4}", _year(d)); case 'z' => - const (sign, zo) = if (chrono::mzone(d).zoff >= 0) { - yield ('+', calc_hmsn(chrono::mzone(d).zoff)); + const (sign, zo) = if (chrono::ozone(d).zoff >= 0) { + yield ('+', calc_hmsn(chrono::ozone(d).zoff)); } else { - yield ('-', calc_hmsn(-chrono::mzone(d).zoff)); + yield ('-', calc_hmsn(-chrono::ozone(d).zoff)); }; const (hr, mi) = (zo.0, zo.1); return fmt::fprintf(out, "{}{:.2}{:.2}", sign, hr, mi); case 'Z' => - return fmt::fprint(out, chrono::mzone(d).abbr); + return fmt::fprint(out, chrono::ozone(d).abbr); case '%' => return fmt::fprint(out, "%"); case => diff --git a/time/date/parithm.ha b/time/date/parithm.ha index 2fb5e6af..1b7788cc 100644 --- a/time/date/parithm.ha +++ b/time/date/parithm.ha @@ -131,44 +131,44 @@ export fn truncate(d: date, u: unit) date = { // realize(), and then use realize() here. return switch (u) { case unit::ERA => - yield new(d.loc, chrono::mzone(&d).zoff, + yield new(d.loc, chrono::ozone(&d).zoff, 1, 1, 1, 0, 0, 0, 0, )!; case unit::YEAR => - yield new(d.loc, chrono::mzone(&d).zoff, + yield new(d.loc, chrono::ozone(&d).zoff, _year(&d), 1, 1, 0, 0, 0, 0, )!; case unit::MONTH => - yield new(d.loc, chrono::mzone(&d).zoff, + yield new(d.loc, chrono::ozone(&d).zoff, _year(&d), _month(&d), 1, 0, 0, 0, 0, )!; case unit::WEEK => const dd = chrono::daydate(&d) - _weekday(&d); const ymd = calc_ymd(dd); - yield new(d.loc, chrono::mzone(&d).zoff, + yield new(d.loc, chrono::ozone(&d).zoff, ymd.0, ymd.1, ymd.2, 0, 0, 0, 0, )!; case unit::DAY => - yield new(d.loc, chrono::mzone(&d).zoff, + yield new(d.loc, chrono::ozone(&d).zoff, _year(&d), _month(&d), _day(&d), 0, 0, 0, 0, )!; case unit::HOUR => - yield new(d.loc, chrono::mzone(&d).zoff, + yield new(d.loc, chrono::ozone(&d).zoff, _year(&d), _month(&d), _day(&d), _hour(&d), 0, 0, 0, )!; case unit::MINUTE => - yield new(d.loc, chrono::mzone(&d).zoff, + yield new(d.loc, chrono::ozone(&d).zoff, _year(&d), _month(&d), _day(&d), _hour(&d), _minute(&d), 0, 0, )!; case unit::SECOND => - yield new(d.loc, chrono::mzone(&d).zoff, + yield new(d.loc, chrono::ozone(&d).zoff, _year(&d), _month(&d), _day(&d), _hour(&d), _minute(&d), _second(&d), 0, )!; diff --git a/time/date/reckon.ha b/time/date/reckon.ha index 77ac0720..e1ddacf0 100644 --- a/time/date/reckon.ha +++ b/time/date/reckon.ha @@ -70,7 +70,7 @@ export type calculus = enum uint { export fn reckon(d: date, calc: calculus, ps: period...) date = { let r = newvirtual(); // our reckoner r.vloc = d.loc; - r.zoff = chrono::mzone(&d).zoff; + r.zoff = chrono::ozone(&d).zoff; r.year = _year(&d); r.month = _month(&d); r.day = _day(&d); diff --git a/time/date/virtual.ha b/time/date/virtual.ha index f330ed26..d87eabca 100644 --- a/time/date/virtual.ha +++ b/time/date/virtual.ha @@ -217,7 +217,7 @@ export fn realize( )); // verify zone offset - const z = chrono::mzone(&d); + const z = chrono::ozone(&d); if (z.zoff != v.zoff as time::duration) { return invalid; }; -- 2.43.2
Rename [[ZONEINFO_PREFIX]] to [[TZDB_PATH]] as "zoneinfo" is merely a convention. Rename [[UTC_LEAPSECS_FILE]] to [[UTC_LEAPSECS_PATH]] for consistency. Breaking-Change: 0.24.0 Signed-off-by: Byron Torres <b@torresjrjr.com> --- time/chrono/+freebsd.ha | 4 ++-- time/chrono/+linux.ha | 4 ++-- time/chrono/+openbsd.ha | 4 ++-- time/chrono/leapsec.ha | 6 +++--- time/chrono/timescale.ha | 2 +- time/chrono/tzdb.ha | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/time/chrono/+freebsd.ha b/time/chrono/+freebsd.ha index 26d78ab1..af49080e 100644 --- a/time/chrono/+freebsd.ha +++ b/time/chrono/+freebsd.ha @@ -2,8 +2,8 @@ // (c) Hare authors <https://harelang.org> def LOCALTIME_PATH: str = "/etc/localtime"; -def ZONEINFO_PREFIX: str = "/usr/share/zoneinfo/"; +def TZDB_PATH: str = "/usr/share/zoneinfo/"; // The filepath of the system's "leap-seconds.list" file, which contains UTC/TAI // leap second data. -export def UTC_LEAPSECS_FILE: str = "/var/db/ntpd.leap-seconds.list"; +export def UTC_LEAPSECS_PATH: str = "/var/db/ntpd.leap-seconds.list"; diff --git a/time/chrono/+linux.ha b/time/chrono/+linux.ha index 600f606c..2756fd6f 100644 --- a/time/chrono/+linux.ha +++ b/time/chrono/+linux.ha @@ -2,8 +2,8 @@ // (c) Hare authors <https://harelang.org> def LOCALTIME_PATH: str = "/etc/localtime"; -def ZONEINFO_PREFIX: str = "/usr/share/zoneinfo/"; +def TZDB_PATH: str = "/usr/share/zoneinfo/"; // The filepath of the system's "leap-seconds.list" file, which contains UTC/TAI // leap second data. -export def UTC_LEAPSECS_FILE: str = "/usr/share/zoneinfo/leap-seconds.list"; +export def UTC_LEAPSECS_PATH: str = "/usr/share/zoneinfo/leap-seconds.list"; diff --git a/time/chrono/+openbsd.ha b/time/chrono/+openbsd.ha index 600f606c..2756fd6f 100644 --- a/time/chrono/+openbsd.ha +++ b/time/chrono/+openbsd.ha @@ -2,8 +2,8 @@ // (c) Hare authors <https://harelang.org> def LOCALTIME_PATH: str = "/etc/localtime"; -def ZONEINFO_PREFIX: str = "/usr/share/zoneinfo/"; +def TZDB_PATH: str = "/usr/share/zoneinfo/"; // The filepath of the system's "leap-seconds.list" file, which contains UTC/TAI // leap second data. -export def UTC_LEAPSECS_FILE: str = "/usr/share/zoneinfo/leap-seconds.list"; +export def UTC_LEAPSECS_PATH: str = "/usr/share/zoneinfo/leap-seconds.list"; diff --git a/time/chrono/leapsec.ha b/time/chrono/leapsec.ha index dc6fbb82..91a1c7f1 100644 --- a/time/chrono/leapsec.ha +++ b/time/chrono/leapsec.ha @@ -38,7 +38,7 @@ type utciniterror = !(fs::error | io::error | utf8::invalid); export def SECS_1900_1970: i64 = 2208988800; // UTC/TAI leap second data; UTC timestamps and their offsets from TAI. -// Sourced from [[UTC_LEAPSECS_FILE]]. +// Sourced from [[UTC_LEAPSECS_PATH]]. let utc_leapsecs: [](i64, i64) = []; let utc_isinitialized: bool = false; @@ -48,12 +48,12 @@ let utc_isinitialized: bool = false; }; fn init_utc_leapsecs() (void | utciniterror) = { - const file = os::open(UTC_LEAPSECS_FILE)?; + const file = os::open(UTC_LEAPSECS_PATH)?; defer io::close(file)!; parse_utc_leapsecs(file)?; }; -// Parse UTC/TAI leap second data from [[UTC_LEAPSECS_FILE]]. +// Parse UTC/TAI leap second data from [[UTC_LEAPSECS_PATH]]. // See file for format details. fn parse_utc_leapsecs(h: io::handle) (void | utf8::invalid | io::error) = { for (true) { diff --git a/time/chrono/timescale.ha b/time/chrono/timescale.ha index 71c0007f..ae4aabce 100644 --- a/time/chrono/timescale.ha +++ b/time/chrono/timescale.ha @@ -104,7 +104,7 @@ fn tai_conv(ts: *timescale, i: time::instant) ([]time::instant | void) = { // Discontinuous (has leap seconds). // // During a program's initialization, this timescale initializes by loading its -// UTC/TAI leap second data from [[UTC_LEAPSECS_FILE]]; otherwise, fails +// UTC/TAI leap second data from [[UTC_LEAPSECS_PATH]]; otherwise, fails // silently. If failed, any attempt to consult UTC leapsec data (e.g. calling // [[convert]] on UTC) causes an abort. This includes [[in]]. export const utc: timescale = timescale { diff --git a/time/chrono/tzdb.ha b/time/chrono/tzdb.ha index 437cd2d8..fa50a541 100644 --- a/time/chrono/tzdb.ha +++ b/time/chrono/tzdb.ha @@ -22,7 +22,7 @@ export type invalidtzif = !void; // database (TZDB), and returns it as a [[locality]]. Each call returns a new // instance. The caller must free the return value; see [[timezone_free]]. // -// The system TZDB is normally located at [[ZONEINFO_PREFIX]]. The timezone +// The system TZDB is normally located at [[TZDB_PATH]]. The timezone // filepath is resolved by appending the name argument to this prefix path. // If [name] is a full filepath (begins with '/'), it is used directly instead. // @@ -30,8 +30,8 @@ export type invalidtzif = !void; // [[EARTH_DAY]] day-length. export fn tz(name: str) (locality | tzdberror) = { const filepath = - if (!strings::hasprefix(name, ZONEINFO_PREFIX)) - path::init(ZONEINFO_PREFIX, name)! + if (!strings::hasprefix(name, TZDB_PATH)) + path::init(TZDB_PATH, name)! else path::init(name)!; const file = os::open(path::string(&filepath))?; -- 2.43.2
Signed-off-by: Byron Torres <b@torresjrjr.com> --- time/date/daydate.ha | 10 +++++----- time/date/parithm.ha | 8 ++++---- time/date/reckon.ha | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/time/date/daydate.ha b/time/date/daydate.ha index 982848f5..026cdb11 100644 --- a/time/date/daydate.ha +++ b/time/date/daydate.ha @@ -25,16 +25,16 @@ export fn isleapyear(y: int) bool = { // Calculates whether a given year, month, and day-of-month, is a valid date. fn is_valid_ymd(y: int, m: int, d: int) bool = { return m >= 1 && m <= 12 && d >= 1 && - d <= calc_month_daycnt(y, m); + d <= calc_days_in_month(y, m); }; // Calculates whether a given year, and day-of-year, is a valid date. fn is_valid_yd(y: int, yd: int) bool = { - return yd >= 1 && yd <= calc_year_daycnt(y); + return yd >= 1 && yd <= calc_days_in_year(y); }; // Calculates the number of days in the given month of the given year. -fn calc_month_daycnt(y: int, m: int) int = { +fn calc_days_in_month(y: int, m: int) int = { const days_per_month: [_]int = [ 31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; @@ -46,7 +46,7 @@ fn calc_month_daycnt(y: int, m: int) int = { }; // Calculates the number of days in a given year. -fn calc_year_daycnt(y: int) int = { +fn calc_days_in_year(y: int) int = { return if (isleapyear(y)) 366 else 365; }; @@ -244,7 +244,7 @@ fn calc_daydate__ywd(y: int, w: int, wd: int) (i64 | invalid) = { // Calculates the daydate, // given a year and day-of-year. fn calc_daydate__yd(y: int, yd: int) (i64 | invalid) = { - if (yd < 1 || yd > calc_year_daycnt(y)) { + if (yd < 1 || yd > calc_days_in_year(y)) { return invalid; }; return calc_daydate__ymd(y, 1, 1)? + yd - 1; diff --git a/time/date/parithm.ha b/time/date/parithm.ha index 1b7788cc..c91b7890 100644 --- a/time/date/parithm.ha +++ b/time/date/parithm.ha @@ -49,21 +49,21 @@ export fn pdiff(a: date, b: date) period = { p.days = _day(&b) - _day(&a); let year = _year(&b); let month = _month(&b); - let daycnt = calc_month_daycnt(year, month); - for (_day(&a) > daycnt || p.days < 0) { + let monthdays = calc_days_in_month(year, month); + for (_day(&a) > monthdays || p.days < 0) { month -= 1; if (month == 0) { year -= 1; month = 12; }; - daycnt = calc_month_daycnt(year, month); + monthdays = calc_days_in_month(year, month); p.months -= 1; if (p.months < 0) { p.years -= 1; p.months += 12; }; - p.days += daycnt; + p.days += monthdays; }; p.hours = _hour(&b) - _hour(&a); diff --git a/time/date/reckon.ha b/time/date/reckon.ha index e1ddacf0..229d7643 100644 --- a/time/date/reckon.ha +++ b/time/date/reckon.ha @@ -151,12 +151,12 @@ fn reckon_days(r: *virtual, days: i64, calc: calculus) void = { day += days: int; // day overflow - let month_daycnt = calc_month_daycnt(year, month); - for (day > month_daycnt) { + let monthdays = calc_days_in_month(year, month); + for (day > monthdays) { if (calc & calculus::FLOOR != 0) { day = 1; } else if (calc & calculus::CEIL != 0) { - day = month_daycnt; + day = monthdays; } else if (calc & calculus::HOP != 0) { r.year = year; r.month = month; @@ -174,9 +174,9 @@ fn reckon_days(r: *virtual, days: i64, calc: calculus) void = { year = r.year as int; month = r.month as int; - day -= month_daycnt; + day -= monthdays; }; - month_daycnt = calc_month_daycnt(year, month); + monthdays = calc_days_in_month(year, month); }; for (day < 1) { r.year = year; @@ -186,7 +186,7 @@ fn reckon_days(r: *virtual, days: i64, calc: calculus) void = { year = r.year as int; month = r.month as int; - day += calc_month_daycnt(year, month); + day += calc_days_in_month(year, month); }; r.year = year; -- 2.43.2
Signed-off-by: Byron Torres <b@torresjrjr.com> --- time/date/error.ha | 14 ++++++++++++-- time/date/virtual.ha | 26 ++++++++++++++++++++------ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/time/date/error.ha b/time/date/error.ha index 0da7cfff..9cc8339c 100644 --- a/time/date/error.ha +++ b/time/date/error.ha @@ -2,6 +2,7 @@ // (c) Hare authors <https://harelang.org> use fmt; +use strings; // All possible errors returned from [[date]]. export type error = !(insufficient | invalid | parsefail); @@ -10,8 +11,17 @@ export type error = !(insufficient | invalid | parsefail); // statically allocated. export fn strerror(err: error) const str = { match (err) { - case insufficient => - return "Insufficient date information"; + case let lack: insufficient => + static let buf: [92]u8 = [0...]; + return strings::rtrim(fmt::bsprint(buf, + "Insufficient date information, could not calculate:", + if (lack & insufficient::DAYDATE: u8 == 0) "" else + "daydate", + if (lack & insufficient::DAYTIME: u8 == 0) "" else + "time-of-day", + if (lack & insufficient::ZOFF: u8 == 0) "" else + "zone-offset", + )); case invalid => return "Invalid date information"; case let rn: parsefail => diff --git a/time/date/virtual.ha b/time/date/virtual.ha index d87eabca..2ab1a4df 100644 --- a/time/date/virtual.ha +++ b/time/date/virtual.ha @@ -5,7 +5,13 @@ use time; use time::chrono; // A [[virtual]] does not have enough information to create a valid [[date]]. -export type insufficient = !void; +export type insufficient = !lack; // TODO: drop alias workaround + +export type lack = enum u8 { + DAYDATE = 1 << 0, // could not infer daydate + DAYTIME = 1 << 1, // could not infer time-of-day + ZOFF = 1 << 2, // could not infer zone offset +}; // A virtual date; a [[date]] wrapper interface, which represents a date of // uncertain validity. Its fields need not be valid observed chronological @@ -118,6 +124,8 @@ export fn realize( v: virtual, locs: chrono::locality... ) (date | insufficient | invalid) = { + let lacking = 0u8; + // determine .daydate if (v.daydate is i64) { void; @@ -154,13 +162,13 @@ export fn realize( void; } else { // cannot deduce daydate - return insufficient; + lacking |= insufficient::DAYDATE; }; // determine .daytime if (v.daytime is i64) { void; - } else { + } else :nodaytime { const hour = if (v.hour is int) { yield v.hour as int; } else if (v.halfhour is int && v.ampm is bool) { @@ -168,7 +176,8 @@ export fn realize( const pm = v.ampm as bool; yield if (pm) hr * 2 else hr; } else { - return insufficient; + lacking |= insufficient::DAYTIME; + yield :nodaytime; }; if ( @@ -182,8 +191,9 @@ export fn realize( v.second as int, v.nanosecond as int, )?; + lacking |= 0u8; } else { - return insufficient; + lacking |= insufficient::DAYTIME; }; }; @@ -191,7 +201,7 @@ export fn realize( if (v.zoff is i64) { void; } else { - return insufficient; + lacking |= insufficient::ZOFF; }; // determine .loc (defaults to time::chrono::UTC) @@ -208,6 +218,10 @@ export fn realize( }; }; + if (lacking != 0u8) { + return lacking: insufficient; + }; + // determine .sec, .nsec const d = from_moment(chrono::from_datetime( v.loc, -- 2.43.2
Breaking-Change: 0.24.0 Signed-off-by: Byron Torres <b@torresjrjr.com> --- time/date/date.ha | 4 ++-- time/date/error.ha | 12 +++++------- time/date/parse.ha | 12 ++++++------ 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/time/date/date.ha b/time/date/date.ha index 4f46fe1f..ded5c818 100644 --- a/time/date/date.ha +++ b/time/date/date.ha @@ -261,8 +261,8 @@ export fn from_str( //("%FT%T%z %L", "2009-06-30T18:30:00+0200 Europe/Amsterdam", [amst], // new(amst, 2 * time::HOUR, 2009, 6, 30, 18, 30)!), - ("%Y", "a", [], 'a': parsefail), - ("%X", "2008", [], '2': parsefail), + ("%Y", "a", [], (0z, 'a'): parsefail), + ("%X", "2008", [], (0z, '2'): parsefail), ]; let buf: [64]u8 = [0...]; diff --git a/time/date/error.ha b/time/date/error.ha index 9cc8339c..8e7641b5 100644 --- a/time/date/error.ha +++ b/time/date/error.ha @@ -24,12 +24,10 @@ export fn strerror(err: error) const str = { )); case invalid => return "Invalid date information"; - case let rn: parsefail => - if (rn == '\0') { - return "Date parsing error"; - }; - def FMTMSG = "Invalid date format for specifier '{}'"; - static let buf: [len(FMTMSG) + 2]u8 = [0...]; - return fmt::bsprintf(buf, FMTMSG, rn); + case let pf: parsefail => + const (bi, rn) = pf; + def FMTMSG = "Date parsing failure for layout rune '{}' at byteindex {}"; + static let buf: [len(FMTMSG) + 3]u8 = [0...]; + return fmt::bsprintf(buf, FMTMSG, rn, bi); }; }; diff --git a/time/date/parse.ha b/time/date/parse.ha index 4edbae45..cda8adfd 100644 --- a/time/date/parse.ha +++ b/time/date/parse.ha @@ -10,9 +10,9 @@ use time::chrono; type failure = !void; -// A parsing error occurred. If appropriate, the offending format specifier is -// stored. A null rune represents all other error cases. -export type parsefail = !rune; +// A parsing error occurred. This shall contain a byteindex of and rune from the +// layout at the position where the parsing failure occured. +export type parsefail = !(size, rune); // Parses a date/time string into a [[virtual]], according to a layout format // string with specifiers as documented under [[format]]. Partial, sequential, @@ -45,12 +45,12 @@ export fn parse(v: *virtual, layout: str, s: str) (void | parsefail) = { if (!escaped) { const sr = match (strings::next(&siter)) { case void => - return '\x00'; + return (liter.dec.offs, lr); case let sr: rune => yield sr; }; if (sr != lr) { - return '\x00'; + return (liter.dec.offs, lr); }; continue; }; @@ -60,7 +60,7 @@ export fn parse(v: *virtual, layout: str, s: str) (void | parsefail) = { match (parse_specifier(v, &siter, lr)) { case void => void; case failure => - return lr; + return (liter.dec.offs, lr); }; }; -- 2.43.2
builds.sr.ht <builds@sr.ht>hare/patches: SUCCESS in 1m9s [time::chrono: rename _lookupzone to lookupzone][0] v2 from [Byron Torres][1] [0]: https://lists.sr.ht/~sircmpwn/hare-dev/patches/49755 [1]: mailto:b@torresjrjr.com ✓ #1155871 SUCCESS hare/patches/freebsd.yml https://builds.sr.ht/~sircmpwn/job/1155871 ✓ #1155870 SUCCESS hare/patches/alpine.yml https://builds.sr.ht/~sircmpwn/job/1155870 ✓ #1155872 SUCCESS hare/patches/openbsd.yml https://builds.sr.ht/~sircmpwn/job/1155872
Rename layout [[EMAILZ]] to [[EMAILZONE]]. Rename layout [[STAMP_NANO]] to [[STAMPNANO]]. Rename layout [[STAMP_ZOFF]] to [[STAMPZOFF]]. Rename layout [[STAMP_ZONE]] to [[STAMPZONE]]. Rename layout [[STAMP_NOZL]] to [[STAMPLOC]]. New layouts with examples: [[JOURNAL]] "2038 Jan 19, Tue 04:14:07 +0100 CET Europe/Amsterdam" [[WRIST]] "Jan-19 Tue 04:14 CET" [[QUARTZ]] "2147480047.012700000" [[QUARTZZOFF]] "2147480047.012700000+0100" [[QUARTZLOC]] "2147480047.012700000:Europe/Amsterdam" Breaking-Change: 0.24.0 Signed-off-by: Byron Torres <b@torresjrjr.com> --- encoding/asn1/+test/decoder_test.ha | 2 +- time/date/date.ha | 4 +-- time/date/format.ha | 39 ++++++++++++++++++++++------- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/encoding/asn1/+test/decoder_test.ha b/encoding/asn1/+test/decoder_test.ha index cb32d7c3..ccb3a279 100644 --- a/encoding/asn1/+test/decoder_test.ha +++ b/encoding/asn1/+test/decoder_test.ha @@ -289,7 +289,7 @@ fn newdatetime(s: str, tag: utag) []u8 = { let derdatetime = newdatetime("20231030133710.1Z", utag::GENERALIZED_TIME); let dt = read_gtime(&d(derdatetime))!; - assert(date::bsformat(fbuf, date::STAMP_NANO, &dt)! + assert(date::bsformat(fbuf, date::STAMPNANO, &dt)! == "2023-10-30 13:37:10.100000000"); // must end with Z diff --git a/time/date/date.ha b/time/date/date.ha index ded5c818..2bacca22 100644 --- a/time/date/date.ha +++ b/time/date/date.ha @@ -214,7 +214,7 @@ export fn from_instant(loc: chrono::locality, i: time::instant) date = { // provided, they default to 0. // // let new = date::from_str( -// date::STAMP_NOZL, +// date::STAMPLOC, // "2019-12-27 22:07:08.000000000 +0100 CET Europe/Amsterdam", // locs... // )!; @@ -240,7 +240,7 @@ export fn from_str( @test fn from_str() void = { let testcases: [_](str, str, []chrono::locality, (date | error)) = [ - (STAMP_NOZL, "2001-02-03 15:16:17.123456789 +0000 UTC UTC", [], + (STAMPLOC, "2001-02-03 15:16:17.123456789 +0000 UTC UTC", [], new(chrono::UTC, 0, 2001, 2, 3, 15, 16, 17, 123456789)!), (STAMP, "2001-02-03 15:16:17", [], new(chrono::UTC, 0, 2001, 2, 3, 15, 16, 17)!), diff --git a/time/date/format.ha b/time/date/format.ha index 592461b4..b86da76e 100644 --- a/time/date/format.ha +++ b/time/date/format.ha @@ -14,7 +14,7 @@ export def EMAIL: str = "%a, %d %b %Y %H:%M:%S %z"; // [[format]] layout for the email date format, with zone offset and // zone abbreviation. -export def EMAILZ: str = "%a, %d %b %Y %H:%M:%S %z %Z"; +export def EMAILZONE: str = "%a, %d %b %Y %H:%M:%S %z %Z"; // [[format]] layout for the POSIX locale's default date & time representation. export def POSIX: str = "%a %b %e %H:%M:%S %Y"; @@ -22,19 +22,40 @@ export def POSIX: str = "%a %b %e %H:%M:%S %Y"; // [[format]] layout compatible with RFC 3339. export def RFC3339: str = "%Y-%m-%dT%H:%M:%S%z"; -// [[format]] layout for a simple timestamp. +// [[format]] layout for a standard, collatable timestamp. export def STAMP: str = "%Y-%m-%d %H:%M:%S"; -// [[format]] layout for a simple timestamp with nanoseconds. -export def STAMP_NANO: str = "%Y-%m-%d %H:%M:%S.%N"; +// [[format]] layout for a standard, collatable timestamp with nanoseconds. +export def STAMPNANO: str = "%Y-%m-%d %H:%M:%S.%N"; -// [[format]] layout for a simple timestamp with nanoseconds and zone -// offset. -export def STAMP_ZOFF: str = "%Y-%m-%d %H:%M:%S.%N %z"; +// [[format]] layout for a standard, collatable timestamp with nanoseconds +// and zone offset. +export def STAMPZOFF: str = "%Y-%m-%d %H:%M:%S.%N %z"; -// [[format]] layout for a simple timestamp with nanoseconds, +// [[format]] layout for a standard, collatable timestamp with nanoseconds, +// zone offset, and zone abbreviation. +export def STAMPZONE: str = "%Y-%m-%d %H:%M:%S.%N %z %Z"; + +// [[format]] layout for a standard, collatable timestamp with nanoseconds, // zone offset, zone abbreviation, and locality. -export def STAMP_NOZL: str = "%Y-%m-%d %H:%M:%S.%N %z %Z %L"; +export def STAMPLOC: str = "%Y-%m-%d %H:%M:%S.%N %z %Z %L"; + +// [[format]] layout for a friendly, comprehensive, serializable datetime. +export def JOURNAL: str = "%Y %b %d, %a %H:%M:%S %z %Z %L"; + +// [[format]] layout for a friendly, terse, glanceable datetime. +export def WRIST: str = "%b-%d %a %H:%M %Z"; + +// [[format]] layout for a precise timescalar second and nanosecond. +export def QUARTZ: str = "%s.%N"; + +// [[format]] layout for a precise timescalar second, nanosecond, +// and zone offset. +export def QUARTZZOFF: str = "%s.%N%z"; + +// [[format]] layout for a precise timescalar second, nanosecond, +// and locality. +export def QUARTZLOC: str = "%s.%N:%L"; def WEEKDAYS: [_]str = [ "Monday", -- 2.43.2
[[virtual]] has two new fields: * .vsec "virtual's second since epoch" * .vnsec "virtual's nanosecond" [[parse]] now handles %s, and %N additionally assigns to .vnsec. [[realize]] now accounts for .vsec and .vnsec, and has improved documentation. This change makes parsing dates using the [[QUARTZ]], [[QUARTZZOFF]], and [[QUARTZLOC]] layouts useful. date::parse(&v, date::QUARTZLOC, "2147483647.0:Europe/Amsterdam"); Signed-off-by: Byron Torres <b@torresjrjr.com> --- time/date/parse.ha | 37 +++++++++-- time/date/virtual.ha | 150 +++++++++++++++++++++++++++---------------- 2 files changed, 126 insertions(+), 61 deletions(-) diff --git a/time/date/parse.ha b/time/date/parse.ha index cda8adfd..8c198073 100644 --- a/time/date/parse.ha +++ b/time/date/parse.ha @@ -102,9 +102,13 @@ fn parse_specifier( case 'M' => v.minute = scan_int(iter, 2)?; case 'N' => - v.nanosecond = scan_decimal(iter, 9)?; + let nsec = scan_decimal(iter, 9)?; + v.nanosecond = nsec: int; + v.vnsec = nsec; case 'p' => // AM=false PM=true v.ampm = scan_for(iter, "AM", "PM", "am", "pm")? % 2 == 1; + case 's' => + v.vsec = scan_num(iter, 20)?; case 'S' => v.second = scan_int(iter, 2)?; case 'T' => @@ -200,10 +204,35 @@ fn scan_int(iter: *strings::iterator, maxrunes: size) (int | failure) = { }; }; +// Scans the iterator for consecutive numeric digits. +// Left-padded whitespace and zeros are permitted. +// Returns the resulting i64. +fn scan_num(iter: *strings::iterator, maxrunes: size) (i64 | failure) = { + let start = *iter; + for (let i = 0z; i < maxrunes; i += 1) { + match (strings::next(iter)) { + case void => + return failure; + case let rn: rune => + if (!ascii::isdigit(rn)) { + strings::prev(iter); + break; + }; + }; + }; + + match (strconv::stoi64(strings::slice(&start, iter))) { + case let num: i64 => + return num; + case => + return failure; + }; +}; + // Scans the iterator for consecutive numeric digits. // Left-padded whitespace and zeros are NOT permitted. // The resulting decimal is right-padded with zeros. -fn scan_decimal(iter: *strings::iterator, maxrunes: size) (int | failure) = { +fn scan_decimal(iter: *strings::iterator, maxrunes: size) (i64 | failure) = { let start = *iter; for (let i = 0z; i < maxrunes; i += 1) { let rn: rune = match (strings::next(iter)) { @@ -218,8 +247,8 @@ fn scan_decimal(iter: *strings::iterator, maxrunes: size) (int | failure) = { }; }; const s = strings::slice(&start, iter); - match (strconv::stoi(s)) { - case let num: int => + match (strconv::stoi64(s)) { + case let num: i64 => for (let i = 0z; i < maxrunes - len(s); i += 1) { num *= 10; }; diff --git a/time/date/virtual.ha b/time/date/virtual.ha index 2ab1a4df..87df4ecf 100644 --- a/time/date/virtual.ha +++ b/time/date/virtual.ha @@ -39,6 +39,10 @@ export type lack = enum u8 { // export type virtual = struct { date, + // virtual's timescalar second + vsec: (void | i64), + // virtual's nanosecond of timescalar second + vnsec: (void | i64), // virtual's locality vloc: (void | chrono::locality), // locality name @@ -78,6 +82,8 @@ export fn newvirtual() virtual = virtual { second = void, nanosecond = void, + vsec = void, + vnsec = void, vloc = void, locname = void, zoff = void, @@ -87,45 +93,96 @@ export fn newvirtual() virtual = virtual { }; // Realizes a valid [[date]] from a [[virtual]], or fails appropriately. -// Four values require determination. Each has various determination strategies, -// each of which use a certain set of non-void fields from the given virtual. -// The following determination strategies will be attempted in order. // -// Field sets for determining the daydate: +// The virtual must hold enough valid date information to be able to calculate +// values for the resulting date. A valid combination of its fields must be +// "filled-in" (hold numerical, non-void values). For example: // -// 1. daydate -// 2. year, month, day -// 3. year, yearday -// 4. year, week, weekday -// 5. isoweekyear, isoweek, weekday +// let v = date::newvirtual(); +// v.locname = "Europe/Amsterdam"; +// v.zoff = 1 * time::HOUR; +// date::parse(&v, // fills-in .year .month .day +// "Date: %Y-%m-%d", "Date: 2038-01-19")!; +// v.hour = 4; +// v.minute = 14; +// v.second = 7; +// v.nanosecond = 0; +// let d = date::realize(v, time::chrono::tz("Europe/Amsterdam")!)!; +// +// This function consults the fields of the given virtual using a predictable +// procedure. In calculating a date, it attempts to calculate values for empty +// fields using sets of other filled-in fields (dependencies), in a order +// described below. +// +// The resultant date depends on a locality and instant. +// +// The locality ([[time::chrono::locality]]) depends on: +// +// - .vloc +// - .locname : This is compared to the .name field of each locality +// provided via the locs parameter, or "UTC" if none are provided. +// The first matching locality is used. +// - (nothing) : Defaults to [[time::chrono::UTC]]. // -// Field sets for determining the time-of-day: +// The instant ([[time::instant]]) depends on: // -// 1. daytime -// 2. hour, minute, second, nanosecond +// - .vsec, .vnsec +// - .daydate, .daytime, .zoff // -// Field sets for determining the zone offset: +// An empty .daydate depends on: // -// 1. zoff +// - .year, .month, .day +// - .year, .yearday +// - .year, .week, .weekday +// - .isoweekyear, .isoweek, .weekday // -// Field sets for determining the [[time::chrono::locality]]: +// An empty .daytime depends on: // -// 1. vloc -// 2. locname -// This is compared to each provided locality's 'name' field, -// or "UTC" if none are provided. The first match is used. -// 3. (none) -// Defaults to [[time::chrono::UTC]]. +// - .hour, .minute, .second, .nanosecond // -// If for any of these values no determination strategy could be attempted, -// [[insufficient]] is returned. If the resultant date is invalid, -// [[invalid]] is returned. +// If none of the possible combinations of fields were filled-in, +// [[insufficient]] is returned. If after calculation the resultant date is +// invalid, [[invalid]] is returned. export fn realize( v: virtual, locs: chrono::locality... ) (date | insufficient | invalid) = { let lacking = 0u8; + // determine .loc (defaults to time::chrono::UTC) + if (v.vloc is chrono::locality) { + v.loc = v.vloc as chrono::locality; + } else if (v.locname is str) { + v.loc = chrono::UTC; + for (let i = 0z; i < len(locs); i += 1) { + const loc = locs[i]; + if (loc.name == v.locname as str) { + v.loc = loc; + break; + }; + }; + }; + + // try using .vsec .vnsec + if (v.vsec is i64 && v.vnsec is i64) { + return from_instant( + v.loc, + time::instant{ + sec = v.vsec as i64, + nsec = v.vnsec as i64, + }, + ); + }; + + // try using .daydate, .daytime, .zoff + + // determine zone offset + if (v.zoff is i64) { + void; + } else { + lacking |= insufficient::ZOFF; + }; + // determine .daydate if (v.daydate is i64) { void; @@ -168,17 +225,18 @@ export fn realize( // determine .daytime if (v.daytime is i64) { void; - } else :nodaytime { - const hour = if (v.hour is int) { - yield v.hour as int; - } else if (v.halfhour is int && v.ampm is bool) { - const hr = v.halfhour as int; - const pm = v.ampm as bool; - yield if (pm) hr * 2 else hr; - } else { - lacking |= insufficient::DAYTIME; - yield :nodaytime; - }; + } else :daytime { + const hour = + if (v.hour is int) { + yield v.hour as int; + } else if (v.halfhour is int && v.ampm is bool) { + const hr = v.halfhour as int; + const pm = v.ampm as bool; + yield if (pm) hr * 2 else hr; + } else { + lacking |= insufficient::DAYTIME; + yield :daytime; + }; if ( v.minute is int && @@ -191,33 +249,11 @@ export fn realize( v.second as int, v.nanosecond as int, )?; - lacking |= 0u8; } else { lacking |= insufficient::DAYTIME; }; }; - // determine zone offset - if (v.zoff is i64) { - void; - } else { - lacking |= insufficient::ZOFF; - }; - - // determine .loc (defaults to time::chrono::UTC) - if (v.vloc is chrono::locality) { - v.loc = v.vloc as chrono::locality; - } else if (v.locname is str) { - v.loc = chrono::UTC; - for (let i = 0z; i < len(locs); i += 1) { - const loc = locs[i]; - if (loc.name == v.locname as str) { - v.loc = loc; - break; - }; - }; - }; - if (lacking != 0u8) { return lacking: insufficient; }; -- 2.43.2
Thanks! To git@git.sr.ht:~sircmpwn/hare 8f42a75a..0dbc18b1 master -> master