commit 9a62f94fc90ce92b4527b92e0e25a6c0d81d636f
parent 0e1a16ac41454af181f13c157bb5735052387b60
Author: Byron Torres <b@torresjrjr.com>
Date: Wed, 17 May 2023 10:12:34 +0000
time::date: rename source files appropriately
Diffstat:
11 files changed, 308 insertions(+), 906 deletions(-)
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -1377,34 +1377,34 @@ time_chrono() {
time_date() {
gen_srcs -plinux time::date \
- arithmetic.ha \
- chronology.ha \
- errors.ha \
date.ha \
- datetime.ha \
- duration.ha \
+ daydate.ha \
+ daytime.ha \
+ error.ha \
format.ha \
+ locality.ha \
+ observe.ha \
+ parithm.ha \
parse.ha \
period.ha \
reckon.ha \
- time.ha \
- timezone.ha \
+ tarithm.ha \
virtual.ha
gen_ssa -plinux time::date \
ascii errors fmt io strconv strings strio time time::chrono
gen_srcs -pfreebsd time::date \
- arithmetic.ha \
- chronology.ha \
- errors.ha \
date.ha \
- datetime.ha \
- duration.ha \
+ daydate.ha \
+ daytime.ha \
+ error.ha \
format.ha \
+ locality.ha \
+ observe.ha \
+ parithm.ha \
parse.ha \
period.ha \
reckon.ha \
- time.ha \
- timezone.ha \
+ tarithm.ha \
virtual.ha
gen_ssa -pfreebsd time::date \
ascii errors fmt io strconv strings strio time time::chrono
diff --git a/stdlib.mk b/stdlib.mk
@@ -2153,18 +2153,18 @@ $(HARECACHE)/time/chrono/time_chrono-freebsd.ssa: $(stdlib_time_chrono_freebsd_s
# time::date (+linux)
stdlib_time_date_linux_srcs = \
- $(STDLIB)/time/date/arithmetic.ha \
- $(STDLIB)/time/date/chronology.ha \
- $(STDLIB)/time/date/errors.ha \
$(STDLIB)/time/date/date.ha \
- $(STDLIB)/time/date/datetime.ha \
- $(STDLIB)/time/date/duration.ha \
+ $(STDLIB)/time/date/daydate.ha \
+ $(STDLIB)/time/date/daytime.ha \
+ $(STDLIB)/time/date/error.ha \
$(STDLIB)/time/date/format.ha \
+ $(STDLIB)/time/date/locality.ha \
+ $(STDLIB)/time/date/observe.ha \
+ $(STDLIB)/time/date/parithm.ha \
$(STDLIB)/time/date/parse.ha \
$(STDLIB)/time/date/period.ha \
$(STDLIB)/time/date/reckon.ha \
- $(STDLIB)/time/date/time.ha \
- $(STDLIB)/time/date/timezone.ha \
+ $(STDLIB)/time/date/tarithm.ha \
$(STDLIB)/time/date/virtual.ha
$(HARECACHE)/time/date/time_date-linux.ssa: $(stdlib_time_date_linux_srcs) $(stdlib_rt) $(stdlib_ascii_$(PLATFORM)) $(stdlib_errors_$(PLATFORM)) $(stdlib_fmt_$(PLATFORM)) $(stdlib_io_$(PLATFORM)) $(stdlib_strconv_$(PLATFORM)) $(stdlib_strings_$(PLATFORM)) $(stdlib_strio_$(PLATFORM)) $(stdlib_time_$(PLATFORM)) $(stdlib_time_chrono_$(PLATFORM))
@@ -2175,18 +2175,18 @@ $(HARECACHE)/time/date/time_date-linux.ssa: $(stdlib_time_date_linux_srcs) $(std
# time::date (+freebsd)
stdlib_time_date_freebsd_srcs = \
- $(STDLIB)/time/date/arithmetic.ha \
- $(STDLIB)/time/date/chronology.ha \
- $(STDLIB)/time/date/errors.ha \
$(STDLIB)/time/date/date.ha \
- $(STDLIB)/time/date/datetime.ha \
- $(STDLIB)/time/date/duration.ha \
+ $(STDLIB)/time/date/daydate.ha \
+ $(STDLIB)/time/date/daytime.ha \
+ $(STDLIB)/time/date/error.ha \
$(STDLIB)/time/date/format.ha \
+ $(STDLIB)/time/date/locality.ha \
+ $(STDLIB)/time/date/observe.ha \
+ $(STDLIB)/time/date/parithm.ha \
$(STDLIB)/time/date/parse.ha \
$(STDLIB)/time/date/period.ha \
$(STDLIB)/time/date/reckon.ha \
- $(STDLIB)/time/date/time.ha \
- $(STDLIB)/time/date/timezone.ha \
+ $(STDLIB)/time/date/tarithm.ha \
$(STDLIB)/time/date/virtual.ha
$(HARECACHE)/time/date/time_date-freebsd.ssa: $(stdlib_time_date_freebsd_srcs) $(stdlib_rt) $(stdlib_ascii_$(PLATFORM)) $(stdlib_errors_$(PLATFORM)) $(stdlib_fmt_$(PLATFORM)) $(stdlib_io_$(PLATFORM)) $(stdlib_strconv_$(PLATFORM)) $(stdlib_strings_$(PLATFORM)) $(stdlib_strio_$(PLATFORM)) $(stdlib_time_$(PLATFORM)) $(stdlib_time_chrono_$(PLATFORM))
@@ -4609,18 +4609,18 @@ $(TESTCACHE)/time/chrono/time_chrono-freebsd.ssa: $(testlib_time_chrono_freebsd_
# time::date (+linux)
testlib_time_date_linux_srcs = \
- $(STDLIB)/time/date/arithmetic.ha \
- $(STDLIB)/time/date/chronology.ha \
- $(STDLIB)/time/date/errors.ha \
$(STDLIB)/time/date/date.ha \
- $(STDLIB)/time/date/datetime.ha \
- $(STDLIB)/time/date/duration.ha \
+ $(STDLIB)/time/date/daydate.ha \
+ $(STDLIB)/time/date/daytime.ha \
+ $(STDLIB)/time/date/error.ha \
$(STDLIB)/time/date/format.ha \
+ $(STDLIB)/time/date/locality.ha \
+ $(STDLIB)/time/date/observe.ha \
+ $(STDLIB)/time/date/parithm.ha \
$(STDLIB)/time/date/parse.ha \
$(STDLIB)/time/date/period.ha \
$(STDLIB)/time/date/reckon.ha \
- $(STDLIB)/time/date/time.ha \
- $(STDLIB)/time/date/timezone.ha \
+ $(STDLIB)/time/date/tarithm.ha \
$(STDLIB)/time/date/virtual.ha
$(TESTCACHE)/time/date/time_date-linux.ssa: $(testlib_time_date_linux_srcs) $(testlib_rt) $(testlib_ascii_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_fmt_$(PLATFORM)) $(testlib_io_$(PLATFORM)) $(testlib_strconv_$(PLATFORM)) $(testlib_strings_$(PLATFORM)) $(testlib_strio_$(PLATFORM)) $(testlib_time_$(PLATFORM)) $(testlib_time_chrono_$(PLATFORM))
@@ -4631,18 +4631,18 @@ $(TESTCACHE)/time/date/time_date-linux.ssa: $(testlib_time_date_linux_srcs) $(te
# time::date (+freebsd)
testlib_time_date_freebsd_srcs = \
- $(STDLIB)/time/date/arithmetic.ha \
- $(STDLIB)/time/date/chronology.ha \
- $(STDLIB)/time/date/errors.ha \
$(STDLIB)/time/date/date.ha \
- $(STDLIB)/time/date/datetime.ha \
- $(STDLIB)/time/date/duration.ha \
+ $(STDLIB)/time/date/daydate.ha \
+ $(STDLIB)/time/date/daytime.ha \
+ $(STDLIB)/time/date/error.ha \
$(STDLIB)/time/date/format.ha \
+ $(STDLIB)/time/date/locality.ha \
+ $(STDLIB)/time/date/observe.ha \
+ $(STDLIB)/time/date/parithm.ha \
$(STDLIB)/time/date/parse.ha \
$(STDLIB)/time/date/period.ha \
$(STDLIB)/time/date/reckon.ha \
- $(STDLIB)/time/date/time.ha \
- $(STDLIB)/time/date/timezone.ha \
+ $(STDLIB)/time/date/tarithm.ha \
$(STDLIB)/time/date/virtual.ha
$(TESTCACHE)/time/date/time_date-freebsd.ssa: $(testlib_time_date_freebsd_srcs) $(testlib_rt) $(testlib_ascii_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_fmt_$(PLATFORM)) $(testlib_io_$(PLATFORM)) $(testlib_strconv_$(PLATFORM)) $(testlib_strings_$(PLATFORM)) $(testlib_strio_$(PLATFORM)) $(testlib_time_$(PLATFORM)) $(testlib_time_chrono_$(PLATFORM))
diff --git a/time/date/date.ha b/time/date/date.ha
@@ -1,598 +1,305 @@
// License: MPL-2.0
// (c) 2021-2022 Byron Torres <b@torresjrjr.com>
-// (c) 2021-2022 Vlad-Stefan Harbuz <vlad@vladh.net>
+// (c) 2022 Drew DeVault <sir@cmpwn.com>
use errors;
+use time;
use time::chrono;
-// Hare internally uses the Unix epoch (1970-01-01) for calendrical logic. Here
-// we provide useful constant for working with the astronomically numbered
-// proleptic Gregorian calendar, as offsets from the Hare epoch.
-
-// The Hare epoch of the Julian Day Number.
-export def EPOCHAL_JULIAN: i64 = -2440588;
-
-// The Hare epoch of the Gregorian Common Era.
-export def EPOCHAL_GREGORIAN: i64 = -719164;
-
-// Calculates whether a year is a leap year.
-export fn isleapyear(y: int) bool = {
- return if (y % 4 != 0) false
- else if (y % 100 != 0) true
- else if (y % 400 != 0) false
- else true;
+// Invalid [[date]].
+export type invalid = !chrono::invalid;
+
+// A date/time object; a [[time::chrono::moment]] wrapper optimized for the
+// Gregorian chronology, and by extension a [[time::instant]] wrapper.
+//
+// This object should be treated as private and immutable. Directly mutating its
+// fields causes undefined behaviour when used with module functions. Likewise,
+// interrogating the fields' type and value (e.g. using match statements) is
+// also improper.
+//
+// A date observes various chronological values, cached in its fields. To
+// evaluate and obtain these values, use the various observer functions
+// ([[year]], [[hour]], etc.). These values are derived from the embedded moment
+// information, and thus are guaranteed to be valid.
+//
+// See [[virtual]] for an public, mutable, intermediary representation of a
+// date, which waives guarantees of validity.
+export type date = struct {
+ chrono::moment,
+
+ era: (void | int),
+ year: (void | int),
+ month: (void | int),
+ day: (void | int),
+ yearday: (void | int),
+ isoweekyear: (void | int),
+ isoweek: (void | int),
+ week: (void | int),
+ sundayweek: (void | int),
+ weekday: (void | int),
+
+ hour: (void | int),
+ minute: (void | int),
+ second: (void | int),
+ nanosecond: (void | int),
};
-// 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);
+fn init() date = date {
+ sec = 0,
+ nsec = 0,
+ loc = chrono::UTC,
+ zone = null,
+ daydate = void,
+ daytime = void,
+
+ era = void,
+ year = void,
+ month = void,
+ day = void,
+ yearday = void,
+ isoweekyear = void,
+ isoweek = void,
+ week = void,
+ sundayweek = void,
+ weekday = void,
+
+ hour = void,
+ minute = void,
+ second = void,
+ nanosecond = void,
};
-// 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);
+// Evaluates and populates all of a [[date]]'s fields.
+fn all(d: *date) *date = {
+ _era(d);
+ _year(d);
+ _month(d);
+ _day(d);
+ _yearday(d);
+ _isoweekyear(d);
+ _isoweek(d);
+ _week(d);
+ _sundayweek(d);
+ _weekday(d);
+
+ _hour(d);
+ _minute(d);
+ _second(d);
+ _nanosecond(d);
+
+ return d;
};
-// Calculates the number of days in the given month of the given year.
-fn calc_month_daycnt(y: int, m: int) int = {
- const days_per_month: [_]int = [
- 31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+// Creates a new [[date]]. A maximum of 7 optional field arguments can be given:
+// year, month, day-of-month, hour, minute, second, nanosecond. 8 or more causes
+// an abort.
+//
+// // 0000-01-01 00:00:00.000000000 +0000 UTC UTC
+// date::new(time::chrono::UTC, 0);
+//
+// // 2019-12-27 20:07:08.000031415 +0000 UTC UTC
+// date::new(time::chrono::UTC, 0, 2019, 12, 27, 20, 07, 08, 31415);
+//
+// // 2019-12-27 21:00:00.000000000 +0100 CET Europe/Amsterdam
+// date::new(time::chrono::tz("Europe/Amsterdam")!, 1 * time::HOUR,
+// 2019, 12, 27, 21);
+//
+// 'zo' is the zone offset from the normal timezone (in most cases, UTC). For
+// example, the "Asia/Tokyo" timezone has a single zoff of +9 hours, but the
+// "Australia/Sydney" timezone has zoffs +10 hours and +11 hours, as they
+// observe Daylight Saving Time.
+//
+// If specified (non-void), 'zo' must match one of the timezone's observed
+// zoffs, or will fail. See [[time::chrono::fixedzone]] for custom timezones.
+//
+// You may omit the zoff. If the givem timezone has a single zone, [[new]]
+// will use that zone's zoff. Otherwise [[new]] will try to infer the zoff
+// from the multiple zones. This will fail during certain timezone transitions,
+// where certain dates are ambiguous or nonexistent. For example:
+//
+// - In the Europe/Amsterdam timezone, at 1995 March 26th,
+// the local time 02:30 was never observed,
+// as the clock jumped forward 1 hour from 02:00 CET to 03:00 CEST.
+//
+// - In the Europe/Amsterdam timezone, at 1995 September 24th,
+// the local time 02:30 was observed twice (00:30 UTC & 01:30 UTC),
+// as the clock jumped back 1 hour from 03:00 CEST to 02:00 CET.
+export fn new(
+ loc: chrono::locality,
+ zo: (time::duration | void),
+ fields: int...
+) (date | invalid) = {
+ // TODO:
+ // - revise examples
+ // - Implement as described.
+ // - fix calls with `years <= -4715`.
+ // https://todo.sr.ht/~sircmpwn/hare/565
+ let _fields: [_]int = [
+ 0, 1, 1, // year month day
+ 0, 0, 0, 0, // hour min sec nsec
];
- if (m == 2) {
- return if (isleapyear(y)) 29 else 28;
- } else {
- return days_per_month[m - 1];
- };
-};
-
-// Calculates the number of days in a given year.
-fn calc_year_daycnt(y: int) int = {
- return if (isleapyear(y)) 366 else 365;
-};
-
-// Calculates the day-of-week of January 1st, given a year.
-fn calc_janfirstweekday(y: int) int = {
- const y = (y % 400) + 400; // keep year > 0 (using Gregorian cycle)
- // Gauss' algorithm
- const wd = (
- + 5 * ((y - 1) % 4)
- + 4 * ((y - 1) % 100)
- + 6 * ((y - 1) % 400)
- ) % 7;
- return wd;
-};
-// Calculates the era, given a year.
-fn calc_era(y: int) int = {
- return if (y >= 0) {
- yield 1; // CE "Common Era"
- } else {
- yield 0; // BCE "Before Common Era"
+ assert(len(fields) <= len(_fields),
+ "time::date::new(): Too many field arguments");
+ _fields[..len(fields)] = fields;
+
+ const year = _fields[0];
+ const month = _fields[1];
+ const day = _fields[2];
+ const hour = _fields[3];
+ const min = _fields[4];
+ const sec = _fields[5];
+ const nsec = _fields[6];
+
+ const mdate = calc_daydate__ymd(year, month, day)?;
+ const mtime = calc_daytime__hmsn(hour, min, sec, nsec)?;
+
+ // 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: time::date::new(zo=void)");
};
-};
-
-// Calculates the year, month, and day-of-month, given an epochal day.
-fn calc_ymd(e: i64) (int, int, int) = {
- // Algorithm adapted from:
- // https://en.wikipedia.org/wiki/Julian_day#Julian_or_Gregorian_calendar_from_Julian_day_number
- //
- // Alternate methods of date calculation should be explored.
- const J = e - EPOCHAL_JULIAN;
-
- // TODO: substitute numbers where possible
- const b = 274277;
- const c = -38;
- const j = 1401;
- const m = 2;
- const n = 12;
- const p = 1461;
- const r = 4;
- const s = 153;
- const u = 5;
- const v = 3;
- const w = 2;
- const y = 4716;
-
- const f = J + j + (((4 * J + b) / 146097) * 3) / 4 + c;
- const a = r * f + v;
- const g = (a % p) / r;
- const h = u * g + w;
- const D = (h % s) / u + 1;
- const M = ((h / s + m) % n) + 1;
- const Y = (a / p) - y + (n + m - M) / n;
-
- return (Y: int, M: int, D: int);
-};
-
-// Calculates the day-of-year, given a year, month, and day-of-month.
-fn calc_yearday(y: int, m: int, d: int) int = {
- const months_firsts: [_]int = [
- 0, 31, 59,
- 90, 120, 151,
- 181, 212, 243,
- 273, 304, 334,
- ];
+ const d = from_moment(m);
- if (m >= 3 && isleapyear(y)) {
- return months_firsts[m - 1] + d + 1;
- } else {
- return months_firsts[m - 1] + d;
+ const zo = match (zo) {
+ case void =>
+ yield chrono::mzone(&m).zoff;
+ case let zo: time::duration =>
+ yield zo;
};
-};
-// Calculates the ISO week-numbering year,
-// given a year, month, day-of-month, and day-of-week.
-fn calc_isoweekyear(y: int, m: int, d: int, wd: int) int = {
+ // check if input values are actually observed
if (
- // if the date is within a week whose Thursday
- // belongs to the previous Gregorian year
- m == 1 && (
- (d == 1 && (wd == 4 || wd == 5 || wd == 6))
- || (d == 2 && (wd == 5 || wd == 6))
- || (d == 3 && wd == 6)
- )
- ) {
- return y - 1;
- } else if (
- // if the date is within a week whose Thursday
- // belongs to the next Gregorian year
- m == 12 && (
- (d == 29 && wd == 0)
- || (d == 30 && (wd == 0 || wd == 1))
- || (d == 31 && (wd == 0 || wd == 1 || wd == 2))
- )
+ zo != chrono::mzone(&d).zoff
+ || year != _year(&d)
+ || month != _month(&d)
+ || day != _day(&d)
+ || hour != _hour(&d)
+ || min != _minute(&d)
+ || sec != _second(&d)
+ || nsec != _nanosecond(&d)
) {
- return y + 1;
- } else {
- return y;
- };
-};
-
-// Calculates the ISO week,
-// given a year, week, day-of-week, and day-of-year.
-fn calc_isoweek(y: int, w: int) int = {
- switch (calc_janfirstweekday(y)) {
- case 0 =>
- return w;
- case 1, 2, 3 =>
- return w + 1;
- case 4 =>
- return if (w != 0) w else 53;
- case 5 =>
- return if (w != 0) w else {
- yield if (isleapyear(y - 1)) 53 else 52;
- };
- case 6 =>
- return if (w != 0) w else 52;
- case =>
- abort("Unreachable");
- };
-};
-
-// Calculates the week within a Gregorian year [0..53],
-// given a day-of-year and day-of-week.
-// All days in a year before the year's first Monday belong to week 0.
-fn calc_week(yd: int, wd: int) int = {
- return (yd + 6 - wd) / 7;
-};
-
-// Calculates the week within a Gregorian year [0..53],
-// given a day-of-year and day-of-week.
-// All days in a year before the year's first Sunday belong to week 0.
-fn calc_sundayweek(yd: int, wd: int) int = {
- return (yd + 6 - ((wd + 1) % 7)) / 7;
-};
-
-// Calculates the day-of-week, given a epochal day,
-// from Monday=0 to Sunday=6.
-fn calc_weekday(e: i64) int = {
- const wd = ((e + 3) % 7): int;
- return (wd + 7) % 7;
-};
-
-// Calculates the daydate,
-// given a year, month, and day-of-month.
-fn calc_daydate__ymd(y: int, m: int, d: int) (i64 | invalid) = {
- if (!is_valid_ymd(y, m, d)) {
- return invalid;
- };
- // Algorithm adapted from:
- // https://en.wikipedia.org/wiki/Julian_day
- //
- // TODO: Review, cite, verify, annotate.
- const jdn = (
- (1461 * (y + 4800 + (m - 14) / 12)) / 4
- + (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12
- - (3 * ((y + 4900 + (m - 14) / 12) / 100)) / 4
- + d
- - 32075
- );
- const e = jdn + EPOCHAL_JULIAN;
- return e;
-};
-
-// Calculates the daydate,
-// given a year, week, and day-of-week.
-fn calc_daydate__ywd(y: int, w: int, wd: int) (i64 | invalid) = {
- const jan1wd = calc_janfirstweekday(y);
- const yd = wd - jan1wd + 7 * w;
- return calc_daydate__yd(y, yd)?;
-};
-
-// 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)) {
return invalid;
};
- return calc_daydate__ymd(y, 1, 1)? + yd - 1;
-};
-
-@test fn calc_daydate__ymd() void = {
- const cases = [
- (( -768, 2, 5), -999999, false),
- (( -1, 12, 31), -719529, false),
- (( 0, 1, 1), -719528, false),
- (( 0, 1, 2), -719527, false),
- (( 0, 12, 31), -719163, false),
- (( 1, 1, 1), -719162, false),
- (( 1, 1, 2), -719161, false),
- (( 1965, 3, 23), -1745, false),
- (( 1969, 12, 31), -1, false),
- (( 1970, 1, 1), 0, false),
- (( 1970, 1, 2), 1, false),
- (( 1999, 12, 31), 10956, false),
- (( 2000, 1, 1), 10957, false),
- (( 2000, 1, 2), 10958, false),
- (( 2038, 1, 18), 24854, false),
- (( 2038, 1, 19), 24855, false),
- (( 2038, 1, 20), 24856, false),
- (( 2243, 10, 17), 100000, false),
- (( 4707, 11, 28), 999999, false),
- (( 4707, 11, 29), 1000000, false),
- ((29349, 1, 25), 9999999, false),
-
- (( 1970,-99,-99), 0, true),
- (( 1970, -9, -9), 0, true),
- (( 1970, -1, -1), 0, true),
- (( 1970, 0, 0), 0, true),
- (( 1970, 0, 1), 0, true),
- (( 1970, 1, 99), 0, true),
- (( 1970, 99, 99), 0, true),
- ];
- for (let i = 0z; i < len(cases); i += 1) {
- const params = cases[i].0;
- const expect = cases[i].1;
- const should_error = cases[i].2;
- const actual = calc_daydate__ymd(
- params.0, params.1, params.2,
- );
- if (should_error) {
- assert(actual is invalid, "invalid date accepted");
- } else {
- assert(actual is i64, "valid date not accepted");
- assert(actual as i64 == expect, "date miscalculation");
- };
- };
+ return d;
};
-@test fn calc_daydate__ywd() void = {
- const cases = [
- (( -768, 0, 4), -1000034),
- (( -768, 5, 4), -999999),
- (( -1, 52, 5), -719529),
- (( 0, 0, 6), -719528),
- (( 0, 0, 7), -719527),
- (( 0, 52, 7), -719163),
- (( 1, 0, 1), -719162),
- (( 1, 0, 2), -719161),
- (( 1965, 12, 2), -1745),
- (( 1969, 52, 3), -1),
- (( 1970, 0, 4), 0),
- (( 1970, 0, 5), 1),
- (( 1999, 52, 5), 10956),
- (( 2000, 0, 6), 10957),
- (( 2000, 0, 7), 10958),
- (( 2020, 0, 3), 18262),
- (( 2022, 9, 1), 19051),
- (( 2022, 9, 2), 19052),
- (( 2023, 51, 7), 19715),
- (( 2024, 8, 3), 19781),
- (( 2024, 8, 4), 19782),
- (( 2024, 8, 5), 19783),
- (( 2024, 49, 4), 20069),
- (( 2024, 52, 2), 20088),
- (( 2038, 3, 1), 24854),
- (( 2038, 3, 2), 24855),
- (( 2038, 3, 3), 24856),
- (( 2243, 41, 2), 99993),
- (( 4707, 47, 4), 999999),
- (( 4707, 47, 5), 1000000),
- ((29349, 3, 6), 9999999),
- ];
-
- for (let i = 0z; i < len(cases); i += 1) {
- const ywd = cases[i].0;
- const expected = cases[i].1;
- const actual = calc_daydate__ywd(ywd.0, ywd.1, ywd.2)!;
- assert(actual == expected,
- "incorrect calc_daydate__ywd() result");
- };
+// Returns a [[date]] of the current system time using
+// [[time::clock::REALTIME]], in the [[time::chrono::LOCAL]] locality.
+export fn now() date = {
+ return from_instant(chrono::LOCAL, time::now(time::clock::REALTIME));
};
-@test fn calc_daydate__yd() void = {
- const cases = [
- ( -768, 36, -999999),
- ( -1, 365, -719529),
- ( 0, 1, -719528),
- ( 0, 2, -719527),
- ( 0, 366, -719163),
- ( 1, 1, -719162),
- ( 1, 2, -719161),
- ( 1965, 82, -1745 ),
- ( 1969, 365, -1 ),
- ( 1970, 1, 0 ),
- ( 1970, 2, 1 ),
- ( 1999, 365, 10956 ),
- ( 2000, 1, 10957 ),
- ( 2000, 2, 10958 ),
- ( 2038, 18, 24854 ),
- ( 2038, 19, 24855 ),
- ( 2038, 20, 24856 ),
- ( 2243, 290, 100000 ),
- ( 4707, 332, 999999 ),
- ( 4707, 333, 1000000),
- (29349, 25, 9999999),
- ];
-
- for (let i = 0z; i < len(cases); i += 1) {
- const y = cases[i].0;
- const yd = cases[i].1;
- const expected = cases[i].2;
- const actual = calc_daydate__yd(y, yd)!;
- assert(expected == actual,
- "error in date calculation from yd");
- };
- assert(calc_daydate__yd(2020, 0) is invalid,
- "calc_daydate__yd() did not reject invalid yearday");
- assert(calc_daydate__yd(2020, 400) is invalid,
- "calc_daydate__yd() did not reject invalid yearday");
+// Returns a [[date]] of the current system time using
+// [[time::clock::REALTIME]], in the [[time::chrono::UTC]] locality.
+export fn nowutc() date = {
+ return from_instant(chrono::UTC, time::now(time::clock::REALTIME));
};
-@test fn calc_ymd() void = {
- const cases = [
- (-999999, ( -768, 2, 5)),
- (-719529, ( -1, 12, 31)),
- (-719528, ( 0, 1, 1)),
- (-719527, ( 0, 1, 2)),
- (-719163, ( 0, 12, 31)),
- (-719162, ( 1, 1, 1)),
- (-719161, ( 1, 1, 2)),
- ( -1745, ( 1965, 3, 23)),
- ( -1, ( 1969, 12, 31)),
- ( 0, ( 1970, 1, 1)),
- ( 1, ( 1970, 1, 2)),
- ( 10956, ( 1999, 12, 31)),
- ( 10957, ( 2000, 1, 1)),
- ( 10958, ( 2000, 1, 2)),
- ( 24854, ( 2038, 1, 18)),
- ( 24855, ( 2038, 1, 19)),
- ( 24856, ( 2038, 1, 20)),
- ( 100000, ( 2243, 10, 17)),
- ( 999999, ( 4707, 11, 28)),
- (1000000, ( 4707, 11, 29)),
- (9999999, (29349, 1, 25)),
- ];
- for (let i = 0z; i < len(cases); i += 1) {
- const paramt = cases[i].0;
- const expect = cases[i].1;
- const actual = calc_ymd(paramt);
- assert(expect.0 == actual.0, "year mismatch");
- assert(expect.1 == actual.1, "month mismatch");
- assert(expect.2 == actual.2, "day mismatch");
- };
-};
-
-@test fn calc_yearday() void = {
- const cases = [
- (( -768, 2, 5), 36),
- (( -1, 12, 31), 365),
- (( 0, 1, 1), 1),
- (( 0, 1, 2), 2),
- (( 0, 12, 31), 366),
- (( 1, 1, 1), 1),
- (( 1, 1, 2), 2),
- (( 1965, 3, 23), 82),
- (( 1969, 12, 31), 365),
- (( 1970, 1, 1), 1),
- (( 1970, 1, 2), 2),
- (( 1999, 12, 31), 365),
- (( 2000, 1, 1), 1),
- (( 2000, 1, 2), 2),
- (( 2020, 2, 12), 43),
- (( 2038, 1, 18), 18),
- (( 2038, 1, 19), 19),
- (( 2038, 1, 20), 20),
- (( 2243, 10, 17), 290),
- (( 4707, 11, 28), 332),
- (( 4707, 11, 29), 333),
- ((29349, 1, 25), 25),
- ];
- for (let i = 0z; i < len(cases); i += 1) {
- const params = cases[i].0;
- const expect = cases[i].1;
- const actual = calc_yearday(params.0, params.1, params.2);
- assert(expect == actual, "yearday miscalculation");
- };
+// Creates a [[date]] from a [[time::chrono::moment]].
+export fn from_moment(m: chrono::moment) date = {
+ const d = init();
+ d.loc = m.loc;
+ d.sec = m.sec;
+ d.nsec = m.nsec;
+ d.daydate = m.daydate;
+ d.daytime = m.daytime;
+ d.zone = m.zone;
+ return d;
};
-@test fn calc_week() void = {
- const cases = [
- (( 1, 0), 1),
- (( 1, 1), 0),
- (( 1, 2), 0),
- (( 1, 3), 0),
- (( 1, 4), 0),
- (( 1, 5), 0),
- (( 1, 6), 0),
- (( 21, 1), 3),
- (( 61, 2), 9),
- ((193, 4), 27),
- ((229, 0), 33),
- ((286, 3), 41),
- ((341, 6), 48),
- ((365, 5), 52),
- ((366, 0), 53),
- ];
-
- for (let i = 0z; i < len(cases); i += 1) {
- const params = cases[i].0;
- const expect = cases[i].1;
- const actual = calc_week(params.0, params.1);
- assert(expect == actual, "week miscalculation");
- };
+// Creates a [[date]] from a [[time::instant]]
+// in a [[time::chrono::locality]].
+export fn from_instant(loc: chrono::locality, i: time::instant) date = {
+ return from_moment(chrono::new(loc, i));
};
-@test fn calc_sundayweek() void = {
- const cases = [
- (( 1, 0), 0),
- (( 1, 1), 0),
- (( 1, 2), 0),
- (( 1, 3), 0),
- (( 1, 4), 0),
- (( 1, 5), 0),
- (( 1, 6), 1),
- (( 21, 1), 3),
- (( 61, 2), 9),
- ((193, 4), 27),
- ((229, 0), 33),
- ((286, 3), 41),
- ((341, 6), 49),
- ((365, 5), 52),
- ((366, 0), 53),
- ];
-
- for (let i = 0z; i < len(cases); i += 1) {
- const params = cases[i].0;
- const expect = cases[i].1;
- const actual = calc_sundayweek(params.0, params.1);
- assert(expect == actual, "week miscalculation");
- };
+// Creates a [[date]] from a string, parsed according to a layout format.
+// See [[parse]] and [[format]]. At least a complete calendar date has to be
+// provided. The if hour, minute, second, nanosecond, or zone offset are not
+// provided, they default to 0.
+//
+// let new = date::from_str(
+// date::STAMP_NOZL,
+// "2019-12-27 22:07:08.000000000 +0100 CET Europe/Amsterdam",
+// locs...
+// )!;
+//
+// The date's [[time::chrono::locality]] will be selected from the provided
+// locality arguments. The 'name' field of these localities will be matched
+// against the parsed result for the %L specifier. If %L is not specified, or if
+// no locality is provided, [[time::chrono::UTC]] is used.
+export fn from_str(
+ layout: str,
+ s: str,
+ locs: time::chrono::locality...
+) (date | parsefail | insufficient | invalid) = {
+ const v = newvirtual();
+ v.zoff = 0;
+ v.hour = 0;
+ v.minute = 0;
+ v.second = 0;
+ v.nanosecond = 0;
+ parse(&v, layout, s)?;
+ return realize(v, locs...)?;
};
-@test fn calc_weekday() void = {
- const cases = [
- (-999999, 3), // -0768-02-05
- (-719529, 4), // -0001-12-31
- (-719528, 5), // 0000-01-01
- (-719527, 6), // 0000-01-02
- (-719163, 6), // 0000-12-31
- (-719162, 0), // 0001-01-01
- (-719161, 1), // 0001-01-02
- ( -1745, 1), // 1965-03-23
- ( -1, 2), // 1969-12-31
- ( 0, 3), // 1970-01-01
- ( 1, 4), // 1970-01-02
- ( 10956, 4), // 1999-12-31
- ( 10957, 5), // 2000-01-01
- ( 10958, 6), // 2000-01-02
- ( 24854, 0), // 2038-01-18
- ( 24855, 1), // 2038-01-19
- ( 24856, 2), // 2038-01-20
- ( 100000, 1), // 2243-10-17
- ( 999999, 3), // 4707-11-28
- (1000000, 4), // 4707-11-29
- (9999999, 5), // 29349-01-25
+@test fn from_str() void = {
+ const amst = chrono::tz("Europe/Amsterdam")!;
+ defer chrono::timezone_free(amst);
+
+ let testcases: [_](str, str, []chrono::locality, (date | error)) = [
+ (STAMP_NOZL, "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)!),
+ (RFC3339, "2001-02-03T15:16:17+0000", [],
+ new(chrono::UTC, 0, 2001, 2, 3, 15, 16, 17)!),
+ ("%F", "2009-06-30", [],
+ new(chrono::UTC, 0, 2009, 6, 30)!),
+ ("%F %L", "2009-06-30 GPS", [chrono::TAI, chrono::GPS],
+ new(chrono::GPS, 0, 2009, 6, 30)!),
+ ("%F %T", "2009-06-30 01:02:03", [],
+ new(chrono::UTC, 0, 2009, 6, 30, 1, 2, 3)!),
+ ("%FT%T%Z", "2009-06-30T18:30:00Z", [],
+ new(chrono::UTC, 0, 2009, 6, 30, 18, 30)!),
+ ("%FT%T.%N%Z", "2009-06-30T18:30:00.987654321Z", [],
+ new(chrono::UTC, 0, 2009, 6, 30, 18, 30, 0, 987654321)!),
+ ("%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),
];
- for (let i = 0z; i < len(cases); i += 1) {
- const paramt = cases[i].0;
- const expect = cases[i].1;
- const actual = calc_weekday(paramt);
- assert(expect == actual, "weekday miscalculation");
- };
-};
-@test fn calc_janfirstweekday() void = {
- const cases = [
- // year weekday
- (1969, 2),
- (1970, 3),
- (1971, 4),
- (1972, 5),
- (1973, 0),
- (1974, 1),
- (1975, 2),
- (1976, 3),
- (1977, 5),
- (1978, 6),
- (1979, 0),
- (1980, 1),
- (1981, 3),
- (1982, 4),
- (1983, 5),
- (1984, 6),
- (1985, 1),
- (1986, 2),
- (1987, 3),
- (1988, 4),
- (1989, 6),
- (1990, 0),
- (1991, 1),
- (1992, 2),
- (1993, 4),
- (1994, 5),
- (1995, 6),
- (1996, 0),
- (1997, 2),
- (1998, 3),
- (1999, 4),
- (2000, 5),
- (2001, 0),
- (2002, 1),
- (2003, 2),
- (2004, 3),
- (2005, 5),
- (2006, 6),
- (2007, 0),
- (2008, 1),
- (2009, 3),
- (2010, 4),
- (2011, 5),
- (2012, 6),
- (2013, 1),
- (2014, 2),
- (2015, 3),
- (2016, 4),
- (2017, 6),
- (2018, 0),
- (2019, 1),
- (2020, 2),
- (2021, 4),
- (2022, 5),
- (2023, 6),
- (2024, 0),
- (2025, 2),
- (2026, 3),
- (2027, 4),
- (2028, 5),
- (2029, 0),
- (2030, 1),
- (2031, 2),
- (2032, 3),
- (2033, 5),
- (2034, 6),
- (2035, 0),
- (2036, 1),
- (2037, 3),
- (2038, 4),
- (2039, 5),
- ];
- for (let i = 0z; i < len(cases); i += 1) {
- const paramt = cases[i].0;
- const expect = cases[i].1;
- const actual = calc_janfirstweekday(paramt);
- assert(expect == actual, "calc_janfirstweekday() miscalculation");
+ let buf: [64]u8 = [0...];
+ for (let i = 0z; i < len(testcases); i += 1) {
+ const t = testcases[i];
+ const expect = t.3;
+ const actual = from_str(t.0, t.1, t.2...);
+
+ match (expect) {
+ case let e: date =>
+ assert(actual is date, "wanted 'date', got 'error'");
+ assert(chrono::eq(&(actual as date), &e)!,
+ "incorrect 'date' value");
+ case let e: parsefail =>
+ assert(actual is parsefail,
+ "wanted 'parsefail', got other");
+ case insufficient =>
+ assert(actual is insufficient,
+ "wanted 'insufficient', got other");
+ case invalid =>
+ assert(actual is invalid,
+ "wanted 'invalid', got other");
+ };
};
};
diff --git a/time/date/datetime.ha b/time/date/datetime.ha
@@ -1,305 +0,0 @@
-// License: MPL-2.0
-// (c) 2021-2022 Byron Torres <b@torresjrjr.com>
-// (c) 2022 Drew DeVault <sir@cmpwn.com>
-use errors;
-use time;
-use time::chrono;
-
-// Invalid [[date]].
-export type invalid = !chrono::invalid;
-
-// A date/time object; a [[time::chrono::moment]] wrapper optimized for the
-// Gregorian chronology, and by extension a [[time::instant]] wrapper.
-//
-// This object should be treated as private and immutable. Directly mutating its
-// fields causes undefined behaviour when used with module functions. Likewise,
-// interrogating the fields' type and value (e.g. using match statements) is
-// also improper.
-//
-// A date observes various chronological values, cached in its fields. To
-// evaluate and obtain these values, use the various observer functions
-// ([[year]], [[hour]], etc.). These values are derived from the embedded moment
-// information, and thus are guaranteed to be valid.
-//
-// See [[virtual]] for an public, mutable, intermediary representation of a
-// date, which waives guarantees of validity.
-export type date = struct {
- chrono::moment,
-
- era: (void | int),
- year: (void | int),
- month: (void | int),
- day: (void | int),
- yearday: (void | int),
- isoweekyear: (void | int),
- isoweek: (void | int),
- week: (void | int),
- sundayweek: (void | int),
- weekday: (void | int),
-
- hour: (void | int),
- minute: (void | int),
- second: (void | int),
- nanosecond: (void | int),
-};
-
-fn init() date = date {
- sec = 0,
- nsec = 0,
- loc = chrono::UTC,
- zone = null,
- daydate = void,
- daytime = void,
-
- era = void,
- year = void,
- month = void,
- day = void,
- yearday = void,
- isoweekyear = void,
- isoweek = void,
- week = void,
- sundayweek = void,
- weekday = void,
-
- hour = void,
- minute = void,
- second = void,
- nanosecond = void,
-};
-
-// Evaluates and populates all of a [[date]]'s fields.
-fn all(d: *date) *date = {
- _era(d);
- _year(d);
- _month(d);
- _day(d);
- _yearday(d);
- _isoweekyear(d);
- _isoweek(d);
- _week(d);
- _sundayweek(d);
- _weekday(d);
-
- _hour(d);
- _minute(d);
- _second(d);
- _nanosecond(d);
-
- return d;
-};
-
-// Creates a new [[date]]. A maximum of 7 optional field arguments can be given:
-// year, month, day-of-month, hour, minute, second, nanosecond. 8 or more causes
-// an abort.
-//
-// // 0000-01-01 00:00:00.000000000 +0000 UTC UTC
-// date::new(time::chrono::UTC, 0);
-//
-// // 2019-12-27 20:07:08.000031415 +0000 UTC UTC
-// date::new(time::chrono::UTC, 0, 2019, 12, 27, 20, 07, 08, 31415);
-//
-// // 2019-12-27 21:00:00.000000000 +0100 CET Europe/Amsterdam
-// date::new(time::chrono::tz("Europe/Amsterdam")!, 1 * time::HOUR,
-// 2019, 12, 27, 21);
-//
-// 'zo' is the zone offset from the normal timezone (in most cases, UTC). For
-// example, the "Asia/Tokyo" timezone has a single zoff of +9 hours, but the
-// "Australia/Sydney" timezone has zoffs +10 hours and +11 hours, as they
-// observe Daylight Saving Time.
-//
-// If specified (non-void), 'zo' must match one of the timezone's observed
-// zoffs, or will fail. See [[time::chrono::fixedzone]] for custom timezones.
-//
-// You may omit the zoff. If the givem timezone has a single zone, [[new]]
-// will use that zone's zoff. Otherwise [[new]] will try to infer the zoff
-// from the multiple zones. This will fail during certain timezone transitions,
-// where certain dates are ambiguous or nonexistent. For example:
-//
-// - In the Europe/Amsterdam timezone, at 1995 March 26th,
-// the local time 02:30 was never observed,
-// as the clock jumped forward 1 hour from 02:00 CET to 03:00 CEST.
-//
-// - In the Europe/Amsterdam timezone, at 1995 September 24th,
-// the local time 02:30 was observed twice (00:30 UTC & 01:30 UTC),
-// as the clock jumped back 1 hour from 03:00 CEST to 02:00 CET.
-export fn new(
- loc: chrono::locality,
- zo: (time::duration | void),
- fields: int...
-) (date | invalid) = {
- // TODO:
- // - revise examples
- // - Implement as described.
- // - fix calls with `years <= -4715`.
- // https://todo.sr.ht/~sircmpwn/hare/565
- let _fields: [_]int = [
- 0, 1, 1, // year month day
- 0, 0, 0, 0, // hour min sec nsec
- ];
-
- assert(len(fields) <= len(_fields),
- "time::date::new(): Too many field arguments");
- _fields[..len(fields)] = fields;
-
- const year = _fields[0];
- const month = _fields[1];
- const day = _fields[2];
- const hour = _fields[3];
- const min = _fields[4];
- const sec = _fields[5];
- const nsec = _fields[6];
-
- const mdate = calc_daydate__ymd(year, month, day)?;
- const mtime = calc_daytime__hmsn(hour, min, sec, nsec)?;
-
- // 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: time::date::new(zo=void)");
- };
-
- const d = from_moment(m);
-
- const zo = match (zo) {
- case void =>
- yield chrono::mzone(&m).zoff;
- case let zo: time::duration =>
- yield zo;
- };
-
- // check if input values are actually observed
- if (
- zo != chrono::mzone(&d).zoff
- || year != _year(&d)
- || month != _month(&d)
- || day != _day(&d)
- || hour != _hour(&d)
- || min != _minute(&d)
- || sec != _second(&d)
- || nsec != _nanosecond(&d)
- ) {
- return invalid;
- };
-
- return d;
-};
-
-// Returns a [[date]] of the current system time using
-// [[time::clock::REALTIME]], in the [[time::chrono::LOCAL]] locality.
-export fn now() date = {
- return from_instant(chrono::LOCAL, time::now(time::clock::REALTIME));
-};
-
-// Returns a [[date]] of the current system time using
-// [[time::clock::REALTIME]], in the [[time::chrono::UTC]] locality.
-export fn nowutc() date = {
- return from_instant(chrono::UTC, time::now(time::clock::REALTIME));
-};
-
-// Creates a [[date]] from a [[time::chrono::moment]].
-export fn from_moment(m: chrono::moment) date = {
- const d = init();
- d.loc = m.loc;
- d.sec = m.sec;
- d.nsec = m.nsec;
- d.daydate = m.daydate;
- d.daytime = m.daytime;
- d.zone = m.zone;
- return d;
-};
-
-// Creates a [[date]] from a [[time::instant]]
-// in a [[time::chrono::locality]].
-export fn from_instant(loc: chrono::locality, i: time::instant) date = {
- return from_moment(chrono::new(loc, i));
-};
-
-// Creates a [[date]] from a string, parsed according to a layout format.
-// See [[parse]] and [[format]]. At least a complete calendar date has to be
-// provided. The if hour, minute, second, nanosecond, or zone offset are not
-// provided, they default to 0.
-//
-// let new = date::from_str(
-// date::STAMP_NOZL,
-// "2019-12-27 22:07:08.000000000 +0100 CET Europe/Amsterdam",
-// locs...
-// )!;
-//
-// The date's [[time::chrono::locality]] will be selected from the provided
-// locality arguments. The 'name' field of these localities will be matched
-// against the parsed result for the %L specifier. If %L is not specified, or if
-// no locality is provided, [[time::chrono::UTC]] is used.
-export fn from_str(
- layout: str,
- s: str,
- locs: time::chrono::locality...
-) (date | parsefail | insufficient | invalid) = {
- const v = newvirtual();
- v.zoff = 0;
- v.hour = 0;
- v.minute = 0;
- v.second = 0;
- v.nanosecond = 0;
- parse(&v, layout, s)?;
- return realize(v, locs...)?;
-};
-
-@test fn from_str() void = {
- const amst = chrono::tz("Europe/Amsterdam")!;
- defer chrono::timezone_free(amst);
-
- let testcases: [_](str, str, []chrono::locality, (date | error)) = [
- (STAMP_NOZL, "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)!),
- (RFC3339, "2001-02-03T15:16:17+0000", [],
- new(chrono::UTC, 0, 2001, 2, 3, 15, 16, 17)!),
- ("%F", "2009-06-30", [],
- new(chrono::UTC, 0, 2009, 6, 30)!),
- ("%F %L", "2009-06-30 GPS", [chrono::TAI, chrono::GPS],
- new(chrono::GPS, 0, 2009, 6, 30)!),
- ("%F %T", "2009-06-30 01:02:03", [],
- new(chrono::UTC, 0, 2009, 6, 30, 1, 2, 3)!),
- ("%FT%T%Z", "2009-06-30T18:30:00Z", [],
- new(chrono::UTC, 0, 2009, 6, 30, 18, 30)!),
- ("%FT%T.%N%Z", "2009-06-30T18:30:00.987654321Z", [],
- new(chrono::UTC, 0, 2009, 6, 30, 18, 30, 0, 987654321)!),
- ("%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),
- ];
-
- let buf: [64]u8 = [0...];
- for (let i = 0z; i < len(testcases); i += 1) {
- const t = testcases[i];
- const expect = t.3;
- const actual = from_str(t.0, t.1, t.2...);
-
- match (expect) {
- case let e: date =>
- assert(actual is date, "wanted 'date', got 'error'");
- assert(chrono::eq(&(actual as date), &e)!,
- "incorrect 'date' value");
- case let e: parsefail =>
- assert(actual is parsefail,
- "wanted 'parsefail', got other");
- case insufficient =>
- assert(actual is insufficient,
- "wanted 'insufficient', got other");
- case invalid =>
- assert(actual is invalid,
- "wanted 'invalid', got other");
- };
- };
-};
diff --git a/time/date/date.ha b/time/date/daydate.ha
diff --git a/time/date/time.ha b/time/date/daytime.ha
diff --git a/time/date/errors.ha b/time/date/error.ha
diff --git a/time/date/timezone.ha b/time/date/locality.ha
diff --git a/time/date/chronology.ha b/time/date/observe.ha
diff --git a/time/date/arithmetic.ha b/time/date/parithm.ha
diff --git a/time/date/duration.ha b/time/date/tarithm.ha