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 };