commit 374b7596a0bd3b61477e1b98cf2c5163a7f364a2
parent 3c1b9cf1a7851a2d70a622cd1bcf5cf638fa3e8f
Author: Byron Torres <b@torresjrjr.com>
Date: Mon, 15 Nov 2021 22:11:07 +0000
adapt fmttime() code
The fmttime() function is outlined and includes code adapted from
Vlad-Stefan Harbuz <vlad@vladh.net>.
The localdate{} and localtime{} structs now have an epochal and lapsed
field respectively, to extend chrono::moment. Choices as the the
underlying types are to be made.
Signed-off-by: Byron Torres <b@torresjrjr.com>
Diffstat:
5 files changed, 257 insertions(+), 51 deletions(-)
diff --git a/datetime/calendar.ha b/datetime/calendar.ha
@@ -46,56 +46,68 @@ export fn conv_moment_datetime(m: chrono::moment, dt: *datetime) void = {
//
// Calculates a moment's number of days since the calendar epoch 0001-01-01
-export fn epocal(m: chrono::moment) int = {
- return (EPOCH_COMMONERA + m.date): int;
+export fn epochal(dt: *datetime) int = {
+ match (dt.date.epochal) {
+ case void =>
+ abort("TODO");
+ case e: int =>
+ return e;
+ };
};
// Calculates a moment's era
-export fn era(m: chrono::moment) int = {
- if (m.date >= EPOCH_COMMONERA) {
- return 1; // CE, Common Era
+export fn era(dt: *datetime) int = {
+ if (epochal(dt) >= EPOCH_COMMONERA) {
+ dt.date.era = 1; // CE, Common Era
} else {
- return 0; // BCE, Before Common Era
+ dt.date.era = 0; // BCE, Before Common Era
+ };
+ const a = match (dt.date.era) {
+ case void =>
+ abort("TODO");
+ case a: int =>
+ yield a;
};
+ return a;
};
// Calculates a moment's year
-export fn year(m: chrono::moment) int = {
+export fn year(dt: *datetime) int = {
return 0; // TODO
};
// Calculates a moment's month
-export fn month(m: chrono::moment) int = {
+export fn month(dt: *datetime) int = {
return 0; // TODO
};
// Calculates a moment's day of the month
-export fn day(m: chrono::moment) int = {
+export fn day(dt: *datetime) int = {
return 0; // TODO
};
// Calculates a moment's ISO week calendar year
-export fn isoweekyear(m: chrono::moment) int = {
+export fn isoweekyear(dt: *datetime) int = {
return 0; // TODO
};
// Calculates a moment's ISO week
-export fn isoweek(m: chrono::moment) int = {
+export fn isoweek(dt: *datetime) int = {
return 0; // TODO
};
// Calculates a moment's Gregorian week
-export fn week(m: chrono::moment) int = {
+export fn week(dt: *datetime) int = {
return 0; // TODO
};
// Calculates a moment's day of the week
-export fn weekday(m: chrono::moment) int = {
+export fn weekday(dt: *datetime) int = {
return 0; // TODO
};
// Calculates a moment's ordinal day of the year
-export fn yearday(m: chrono::moment) int = {
+export fn yearday(dt: *datetime) int = {
return 0; // TODO
};
@@ -104,23 +116,43 @@ export fn yearday(m: chrono::moment) int = {
//
// Calculates a moment's hour of the day
-export fn hour(m: chrono::moment) int = {
- return (m.time / time::HOUR): int;
+export fn hour(dt: *datetime) int = {
+ match (dt.time.hour) {
+ case void =>
+ abort("TODO");
+ case h: int =>
+ return h;
+ };
};
// Calculates a moment's minute of the hour
-export fn min(m: chrono::moment) int = {
- return ((m.time / time::MINUTE) % 60): int;
+export fn min(dt: *datetime) int = {
+ match (dt.time.min) {
+ case void =>
+ abort("TODO");
+ case m: int =>
+ return m;
+ };
};
// Calculates a moment's second of the minute
-export fn sec(m: chrono::moment) int = {
- return ((m.time / time::SECOND) % 60): int;
+export fn sec(dt: *datetime) int = {
+ match (dt.time.sec) {
+ case void =>
+ abort("TODO");
+ case s: int =>
+ return s;
+ };
};
// Calculates a moment's nanosecond of the second
-export fn nsec(m: chrono::moment) int = {
- return (m.time % time::SECOND): int;
+export fn nsec(dt: *datetime) int = {
+ match (dt.time.nsec) {
+ case void =>
+ abort("TODO");
+ case n: int =>
+ return n;
+ };
};
diff --git a/datetime/date.ha b/datetime/date.ha
@@ -4,6 +4,7 @@ use errors;
// 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),
year: (void | int),
month: (void | int),
@@ -16,6 +17,7 @@ export type localdate = struct {
};
export fn init_date() localdate = localdate {
+ epochal = void,
era = void,
year = void,
month = void,
@@ -27,17 +29,6 @@ export fn init_date() localdate = localdate {
weekday = void,
};
-export type WEEKDAYS = enum int {
- MON = 1,
- TUE = 2,
- WED = 3,
- THU = 4,
- FRI = 5,
- SAT = 6,
- SUN = 7,
-};
-
-
// Calculates an era, given a year
fn calc_era(y: int) int = {
return if (y >= 0) {
@@ -48,10 +39,8 @@ fn calc_era(y: int) int = {
};
-// Calculates the (year, month, day) of a [[chrono::epochal]]
-//
-// TODO: Split up this function? Probably not. Most useful as is.
-fn calc_ymd(e: chrono::epochal) (int, int, int) = {
+// Calculates the (year, month, day), given an epochal day
+fn calc_ymd(e: int) (int, int, int) = {
// Algorithm adapted from:
// https://en.wikipedia.org/wiki/Julian_day#Julian_or_Gregorian_calendar_from_Julian_day_number
//
@@ -167,14 +156,14 @@ fn calc_week(yd: int, wd: int) int = {
return (5 + yd - wd) / 7;
};
-// Calculates the weekday of a [[chrono::epochal]],
+// 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 of a [[chrono::epochal]],
+// Calculates the weekday, given a epochal day
// from Monday=0 to Sunday=6
fn calc_zeroweekday(wd: int) int = {
return wd - 1;
@@ -202,7 +191,7 @@ export fn conv_epochal_localdate(
want: *localdate,
) void = {
if (want.year is int || want.month is int || want.day is int) {
- const ymd = calc_ymd(e);
+ const ymd = calc_ymd(e: int);
date.year = ymd.0;
date.month = ymd.1;
date.day = ymd.2;
@@ -210,7 +199,7 @@ export fn conv_epochal_localdate(
if (want.yearday is int) {
if (date.year is void || date.month is void || date.day is void) {
- const ymd = calc_ymd(e);
+ const ymd = calc_ymd(e: int);
date.year = ymd.0;
date.month = ymd.1;
date.day = ymd.2;
diff --git a/datetime/datetime.ha b/datetime/datetime.ha
@@ -67,6 +67,7 @@ export fn new_datetime(
) (datetime | errors::invalid) = {
const dt = datetime {
date = localdate {
+ epochal = void,
era = if (year >= EPOCH_COMMONERA) 1 else 0,
year = year,
month = month,
@@ -79,6 +80,7 @@ export fn new_datetime(
},
time = localtime {
+ lapsed = void,
hour = hour,
min = min,
sec = sec,
@@ -124,6 +126,7 @@ export fn now_datetime() datetime = {
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,
diff --git a/datetime/format.ha b/datetime/format.ha
@@ -1,9 +1,44 @@
use errors;
use fmt;
use io;
+use strconv;
use strings;
use strio;
+def WEEKDAYS: [_]str = [
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday",
+ "Sunday",
+];
+
+def WEEKDAYS_SHORT: [_]str = ["Mon", "Tue", "Wed", "Thu", "Fr", "Sat", "Sun"];
+
+def MONTHS: [_]str = [
+ "January",
+ "Feburary",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+];
+
+def MONTHS_SHORT: [_]str = [
+ "Jan", "Feb", "Mar",
+ "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec",
+];
+
// Parses a string into a [[datetime]]
export fn strptime(format: str, s: str, dt: *datetime) (void | errors::invalid) = {
// TODO
@@ -32,6 +67,150 @@ 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) = {
- // TODO
- return 0z;
+ let iter = strings::iter(format);
+ let escaped = false;
+ let n = 0z;
+ for (true) {
+ let r: rune = match (strings::next(&iter)) {
+ case void =>
+ break;
+ case r: rune =>
+ yield r;
+ };
+
+ if (!escaped && r == '%') {
+ escaped = true;
+ continue;
+ };
+
+ if (!escaped) {
+ strio::appendrune(h, r)?;
+ continue;
+ };
+
+ escaped = false;
+ let new = switch (r) {
+ case 'a' =>
+ yield WEEKDAYS_SHORT[weekday(dt) - 1];
+ case 'A' =>
+ yield WEEKDAYS[weekday(dt) - 1];
+ case 'b' =>
+ yield MONTHS_SHORT[month(dt) - 1];
+ case 'h' =>
+ yield strftime("%b", dt)?;
+ case 'B' =>
+ yield MONTHS[month(dt) - 1];
+ case 'c' =>
+ // TODO: Localization
+ yield strftime("%a %b %e %H:%M:%S %Y", dt)?;
+ case 'C' =>
+ yield strconv::itos(year(dt) / 100);
+ case 'D' =>
+ yield strftime("%m/%d/%y", dt)?;
+ case 'd' =>
+ yield fmt::asprintf("{:02}", day(dt));
+ case 'e' =>
+ yield fmt::asprintf("{:2}", day(dt));
+ case 'F' =>
+ yield strftime("%Y-%m-%d", dt)?;
+ case 'g' =>
+ let year_str = strconv::itos(isoweekyear(dt));
+ yield strings::sub(year_str,
+ len(year_str) - 2, strings::end);
+ case 'G' =>
+ yield strconv::itos(isoweekyear(dt));
+ case 'H' =>
+ yield fmt::asprintf("{:02}", hour(dt));
+ case 'I' =>
+ let mod_hour = hour(dt) % 12;
+ if (mod_hour == 0) {
+ mod_hour = 12;
+ };
+ yield fmt::asprintf("{:02}", mod_hour);
+ case 'j' =>
+ yield strconv::itos(yearday(dt));
+ case 'k' =>
+ yield strconv::itos(hour(dt));
+ case 'l' =>
+ let mod_hour = hour(dt) % 12;
+ if (mod_hour == 0) {
+ mod_hour = 12;
+ };
+ yield strconv::itos(mod_hour);
+ case 'm' =>
+ yield fmt::asprintf("{:02}", month(dt));
+ case 'M' =>
+ yield fmt::asprintf("{:02}", min(dt));
+ case 'n' =>
+ yield "\n";
+ case 'N' =>
+ yield strconv::itos(nsec(dt));
+ case 'p' =>
+ // TODO: Localization
+ yield if (hour(dt) < 12) {
+ yield "AM";
+ } else {
+ yield "PM";
+ };
+ case 'P' =>
+ // TODO: Localization
+ yield if (hour(dt) < 12) {
+ yield "am";
+ } else {
+ yield "pm";
+ };
+ case 'r' =>
+ // TODO: Localization
+ yield strftime("%I:%M:%S %p", dt)?;
+ case 'R' =>
+ yield strftime("%H:%M", dt)?;
+ case 'S' =>
+ yield fmt::asprintf("{:02}", sec(dt));
+ case 't' =>
+ yield "\t";
+ case 'T' =>
+ yield strftime("%H:%M:%S", dt)?;
+ case 'u' =>
+ yield strconv::itos(weekday(dt));
+ case 'U' =>
+ // yield fmt::asprintf("{:02}", week_starting_sunday(dt));
+ // TODO
+ yield "";
+ case 'V' =>
+ yield fmt::asprintf("{:02}", isoweek(dt));
+ case 'w' =>
+ yield strconv::itos(weekday(dt) % 7);
+ case 'W' =>
+ yield fmt::asprintf("{:02}", week(dt));
+ case 'x' =>
+ // TODO: Localization
+ yield strftime("%m/%d/%y", dt)?;
+ case 'X' =>
+ // TODO: Localization
+ yield strftime("%H:%M:%S", dt)?;
+ case 'y' =>
+ let year_str = strconv::itos(year(dt));
+ yield strings::sub(year_str,
+ len(year_str) - 2, strings::end);
+ case 'Y' =>
+ yield strconv::itos(year(dt));
+ case 'z' =>
+ //yield get_tz_hhmm(dt);
+ // TODO
+ yield "";
+ case 'Z' =>
+ //yield get_tz_name(dt);
+ // TODO
+ yield "";
+ case '%' =>
+ yield "%";
+ case =>
+ // Pass-through invalid conversion specifier characters.
+ strio::appendrune(h, '%')?;
+ strio::appendrune(h, r)?;
+ continue;
+ };
+ n += strio::concat(h, new)?;
+ };
+ return n;
};
diff --git a/datetime/time.ha b/datetime/time.ha
@@ -3,22 +3,25 @@ use time;
// Represents a time of day as represented on a wall clock
export type localtime = struct {
- hour: (void | int),
- min: (void | int),
- sec: (void | int),
- nsec: (void | int),
+ lapsed: (void | int),
+ hour: (void | int),
+ min: (void | int),
+ sec: (void | int),
+ nsec: (void | int),
};
export fn init_time() localtime = localtime {
- hour = void,
- min = void,
- sec = void,
- nsec = void,
+ lapsed = void,
+ hour = void,
+ min = void,
+ sec = void,
+ nsec = void,
};
// Converts a [[time::duration]] to a [[localtime]]
export 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,