hare

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

commit c638d82223d06dd18e71eb200ef89a5a33eed083
parent 9e4ae51b1f1624325dc4c0e09be0b6cd6ce912a7
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 13 Apr 2022 14:09:28 +0200

datetime: overhaul format functions

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mdatetime/README | 8+++-----
Mdatetime/format.ha | 89++++++++++++++++++++++++++++++++++++++++---------------------------------------
2 files changed, 48 insertions(+), 49 deletions(-)

diff --git a/datetime/README b/datetime/README @@ -16,17 +16,15 @@ internal caching. [[datetime]] fields are accessed, evaluated, and cached via the various "field" functions ([[year]], [[month]], [[day]], etc). Accessing or modifying a -[[datetime]]'s fields directly is highly discouraged. See [[mock]] for -"modifiable datetimes". +[[datetime]]'s fields directly is discouraged. To mutate a datetime in code, the +use of the [[builder]] interface is recommended. [[datetime]]s may be localized to different [[chrono::timezone]]s via the [[in]] function. The "field" functions will evaluate the correct values accordingly. You'll find a standard selection of world timezones in the [[time::tzdb]] module. -Both formatting and parsing use a sensible subset of the POSIX format specifiers -(see strptime(3)), and it is trivial to contruct your own textual -representations with the functions herein. +To convert datetimes to and from strings, use [[parse]] and [[format]]. For arithmetics, use [[diff]], [[add]] and [[hop]]. Note that calendrical arithmetic is highly irregular with many edge cases, so think carefully about diff --git a/datetime/format.ha b/datetime/format.ha @@ -55,71 +55,76 @@ def MONTHS_SHORT: [_]str = [ // // See https://en.wikipedia.org/wiki/ISO_8601#Time_intervals -// Formats a [[datetime]] and writes it into a caller supplied buffer. -// The returned string is borrowed from this buffer. -export fn bformat(buf: []u8, layout: str, dt: *datetime) (str | invalid | io::error) = { +// Formats a [[datetime]] and writes it into a caller supplied buffer. The +// returned string is borrowed from this buffer. +export fn bsformat( + buf: []u8, + layout: str, + dt: *datetime, +) (str | invalid | io::error) = { let sink = strio::fixed(buf); - fmtstream(&sink, layout, dt)?; + format(&sink, layout, dt)?; return strio::string(&sink); }; -// Formats a [[datetime]] and writes it into a heap-allocated string. -// The caller must free the return value. -export fn format(layout: str, dt: *datetime) (str | invalid | io::error) = { +// Formats a [[datetime]] and writes it into a heap-allocated string. The caller +// must free the return value. +export fn asformat(layout: str, dt: *datetime) (str | invalid | io::error) = { let sink = strio::dynamic(); - fmtstream(&sink, layout, dt)?; + format(&sink, layout, dt)?; return strio::string(&sink); }; -fn fmt_specifier(r: rune, dt: *datetime) (str | invalid | io::error) = { - return switch (r) { +fn fmtout(out: io::handle, r: rune, dt: *datetime) (size | io::error) = { + switch (r) { case 'a' => - yield WEEKDAYS_SHORT[weekday(dt) - 1]; + return fmt::fprint(out, WEEKDAYS_SHORT[weekday(dt) - 1]); case 'A' => - yield WEEKDAYS[weekday(dt) - 1]; + return fmt::fprint(out, WEEKDAYS[weekday(dt) - 1]); case 'b' => - yield MONTHS_SHORT[month(dt) - 1]; + return fmt::fprint(out, MONTHS_SHORT[month(dt) - 1]); case 'B' => - yield MONTHS[month(dt) - 1]; + return fmt::fprint(out, MONTHS[month(dt) - 1]); case 'd' => - yield fmt::asprintf("{:02}", day(dt)); + return fmt::fprintf(out, "{:02}", day(dt)); case 'H' => - yield fmt::asprintf("{:02}", hour(dt)); + return fmt::fprintf(out, "{:02}", hour(dt)); case 'I' => - yield fmt::asprintf("{:02}", hour12(dt)); + return fmt::fprintf(out, "{:02}", hour12(dt)); case 'j' => - yield strconv::itos(yearday(dt)); + return fmt::fprint(out, strconv::itos(yearday(dt))); case 'm' => - yield fmt::asprintf("{:02}", month(dt)); + return fmt::fprintf(out, "{:02}", month(dt)); case 'M' => - yield fmt::asprintf("{:02}", min(dt)); + return fmt::fprintf(out, "{:02}", min(dt)); case 'N' => - yield fmt::asprintf("{:09}", strconv::itos(nsec(dt))); + return fmt::fprintf(out, "{:09}", strconv::itos(nsec(dt))); case 'p' => - yield if (hour(dt) < 12) { + const s = if (hour(dt) < 12) { yield "AM"; } else { yield "PM"; }; + return fmt::fprint(out, s); case 'S' => - yield fmt::asprintf("{:02}", sec(dt)); + return fmt::fprintf(out, "{:02}", sec(dt)); case 'u' => - yield strconv::itos(weekday(dt)); + return fmt::fprint(out, strconv::itos(weekday(dt))); case 'U' => - // yield fmt::asprintf("{:02}", week_starting_sunday(dt)); - // TODO - yield ""; + // return fmt::fprintf(out, "{:02}", week_starting_sunday(dt)); + abort("datetime::format: %U: TODO"); // TODO case 'w' => - yield strconv::itos(weekday(dt) % 7); + return fmt::fprint(out, strconv::itos(weekday(dt) % 7)); case 'W' => - yield fmt::asprintf("{:02}", week(dt)); + return fmt::fprintf(out, "{:02}", week(dt)); case 'y' => let year_str = strconv::itos(year(dt)); - yield strings::sub(year_str, len(year_str) - 2, strings::end); + year_str = strings::sub(year_str, len(year_str) - 2, strings::end); + return fmt::fprint(out, year_str); case 'Y' => - yield strconv::itos(year(dt)); + return fmt::fprint(out, strconv::itos(year(dt))); case 'z' => - // TODO: test + // TODO: test me let pm = '+'; const z = if (dt.zone.zoffset >= 0) { yield calc_hmsn(dt.zone.zoffset); @@ -127,23 +132,18 @@ fn fmt_specifier(r: rune, dt: *datetime) (str | invalid | io::error) = { pm = '-'; yield calc_hmsn(-dt.zone.zoffset); }; - yield fmt::asprintf("{}{:02}{:02}", pm, z.0, z.1); + return fmt::fprintf(out, "{}{:02}{:02}", pm, z.0, z.1); case 'Z' => - yield dt.zone.abbr; + return fmt::fprint(out, dt.zone.abbr); case '%' => - yield "%"; + return fmt::fprint(out, "%"); case => - // Pass-through invalid conversion specifier - // characters. - const passthrough = strio::dynamic(); - strio::appendrune(&passthrough, '%')!; - strio::appendrune(&passthrough, r)!; - yield strio::string(&passthrough); + abort("Invalid format string provided to datetime::format"); }; }; // Formats a [[datetime]] and writes to an [[io::handle]]. -export fn fmtstream( +export fn format( h: io::handle, layout: str, dt: *datetime @@ -161,7 +161,7 @@ export fn fmtstream( if (escaped) { escaped = false; - n += strio::concat(h, fmt_specifier(r, dt)?)?; + n += fmtout(h, r, dt)?; } else { if (r == '%') { escaped = true; @@ -293,7 +293,8 @@ fn hour12(dt: *datetime) int = { for (let i = 0z; i < len(cases); i += 1) { const layout = cases[i].0; const expected = cases[i].1; - const actual = format(layout, &dt)!; + const actual = asformat(layout, &dt)!; + defer free(actual); if (actual != expected) { fmt::printfln( "expected format({}, &dt) to be {} but was {}",