hare

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

chronology.ha (4546B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use time;
      5 
      6 // Invalid [[moment]].
      7 export type invalid = !void;
      8 
      9 // A moment in time within a [[locality]]. Create one with [[new]]. This type
     10 // extends the [[time::instant]] type and couples it with a [[timescale]] via
     11 // its [[locality]] field.
     12 //
     13 // This object should be treated as private and immutable. Directly mutating its
     14 // fields causes undefined behavour when used with module functions. Likewise,
     15 // interrogating the fields' type and value (e.g. using match statements) is
     16 // also improper.
     17 //
     18 // Moments observe a daydate, time-of-day, and [[zone]], which are evaluated,
     19 // cached and obtained with the observer functions [[daydate]], [[daytime]], and
     20 // [[ozone]]. These values are derived from the embedded instant and locality
     21 // information, and thus are guaranteed to be valid.
     22 export type moment = struct {
     23 	// The embedded [[time::instant]].
     24 	time::instant,
     25 
     26 	// The [[locality]] with which to interpret this moment.
     27 	loc: locality,
     28 
     29 	// The observed [[zone]].
     30 	zone: nullable *zone,
     31 
     32 	// The observed daydate (scalar day number)
     33 	// since an abitrary epoch (e.g. the Unix epoch 1970-01-01).
     34 	daydate: (void | i64),
     35 
     36 	// The observed time-of-day (amount of daytime progressed in a day)
     37 	// as nanoseconds.
     38 	daytime: (void | i64),
     39 };
     40 
     41 // Creates a new [[moment]]. Uses a given [[time::instant]] with a [[timescale]]
     42 // associated with a given [[locality]].
     43 export fn new(loc: locality, i: time::instant) moment = {
     44 	return moment {
     45 		sec = i.sec,
     46 		nsec = i.nsec,
     47 		loc = loc,
     48 		zone = null,
     49 		daydate = void,
     50 		daytime = void,
     51 	};
     52 };
     53 
     54 // Observes a [[moment]]'s observed [[zone]].
     55 export fn ozone(m: *moment) zone = {
     56 	match (m.zone) {
     57 	case let z: *zone =>
     58 		return *z;
     59 	case null =>
     60 		const z = lookupzone(m.loc, *(m: *time::instant));
     61 		m.zone = z;
     62 		return *z;
     63 	};
     64 };
     65 
     66 // Observes a [[moment]]'s observed daydate (day number since epoch).
     67 //
     68 // For moments with [[locality]]s based on the [[utc]], [[tai]], [[gps]], and
     69 // similar timescales, their epoch date should be interpreted as the Unix epoch
     70 // (1970 Janurary 1st). Other timescales may suggest their own interpretations
     71 // applicable to other chronologies.
     72 export fn daydate(m: *moment) i64 = {
     73 	match (m.daydate) {
     74 	case let dd: i64 =>
     75 		return dd;
     76 	case void =>
     77 		const (dd, dt) = calc_datetime(
     78 			m.loc, *(m: *time::instant), ozone(m).zoff,
     79 		);
     80 		m.daytime = dt;
     81 		m.daydate = dd;
     82 		return dd;
     83 	};
     84 };
     85 
     86 // Observes a [[moment]]'s observed time-of-day (amount of daytime progressed in
     87 // a day) as nanoseconds.
     88 export fn daytime(m: *moment) i64 = {
     89 	match (m.daytime) {
     90 	case let dt: i64 =>
     91 		return dt;
     92 	case void =>
     93 		const (dd, dt) = calc_datetime(
     94 			m.loc, *(m: *time::instant), ozone(m).zoff,
     95 		);
     96 		m.daytime = dt;
     97 		m.daydate = dd;
     98 		return dt;
     99 	};
    100 };
    101 
    102 // Calculates the observed daydate and time-of-day of a [[time::instant]] in a
    103 // [[locality]] at a particular zone offset.
    104 fn calc_datetime(
    105 	loc: locality,
    106 	inst: time::instant,
    107 	zoff: time::duration,
    108 ) (i64, time::duration) = {
    109 	const i = time::add(inst, zoff);
    110 	const day = loc.daylength;
    111 	const daysec = day / time::SECOND;
    112 	const dd = if (i.sec >= 0) i.sec / daysec else (i.sec + 1) / daysec - 1;
    113 	const dt = ((i.sec % daysec + daysec) * time::SECOND + i.nsec) % day;
    114 	return (dd, dt);
    115 };
    116 
    117 // Creates a [[moment]] from a given [[locality]], zone offset, daydate, and
    118 // time-of-day.
    119 export fn from_datetime(
    120 	loc: locality,
    121 	zo: time::duration,
    122 	dd: i64,
    123 	dt: i64,
    124 ) moment = {
    125 	const inst = calc_instant(loc.daylength, zo, dd, dt);
    126 	return moment {
    127 		sec = inst.sec,
    128 		nsec = inst.nsec,
    129 		loc = loc,
    130 		zone = null,
    131 		daydate = dd,
    132 		daytime = dt,
    133 	};
    134 };
    135 
    136 fn calc_instant(
    137 	day: time::duration, // length of a day
    138 	zo: time::duration,  // zone offset
    139 	dd: i64,             // date since epoch
    140 	dt: i64,             // time since start of day (ns)
    141 ) time::instant = {
    142 	const daysec = (day / time::SECOND): i64;
    143 	const dayrem = day % time::SECOND;
    144 	let i = time::instant {
    145 		sec = dd * daysec,
    146 		nsec = 0,
    147 	};
    148 	i = time::add(i, dd * dayrem);
    149 	i = time::add(i, dt);
    150 	i = time::add(i, -zo);
    151 	return i;
    152 };
    153 
    154 // The duration of a day on Earth, in terrestrial (SI) seconds.
    155 export def EARTH_DAY: time::duration = 86400 * time::SECOND;
    156 
    157 // The duration of a solar day on Mars, in Martian seconds.
    158 export def MARS_SOL_MARTIAN: time::duration = 86400 * time::SECOND;
    159 
    160 // The duration of a solar day on Mars, in terrestrial (SI) seconds.
    161 export def MARS_SOL_TERRESTRIAL: time::duration = 88775244147000 * time::NANOSECOND;