hare

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

commit 2bce15db8ba2f3c6711fab3d34bd34f00b6be6b1
parent 3f7e48299b131d7e7d010f1fbbabee65ac021875
Author: Byron Torres <b@torresjrjr.com>
Date:   Sat, 14 Oct 2023 03:24:52 +0100

time::date: fix calendar before 4715 BCE

Fixes: https://todo.sr.ht/~sircmpwn/hare/565
Signed-off-by: Byron Torres <b@torresjrjr.com>

Diffstat:
Mtime/date/date.ha | 4----
Mtime/date/daydate.ha | 41+++++++++++++++++++++++++++++++++--------
2 files changed, 33 insertions(+), 12 deletions(-)

diff --git a/time/date/date.ha b/time/date/date.ha @@ -139,10 +139,6 @@ export fn new( zoff: time::duration, fields: int... ) (date | invalid) = { - // TODO: - // - revise examples - // - fix calls with `years <= -4715`. - // https://todo.sr.ht/~sircmpwn/hare/565 let _fields: [_]int = [ 0, 1, 1, // year month day 0, 0, 0, 0, // hour min sec nsec diff --git a/time/date/daydate.ha b/time/date/daydate.ha @@ -11,6 +11,9 @@ export def EPOCHAL_JULIAN: i64 = -2440588; // The Hare epoch of the Gregorian Common Era. export def EPOCHAL_GREGORIAN: i64 = -719164; +// Number of days in the Gregorian 400 year cycle +def GREGORIAN_CYCLE_DAYS: i64 = 146097; + // Calculates whether a year is a leap year. export fn isleapyear(y: int) bool = { return if (y % 4 != 0) false @@ -72,10 +75,18 @@ fn calc_ymd(e: i64) (int, int, int) = { // Algorithm adapted from: // https://en.wikipedia.org/wiki/Julian_day#Julian_or_Gregorian_calendar_from_Julian_day_number // - // Alternate methods of date calculation should be explored. - const J = e - EPOCHAL_JULIAN; + // TODO: Review, cite, verify, annotate. + + // workaround for dates before -4716 March 1st + let E = e; + let cycles = 0; + for (E < -2441951) { + E += GREGORIAN_CYCLE_DAYS; + cycles += 1; + }; + + const J = E - EPOCHAL_JULIAN; - // TODO: substitute numbers where possible const b = 274277; const c = -38; const j = 1401; @@ -89,7 +100,7 @@ fn calc_ymd(e: i64) (int, int, int) = { const w = 2; const y = 4716; - const f = J + j + (((4 * J + b) / 146097) * 3) / 4 + c; + const f = J + j + (((4 * J + b) / GREGORIAN_CYCLE_DAYS) * 3) / 4 + c; const a = r * f + v; const g = (a % p) / r; const h = u * g + w; @@ -98,6 +109,8 @@ fn calc_ymd(e: i64) (int, int, int) = { const M = ((h / s + m) % n) + 1; const Y = (a / p) - y + (n + m - M) / n; + const Y = Y - (400 * cycles); + return (Y: int, M: int, D: int); }; @@ -193,18 +206,30 @@ fn calc_daydate__ymd(y: int, m: int, d: int) (i64 | invalid) = { if (!is_valid_ymd(y, m, d)) { return invalid; }; + // Algorithm adapted from: // https://en.wikipedia.org/wiki/Julian_day // // TODO: Review, cite, verify, annotate. - const jdn = ( - (1461 * (y + 4800 + (m - 14) / 12)) / 4 + + // workaround for dates before -4800 March 1st + let Y = y; + let cycles = 0; + for (Y <= -4800) { + Y += 400; // Gregorian 400 year cycle + cycles += 1; + }; + + const jdn = ( // Julian Date Number + (1461 * (Y + 4800 + (m - 14) / 12)) / 4 + (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12 - - (3 * ((y + 4900 + (m - 14) / 12) / 100)) / 4 + - (3 * ((Y + 4900 + (m - 14) / 12) / 100)) / 4 + d - 32075 ); - const e = jdn + EPOCHAL_JULIAN; + + const e = jdn + EPOCHAL_JULIAN - (GREGORIAN_CYCLE_DAYS * cycles); + return e; };