hare

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

commit 5bf82661b4989dd93db5cbe07655196f7c78f2b9
parent a6448dbd209d83e01c673cbca5c505418ebf91b4
Author: Byron Torres <b@torresjrjr.com>
Date:   Sat, 20 Nov 2021 01:01:16 +0000

tidy code and comments, add arithmetic.ha

TODO datetime:
- Write some use cases and more tests.
- Tidy, validate, and accredit magic "calc ymd" code and formulae.
- Write and incorporate validation near datetime creation.
- Write add(), hop() and maybe reckon(), a combo of the two.
- Reconcile separation of concerns of datetime, localdate, localtime
- Make some basic timezones.
- Make now() use time::clock and/or chrono::timescale.

TODO time::chrono:
- Complete to_tai and from_tai functions.
- Add difftai to the timescale interface.
- Complete timezone type system.
- Reconcile chrono::moment's relation with datetime::datetime.

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

Diffstat:
Adatetime/arithmetic.ha | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdatetime/calendar.ha | 172++++++++++++++++++++++++++-----------------------------------------------------
Mdatetime/date+test.ha | 1-
Mdatetime/date.ha | 187++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mdatetime/datetime.ha | 101++++++++++++++++++++++++++++++++++---------------------------------------------
Mdatetime/format+test.ha | 2+-
Mdatetime/format.ha | 9+++++----
Mdatetime/time.ha | 12+++++-------
Mdatetime/timezone.ha | 12++++++++----
Mscripts/gen-stdlib | 17++++++++++-------
Mstdlib.mk | 10++++++----
11 files changed, 301 insertions(+), 297 deletions(-)

diff --git a/datetime/arithmetic.ha b/datetime/arithmetic.ha @@ -0,0 +1,75 @@ +use time::chrono; + +// Represents a span of time in the proleptic Gregorian calendar, +// using relative units of time. Used for calendar arithmetic. +export type period = struct { + eras: int, + years: int, + + // Can be 28, 29, 30, or 31 days long + months: int, + + // Weeks start on Monday + weeks: int, + + days: int, + hours: int, + minutes: int, + seconds: int, + nanoseconds: int, +}; + +// Specifies behaviour during calendar arithmetic +export type calculus = enum int { + LOGICAL, + PHYSICAL, +}; + +// Hops, starting from a datetime, to static inter-period points along the +// calendar, according to the given periods, and returns a new datetime. +// Inter-period points are the starts of years, months, days, etc. +// +// hop() consults each period's fields in order of largest to smallest +// calendrically (from years to nanoseconds). +// +// If a field's value N is zero, nothing happens. Otherwise, hop() will reckon +// to the Nth inter-period point from where last reckoned. This repeats until +// all the given period's fields are exhausted. +// +// let dt = ... // 1999-05-13 12:30:45 +// datetime::hop(dt, datetime::period { +// years = 22, // produces 2021-01-01 00:00:00 +// months = -1, // produces 2020-11-01 00:00:00 +// days = -4, // produces 2020-10-27 00:00:00 +// }); +// +export fn hop(dt: datetime, pp: period...) datetime = { + // TODO + for (let i = 0z; i < len(pp); i += 1) { + const p = pp[i]; + }; + return dt; +}; + +// Adds a calindrical period of time to a datetime, largest units first. +// Tries to conserve relative distance from cyclical points on the calendar. +// +// let dt = ... // 1999-05-13 12:30:45 +// datetime::hop(dt, datetime::calculus::LOGICAL, datetime::period { +// years = 22, // 2021-05-13 00:00:00 +// months = -1, // 2021-04-13 00:00:00 +// days = -4, // 2020-04-09 00:00:00 +// }); +// +// When units overflow, such as when adding a month to Jan 31st would +// erroneously result in Feb 31th, the flag is consulted on how to handle this. +// +// TODO: +// How to handle overflows and predictability with cal-arithm in general? +export fn add(m: datetime, flag: int, pp: period...) datetime = { + // TODO + for (let i = 0z; i < len(pp); i += 1) { + const p = pp[i]; + }; + return dt; +}; diff --git a/datetime/calendar.ha b/datetime/calendar.ha @@ -1,61 +1,37 @@ use errors; -use time::chrono; use time; +use time::chrono; +// TODO: reconcile what epochs to use for time::chrono and datetime:: +// +// 1970-01-01 "Hare epoch" +// 0000-01-01 Gregorian epoch -// The epoch of the Julian Day Number +// The Hare epoch of the Julian Day Number export def EPOCH_JULIAN: i64 = -2440588; -// The epoch of the Common Era +// The Hare epoch of the Common Era export def EPOCH_COMMONERA: i64 = -719164; -// 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; -}; - - // // date-like // -// Evaluates a datetime's number of days since the calendar epoch 0001-01-01 +// Evaluates a [[datetime]]'s number of days since the calendar epoch 0000-01-01 export fn epochal(dt: *datetime) int = { match (dt.date.epochal) { case void => - abort("TODO"); // How to resolve? Use calc_epochal_from_*? + // How to resolve? + // Use calc_epochal_from_*? How to avoid recursion? + // When to rely on input validation? + abort("TODO"); case e: int => return e; }; }; -// Evaluates a datetime's era +// Evaluates a [[datetime]]'s era export fn era(dt: *datetime) int = { match (dt.date.era) { case void => @@ -69,7 +45,7 @@ export fn era(dt: *datetime) int = { }; }; -// Evaluates a datetime's year +// Evaluates a [[datetime]]'s year export fn year(dt: *datetime) int = { match (dt.date.year) { case void => @@ -86,7 +62,7 @@ export fn year(dt: *datetime) int = { }; }; -// Evaluates a datetime's month +// Evaluates a [[datetime]]'s month of the year export fn month(dt: *datetime) int = { match (dt.date.month) { case void => @@ -103,7 +79,7 @@ export fn month(dt: *datetime) int = { }; }; -// Evaluates a datetime's day of the month +// Evaluates a [[datetime]]'s day of the month export fn day(dt: *datetime) int = { match (dt.date.day) { case void => @@ -120,8 +96,7 @@ export fn day(dt: *datetime) int = { }; }; - -// Evaluates a datetime's day of the week +// Evaluates a [[datetime]]'s day of the week export fn weekday(dt: *datetime) int = { match (dt.date.weekday) { case void => @@ -135,7 +110,7 @@ export fn weekday(dt: *datetime) int = { }; }; -// Evaluates a datetime's ordinal day of the year +// Evaluates a [[datetime]]'s ordinal day of the year export fn yearday(dt: *datetime) int = { match (dt.date.yearday) { case void => @@ -158,7 +133,8 @@ export fn yearday(dt: *datetime) int = { return yd; }; }; -// Evaluates a datetime's ISO week calendar year + +// Evaluates a [[datetime]]'s ISO week-numbering year export fn isoweekyear(dt: *datetime) int = { match (dt.date.isoweekyear) { case void => @@ -186,7 +162,7 @@ export fn isoweekyear(dt: *datetime) int = { }; }; -// Evaluates a datetime's Gregorian week +// Evaluates a [[datetime]]'s Gregorian week export fn week(dt: *datetime) int = { match (dt.date.week) { case void => @@ -206,7 +182,7 @@ export fn week(dt: *datetime) int = { }; }; -// Evaluates a datetime's ISO week +// Evaluates a [[datetime]]'s ISO week export fn isoweek(dt: *datetime) int = { match (dt.date.isoweek) { case void => @@ -234,11 +210,12 @@ export fn isoweek(dt: *datetime) int = { }; }; + // // time-like // -// Evaluates a datetime's hour of the day +// Evaluates a [[datetime]]'s hour of the day export fn hour(dt: *datetime) int = { match (dt.time.hour) { case void => @@ -248,7 +225,7 @@ export fn hour(dt: *datetime) int = { }; }; -// Evaluates a datetime's minute of the hour +// Evaluates a [[datetime]]'s minute of the hour export fn min(dt: *datetime) int = { match (dt.time.min) { case void => @@ -258,7 +235,7 @@ export fn min(dt: *datetime) int = { }; }; -// Evaluates a datetime's second of the minute +// Evaluates a [[datetime]]'s second of the minute export fn sec(dt: *datetime) int = { match (dt.time.sec) { case void => @@ -268,7 +245,7 @@ export fn sec(dt: *datetime) int = { }; }; -// Evaluates a datetime's nanosecond of the second +// Evaluates a [[datetime]]'s nanosecond of the second export fn nsec(dt: *datetime) int = { match (dt.time.nsec) { case void => @@ -278,69 +255,32 @@ export fn nsec(dt: *datetime) int = { }; }; - -// A contextual span of time in the proleptic Gregorian calendar. -// Used for calendar arithmetic. -export type period = struct { - eras: int, - years: int, - - // Can be 28, 29, 30, or 31 days long - months: int, - - // Weeks start on Monday - weeks: int, - - days: int, - hours: int, - minutes: int, - seconds: int, - nanoseconds: int, -}; - -// Hops along the calendar from a moment, according to the given periods -// sequencially, consulting period's units from largest to smallest. -// -// // m := 1999-05-13 12:30:45 -// datetime::hop(m, datetime::period { -// years = 22, // 2021-01-01 00:00:00 -// months = -1, // 2020-11-01 00:00:00 -// days = -4, // 2020-10-27 00:00:00 -// }); -// -export fn hop(m: chrono::moment, pp: period...) chrono::moment = { - // TODO - for (let i = 0z; i < len(pp); i += 1) { - const p = pp[i]; - }; - return m; -}; - -// Adds a calindrical period of time to a moment, largest units first. -// Tries to conserve relative distance from cyclical points on the calendar. -// -// // m := 1999-05-13 12:30:45 -// datetime::hop(m, datetime::period { -// years = 22, // 2021-05-13 00:00:00 -// months = -1, // 2021-04-13 00:00:00 -// days = -4, // 2020-04-09 00:00:00 -// }); -// -// When units overflow, such as when adding a month to Jan 31st inaccurately -// results to Feb 31th, the flag is consulted on how to handle this. -// -// TODO: -// How to handle overflows and predictability with cal-arithm in general? -export fn add(m: chrono::moment, flag: int, pp: period...) chrono::moment = { - // TODO - for (let i = 0z; i < len(pp); i += 1) { - const p = pp[i]; - }; - return m; -}; - -// Specifies behaviour during calendar arithmetic -export type calculus = enum int { - LOGICAL, - PHYSICAL, -}; +// // 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+test.ha b/datetime/date+test.ha @@ -147,4 +147,3 @@ use time::chrono; assert(expect == actual, "weekday miscalculation"); }; }; - diff --git a/datetime/date.ha b/datetime/date.ha @@ -1,8 +1,6 @@ -use time::chrono; use errors; +use time::chrono; -// Represents an ISO calendar date. -// Instances created from datetime:: functions are guaranteed to be valid. export type localdate = struct { epochal: (void | int), era: (void | int), @@ -16,7 +14,7 @@ export type localdate = struct { weekday: (void | int), }; -export fn init_date() localdate = localdate { +fn init_date() localdate = localdate { epochal = void, era = void, year = void, @@ -29,7 +27,15 @@ export fn init_date() localdate = localdate { weekday = void, }; -// Calculates an era, given a year +// Calculates whether a given year is a leap year. +export fn is_leap_year(y: int) bool = { + return if (y % 4 != 0) false + else if (y % 100 != 0) true + else if (y % 400 != 0) false + else true; +}; + +// Calculates the era, given a year fn calc_era(y: int) int = { return if (y >= 0) { yield 1; // CE "Common Era" @@ -38,7 +44,6 @@ fn calc_era(y: int) int = { }; }; - // Calculates the (year, month, day), given an epochal day fn calc_ymd(e: int) (int, int, int) = { // Algorithm adapted from: @@ -73,7 +78,7 @@ fn calc_ymd(e: int) (int, int, int) = { return (Y: int, M: int, D: int); }; -// Calculates the day of a year, given a year, month, and day +// Calculates the day of a year, given a (year, month, day) date fn calc_yearday(y: int, m: int, d: int) int = { const months_firsts: [_]int = [ 0, 31, 59, @@ -90,7 +95,7 @@ fn calc_yearday(y: int, m: int, d: int) int = { }; // Calculates the ISO week-numbering year, -// given a year, month, day, and weekday +// given a (year, month, day, weekday) date fn calc_isoweekyear(y: int, m: int, d: int, wd: int) int = { if ( // if the date is within a week whose Thurday @@ -117,7 +122,7 @@ fn calc_isoweekyear(y: int, m: int, d: int, wd: int) int = { }; }; -// Calculates a ISO week, given a year, week, Gregorian weekday, and yearday +// Calculates the ISO week, given a (year, week, weekday, yearday) date fn calc_isoweek(y: int, w: int, wd: int, yd: int) int = { const jan1wd = (yd - wd + 7) % 7 + 1; @@ -145,7 +150,6 @@ fn calc_isoweek(y: int, w: int, wd: int, yd: int) int = { yield w; }; }; - return iw; }; @@ -156,69 +160,20 @@ fn calc_week(yd: int, wd: int) int = { return (5 + yd - wd) / 7; }; -// Calculates the weekday, given a epochal day +// Calculates the weekday, given a epochal day, // from Monday=1 to Sunday=7 fn calc_weekday(e: chrono::epochal) int = { const wd = ((e + 3) % 7 + 1): int; return if (wd > 0) wd else wd + 7; }; -// Calculates the weekday, given a epochal day +// Calculates the zeroed weekday, given a weekday, // from Monday=0 to Sunday=6 fn calc_zeroweekday(wd: int) int = { return wd - 1; }; -// Calculates whether a given year is a leap year. -fn is_leap_year(y: int) bool = { - return if (y % 4 != 0) false - else if (y % 100 != 0) true - else if (y % 400 != 0) false - else true; -}; - -// 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 year-month-day date into an [[chrono::epochal]] +// Calculates the [[chrono::epochal]], given a (year, month, day) date fn calc_epochal_from_ymd(y: int, m: int, d: int) (chrono::epochal | errors::invalid) = { // Algorithm adapted from: // https://en.wikipedia.org/wiki/Julian_day @@ -231,48 +186,88 @@ fn calc_epochal_from_ymd(y: int, m: int, d: int) (chrono::epochal | errors::inva + d - 32075 ); - const epochal = jdn + EPOCH_JULIAN; - return epochal; - + const e = jdn + EPOCH_JULIAN; + return e; }; -// Converts a year-week-weekday date into an [[chrono::epochal]] -fn calc_epochal_from_ywd() (chrono::epochal | errors::invalid) = { +// Calculates the [[chrono::epochal]], given a (year, week, weekday) date +fn calc_epochal_from_ywd(y: int, w: int, wd: int) (chrono::epochal | errors::invalid) = { // TODO return 0; }; -// Converts a year-yearday date into an [[chrono::epochal]] -fn calc_epochal_from_yd() (chrono::epochal | errors::invalid) = { +// Calculates the [[chrono::epochal]], given a (year, yearday) date +fn calc_epochal_from_yd(y: int, yd: int) (chrono::epochal | errors::invalid) = { // TODO return 0; }; -// 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 - }; +// // 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); +// }; +// }; - return errors::invalid; -}; +// // 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 @@ -1,60 +1,26 @@ +use errors; use time; use time::chrono; -use errors; - -// Represents a ISO datetime -// -// Notes: -// Java has good separation of types: A LocalDatetime, ZonedDatetime, -// OffsetDatetime. Python instead reasons about datetimes as being -// timezone-aware/naive. Here's I try to leaverage Hare's type system to combine -// the two. -// -// Putting the date and time related fields into separate typed structs maybe -// isn't a good idea (see `type localdate` below), but I still put it here -// because localtime is often used on it's own, and it makes some sense to have -// a datetime be composed of a date and time. +// 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, }; -// Returns a [[datetime]], with all subfields initialised with void -export fn init_datetime() datetime = datetime { +fn init_datetime() datetime = datetime { date = init_date(), time = init_time(), loc = chrono::local, }; -// 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; -}; - // Creates a new datetime // -// // 1995 July 18th 09:16:00.000 -// datetime::new(1995, 07, 18, 9, 16, 0, 0, chrono::local) +// // 2038 January 19th 03:14:07.000 +// datetime::new(2038, 01, 19, 03, 14, 07, 0, chrono::local) // -// For alternative forms, assemble a datetime manually using the desired types. export fn new( year: int, month: int, @@ -77,7 +43,6 @@ export fn new( week = void, weekday = void, yearday = void, - }, time = localtime { lapsed = void, @@ -94,22 +59,6 @@ export fn new( return dt; }; -// 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; -}; - // Returns the current datetime export fn now() datetime = { const i = time::now(time::clock::REALTIME); @@ -135,7 +84,45 @@ export fn now() datetime = { return dt; }; +// Validates a datetime's internal date & time values export fn validate(dt: (datetime | localdate | localtime)) 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+test.ha b/datetime/format+test.ha @@ -1,6 +1,6 @@ use errors; -use time::chrono; use fmt; +use time::chrono; @test fn strptime() void = { let dt = datetime {...}; diff --git a/datetime/format.ha b/datetime/format.ha @@ -112,7 +112,11 @@ fn clamp_int(i: int, min: int, max: int) int = { }; }; -// Parses a string into a [[datetime]] +// Parses a datetime string into a [[datetime::datetime]]. +// +// The resulting [[datetime::datetime]] may not contain sufficient information +// to be valid. Incremental parsing of data is possible, but the caller should +// validate the [[datetime::datetime]] when appropriate. export fn strptime(format: str, s: str, dt: *datetime) (void | errors::invalid) = { const format_iter = strings::iter(format); const s_iter = strings::iter(s); @@ -427,7 +431,6 @@ export fn strptime(format: str, s: str, dt: *datetime) (void | errors::invalid) // Formats a [[datetime]] and writes it into a caller supplied buffer. // The returned string is borrowed from this buffer. -// Fails if a particular datetime field is required but void. export fn bstrftime(buf: []u8, format: str, dt: *datetime) (str | errors::invalid | io::error) = { let sink = strio::fixed(buf); defer io::close(sink); @@ -437,7 +440,6 @@ export fn bstrftime(buf: []u8, format: str, dt: *datetime) (str | errors::invali // Formats a [[datetime]] and writes it into a heap-allocated string. // The caller must free the return value. -// Fails if a particular datetime field is required but void. export fn strftime(format: str, dt: *datetime) (str | errors::invalid | io::error) = { let sink = strio::dynamic(); fmttime(sink, format, dt)?; @@ -445,7 +447,6 @@ export fn strftime(format: str, dt: *datetime) (str | errors::invalid | io::erro }; // Formats a [[datetime]] and writes it into a [[io::handle]]. -// Fails a particular field is required but void. export fn fmttime(h: io::handle, format: str, dt: *datetime) (size | errors::invalid | io::error) = { const iter = strings::iter(format); let escaped = false; diff --git a/datetime/time.ha b/datetime/time.ha @@ -1,7 +1,6 @@ use errors; use time; -// Represents a time of day as represented on a wall clock export type localtime = struct { lapsed: (void | int), hour: (void | int), @@ -10,7 +9,7 @@ export type localtime = struct { nsec: (void | int), }; -export fn init_time() localtime = localtime { +fn init_time() localtime = localtime { lapsed = void, hour = void, min = void, @@ -18,8 +17,9 @@ export fn init_time() localtime = localtime { nsec = void, }; -// Converts a [[time::duration]] to a [[localtime]] -export fn conv_time_localtime(t: time::duration) localtime = { +// 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, @@ -30,9 +30,7 @@ export fn conv_time_localtime(t: time::duration) localtime = { return lt; }; -// Converts a [[localtime]] to a [[time::duration]]. -// Fails if any of the [[localtime]] fields are void. -export fn conv_localtime_time(lt: localtime) (time::duration | errors::invalid) = { +fn conv_localtime_time(lt: localtime) (time::duration | errors::invalid) = { if ( lt.hour is void || lt.min is void diff --git a/datetime/timezone.ha b/datetime/timezone.ha @@ -19,14 +19,18 @@ export fn tzdb(name: str) chrono::timezone = { }; -// Europe/Amsterdam timezone +// +// Some example timezones +// + +// "Europe/Amsterdam" timezone export const TZ_Europe_Amsterdam: chrono::timezone = chrono::timezone { aliasof = &TZ_CET, name = "Europe/Amsterdam", ... }; -// Central European Time +// "CET", "Central European Time" timezone export const TZ_CET: chrono::timezone = chrono::timezone { aliasof = void, scale = &chrono::UTC, @@ -50,7 +54,8 @@ export const TZ_CET: chrono::timezone = chrono::timezone { fn zone_cet(m: chrono::moment) uint = { const dt = datetime { ... }; - conv_moment_datetime(m, &dt); + // TODO: reconcile chrono::moment // datetime::datetime types + //conv_moment_datetime(m, &dt); const dst = ( dt.date.month: int == 3 && dt.date.day: int == 28 && @@ -72,4 +77,3 @@ fn zone_cet(m: chrono::moment) uint = { return 0u; }; }; - diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -185,21 +185,24 @@ datetime() { if [ $testing -eq 0 ] then gen_srcs datetime \ + arithmetic.ha \ calendar.ha \ - datetime.ha \ - timezone.ha \ date.ha \ + datetime.ha \ + format.ha \ time.ha \ - format.ha + timezone.ha else gen_srcs datetime \ + arithmetic.ha \ calendar.ha \ - datetime.ha \ - timezone.ha \ - date.ha \ date+test.ha \ + date.ha \ + datetime.ha \ + format.ha \ + format+test.ha \ time.ha \ - format.ha + timezone.ha fi gen_ssa datetime errors fmt strings strio time time::chrono } diff --git a/stdlib.mk b/stdlib.mk @@ -929,12 +929,13 @@ $(HARECACHE)/crypto/curve25519/crypto_curve25519-any.ssa: $(stdlib_crypto_curve2 # datetime (+any) stdlib_datetime_any_srcs= \ + $(STDLIB)/datetime/arithmetic.ha \ $(STDLIB)/datetime/calendar.ha \ - $(STDLIB)/datetime/datetime.ha \ - $(STDLIB)/datetime/timezone.ha \ $(STDLIB)/datetime/date.ha \ + $(STDLIB)/datetime/datetime.ha \ + $(STDLIB)/datetime/format.ha \ $(STDLIB)/datetime/time.ha \ - $(STDLIB)/datetime/format.ha + $(STDLIB)/datetime/timezone.ha $(HARECACHE)/datetime/datetime-any.ssa: $(stdlib_datetime_any_srcs) $(stdlib_rt) $(stdlib_errors_$(PLATFORM)) $(stdlib_fmt_$(PLATFORM)) $(stdlib_strings_$(PLATFORM)) $(stdlib_strio_$(PLATFORM)) $(stdlib_time_$(PLATFORM)) $(stdlib_time_chrono_$(PLATFORM)) @printf 'HAREC \t$@\n' @@ -2892,12 +2893,13 @@ $(TESTCACHE)/crypto/curve25519/crypto_curve25519-any.ssa: $(testlib_crypto_curve # datetime (+any) testlib_datetime_any_srcs= \ + $(STDLIB)/datetime/arithmetic.ha \ $(STDLIB)/datetime/calendar.ha \ $(STDLIB)/datetime/date+test.ha \ $(STDLIB)/datetime/date.ha \ $(STDLIB)/datetime/datetime.ha \ - $(STDLIB)/datetime/format+test.ha \ $(STDLIB)/datetime/format.ha \ + $(STDLIB)/datetime/format+test.ha \ $(STDLIB)/datetime/time.ha \ $(STDLIB)/datetime/timezone.ha