hare

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

leapsec.ha (2904B)


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