hare

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

commit bad6a75a6e68b5938bea74c5ceb9fa6d1b8cd3fc
parent d175a565cee80690e09ee41d4a3e0b2beabfae94
Author: Byron Torres <b@torresjrjr.com>
Date:   Sun, 19 Dec 2021 15:48:25 +0000

merge date & time, improve tz

Signed-off-by: Byron Torres <b@torresjrjr.com>

Diffstat:
Mdatetime/calendar.ha | 180+++++++++++++++++++++++++++++++++----------------------------------------------
Mdatetime/date.ha | 98+------------------------------------------------------------------------------
Mdatetime/datetime.ha | 162+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdatetime/format.ha | 106++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdatetime/time.ha | 54++++++++++++++++--------------------------------------
Mdatetime/timezone.ha | 11-----------
Mtime/chrono/timezone.ha | 28++++++++++++++++++++++------
7 files changed, 247 insertions(+), 392 deletions(-)

diff --git a/datetime/calendar.ha b/datetime/calendar.ha @@ -19,27 +19,27 @@ export def EPOCH_COMMONERA: i64 = -719164; // // Evaluates a [[datetime]]'s number of days since the calendar epoch 0000-01-01 -export fn epochal(dt: *datetime) int = { - match (dt.date.epochal) { +export fn epochal(dt: *datetime) chrono::epochal = { + match (dt.date) { case void => // How to resolve? // Use calc_epochal_from_*? How to avoid recursion? // When to rely on input validation? abort("TODO"); - case let e: int => + case let e: chrono::epochal => return e; }; }; // Evaluates a [[datetime]]'s era export fn era(dt: *datetime) int = { - match (dt.date.era) { + match (dt.era) { case void => - if (dt.date.year is void) { - dt.date.year = year(dt); + if (dt.year is void) { + dt.year = year(dt); }; - dt.date.era = calc_era(dt.date.year: int); - return dt.date.era: int; + dt.era = calc_era(dt.year: int); + return dt.era: int; case let a: int => return a; }; @@ -47,16 +47,16 @@ export fn era(dt: *datetime) int = { // Evaluates a [[datetime]]'s year export fn year(dt: *datetime) int = { - match (dt.date.year) { + match (dt.year) { case void => - if (dt.date.epochal is void) { + if (dt.date is void) { epochal(dt); }; - const ymd = calc_ymd(dt.date.epochal: int); - dt.date.year = ymd.0; - dt.date.month = ymd.1; - dt.date.day = ymd.2; - return dt.date.year: int; + const ymd = calc_ymd(dt.date: chrono::epochal); + dt.year = ymd.0; + dt.month = ymd.1; + dt.day = ymd.2; + return dt.year: int; case let y: int => return y; }; @@ -64,16 +64,16 @@ export fn year(dt: *datetime) int = { // Evaluates a [[datetime]]'s month of the year export fn month(dt: *datetime) int = { - match (dt.date.month) { + match (dt.month) { case void => - if (dt.date.epochal is void) { + if (dt.date is void) { epochal(dt); }; - const ymd = calc_ymd(dt.date.epochal: int); - dt.date.year = ymd.0; - dt.date.month = ymd.1; - dt.date.day = ymd.2; - return dt.date.month: int; + const ymd = calc_ymd(dt.date: chrono::epochal); + dt.year = ymd.0; + dt.month = ymd.1; + dt.day = ymd.2; + return dt.month: int; case let y: int => return y; }; @@ -81,16 +81,16 @@ export fn month(dt: *datetime) int = { // Evaluates a [[datetime]]'s day of the month export fn day(dt: *datetime) int = { - match (dt.date.day) { + match (dt.day) { case void => - if (dt.date.epochal is void) { + if (dt.date is void) { epochal(dt); }; - const ymd = calc_ymd(dt.date.epochal: int); - dt.date.year = ymd.0; - dt.date.month = ymd.1; - dt.date.day = ymd.2; - return dt.date.day: int; + const ymd = calc_ymd(dt.date: chrono::epochal); + dt.year = ymd.0; + dt.month = ymd.1; + dt.day = ymd.2; + return dt.day: int; case let y: int => return y; }; @@ -98,13 +98,13 @@ export fn day(dt: *datetime) int = { // Evaluates a [[datetime]]'s day of the week export fn weekday(dt: *datetime) int = { - match (dt.date.weekday) { + match (dt.weekday) { case void => - if (dt.date.epochal is void) { + if (dt.date is void) { epochal(dt); }; - dt.date.weekday = calc_weekday(dt.date.epochal: int); - return dt.date.weekday: int; + dt.weekday = calc_weekday(dt.date: chrono::epochal); + return dt.weekday: int; case let y: int => return y; }; @@ -112,23 +112,23 @@ export fn weekday(dt: *datetime) int = { // Evaluates a [[datetime]]'s ordinal day of the year export fn yearday(dt: *datetime) int = { - match (dt.date.yearday) { + match (dt.yearday) { case void => - if (dt.date.year is void) { + if (dt.year is void) { year(dt); }; - if (dt.date.month is void) { + if (dt.month is void) { month(dt); }; - if (dt.date.day is void) { + if (dt.day is void) { day(dt); }; - dt.date.yearday = calc_yearday( - dt.date.year: int, - dt.date.month: int, - dt.date.day: int, + dt.yearday = calc_yearday( + dt.year: int, + dt.month: int, + dt.day: int, ); - return dt.date.yearday: int; + return dt.yearday: int; case let yd: int => return yd; }; @@ -136,27 +136,27 @@ export fn yearday(dt: *datetime) int = { // Evaluates a [[datetime]]'s ISO week-numbering year export fn isoweekyear(dt: *datetime) int = { - match (dt.date.isoweekyear) { + match (dt.isoweekyear) { case void => - if (dt.date.year is void) { + if (dt.year is void) { year(dt); }; - if (dt.date.month is void) { + if (dt.month is void) { month(dt); }; - if (dt.date.day is void) { + if (dt.day is void) { day(dt); }; - if (dt.date.weekday is void) { + if (dt.weekday is void) { weekday(dt); }; - dt.date.isoweekyear = calc_isoweekyear( - dt.date.year: int, - dt.date.month: int, - dt.date.day: int, - dt.date.weekday: int, + dt.isoweekyear = calc_isoweekyear( + dt.year: int, + dt.month: int, + dt.day: int, + dt.weekday: int, ); - return dt.date.isoweekyear: int; + return dt.isoweekyear: int; case let iwy: int => return iwy; }; @@ -164,19 +164,19 @@ export fn isoweekyear(dt: *datetime) int = { // Evaluates a [[datetime]]'s Gregorian week export fn week(dt: *datetime) int = { - match (dt.date.week) { + match (dt.week) { case void => - if (dt.date.yearday is void) { + if (dt.yearday is void) { yearday(dt); }; - if (dt.date.weekday is void) { + if (dt.weekday is void) { weekday(dt); }; - dt.date.week = calc_week( - dt.date.yearday: int, - dt.date.weekday: int, + dt.week = calc_week( + dt.yearday: int, + dt.weekday: int, ); - return dt.date.week: int; + return dt.week: int; case let w: int => return w; }; @@ -184,27 +184,27 @@ export fn week(dt: *datetime) int = { // Evaluates a [[datetime]]'s ISO week export fn isoweek(dt: *datetime) int = { - match (dt.date.isoweek) { + match (dt.isoweek) { case void => - if (dt.date.year is void) { + if (dt.year is void) { year(dt); }; - if (dt.date.week is void) { + if (dt.week is void) { week(dt); }; - if (dt.date.weekday is void) { + if (dt.weekday is void) { weekday(dt); }; - if (dt.date.yearday is void) { + if (dt.yearday is void) { yearday(dt); }; - dt.date.isoweek = calc_isoweek( - dt.date.year: int, - dt.date.week: int, - dt.date.weekday: int, - dt.date.yearday: int, + dt.isoweek = calc_isoweek( + dt.year: int, + dt.week: int, + dt.weekday: int, + dt.yearday: int, ); - return dt.date.isoweek: int; + return dt.isoweek: int; case let iw: int => return iw; }; @@ -217,7 +217,7 @@ export fn isoweek(dt: *datetime) int = { // Evaluates a [[datetime]]'s hour of the day export fn hour(dt: *datetime) int = { - match (dt.time.hour) { + match (dt.hour) { case void => abort("TODO"); case let h: int => @@ -227,7 +227,7 @@ export fn hour(dt: *datetime) int = { // Evaluates a [[datetime]]'s minute of the hour export fn min(dt: *datetime) int = { - match (dt.time.min) { + match (dt.min) { case void => abort("TODO"); case let m: int => @@ -237,7 +237,7 @@ export fn min(dt: *datetime) int = { // Evaluates a [[datetime]]'s second of the minute export fn sec(dt: *datetime) int = { - match (dt.time.sec) { + match (dt.sec) { case void => abort("TODO"); case let s: int => @@ -247,40 +247,10 @@ export fn sec(dt: *datetime) int = { // Evaluates a [[datetime]]'s nanosecond of the second export fn nsec(dt: *datetime) int = { - match (dt.time.nsec) { + match (dt.nsec) { case void => abort("TODO"); case let n: int => return n; }; }; - -// // Converts a [[datetime]] to a [[chrono::moment]]. -// // Fails if there is insufficient information in the [[datetime]]. -// export fn conv_datetime_moment(dt: datetime) (chrono::moment | errors::invalid) = { -// const d = conv_localdate_epochal(dt.date)?; -// const t = conv_localtime_time(dt.time)?; -// const m = chrono::moment { -// date = d, -// time = t, -// loc = chrono::local, // TODO -// }; -// return m; -// }; - -// // Converts a [[chrono::moment]] to a [[datetime]] -// export fn conv_moment_datetime(m: chrono::moment, dt: *datetime) void = { -// conv_epochal_localdate(m.date, &(dt.date), &localdate { -// // TODO: reconcile, add more fields when ready -// year = 0, -// month = 0, -// day = 0, -// ... -// }); -// -// const lt = conv_time_localtime(m.time); -// dt.time = lt; -// -// dt.loc = chrono::local; // TODO -// return dt; -// }; diff --git a/datetime/date.ha b/datetime/date.ha @@ -1,32 +1,6 @@ use errors; use time::chrono; -export type localdate = struct { - epochal: (void | int), - era: (void | int), - year: (void | int), - month: (void | int), - day: (void | int), - yearday: (void | int), - isoweekyear: (void | int), - isoweek: (void | int), - week: (void | int), - weekday: (void | int), -}; - -fn init_date() localdate = localdate { - epochal = void, - era = void, - year = void, - month = void, - day = void, - yearday = void, - isoweekyear = void, - isoweek = void, - week = void, - weekday = void, -}; - // Calculates whether a given year is a leap year. export fn is_leap_year(y: int) bool = { return if (y % 4 != 0) false @@ -45,7 +19,7 @@ fn calc_era(y: int) int = { }; // Calculates the (year, month, day), given an epochal day -fn calc_ymd(e: int) (int, int, int) = { +fn calc_ymd(e: chrono::epochal) (int, int, int) = { // Algorithm adapted from: // https://en.wikipedia.org/wiki/Julian_day#Julian_or_Gregorian_calendar_from_Julian_day_number // @@ -201,73 +175,3 @@ fn calc_epochal_from_yd(y: int, yd: int) (chrono::epochal | errors::invalid) = { // TODO return 0; }; - -// // Converts a [[chrono::epochal]] to a [[localdate]]. The fields in "date" are -// // populated acording to which fields in "want" are non-void. -// // -// // let date = localdate { ... }; -// // let want = localdate { year=0, month=0, day=0, ... }; -// // conv_epochal_localdate(10724, &date, &want); -// // assert(date == localdate { year=1999, month=5, day=13, ... }); -// // -// export fn conv_epochal_localdate( -// e: chrono::epochal, -// date: *localdate, -// want: *localdate, -// ) void = { -// if (want.year is int || want.month is int || want.day is int) { -// const ymd = calc_ymd(e: int); -// date.year = ymd.0; -// date.month = ymd.1; -// date.day = ymd.2; -// }; -// -// if (want.yearday is int) { -// if (date.year is void || date.month is void || date.day is void) { -// const ymd = calc_ymd(e: int); -// date.year = ymd.0; -// date.month = ymd.1; -// date.day = ymd.2; -// }; -// calc_yearday(date.year: int, date.month: int, date.day: int); -// }; -// -// if (want.week is int) { -// // TODO -// //calc_isoweek(e); -// void; -// }; -// -// if (want.weekday is int) { -// date.weekday = calc_weekday(e); -// }; -// }; - -// // Converts a [[localdate]] to a [[chrono::epochal]]. -// // Fails if there is insufficient information in the given [[localdate]]. -// export fn conv_localdate_epochal(ld: localdate) (chrono::epochal | errors::invalid) = { -// if ( -// ld.year is int -// && ld.month is int -// && ld.day is int -// ) { -// return calc_epochal_from_ymd( -// ld.year: int, -// ld.month: int, -// ld.day: int, -// ); -// } else if ( -// ld.year is int -// && ld.week is int -// && ld.weekday is int -// ) { -// return 0; // TODO -// } else if ( -// ld.year is int -// && ld.yearday is int -// ) { -// return 0; // TODO -// }; -// -// return errors::invalid; -// }; diff --git a/datetime/datetime.ha b/datetime/datetime.ha @@ -5,15 +5,47 @@ use time::chrono; // Represents a datetime; a single, reasonably unique moment in time, specified // by a calendar date and a wallclock time, contextualised within a locality. export type datetime = struct { - date: localdate, - time: localtime, - loc: locality, + date: (void | chrono::epochal), + time: (void | time::duration), + loc: chrono::locality, + + era: (void | int), + year: (void | int), + month: (void | int), + day: (void | int), + yearday: (void | int), + isoweekyear: (void | int), + isoweek: (void | int), + week: (void | int), + weekday: (void | int), + + hour: (void | int), + min: (void | int), + sec: (void | int), + nsec: (void | int), }; -fn init_datetime() datetime = datetime { - date = init_date(), - time = init_time(), - loc = chrono::local, + + +fn init() datetime = datetime { + date = void, + time = void, + loc = chrono::locality{ timezone = chrono::local, ... }, + + era = void, + year = void, + month = void, + day = void, + yearday = void, + isoweekyear = void, + isoweek = void, + week = void, + weekday = void, + + hour = void, + min = void, + sec = void, + nsec = void, }; // Creates a new datetime @@ -29,32 +61,27 @@ export fn new( min: int, sec: int, nsec: int, - loc: locality, + loc: chrono::locality, ) (datetime | errors::invalid) = { const dt = datetime { - date = localdate { - epochal = void, - era = void, - year = year, - month = month, - day = day, - isoweekyear = void, - isoweek = void, - week = void, - weekday = void, - yearday = void, - }, - time = localtime { - lapsed = void, - hour = hour, - min = min, - sec = sec, - nsec = nsec, - }, - loc = loc, - }; - if (!validate(dt)) { - return errors::invalid; + date = calc_epochal_from_ymd(year, month, day)?, + time = calc_time_from_hmsn(hour, min, sec, nsec)?, + loc = loc, + + era = void, + year = year, + month = month, + day = day, + isoweekyear = void, + isoweek = void, + week = void, + weekday = void, + yearday = void, + + hour = hour, + min = min, + sec = sec, + nsec = nsec, }; return dt; }; @@ -62,67 +89,38 @@ export fn new( // Returns the current datetime export fn now() datetime = { const i = time::now(time::clock::REALTIME); - const u = time::unix(i); - const d = (u / 86400); - const ld = init_date(); - ld.epochal = d: int; + const unix = time::unix(i); + const daynum = (unix / 86400); + const date = calc_ymd(daynum); const dt = datetime { - date = ld, - time = localtime { - lapsed = ((i.sec / 86400) * time::NANOSECOND + i.nsec): int, - hour = (i.sec / 3600): int % 24, - min = (i.sec / 60): int % 60, - sec = i.sec: int % 60, - nsec = i.nsec: int, - }, + date = daynum, + time = ((i.sec / 86400) * time::NANOSECOND + i.nsec), // TODO: What to do here? How to get the timezone from // /etc/localtime or $TZ? How to determine the system's // timescale? Assuming UTC may be sufficient. - loc = chrono::local, + loc = chrono::locality{ timezone = chrono::local, ... }, + + era = void, + year = date.0, + month = date.1, + day = date.2, + isoweekyear = void, + isoweek = void, + week = void, + weekday = void, + yearday = void, + + hour = (i.sec / 3600): int % 24, + min = (i.sec / 60): int % 60, + sec = i.sec: int % 60, + nsec = i.nsec: int, }; return dt; }; // Validates a datetime's internal date & time values -export fn validate(dt: (datetime | localdate | localtime)) bool = { +export fn validate(dt: datetime) bool = { // TODO return true; }; - -// // Creates a new moment -// // -// // // 1995 July 18th 09:16:00.000 -// // datetime::new_moment(1995, 07, 18, 9, 16, 0, 0, chrono::local) -// // -// // For alternative forms, assemble a datetime manually using the desired types. -// export fn new_moment( -// year: int, -// month: int, -// day: int, -// hour: int, -// min: int, -// sec: int, -// nsec: int, -// loc: locality, -// ) (chrono::moment | errors::invalid) = { -// const dt = new(year, month, day, hour, min, sec, nsec, loc)?; -// const m = conv_datetime_moment(dt)?; -// return m; -// }; - -// // Returns the current moment -// export fn now_moment() chrono::moment = { -// const i = time::now(time::clock::REALTIME); -// const u = time::unix(i); -// const d = (u / 86400); -// const t = ( -// (i.sec * time::SECOND) + (i.nsec * time::NANOSECOND) -// ) % (24 * time::HOUR); -// const m = chrono::moment { -// date = d, -// time = t, -// loc = chrono::local, -// }; -// return m; -// }; diff --git a/datetime/format.ha b/datetime/format.ha @@ -85,35 +85,35 @@ export fn strptime(fmt: str, s: str, dt: *datetime) (void | errors::invalid) = { // Basic specifiers case 'a' => // TODO: Localization - dt.date.weekday = get_default_locale_string_index( + dt.weekday = get_default_locale_string_index( &s_iter, WEEKDAYS_SHORT[..])?; case 'A' => // TODO: Localization - dt.date.weekday = get_default_locale_string_index( + dt.weekday = get_default_locale_string_index( &s_iter, WEEKDAYS[..])?; case 'b', 'h' => // TODO: Localization - dt.date.month = get_default_locale_string_index( + dt.month = get_default_locale_string_index( &s_iter, MONTHS_SHORT[..])?; case 'B' => // TODO: Localization - dt.date.month = get_default_locale_string_index( + dt.month = get_default_locale_string_index( &s_iter, MONTHS[..])?; case 'd', 'e' => let max_n_digits = 2u; if (format_r == 'e') { max_n_digits -= eat_one_rune(&s_iter, ' ')?; }; - dt.date.day = clamp_int( + dt.day = clamp_int( get_max_n_digits(&s_iter, max_n_digits)?, 1, 31); case 'G' => - dt.date.isoweekyear = get_max_n_digits(&s_iter, 4)?; + dt.isoweekyear = get_max_n_digits(&s_iter, 4)?; case 'H', 'k' => let max_n_digits = 2u; if (format_r == 'k') { max_n_digits -= eat_one_rune(&s_iter, ' ')?; }; - dt.time.hour = clamp_int( + dt.hour = clamp_int( get_max_n_digits(&s_iter, max_n_digits)?, 0, 23); case 'I', 'l' => let max_n_digits = 2u; @@ -121,7 +121,7 @@ export fn strptime(fmt: str, s: str, dt: *datetime) (void | errors::invalid) = { max_n_digits -= eat_one_rune(&s_iter, ' ')?; }; const hour = get_max_n_digits(&s_iter, max_n_digits); - dt.time.hour = match (hour) { + dt.hour = match (hour) { case let hour: int => yield if (hour > 12) { yield clamp_int(hour - 12, 1, 12); @@ -132,22 +132,22 @@ export fn strptime(fmt: str, s: str, dt: *datetime) (void | errors::invalid) = { return errors::invalid; }; case 'j' => - dt.date.yearday = clamp_int( + dt.yearday = clamp_int( get_max_n_digits(&s_iter, 3)?, 1, 366); case 'm' => - dt.date.month = clamp_int( + dt.month = clamp_int( get_max_n_digits(&s_iter, 2)?, 1, 12); case 'M' => - dt.time.min = clamp_int( + dt.min = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 59); case 'n' => eat_one_rune(&s_iter, '\n')?; case 'N' => - dt.time.nsec = clamp_int( + dt.nsec = clamp_int( get_max_n_digits(&s_iter, 3)?, 0, 999); case 'p', 'P' => // TODO: Localization - if (dt.time.hour is void) { + if (dt.hour is void) { // We can't change the hour's am/pm because we // have no hour. return errors::invalid; @@ -164,19 +164,19 @@ export fn strptime(fmt: str, s: str, dt: *datetime) (void | errors::invalid) = { yield "pm"; }; if (strings::hasprefix(rest, prefix_am)) { - if (dt.time.hour as int > 12) { + if (dt.hour as int > 12) { // 13 AM? return errors::invalid; - } else if (dt.time.hour as int == 12) { - dt.time.hour = 0; + } else if (dt.hour as int == 12) { + dt.hour = 0; }; } else if (strings::hasprefix(rest, prefix_pm)) { - if (dt.time.hour as int > 12) { + if (dt.hour as int > 12) { // 13 PM? return errors::invalid; - } else if (dt.time.hour as int < 12) { - dt.time.hour = - (dt.time.hour as int) + 12; + } else if (dt.hour as int < 12) { + dt.hour = + (dt.hour as int) + 12; }; } else { return errors::invalid; @@ -184,12 +184,12 @@ export fn strptime(fmt: str, s: str, dt: *datetime) (void | errors::invalid) = { strings::next(&s_iter); strings::next(&s_iter); case 'S' => - dt.time.sec = clamp_int( + dt.sec = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 61); case 't' => eat_one_rune(&s_iter, '\t')?; case 'u', 'w' => - dt.date.weekday = match (get_max_n_digits(&s_iter, 1)) { + dt.weekday = match (get_max_n_digits(&s_iter, 1)) { case let i: int => yield if (format_r == 'w') { yield if (i == 0) { @@ -204,13 +204,13 @@ export fn strptime(fmt: str, s: str, dt: *datetime) (void | errors::invalid) = { return errors::invalid; }; case 'U', 'W' => - dt.date.week = clamp_int( + dt.week = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 53); case 'V' => - dt.date.isoweek = clamp_int( + dt.isoweek = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 53); case 'Y' => - dt.date.year = get_max_n_digits(&s_iter, 4)?; + dt.year = get_max_n_digits(&s_iter, 4)?; case 'z' => // TODO continue; @@ -220,73 +220,73 @@ export fn strptime(fmt: str, s: str, dt: *datetime) (void | errors::invalid) = { // Expansion specifiers case 'c' => // TODO: Localization - dt.date.weekday = get_default_locale_string_index( + dt.weekday = get_default_locale_string_index( &s_iter, WEEKDAYS_SHORT[..])?; if (eat_one_rune(&s_iter, ' ')? != 1) { fmtlib::printfln("no space after weekday")!; return errors::invalid; }; - dt.date.month = get_default_locale_string_index( + dt.month = get_default_locale_string_index( &s_iter, MONTHS_SHORT[..])?; if (eat_one_rune(&s_iter, ' ')? != 1) { fmtlib::printfln("no space after month")!; return errors::invalid; }; const max_n_digits = 2 - eat_one_rune(&s_iter, ' ')?; - dt.date.day = clamp_int( + dt.day = clamp_int( get_max_n_digits(&s_iter, max_n_digits)?, 1, 31); if (eat_one_rune(&s_iter, ' ')? != 1) { fmtlib::printfln("no space after day")!; return errors::invalid; }; - dt.time.hour = clamp_int( + dt.hour = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 23); if (eat_one_rune(&s_iter, ':')? != 1) { fmtlib::printfln("no : after hour")!; return errors::invalid; }; - dt.time.min = clamp_int( + dt.min = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 59); if (eat_one_rune(&s_iter, ':')? != 1) { fmtlib::printfln("no : after minute")!; return errors::invalid; }; - dt.time.sec = clamp_int( + dt.sec = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 61); if (eat_one_rune(&s_iter, ' ')? != 1) { fmtlib::printfln("no space after sec")!; return errors::invalid; }; - dt.date.year = get_max_n_digits(&s_iter, 4)?; + dt.year = get_max_n_digits(&s_iter, 4)?; case 'D', 'x' => // TODO: Localization for %x - dt.date.month = clamp_int( + dt.month = clamp_int( get_max_n_digits(&s_iter, 2)?, 1, 12); if (eat_one_rune(&s_iter, '/')? != 1) { return errors::invalid; }; - dt.date.day = clamp_int( + dt.day = clamp_int( get_max_n_digits(&s_iter, 2)?, 1, 31); if (eat_one_rune(&s_iter, '/')? != 1) { return errors::invalid; }; - dt.date.year = get_max_n_digits(&s_iter, 4)?; + dt.year = get_max_n_digits(&s_iter, 4)?; case 'F' => - dt.date.year = get_max_n_digits(&s_iter, 4)?; + dt.year = get_max_n_digits(&s_iter, 4)?; if (eat_one_rune(&s_iter, '-')? != 1) { return errors::invalid; }; - dt.date.month = clamp_int( + dt.month = clamp_int( get_max_n_digits(&s_iter, 2)?, 1, 12); if (eat_one_rune(&s_iter, '-')? != 1) { return errors::invalid; }; - dt.date.day = clamp_int( + dt.day = clamp_int( get_max_n_digits(&s_iter, 2)?, 1, 31); case 'r' => // TODO: Localization // Time - dt.time.hour = match (get_max_n_digits(&s_iter, 2)) { + dt.hour = match (get_max_n_digits(&s_iter, 2)) { case let hour: int => yield if (hour > 12) { yield clamp_int(hour - 12, 1, 12); @@ -299,12 +299,12 @@ export fn strptime(fmt: str, s: str, dt: *datetime) (void | errors::invalid) = { if (eat_one_rune(&s_iter, ':')? != 1) { return errors::invalid; }; - dt.time.min = clamp_int( + dt.min = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 59); if (eat_one_rune(&s_iter, ':')? != 1) { return errors::invalid; }; - dt.time.sec = clamp_int( + dt.sec = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 61); if (eat_one_rune(&s_iter, ' ')? != 1) { return errors::invalid; @@ -312,19 +312,19 @@ export fn strptime(fmt: str, s: str, dt: *datetime) (void | errors::invalid) = { let rest = strings::iter_str(&s_iter); // AM/PM if (strings::hasprefix(rest, "AM")) { - if (dt.time.hour as int > 12) { + if (dt.hour as int > 12) { // 13 AM? return errors::invalid; - } else if (dt.time.hour as int == 12) { - dt.time.hour = 0; + } else if (dt.hour as int == 12) { + dt.hour = 0; }; } else if (strings::hasprefix(rest, "PM")) { - if (dt.time.hour as int > 12) { + if (dt.hour as int > 12) { // 13 PM? return errors::invalid; - } else if (dt.time.hour as int < 12) { - dt.time.hour = - (dt.time.hour as int) + 12; + } else if (dt.hour as int < 12) { + dt.hour = + (dt.hour as int) + 12; }; } else { return errors::invalid; @@ -332,26 +332,26 @@ export fn strptime(fmt: str, s: str, dt: *datetime) (void | errors::invalid) = { strings::next(&s_iter); strings::next(&s_iter); case 'R' => - dt.time.hour = clamp_int( + dt.hour = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 23); if (eat_one_rune(&s_iter, ':')? != 1) { return errors::invalid; }; - dt.time.min = clamp_int( + dt.min = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 59); case 'T', 'X' => // TODO: Localization for %X - dt.time.hour = clamp_int( + dt.hour = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 23); if (eat_one_rune(&s_iter, ':')? != 1) { return errors::invalid; }; - dt.time.min = clamp_int( + dt.min = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 59); if (eat_one_rune(&s_iter, ':')? != 1) { return errors::invalid; }; - dt.time.sec = clamp_int( + dt.sec = clamp_int( get_max_n_digits(&s_iter, 2)?, 0, 61); case => diff --git a/datetime/time.ha b/datetime/time.ha @@ -1,49 +1,27 @@ use errors; use time; -export type localtime = struct { - lapsed: (void | int), - hour: (void | int), - min: (void | int), - sec: (void | int), - nsec: (void | int), -}; - -fn init_time() localtime = localtime { - lapsed = void, - hour = void, - min = void, - sec = void, - nsec = void, -}; - // TODO: rework the following functions and their signatures -fn conv_time_localtime(t: time::duration) localtime = { - const lt = localtime { - lapsed = t: int, - hour = (t / time::HOUR): int, - min = ((t / time::MINUTE) % 60): int, - sec = ((t / time::SECOND) % 60): int, - nsec = (t % time::SECOND): int, - }; - return lt; +fn calc_hmsn(t: time::duration) (int, int, int, int) = { + const hour = (t / time::HOUR): int; + const min = ((t / time::MINUTE) % 60): int; + const sec = ((t / time::SECOND) % 60): int; + const nsec = (t % time::SECOND): int; + return (hour, min, sec, nsec); }; -fn conv_localtime_time(lt: localtime) (time::duration | errors::invalid) = { - if ( - lt.hour is void - || lt.min is void - || lt.sec is void - || lt.nsec is void - ) { - return errors::invalid; - }; +fn calc_time_from_hmsn( + hour: int, + min: int, + sec: int, + nsec: int, +) (time::duration | errors::invalid) = { const t = ( - (lt.hour: int * time::HOUR) + - (lt.min: int * time::MINUTE) + - (lt.sec: int * time::SECOND) + - (lt.nsec: int * time::NANOSECOND) + (hour * time::HOUR) + + (min * time::MINUTE) + + (sec * time::SECOND) + + (nsec * time::NANOSECOND) ); return t; }; diff --git a/datetime/timezone.ha b/datetime/timezone.ha @@ -1,17 +1,6 @@ use time; use time::chrono; -// Represents the locality of a [[datetime]] -// -// Notes: how to expand? -export type locality = (chrono::local | tzrepr); - -export type tzrepr = struct { - name: str, - abbrev: str, - zoffset: localtime, -}; - // Retrieves a IANA timezone object by name export fn tzdb(name: str) chrono::timezone = { // TODO diff --git a/time/chrono/timezone.ha b/time/chrono/timezone.ha @@ -1,15 +1,31 @@ use time; -// Represents the locality of a datetime -export type locality = (local | zoffset | *timezone | *tzalias); +// The locality of a datetime +export type locality = struct { + zrepr: str, // %Z + zoffset: zoffset, // %z + timezone: (*timezone | local), +}; + +// A destructured dual std/dst POSIX timezone. See tzset(3). +type tzname = struct { + std_name: str, + std_offset: zoffset, + dst_name: str, + dst_offset: zoffset, + dst_start: str, + dst_starttime: str, + dst_end: str, + dst_endtime: str, +}; // Represents its associated datetime as local export type local = void; -// Represents a simple, constant zone offset +// A simple, constant zone offset export type zoffset = time::duration; -// Represents a timezone; a political region with a ruleset regarding offsets +// A timezone; a political region with a ruleset regarding offsets export type timezone = struct { name: str, // "Europe/Amsterdam" scale: *timescale, @@ -17,7 +33,7 @@ export type timezone = struct { trans: []zonetran, }; -// Represents a conditional offset, dependant on the time of year +// A conditional offset, dependant on the time of year export type zone = struct { zoffset: zoffset, // 2 * time::HOUR name: str, // "Central European Summer Time" @@ -25,7 +41,7 @@ export type zone = struct { dst: bool, // true }; -// Represents a timezone transition +// A timezone transition export type zonetran = struct { when: time::instant, zoneindex: int,