hare

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

leapsec.ha (3090B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use bufio;
      5 use encoding::utf8;
      6 use fs;
      7 use io;
      8 use os;
      9 use strconv;
     10 use strings;
     11 
     12 // Hare uses raw leap second information when dealing with the UTC and TAI
     13 // timescales. This information is source from a standard file installed at
     14 // /usr/share/zoneinfo/leap-seconds.list, which itself is fetched from and
     15 // periodically maintained at various observatories.
     16 //
     17 // https://data.iana.org/time-zones/code/leap-seconds.list
     18 // https://www.ietf.org/timezones/data/leap-seconds.list
     19 // ftp://ftp.nist.gov/pub/time/leap-seconds.list
     20 // ftp://ftp.boulder.nist.gov/pub/time/leap-seconds.list
     21 //
     22 // This is in contrast to previous systems which rely on TZif files, which are
     23 // installed typically at /usr/share/zoneinfo, as part of the "Olson" IANA
     24 // Timezone database. These files couple timezone and leap second data.
     25 //
     26 // Depending on a system's installation, leap second information may be
     27 // deliberately left out of the TZif files, or duplicated throughout. This
     28 // design also inhibits our ambitions for dealing with multiple, dynamic
     29 // timescales. Therefore, we have decided to take an alternative approach.
     30 
     31 // Error initializing the [[utc]] [[timescale]].
     32 type utciniterror = !(fs::error | io::error | utf8::invalid);
     33 
     34 // The number of seconds between the years 1900 and 1970.
     35 //
     36 // This number is hypothetical since timekeeping before atomic clocks was not
     37 // accurate enough to account for small changes in time.
     38 export def SECS_1900_1970: i64 = 2208988800;
     39 
     40 // UTC/TAI leap second data; UTC timestamps and their offsets from TAI.
     41 // Sourced from [[UTC_LEAPSECS_PATH]].
     42 let utc_leapsecs: [](i64, i64) = [];
     43 
     44 let utc_isinitialized: bool = false;
     45 
     46 @fini fn free_utc() void = {
     47 	free(utc_leapsecs);
     48 };
     49 
     50 fn init_utc_leapsecs() (void | utciniterror) = {
     51 	const file = os::open(UTC_LEAPSECS_PATH)?;
     52 	defer io::close(file)!;
     53 	parse_utc_leapsecs(file)?;
     54 };
     55 
     56 // Parse UTC/TAI leap second data from [[UTC_LEAPSECS_PATH]].
     57 // See file for format details.
     58 fn parse_utc_leapsecs(h: io::handle) (void | utf8::invalid | io::error) = {
     59 	for (let line => bufio::read_line(h)?) {
     60 		let line = strings::fromutf8(line)?;
     61 		defer free(line);
     62 
     63 		if (strings::hasprefix(line, '#')) {
     64 			continue;
     65 		};
     66 
     67 		const iter = strings::iter(line);
     68 		const secs = scan_number(&iter); scan_whitespace(&iter);
     69 		const diff = scan_number(&iter);
     70 		if (secs is void || diff is void) {
     71 			continue;
     72 		};
     73 
     74 		let secs = secs as i64 - SECS_1900_1970;
     75 		let diff = diff as i64;
     76 		append(utc_leapsecs, (secs, diff));
     77 	};
     78 };
     79 
     80 fn scan_number(iter: *strings::iterator) (i64 | void) = {
     81 	let begin = *iter;
     82 
     83 	for (let rn => strings::next(iter)) {
     84 		switch (rn) {
     85 		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' =>
     86 			continue;
     87 		case =>
     88 			strings::prev(iter);
     89 			break;
     90 		};
     91 	};
     92 
     93 	return strconv::stoi64(strings::slice(&begin, iter))!;
     94 };
     95 
     96 fn scan_whitespace(iter: *strings::iterator) void = {
     97 	for (let rn => strings::next(iter)) {
     98 		switch (rn) {
     99 		case ' ', '\t' =>
    100 			continue;
    101 		case =>
    102 			strings::prev(iter);
    103 			return;
    104 		};
    105 	};
    106 };