hare

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

timescale.ha (18752B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use time;
      5 
      6 // Represents a scale of time; a time standard. See [[convert]].
      7 export type timescale = struct {
      8 	name: str,
      9 	abbr: str,
     10 	convto: *tsconverter,
     11 	convfrom: *tsconverter,
     12 };
     13 
     14 export type tsconverter = fn(ts: *timescale, i: time::instant) ([]time::instant | void);
     15 
     16 // A discontinuity between two [[timescale]]s caused a one-to-one
     17 // [[time::instant]] conversion to fail.
     18 export type discontinuity = !void;
     19 
     20 // The analytical result of a [[time::instant]] conversion between two
     21 // [[timescale]]s at a point of [[discontinuity]].
     22 //
     23 // An empty slice represents a nonexistent conversion result.
     24 // A populated (>1) slice represents an ambiguous conversion result.
     25 export type analytical = ![]time::instant;
     26 
     27 // Converts a [[time::instant]] from one [[timescale]] to the next exhaustively.
     28 // The final conversion result is returned. For each active pair of timescales,
     29 // if neither implements conversion from the first to the second, a two-step
     30 // intermediary TAI conversion will occur. If given zero or one timescales, the
     31 // given instant is returned.
     32 export fn convert(i: time::instant, tscs: *timescale...) (time::instant | analytical) = {
     33 	let ts: []time::instant = [i];
     34 	let tmps: []time::instant = [];
     35 
     36 	for (let j = 1z; j < len(tscs); j += 1) {
     37 		let a = tscs[j - 1];
     38 		let b = tscs[j];
     39 
     40 		for (let k = 0z; k < len(ts); k += 1) {
     41 			const t = ts[k];
     42 
     43 			// try .convto
     44 			match (a.convto(b, t)) {
     45 			case let convs: []time::instant =>
     46 				append(tmps, convs...)!;
     47 				continue;
     48 			case void => void;
     49 			};
     50 
     51 			// try .convfrom
     52 			match (b.convfrom(a, t)) {
     53 			case let convs: []time::instant =>
     54 				append(tmps, convs...)!;
     55 				continue;
     56 			case void => void;
     57 			};
     58 
     59 			// default to TAI intermediary
     60 			const convs = a.convto(&tai, t) as []time::instant;
     61 
     62 			for (let conv .. convs) {
     63 				append(tmps, (
     64 					b.convfrom(&tai, conv) as []time::instant
     65 				)...)!;
     66 			};
     67 		};
     68 
     69 		// TODO: sort and deduplicate 'ts' here
     70 		ts = tmps;
     71 		tmps = [];
     72 	};
     73 
     74 	return if (len(ts) == 1) ts[0] else ts;
     75 };
     76 
     77 
     78 // International Atomic Time
     79 //
     80 // The realisation of proper time on Earth's geoid.
     81 // Continuous (no leap seconds).
     82 export const tai: timescale = timescale {
     83 	name = "International Atomic Time",
     84 	abbr = "TAI",
     85 	convto = &tai_conv,
     86 	convfrom = &tai_conv,
     87 };
     88 
     89 fn tai_conv(ts: *timescale, i: time::instant) ([]time::instant | void) = {
     90 	if (ts == &tai) {
     91 		return alloc([i]...)!;
     92 	};
     93 };
     94 
     95 
     96 // Coordinated Universal Time
     97 //
     98 // Used as the basis of civil timekeeping.
     99 // Based on TAI; time-dependent offset.
    100 // Discontinuous (has leap seconds).
    101 //
    102 // During a program's initialization, this timescale initializes by loading its
    103 // UTC/TAI leap second data from [[UTC_LEAPSECS_PATH]]; otherwise, fails
    104 // silently. If failed, any attempt to consult UTC leapsec data (e.g. calling
    105 // [[convert]] on UTC) causes an abort. This includes [[in]].
    106 export const utc: timescale = timescale {
    107 	name = "Coordinated Universal Time",
    108 	abbr = "UTC",
    109 	convto = &utc_convto,
    110 	convfrom = &utc_convfrom,
    111 };
    112 
    113 fn utc_convto(ts: *timescale, i: time::instant) ([]time::instant | void) = {
    114 	if (ts == &utc) {
    115 		return alloc([i]...)!;
    116 	} else if (ts == &tai) {
    117 		let ret: []time::instant = [];
    118 		if (!utc_isinitialized) {
    119 			match (init_utc_leapsecs()) {
    120 			case void =>
    121 				utc_isinitialized = true;
    122 			case =>
    123 				abort("Failed to initialize UTC timescale");
    124 			};
    125 		};
    126 
    127 		const firstleap = utc_leapsecs[0]; // TODO: no leapsecs loaded
    128 		if (time::compare(i, time::from_unix(firstleap.0)) < 0) {
    129 			append(ret, time::instant {
    130 				sec = i.sec + firstleap.1,
    131 				nsec = i.nsec,
    132 			})!;
    133 			return ret;
    134 		};
    135 
    136 		for (let idx = len(utc_leapsecs) - 1; idx >= 0 ; idx -= 1) {
    137 			const leap = utc_leapsecs[idx];
    138 			const leapsecond = time::from_unix(leap.0);
    139 			const leapoffset = leap.1;
    140 			const diff = time::diff(leapsecond, i);
    141 
    142 			const prevleapoffset =
    143 				if (idx == 0) 0i64 else utc_leapsecs[idx - 1].1;
    144 			const offsetdiff =
    145 				(leapoffset - prevleapoffset) * time::SECOND;
    146 
    147 			// case of positive leap second (UTC repeats a second)
    148 			if (offsetdiff >= 0) {
    149 				if (diff >= 0) {
    150 					append(ret, time::instant {
    151 						sec = i.sec + leapoffset,
    152 						nsec = i.nsec,
    153 					})!;
    154 					return ret;
    155 				};
    156 
    157 				if (diff >= -offsetdiff && diff < 0) {
    158 					append(ret, [
    159 						time::instant {
    160 							sec = i.sec + prevleapoffset,
    161 							nsec = i.nsec,
    162 						},
    163 						time::instant {
    164 							sec = i.sec + leapoffset,
    165 							nsec = i.nsec,
    166 						},
    167 					]...)!;
    168 					return ret;
    169 				};
    170 
    171 				continue;
    172 			};
    173 
    174 			// case of negative leap second (UTC skips a second)
    175 			if (offsetdiff < 0) {
    176 				if (diff >= 0) {
    177 					append(ret, time::instant {
    178 						sec = i.sec + leapoffset,
    179 						nsec = i.nsec,
    180 					})!;
    181 					return ret;
    182 				};
    183 
    184 				if (diff >= offsetdiff && diff < 0) {
    185 					return ret;
    186 				};
    187 
    188 				continue;
    189 			};
    190 		};
    191 	};
    192 };
    193 
    194 fn utc_convfrom(ts: *timescale, i: time::instant) ([]time::instant | void) = {
    195 	if (ts == &utc) {
    196 		return alloc([i]...)!;
    197 	} else if (ts == &tai) {
    198 		let ret: []time::instant = [];
    199 		if (!utc_isinitialized) {
    200 			match (init_utc_leapsecs()) {
    201 			case void =>
    202 				utc_isinitialized = true;
    203 			case =>
    204 				abort("Failed to initialize UTC timescale");
    205 			};
    206 		};
    207 
    208 		const firstleap = utc_leapsecs[0]; // TODO: no leapsecs loaded
    209 		if (time::compare(i, time::from_unix(firstleap.0 + firstleap.1)) < 0) {
    210 			append(ret, time::instant {
    211 				sec = i.sec - firstleap.1,
    212 				nsec = i.nsec,
    213 			})!;
    214 			return ret;
    215 		};
    216 
    217 		for (let idx = len(utc_leapsecs) - 1; idx >= 0 ; idx -= 1) {
    218 			const leap = utc_leapsecs[idx];
    219 			const leapsecond = time::from_unix(leap.0 + leap.1);
    220 			const leapoffset = leap.1;
    221 			const diff = time::diff(leapsecond, i);
    222 
    223 			const prevleapoffset =
    224 				if (idx == 0) 10i64 else utc_leapsecs[idx - 1].1;
    225 			const offsetdiff
    226 				= (leapoffset - prevleapoffset) * time::SECOND;
    227 
    228 			// case of positive leap second (UTC repeats a second)
    229 			if (offsetdiff >= 0) {
    230 				if (diff >= -offsetdiff) {
    231 					append(ret, time::instant {
    232 						sec = i.sec - leapoffset,
    233 						nsec = i.nsec,
    234 					})!;
    235 					return ret;
    236 				};
    237 
    238 				continue;
    239 			};
    240 
    241 			// case of negative leap second (UTC skips a second)
    242 			if (offsetdiff < 0) {
    243 				if (diff >= 0) {
    244 					append(ret, time::instant {
    245 						sec = i.sec - leapoffset,
    246 						nsec = i.nsec,
    247 					})!;
    248 					return ret;
    249 				};
    250 
    251 				continue;
    252 			};
    253 		};
    254 	};
    255 };
    256 
    257 
    258 // Global Positioning System Time
    259 //
    260 // Used for GPS coordination.
    261 // Based on TAI; constant -19 second offset.
    262 // Continuous (no leap seconds).
    263 export const gps: timescale = timescale {
    264 	name = "Global Positioning System Time",
    265 	abbr = "GPS",
    266 	convto = &gps_convto,
    267 	convfrom = &gps_convfrom,
    268 };
    269 
    270 // The constant offset between GPS-Time (Global Positioning System Time) and TAI
    271 // (International Atomic Time). Used by [[gps]].
    272 def GPS_OFFSET: time::duration = -19 * time::SECOND;
    273 
    274 fn gps_convto(ts: *timescale, i: time::instant) ([]time::instant | void) = {
    275 	if (ts == &gps) {
    276 		return alloc([i]...)!;
    277 	} else if (ts == &tai) {
    278 		return alloc([time::add(i, -GPS_OFFSET)]...)!;
    279 	};
    280 };
    281 
    282 fn gps_convfrom(ts: *timescale, i: time::instant) ([]time::instant | void) = {
    283 	if (ts == &gps) {
    284 		return alloc([i]...)!;
    285 	} else if (ts == &tai) {
    286 		return alloc([time::add(i, GPS_OFFSET)]...)!;
    287 	};
    288 };
    289 
    290 
    291 // Terrestrial Time
    292 //
    293 // Used for astronomical timekeeping.
    294 // Based on TAI; constant +32.184 offset.
    295 // Continuous (no leap seconds).
    296 export const tt: timescale = timescale {
    297 	name = "Terrestrial Time",
    298 	abbr = "TT",
    299 	convto = &tt_convto,
    300 	convfrom = &tt_convfrom,
    301 };
    302 
    303 // The constant offset between TT (Terrestrial Time) and TAI (International
    304 // Atomic Time). Used by [[tt]].
    305 def TT_OFFSET: time::duration = 32184 * time::MILLISECOND; // 32.184 seconds
    306 
    307 fn tt_convto(ts: *timescale, i: time::instant) ([]time::instant | void) = {
    308 	if (ts == &tt) {
    309 		return alloc([i]...)!;
    310 	} else if (ts == &tai) {
    311 		return alloc([time::add(i, -TT_OFFSET)]...)!;
    312 	};
    313 };
    314 
    315 
    316 fn tt_convfrom(ts: *timescale, i: time::instant) ([]time::instant | void) = {
    317 	if (ts == &tt) {
    318 		return alloc([i]...)!;
    319 	} else if (ts == &tai) {
    320 		return alloc([time::add(i, TT_OFFSET)]...)!;
    321 	};
    322 };
    323 
    324 // Arthur David Olson had expressed support for Martian time in his timezone
    325 // database project <https://data.iana.org/time-zones/theory.html>:
    326 //
    327 // > The tz database does not currently support Mars time, but it is documented
    328 // > here in the hopes that support will be added eventually.
    329 
    330 // Coordinated Mars Time
    331 //
    332 // Used for timekeeping on Mars.
    333 // Based on TT; constant factor.
    334 // Continuous (no leap seconds).
    335 export const mtc: timescale = timescale {
    336 	name = "Coordinated Mars Time",
    337 	abbr = "MTC",
    338 	convto = &mtc_convto,
    339 	convfrom = &mtc_convfrom,
    340 };
    341 
    342 // Factor f, where Martian-time * f == Earth-time.
    343 def FACTOR_TERRESTRIAL_MARTIAN: f64 = 1.0274912517;
    344 
    345 // [[time::duration]] in Earth-time between the Unix epoch of 1970 Jan 1st
    346 // midnight, and the Earth-Mars convergence date of 2000 Jan 6th midnight.
    347 def DELTA_UNIXEPOCH_JANSIX: time::duration = 10962 * 24 * time::HOUR;
    348 
    349 // [[time::duration]] in Mars-time between the Mars Sol Date epoch corresponding
    350 // to the Gregorian Earth date 1873 Dec 29th, and the Earth-Mars convergence
    351 // date of 2000 Jan 6.
    352 def DELTA_MARSEPOCH_JANSIX: time::duration = 44796 * 24 * time::HOUR;
    353 
    354 // [[time::duration]] in Mars-time between the midnights of 2000 Jan 6th on
    355 // Earth and Mars. Earth's midnight occurred first.
    356 def DELTA_JANSIX_ADJUSTMENT: time::duration = 82944 * time::MILLISECOND;
    357 
    358 fn mtc_convto(ts: *timescale, i: time::instant) ([]time::instant | void) = {
    359 	let ret: []time::instant = [];
    360 	if (ts == &mtc) {
    361 		return alloc([i]...)!;
    362 	} else if (ts == &tai) {
    363 		// Change epoch from that of the Mars Sol Date
    364 		// to the Earth-Mars convergence date 2000 Jan 6th.
    365 		let i = time::add(i, -DELTA_MARSEPOCH_JANSIX);
    366 
    367 		// Slightly adjust epoch for the actual Martian midnight.
    368 		// Earth's midnight occurred before Mars'.
    369 		i = time::add(i, DELTA_JANSIX_ADJUSTMENT);
    370 
    371 		// Scale from Mars-time to Earth-time.
    372 		i = time::mult(i, FACTOR_TERRESTRIAL_MARTIAN);
    373 
    374 		// Change epoch to the Unix epoch 1970 Jan 1st (Terrestrial Time).
    375 		i = time::add(i, DELTA_UNIXEPOCH_JANSIX);
    376 
    377 		// Get the TAI time.
    378 		// assertion since TT and TAI are continuous.
    379 		return alloc([(tt.convto(&tai, i) as []time::instant)[0]]...)!;
    380 	};
    381 };
    382 
    383 fn mtc_convfrom(ts: *timescale, i: time::instant) ([]time::instant | void) = {
    384 	if (ts == &mtc) {
    385 		return alloc([i]...)!;
    386 	} else if (ts == &tai) {
    387 		// Get the "Terrestrial Time".
    388 		// assertion since TT and TAI are continuous.
    389 		let i = (tt.convfrom(&tai, i) as []time::instant)[0];
    390 
    391 		// Change epoch from the Unix epoch 1970 Jan 1st (Terrestrial Time)
    392 		// to the Earth-Mars convergence date 2000 Jan 6th midnight.
    393 		i = time::add(i, -DELTA_UNIXEPOCH_JANSIX);
    394 
    395 		// Scale from Earth-time to Mars-time.
    396 		i = time::mult(i, 1.0 / FACTOR_TERRESTRIAL_MARTIAN);
    397 
    398 		// Slightly adjust epoch for the actual Martian midnight.
    399 		// Earth's midnight occurred before Mars'.
    400 		i = time::add(i, -DELTA_JANSIX_ADJUSTMENT);
    401 
    402 		// Change epoch to that of the Mars Sol Date.
    403 		return alloc([time::add(i, DELTA_MARSEPOCH_JANSIX)]...)!;
    404 	};
    405 };
    406 
    407 
    408 @test fn utc_convto_tai() void = {
    409 	// TODO: skip test if no leapsec data available (!utc_isinitialized)
    410 	// TODO: test negative leapsecs somehow
    411 	let testcases: [](
    412 		(i64, i64), // give
    413 		(void | [0](i64, i64) | [1](i64, i64) | [2](i64, i64)) // expect
    414 	) = [
    415 		((-      1000i64,         0i64), [(-       990i64,         0i64)]),
    416 		((          0i64,         0i64), [(         10i64,         0i64)]),
    417 		((       1000i64,         0i64), [(       1010i64,         0i64)]),
    418 		// 1970 Jan 01
    419 		((   63071998i64,         0i64), [(   63072008i64,         0i64)]),
    420 		((   63071998i64, 500000000i64), [(   63072008i64, 500000000i64)]),
    421 		((   63071999i64,         0i64), [(   63072009i64,         0i64)]),
    422 		((   63071999i64, 500000000i64), [(   63072009i64, 500000000i64)]),
    423 		((   63072000i64,         0i64), [(   63072010i64,         0i64)]),
    424 		((   63072000i64, 500000000i64), [(   63072010i64, 500000000i64)]),
    425 		((   63072001i64,         0i64), [(   63072011i64,         0i64)]),
    426 		((   63072001i64, 500000000i64), [(   63072011i64, 500000000i64)]),
    427 		((   63072002i64,         0i64), [(   63072012i64,         0i64)]),
    428 		// 1981 Jul 01
    429 		((  362793598i64,         0i64), [(  362793617i64,         0i64)]),
    430 		((  362793598i64, 500000000i64), [(  362793617i64, 500000000i64)]),
    431 		((  362793599i64,         0i64), [
    432 			(  362793618i64,         0i64),
    433 			(  362793619i64,         0i64),
    434 		]),
    435 		((  362793599i64, 500000000i64), [
    436 			(  362793618i64, 500000000i64),
    437 			(  362793619i64, 500000000i64),
    438 		]),
    439 		((  362793600i64,         0i64), [(  362793620i64,         0i64)]),
    440 		((  362793600i64, 500000000i64), [(  362793620i64, 500000000i64)]),
    441 		((  362793601i64,         0i64), [(  362793621i64,         0i64)]),
    442 		((  362793601i64, 500000000i64), [(  362793621i64, 500000000i64)]),
    443 		((  362793602i64,         0i64), [(  362793622i64,         0i64)]),
    444 		// 2017 Jan 01
    445 		(( 1483228798i64,         0i64), [( 1483228834i64,         0i64)]),
    446 		(( 1483228798i64, 500000000i64), [( 1483228834i64, 500000000i64)]),
    447 		(( 1483228799i64,         0i64), [
    448 			( 1483228835i64,         0i64),
    449 			( 1483228836i64,         0i64),
    450 		]),
    451 		(( 1483228799i64, 500000000i64), [
    452 			( 1483228835i64, 500000000i64),
    453 			( 1483228836i64, 500000000i64),
    454 		]),
    455 		(( 1483228800i64,         0i64), [( 1483228837i64,         0i64)]),
    456 		(( 1483228800i64, 500000000i64), [( 1483228837i64, 500000000i64)]),
    457 		(( 1483228801i64,         0i64), [( 1483228838i64,         0i64)]),
    458 		(( 1483228801i64, 500000000i64), [( 1483228838i64, 500000000i64)]),
    459 		(( 1483228802i64,         0i64), [( 1483228839i64,         0i64)]),
    460 	];
    461 
    462 	for (let testcase .. testcases) {
    463 		let params = testcase.0;
    464 		let param = time::instant{ sec = params.0, nsec = params.1 };
    465 		let expect = testcase.1;
    466 		let actual = utc_convto(&tai, param);
    467 
    468 		match (expect) {
    469 		case void =>
    470 			assert(actual is void);
    471 
    472 		case [0](i64, i64) =>
    473 			assert(actual is []time::instant);
    474 			const actual = actual as []time::instant;
    475 			assert(len(actual) == 0);
    476 
    477 		case let insts: [1](i64, i64) =>
    478 			assert(actual is []time::instant);
    479 			const actual = actual as []time::instant;
    480 			assert(len(actual) == 1);
    481 			assert(0 == time::compare(
    482 				actual[0],
    483 				time::instant{
    484 					sec = insts[0].0,
    485 					nsec = insts[0].1,
    486 				},
    487 			));
    488 
    489 		case let insts: [2](i64, i64) =>
    490 			assert(actual is []time::instant);
    491 			const actual = actual as []time::instant;
    492 			assert(len(actual) == 2);
    493 			assert(0 == time::compare(
    494 				actual[0],
    495 				time::instant{
    496 					sec = insts[0].0,
    497 					nsec = insts[0].1,
    498 				},
    499 			));
    500 			assert(0 == time::compare(
    501 				actual[1],
    502 				time::instant{
    503 					sec = insts[1].0,
    504 					nsec = insts[1].1,
    505 				},
    506 			));
    507 		};
    508 		if (actual is []time::instant) {
    509 			free(actual as []time::instant);
    510 		};
    511 
    512 	};
    513 };
    514 
    515 @test fn utc_convfrom_tai() void = {
    516 	// TODO: skip test if no leapsec data available (!utc_isinitialized)
    517 	// TODO: test negative leapsecs somehow
    518 	let testcases: [](
    519 		(i64, i64), // give
    520 		(void | [0](i64, i64) | [1](i64, i64) | [2](i64, i64)) // expect
    521 	) = [
    522 		((-       990i64,         0i64), [(-      1000i64,         0i64)]),
    523 		((         10i64,         0i64), [(          0i64,         0i64)]),
    524 		((       1010i64,         0i64), [(       1000i64,         0i64)]),
    525 		// 1970 Jan 01
    526 		((   63072008i64,         0i64), [(   63071998i64,         0i64)]),
    527 		((   63072008i64, 500000000i64), [(   63071998i64, 500000000i64)]),
    528 		((   63072009i64,         0i64), [(   63071999i64,         0i64)]),
    529 		((   63072009i64, 500000000i64), [(   63071999i64, 500000000i64)]),
    530 		((   63072010i64,         0i64), [(   63072000i64,         0i64)]),
    531 		((   63072010i64, 500000000i64), [(   63072000i64, 500000000i64)]),
    532 		((   63072011i64,         0i64), [(   63072001i64,         0i64)]),
    533 		((   63072011i64, 500000000i64), [(   63072001i64, 500000000i64)]),
    534 		((   63072012i64,         0i64), [(   63072002i64,         0i64)]),
    535 		// 1981 Jul 01
    536 		((  362793617i64,         0i64), [(  362793598i64,         0i64)]),
    537 		((  362793617i64, 500000000i64), [(  362793598i64, 500000000i64)]),
    538 		((  362793618i64,         0i64), [(  362793599i64,         0i64)]),
    539 		((  362793618i64, 500000000i64), [(  362793599i64, 500000000i64)]),
    540 		((  362793619i64,         0i64), [(  362793599i64,         0i64)]),
    541 		((  362793619i64, 500000000i64), [(  362793599i64, 500000000i64)]),
    542 		((  362793620i64,         0i64), [(  362793600i64,         0i64)]),
    543 		((  362793620i64, 500000000i64), [(  362793600i64, 500000000i64)]),
    544 		((  362793621i64,         0i64), [(  362793601i64,         0i64)]),
    545 		((  362793621i64, 500000000i64), [(  362793601i64, 500000000i64)]),
    546 		((  362793622i64,         0i64), [(  362793602i64,         0i64)]),
    547 		// 2017 Jan 01
    548 		(( 1483228834i64,         0i64), [( 1483228798i64,         0i64)]),
    549 		(( 1483228834i64, 500000000i64), [( 1483228798i64, 500000000i64)]),
    550 		(( 1483228835i64,         0i64), [( 1483228799i64,         0i64)]),
    551 		(( 1483228835i64, 500000000i64), [( 1483228799i64, 500000000i64)]),
    552 		(( 1483228836i64,         0i64), [( 1483228799i64,         0i64)]),
    553 		(( 1483228836i64, 500000000i64), [( 1483228799i64, 500000000i64)]),
    554 		(( 1483228837i64,         0i64), [( 1483228800i64,         0i64)]),
    555 		(( 1483228837i64, 500000000i64), [( 1483228800i64, 500000000i64)]),
    556 		(( 1483228838i64,         0i64), [( 1483228801i64,         0i64)]),
    557 		(( 1483228838i64, 500000000i64), [( 1483228801i64, 500000000i64)]),
    558 		(( 1483228839i64,         0i64), [( 1483228802i64,         0i64)]),
    559 	];
    560 
    561 	for (let testcase .. testcases) {
    562 		let params = testcase.0;
    563 		let param = time::instant{ sec = params.0, nsec = params.1 };
    564 		let expect = testcase.1;
    565 		let actual = utc_convfrom(&tai, param);
    566 
    567 		match (expect) {
    568 		case void =>
    569 			assert(actual is void);
    570 
    571 		case [0](i64, i64) =>
    572 			assert(actual is []time::instant);
    573 			const actual = actual as []time::instant;
    574 			assert(len(actual) == 0);
    575 
    576 		case let insts: [1](i64, i64) =>
    577 			assert(actual is []time::instant);
    578 			const actual = actual as []time::instant;
    579 			assert(len(actual) == 1);
    580 			assert(0 == time::compare(
    581 				actual[0],
    582 				time::instant{
    583 					sec = insts[0].0,
    584 					nsec = insts[0].1,
    585 				},
    586 			));
    587 
    588 		case let insts: [2](i64, i64) =>
    589 			assert(actual is []time::instant);
    590 			const actual = actual as []time::instant;
    591 			assert(len(actual) == 2);
    592 			assert(0 == time::compare(
    593 				actual[0],
    594 				time::instant{
    595 					sec = insts[0].0,
    596 					nsec = insts[0].1,
    597 				},
    598 			));
    599 			assert(0 == time::compare(
    600 				actual[1],
    601 				time::instant{
    602 					sec = insts[1].0,
    603 					nsec = insts[1].1,
    604 				},
    605 			));
    606 		};
    607 		if (actual is []time::instant) {
    608 			free(actual as []time::instant);
    609 		};
    610 	};
    611 };