hare

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

commit f541578df9172214a93e41ed14f02543780268ba
parent f8c6ae89f94c2cb9895508754c7f117bd08a6ef0
Author: Byron Torres <b@torresjrjr.com>
Date:   Tue, 16 Nov 2021 13:31:18 +0000

add exported API lazy eval date functions

Exported functions in calendar.ha like year(), month(), day(), now act
as getters to *datetime fields. Void fields will be evaluated at
call-time.

If the given *datetime field has a stored value, it will be returned.
Otherwise, the field is evaluated from the other fields, whose values
are evaluated recursively (upto epochal), using pure functions from
date.ha, and returned.

This means, for example, that a call to year() will also evaluate and
store values for `.month` and `.day` in the given *datetime.

Work to make epochal() evaluate using other fields is in progress.
epochal() shall use the calc_epochal_from_* functions in date.ha.

A consideration: how to deal with invalid field values internally? Do we
rely on strong input validation? Do we validate at call-time? Which,
when, and where?

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

Diffstat:
Mdatetime/calendar.ha | 201++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mdatetime/datetime.ha | 2+-
2 files changed, 164 insertions(+), 39 deletions(-)

diff --git a/datetime/calendar.ha b/datetime/calendar.ha @@ -45,77 +45,202 @@ export fn conv_moment_datetime(m: chrono::moment, dt: *datetime) void = { // date-like // -// Calculates a moment's number of days since the calendar epoch 0001-01-01 +// Evaluates a datetime's number of days since the calendar epoch 0001-01-01 export fn epochal(dt: *datetime) int = { match (dt.date.epochal) { case void => - abort("TODO"); + abort("TODO"); // How to resolve? Use calc_epochal_from_*? case e: int => return e; }; }; -// Calculates a moment's era +// Evaluates a datetime's era export fn era(dt: *datetime) int = { - if (epochal(dt) >= EPOCH_COMMONERA) { - dt.date.era = 1; // CE, Common Era - } else { - dt.date.era = 0; // BCE, Before Common Era - }; - const a = match (dt.date.era) { + match (dt.date.era) { case void => - abort("TODO"); + if (dt.date.year is void) { + dt.date.year = year(dt); + }; + dt.date.era = calc_era(dt.date.year: int); + return dt.date.era: int; case a: int => - yield a; + return a; }; - return a; }; -// Calculates a moment's year +// Evaluates a datetime's year export fn year(dt: *datetime) int = { - return 0; // TODO + match (dt.date.year) { + case void => + match (dt.date.epochal) { + case void => + abort("TODO"); + case e: int => + const ymd = calc_ymd(e); + dt.date.year = ymd.0; + return dt.date.year: int; + }; + case y: int => + return y; + }; }; -// Calculates a moment's month +// Evaluates a datetime's month export fn month(dt: *datetime) int = { - return 0; // TODO + match (dt.date.month) { + case void => + match (dt.date.epochal) { + case void => + abort("TODO"); + case e: int => + const ymd = calc_ymd(e); + dt.date.month = ymd.1; + return dt.date.month: int; + }; + case y: int => + return y; + }; }; -// Calculates a moment's day of the month +// Evaluates a datetime's day of the month export fn day(dt: *datetime) int = { - return 0; // TODO + match (dt.date.day) { + case void => + match (dt.date.epochal) { + case void => + abort("TODO"); + case e: int => + const ymd = calc_ymd(e); + dt.date.day = ymd.2; + return dt.date.day: int; + }; + case y: int => + return y; + }; }; -// Calculates a moment's ISO week calendar year -export fn isoweekyear(dt: *datetime) int = { - return 0; // TODO -}; -// Calculates a moment's ISO week -export fn isoweek(dt: *datetime) int = { - return 0; // TODO +// Evaluates a datetime's day of the week +export fn weekday(dt: *datetime) int = { + match (dt.date.weekday) { + case void => + match (dt.date.epochal) { + case void => + abort("TODO"); + case e: int => + dt.date.weekday = calc_weekday(e); + return dt.date.weekday: int; + }; + case y: int => + return y; + }; }; -// Calculates a moment's Gregorian week -export fn week(dt: *datetime) int = { - return 0; // TODO +// Evaluates a datetime's ordinal day of the year +export fn yearday(dt: *datetime) int = { + match (dt.date.yearday) { + case void => + if (dt.date.year is void) { + year(dt); + }; + if (dt.date.month is void) { + month(dt); + }; + if (dt.date.day is void) { + day(dt); + }; + dt.date.yearday = calc_yearday( + dt.date.year: int, + dt.date.month: int, + dt.date.day: int, + ); + return dt.date.yearday: int; + case yd: int => + return yd; + }; +}; +// Evaluates a datetime's ISO week calendar year +export fn isoweekyear(dt: *datetime) int = { + match (dt.date.isoweekyear) { + case void => + if (dt.date.year is void) { + year(dt); + }; + if (dt.date.month is void) { + month(dt); + }; + if (dt.date.day is void) { + day(dt); + }; + if (dt.date.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, + ); + return dt.date.isoweekyear: int; + case iwy: int => + return iwy; + }; }; -// Calculates a moment's day of the week -export fn weekday(dt: *datetime) int = { - return 0; // TODO +// Evaluates a datetime's Gregorian week +export fn week(dt: *datetime) int = { + match (dt.date.week) { + case void => + if (dt.date.yearday is void) { + yearday(dt); + }; + if (dt.date.weekday is void) { + weekday(dt); + }; + dt.date.week = calc_week( + dt.date.yearday: int, + dt.date.weekday: int, + ); + return dt.date.week: int; + case w: int => + return w; + }; }; -// Calculates a moment's ordinal day of the year -export fn yearday(dt: *datetime) int = { - return 0; // TODO +// Evaluates a datetime's ISO week +export fn isoweek(dt: *datetime) int = { + match (dt.date.isoweek) { + case void => + if (dt.date.year is void) { + year(dt); + }; + if (dt.date.week is void) { + week(dt); + }; + if (dt.date.weekday is void) { + weekday(dt); + }; + if (dt.date.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, + ); + return dt.date.isoweek: int; + case iw: int => + return iw; + }; }; // // time-like // -// Calculates a moment'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 => @@ -125,7 +250,7 @@ export fn hour(dt: *datetime) int = { }; }; -// Calculates a moment'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 => @@ -135,7 +260,7 @@ export fn min(dt: *datetime) int = { }; }; -// Calculates a moment'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 => @@ -145,7 +270,7 @@ export fn sec(dt: *datetime) int = { }; }; -// Calculates a moment'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 => diff --git a/datetime/datetime.ha b/datetime/datetime.ha @@ -68,7 +68,7 @@ export fn new_datetime( const dt = datetime { date = localdate { epochal = void, - era = if (year >= EPOCH_COMMONERA) 1 else 0, + era = void, year = year, month = month, day = day,