1*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 2*0fca6ea1SDimitry Andric // 3*0fca6ea1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0fca6ea1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0fca6ea1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0fca6ea1SDimitry Andric // 7*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 8*0fca6ea1SDimitry Andric 9*0fca6ea1SDimitry Andric // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html 10*0fca6ea1SDimitry Andric 11*0fca6ea1SDimitry Andric #include <algorithm> 12*0fca6ea1SDimitry Andric #include <chrono> 13*0fca6ea1SDimitry Andric #include <filesystem> 14*0fca6ea1SDimitry Andric #include <fstream> 15*0fca6ea1SDimitry Andric #include <stdexcept> 16*0fca6ea1SDimitry Andric #include <string> 17*0fca6ea1SDimitry Andric 18*0fca6ea1SDimitry Andric #include "include/tzdb/time_zone_private.h" 19*0fca6ea1SDimitry Andric #include "include/tzdb/types_private.h" 20*0fca6ea1SDimitry Andric #include "include/tzdb/tzdb_list_private.h" 21*0fca6ea1SDimitry Andric #include "include/tzdb/tzdb_private.h" 22*0fca6ea1SDimitry Andric 23*0fca6ea1SDimitry Andric // Contains a parser for the IANA time zone data files. 24*0fca6ea1SDimitry Andric // 25*0fca6ea1SDimitry Andric // These files can be found at https://data.iana.org/time-zones/ and are in the 26*0fca6ea1SDimitry Andric // public domain. Information regarding the input can be found at 27*0fca6ea1SDimitry Andric // https://data.iana.org/time-zones/tz-how-to.html and 28*0fca6ea1SDimitry Andric // https://man7.org/linux/man-pages/man8/zic.8.html. 29*0fca6ea1SDimitry Andric // 30*0fca6ea1SDimitry Andric // As indicated at https://howardhinnant.github.io/date/tz.html#Installation 31*0fca6ea1SDimitry Andric // For Windows another file seems to be required 32*0fca6ea1SDimitry Andric // https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml 33*0fca6ea1SDimitry Andric // This file seems to contain the mapping of Windows time zone name to IANA 34*0fca6ea1SDimitry Andric // time zone names. 35*0fca6ea1SDimitry Andric // 36*0fca6ea1SDimitry Andric // However this article mentions another way to do the mapping on Windows 37*0fca6ea1SDimitry Andric // https://devblogs.microsoft.com/oldnewthing/20210527-00/?p=105255 38*0fca6ea1SDimitry Andric // This requires Windows 10 Version 1903, which was released in May of 2019 39*0fca6ea1SDimitry Andric // and considered end of life in December 2020 40*0fca6ea1SDimitry Andric // https://learn.microsoft.com/en-us/lifecycle/announcements/windows-10-1903-end-of-servicing 41*0fca6ea1SDimitry Andric // 42*0fca6ea1SDimitry Andric // TODO TZDB Implement the Windows mapping in tzdb::current_zone 43*0fca6ea1SDimitry Andric 44*0fca6ea1SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD 45*0fca6ea1SDimitry Andric 46*0fca6ea1SDimitry Andric namespace chrono { 47*0fca6ea1SDimitry Andric 48*0fca6ea1SDimitry Andric // This function is weak so it can be overriden in the tests. The 49*0fca6ea1SDimitry Andric // declaration is in the test header test/support/test_tzdb.h 50*0fca6ea1SDimitry Andric _LIBCPP_WEAK string_view __libcpp_tzdb_directory() { 51*0fca6ea1SDimitry Andric #if defined(__linux__) 52*0fca6ea1SDimitry Andric return "/usr/share/zoneinfo/"; 53*0fca6ea1SDimitry Andric #else 54*0fca6ea1SDimitry Andric # error "unknown path to the IANA Time Zone Database" 55*0fca6ea1SDimitry Andric #endif 56*0fca6ea1SDimitry Andric } 57*0fca6ea1SDimitry Andric 58*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 59*0fca6ea1SDimitry Andric // Details 60*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 61*0fca6ea1SDimitry Andric 62*0fca6ea1SDimitry Andric [[nodiscard]] static bool __is_whitespace(int __c) { return __c == ' ' || __c == '\t'; } 63*0fca6ea1SDimitry Andric 64*0fca6ea1SDimitry Andric static void __skip_optional_whitespace(istream& __input) { 65*0fca6ea1SDimitry Andric while (chrono::__is_whitespace(__input.peek())) 66*0fca6ea1SDimitry Andric __input.get(); 67*0fca6ea1SDimitry Andric } 68*0fca6ea1SDimitry Andric 69*0fca6ea1SDimitry Andric static void __skip_mandatory_whitespace(istream& __input) { 70*0fca6ea1SDimitry Andric if (!chrono::__is_whitespace(__input.get())) 71*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb: expected whitespace"); 72*0fca6ea1SDimitry Andric 73*0fca6ea1SDimitry Andric chrono::__skip_optional_whitespace(__input); 74*0fca6ea1SDimitry Andric } 75*0fca6ea1SDimitry Andric 76*0fca6ea1SDimitry Andric [[nodiscard]] static bool __is_eol(int __c) { return __c == '\n' || __c == std::char_traits<char>::eof(); } 77*0fca6ea1SDimitry Andric 78*0fca6ea1SDimitry Andric static void __skip_line(istream& __input) { 79*0fca6ea1SDimitry Andric while (!chrono::__is_eol(__input.peek())) { 80*0fca6ea1SDimitry Andric __input.get(); 81*0fca6ea1SDimitry Andric } 82*0fca6ea1SDimitry Andric __input.get(); 83*0fca6ea1SDimitry Andric } 84*0fca6ea1SDimitry Andric 85*0fca6ea1SDimitry Andric static void __skip(istream& __input, char __suffix) { 86*0fca6ea1SDimitry Andric if (std::tolower(__input.peek()) == __suffix) 87*0fca6ea1SDimitry Andric __input.get(); 88*0fca6ea1SDimitry Andric } 89*0fca6ea1SDimitry Andric 90*0fca6ea1SDimitry Andric static void __skip(istream& __input, string_view __suffix) { 91*0fca6ea1SDimitry Andric for (auto __c : __suffix) 92*0fca6ea1SDimitry Andric if (std::tolower(__input.peek()) == __c) 93*0fca6ea1SDimitry Andric __input.get(); 94*0fca6ea1SDimitry Andric } 95*0fca6ea1SDimitry Andric 96*0fca6ea1SDimitry Andric static void __matches(istream& __input, char __expected) { 97*0fca6ea1SDimitry Andric if (std::tolower(__input.get()) != __expected) 98*0fca6ea1SDimitry Andric std::__throw_runtime_error((string("corrupt tzdb: expected character '") + __expected + '\'').c_str()); 99*0fca6ea1SDimitry Andric } 100*0fca6ea1SDimitry Andric 101*0fca6ea1SDimitry Andric static void __matches(istream& __input, string_view __expected) { 102*0fca6ea1SDimitry Andric for (auto __c : __expected) 103*0fca6ea1SDimitry Andric if (std::tolower(__input.get()) != __c) 104*0fca6ea1SDimitry Andric std::__throw_runtime_error((string("corrupt tzdb: expected string '") + string(__expected) + '\'').c_str()); 105*0fca6ea1SDimitry Andric } 106*0fca6ea1SDimitry Andric 107*0fca6ea1SDimitry Andric [[nodiscard]] static string __parse_string(istream& __input) { 108*0fca6ea1SDimitry Andric string __result; 109*0fca6ea1SDimitry Andric while (true) { 110*0fca6ea1SDimitry Andric int __c = __input.get(); 111*0fca6ea1SDimitry Andric switch (__c) { 112*0fca6ea1SDimitry Andric case ' ': 113*0fca6ea1SDimitry Andric case '\t': 114*0fca6ea1SDimitry Andric case '\n': 115*0fca6ea1SDimitry Andric __input.unget(); 116*0fca6ea1SDimitry Andric [[fallthrough]]; 117*0fca6ea1SDimitry Andric case istream::traits_type::eof(): 118*0fca6ea1SDimitry Andric if (__result.empty()) 119*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb: expected a string"); 120*0fca6ea1SDimitry Andric 121*0fca6ea1SDimitry Andric return __result; 122*0fca6ea1SDimitry Andric 123*0fca6ea1SDimitry Andric default: 124*0fca6ea1SDimitry Andric __result.push_back(__c); 125*0fca6ea1SDimitry Andric } 126*0fca6ea1SDimitry Andric } 127*0fca6ea1SDimitry Andric } 128*0fca6ea1SDimitry Andric 129*0fca6ea1SDimitry Andric [[nodiscard]] static int64_t __parse_integral(istream& __input, bool __leading_zero_allowed) { 130*0fca6ea1SDimitry Andric int64_t __result = __input.get(); 131*0fca6ea1SDimitry Andric if (__leading_zero_allowed) { 132*0fca6ea1SDimitry Andric if (__result < '0' || __result > '9') 133*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb: expected a digit"); 134*0fca6ea1SDimitry Andric } else { 135*0fca6ea1SDimitry Andric if (__result < '1' || __result > '9') 136*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb: expected a non-zero digit"); 137*0fca6ea1SDimitry Andric } 138*0fca6ea1SDimitry Andric __result -= '0'; 139*0fca6ea1SDimitry Andric while (true) { 140*0fca6ea1SDimitry Andric if (__input.peek() < '0' || __input.peek() > '9') 141*0fca6ea1SDimitry Andric return __result; 142*0fca6ea1SDimitry Andric 143*0fca6ea1SDimitry Andric // In order to avoid possible overflows we limit the accepted range. 144*0fca6ea1SDimitry Andric // Most values parsed are expected to be very small: 145*0fca6ea1SDimitry Andric // - 8784 hours in a year 146*0fca6ea1SDimitry Andric // - 31 days in a month 147*0fca6ea1SDimitry Andric // - year no real maximum, these values are expected to be less than 148*0fca6ea1SDimitry Andric // the range of the year type. 149*0fca6ea1SDimitry Andric // 150*0fca6ea1SDimitry Andric // However the leapseconds use a seconds after epoch value. Using an 151*0fca6ea1SDimitry Andric // int would run into an overflow in 2038. By using a 64-bit value 152*0fca6ea1SDimitry Andric // the range is large enough for the bilions of years. Limiting that 153*0fca6ea1SDimitry Andric // range slightly to make the code easier is not an issue. 154*0fca6ea1SDimitry Andric if (__result > (std::numeric_limits<int64_t>::max() / 16)) 155*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb: integral too large"); 156*0fca6ea1SDimitry Andric 157*0fca6ea1SDimitry Andric __result *= 10; 158*0fca6ea1SDimitry Andric __result += __input.get() - '0'; 159*0fca6ea1SDimitry Andric } 160*0fca6ea1SDimitry Andric } 161*0fca6ea1SDimitry Andric 162*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 163*0fca6ea1SDimitry Andric // Calendar 164*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 165*0fca6ea1SDimitry Andric 166*0fca6ea1SDimitry Andric [[nodiscard]] static day __parse_day(istream& __input) { 167*0fca6ea1SDimitry Andric unsigned __result = chrono::__parse_integral(__input, false); 168*0fca6ea1SDimitry Andric if (__result > 31) 169*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb day: value too large"); 170*0fca6ea1SDimitry Andric return day{__result}; 171*0fca6ea1SDimitry Andric } 172*0fca6ea1SDimitry Andric 173*0fca6ea1SDimitry Andric [[nodiscard]] static weekday __parse_weekday(istream& __input) { 174*0fca6ea1SDimitry Andric // TZDB allows the shortest unique name. 175*0fca6ea1SDimitry Andric switch (std::tolower(__input.get())) { 176*0fca6ea1SDimitry Andric case 'f': 177*0fca6ea1SDimitry Andric chrono::__skip(__input, "riday"); 178*0fca6ea1SDimitry Andric return Friday; 179*0fca6ea1SDimitry Andric 180*0fca6ea1SDimitry Andric case 'm': 181*0fca6ea1SDimitry Andric chrono::__skip(__input, "onday"); 182*0fca6ea1SDimitry Andric return Monday; 183*0fca6ea1SDimitry Andric 184*0fca6ea1SDimitry Andric case 's': 185*0fca6ea1SDimitry Andric switch (std::tolower(__input.get())) { 186*0fca6ea1SDimitry Andric case 'a': 187*0fca6ea1SDimitry Andric chrono::__skip(__input, "turday"); 188*0fca6ea1SDimitry Andric return Saturday; 189*0fca6ea1SDimitry Andric 190*0fca6ea1SDimitry Andric case 'u': 191*0fca6ea1SDimitry Andric chrono::__skip(__input, "nday"); 192*0fca6ea1SDimitry Andric return Sunday; 193*0fca6ea1SDimitry Andric } 194*0fca6ea1SDimitry Andric break; 195*0fca6ea1SDimitry Andric 196*0fca6ea1SDimitry Andric case 't': 197*0fca6ea1SDimitry Andric switch (std::tolower(__input.get())) { 198*0fca6ea1SDimitry Andric case 'h': 199*0fca6ea1SDimitry Andric chrono::__skip(__input, "ursday"); 200*0fca6ea1SDimitry Andric return Thursday; 201*0fca6ea1SDimitry Andric 202*0fca6ea1SDimitry Andric case 'u': 203*0fca6ea1SDimitry Andric chrono::__skip(__input, "esday"); 204*0fca6ea1SDimitry Andric return Tuesday; 205*0fca6ea1SDimitry Andric } 206*0fca6ea1SDimitry Andric break; 207*0fca6ea1SDimitry Andric case 'w': 208*0fca6ea1SDimitry Andric chrono::__skip(__input, "ednesday"); 209*0fca6ea1SDimitry Andric return Wednesday; 210*0fca6ea1SDimitry Andric } 211*0fca6ea1SDimitry Andric 212*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb weekday: invalid name"); 213*0fca6ea1SDimitry Andric } 214*0fca6ea1SDimitry Andric 215*0fca6ea1SDimitry Andric [[nodiscard]] static month __parse_month(istream& __input) { 216*0fca6ea1SDimitry Andric // TZDB allows the shortest unique name. 217*0fca6ea1SDimitry Andric switch (std::tolower(__input.get())) { 218*0fca6ea1SDimitry Andric case 'a': 219*0fca6ea1SDimitry Andric switch (std::tolower(__input.get())) { 220*0fca6ea1SDimitry Andric case 'p': 221*0fca6ea1SDimitry Andric chrono::__skip(__input, "ril"); 222*0fca6ea1SDimitry Andric return April; 223*0fca6ea1SDimitry Andric 224*0fca6ea1SDimitry Andric case 'u': 225*0fca6ea1SDimitry Andric chrono::__skip(__input, "gust"); 226*0fca6ea1SDimitry Andric return August; 227*0fca6ea1SDimitry Andric } 228*0fca6ea1SDimitry Andric break; 229*0fca6ea1SDimitry Andric 230*0fca6ea1SDimitry Andric case 'd': 231*0fca6ea1SDimitry Andric chrono::__skip(__input, "ecember"); 232*0fca6ea1SDimitry Andric return December; 233*0fca6ea1SDimitry Andric 234*0fca6ea1SDimitry Andric case 'f': 235*0fca6ea1SDimitry Andric chrono::__skip(__input, "ebruary"); 236*0fca6ea1SDimitry Andric return February; 237*0fca6ea1SDimitry Andric 238*0fca6ea1SDimitry Andric case 'j': 239*0fca6ea1SDimitry Andric switch (std::tolower(__input.get())) { 240*0fca6ea1SDimitry Andric case 'a': 241*0fca6ea1SDimitry Andric chrono::__skip(__input, "nuary"); 242*0fca6ea1SDimitry Andric return January; 243*0fca6ea1SDimitry Andric 244*0fca6ea1SDimitry Andric case 'u': 245*0fca6ea1SDimitry Andric switch (std::tolower(__input.get())) { 246*0fca6ea1SDimitry Andric case 'n': 247*0fca6ea1SDimitry Andric chrono::__skip(__input, 'e'); 248*0fca6ea1SDimitry Andric return June; 249*0fca6ea1SDimitry Andric 250*0fca6ea1SDimitry Andric case 'l': 251*0fca6ea1SDimitry Andric chrono::__skip(__input, 'y'); 252*0fca6ea1SDimitry Andric return July; 253*0fca6ea1SDimitry Andric } 254*0fca6ea1SDimitry Andric } 255*0fca6ea1SDimitry Andric break; 256*0fca6ea1SDimitry Andric 257*0fca6ea1SDimitry Andric case 'm': 258*0fca6ea1SDimitry Andric if (std::tolower(__input.get()) == 'a') 259*0fca6ea1SDimitry Andric switch (std::tolower(__input.get())) { 260*0fca6ea1SDimitry Andric case 'y': 261*0fca6ea1SDimitry Andric return May; 262*0fca6ea1SDimitry Andric 263*0fca6ea1SDimitry Andric case 'r': 264*0fca6ea1SDimitry Andric chrono::__skip(__input, "ch"); 265*0fca6ea1SDimitry Andric return March; 266*0fca6ea1SDimitry Andric } 267*0fca6ea1SDimitry Andric break; 268*0fca6ea1SDimitry Andric 269*0fca6ea1SDimitry Andric case 'n': 270*0fca6ea1SDimitry Andric chrono::__skip(__input, "ovember"); 271*0fca6ea1SDimitry Andric return November; 272*0fca6ea1SDimitry Andric 273*0fca6ea1SDimitry Andric case 'o': 274*0fca6ea1SDimitry Andric chrono::__skip(__input, "ctober"); 275*0fca6ea1SDimitry Andric return October; 276*0fca6ea1SDimitry Andric 277*0fca6ea1SDimitry Andric case 's': 278*0fca6ea1SDimitry Andric chrono::__skip(__input, "eptember"); 279*0fca6ea1SDimitry Andric return September; 280*0fca6ea1SDimitry Andric } 281*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb month: invalid name"); 282*0fca6ea1SDimitry Andric } 283*0fca6ea1SDimitry Andric 284*0fca6ea1SDimitry Andric [[nodiscard]] static year __parse_year_value(istream& __input) { 285*0fca6ea1SDimitry Andric bool __negative = __input.peek() == '-'; 286*0fca6ea1SDimitry Andric if (__negative) [[unlikely]] 287*0fca6ea1SDimitry Andric __input.get(); 288*0fca6ea1SDimitry Andric 289*0fca6ea1SDimitry Andric int64_t __result = __parse_integral(__input, true); 290*0fca6ea1SDimitry Andric if (__result > static_cast<int>(year::max())) { 291*0fca6ea1SDimitry Andric if (__negative) 292*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb year: year is less than the minimum"); 293*0fca6ea1SDimitry Andric 294*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb year: year is greater than the maximum"); 295*0fca6ea1SDimitry Andric } 296*0fca6ea1SDimitry Andric 297*0fca6ea1SDimitry Andric return year{static_cast<int>(__negative ? -__result : __result)}; 298*0fca6ea1SDimitry Andric } 299*0fca6ea1SDimitry Andric 300*0fca6ea1SDimitry Andric [[nodiscard]] static year __parse_year(istream& __input) { 301*0fca6ea1SDimitry Andric if (std::tolower(__input.peek()) != 'm') [[likely]] 302*0fca6ea1SDimitry Andric return chrono::__parse_year_value(__input); 303*0fca6ea1SDimitry Andric 304*0fca6ea1SDimitry Andric __input.get(); 305*0fca6ea1SDimitry Andric switch (std::tolower(__input.peek())) { 306*0fca6ea1SDimitry Andric case 'i': 307*0fca6ea1SDimitry Andric __input.get(); 308*0fca6ea1SDimitry Andric chrono::__skip(__input, 'n'); 309*0fca6ea1SDimitry Andric [[fallthrough]]; 310*0fca6ea1SDimitry Andric 311*0fca6ea1SDimitry Andric case ' ': 312*0fca6ea1SDimitry Andric // The m is minimum, even when that is ambiguous. 313*0fca6ea1SDimitry Andric return year::min(); 314*0fca6ea1SDimitry Andric 315*0fca6ea1SDimitry Andric case 'a': 316*0fca6ea1SDimitry Andric __input.get(); 317*0fca6ea1SDimitry Andric chrono::__skip(__input, 'x'); 318*0fca6ea1SDimitry Andric return year::max(); 319*0fca6ea1SDimitry Andric } 320*0fca6ea1SDimitry Andric 321*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb year: expected 'min' or 'max'"); 322*0fca6ea1SDimitry Andric } 323*0fca6ea1SDimitry Andric 324*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 325*0fca6ea1SDimitry Andric // TZDB fields 326*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 327*0fca6ea1SDimitry Andric 328*0fca6ea1SDimitry Andric [[nodiscard]] static year __parse_to(istream& __input, year __only) { 329*0fca6ea1SDimitry Andric if (std::tolower(__input.peek()) != 'o') 330*0fca6ea1SDimitry Andric return chrono::__parse_year(__input); 331*0fca6ea1SDimitry Andric 332*0fca6ea1SDimitry Andric __input.get(); 333*0fca6ea1SDimitry Andric chrono::__skip(__input, "nly"); 334*0fca6ea1SDimitry Andric return __only; 335*0fca6ea1SDimitry Andric } 336*0fca6ea1SDimitry Andric 337*0fca6ea1SDimitry Andric [[nodiscard]] static __tz::__constrained_weekday::__comparison_t __parse_comparison(istream& __input) { 338*0fca6ea1SDimitry Andric switch (__input.get()) { 339*0fca6ea1SDimitry Andric case '>': 340*0fca6ea1SDimitry Andric chrono::__matches(__input, '='); 341*0fca6ea1SDimitry Andric return __tz::__constrained_weekday::__ge; 342*0fca6ea1SDimitry Andric 343*0fca6ea1SDimitry Andric case '<': 344*0fca6ea1SDimitry Andric chrono::__matches(__input, '='); 345*0fca6ea1SDimitry Andric return __tz::__constrained_weekday::__le; 346*0fca6ea1SDimitry Andric } 347*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb on: expected '>=' or '<='"); 348*0fca6ea1SDimitry Andric } 349*0fca6ea1SDimitry Andric 350*0fca6ea1SDimitry Andric [[nodiscard]] static __tz::__on __parse_on(istream& __input) { 351*0fca6ea1SDimitry Andric if (std::isdigit(__input.peek())) 352*0fca6ea1SDimitry Andric return chrono::__parse_day(__input); 353*0fca6ea1SDimitry Andric 354*0fca6ea1SDimitry Andric if (std::tolower(__input.peek()) == 'l') { 355*0fca6ea1SDimitry Andric chrono::__matches(__input, "last"); 356*0fca6ea1SDimitry Andric return weekday_last(chrono::__parse_weekday(__input)); 357*0fca6ea1SDimitry Andric } 358*0fca6ea1SDimitry Andric 359*0fca6ea1SDimitry Andric return __tz::__constrained_weekday{ 360*0fca6ea1SDimitry Andric chrono::__parse_weekday(__input), chrono::__parse_comparison(__input), chrono::__parse_day(__input)}; 361*0fca6ea1SDimitry Andric } 362*0fca6ea1SDimitry Andric 363*0fca6ea1SDimitry Andric [[nodiscard]] static seconds __parse_duration(istream& __input) { 364*0fca6ea1SDimitry Andric seconds __result{0}; 365*0fca6ea1SDimitry Andric int __c = __input.peek(); 366*0fca6ea1SDimitry Andric bool __negative = __c == '-'; 367*0fca6ea1SDimitry Andric if (__negative) { 368*0fca6ea1SDimitry Andric __input.get(); 369*0fca6ea1SDimitry Andric // Negative is either a negative value or a single -. 370*0fca6ea1SDimitry Andric // The latter means 0 and the parsing is complete. 371*0fca6ea1SDimitry Andric if (!std::isdigit(__input.peek())) 372*0fca6ea1SDimitry Andric return __result; 373*0fca6ea1SDimitry Andric } 374*0fca6ea1SDimitry Andric 375*0fca6ea1SDimitry Andric __result += hours(__parse_integral(__input, true)); 376*0fca6ea1SDimitry Andric if (__input.peek() != ':') 377*0fca6ea1SDimitry Andric return __negative ? -__result : __result; 378*0fca6ea1SDimitry Andric 379*0fca6ea1SDimitry Andric __input.get(); 380*0fca6ea1SDimitry Andric __result += minutes(__parse_integral(__input, true)); 381*0fca6ea1SDimitry Andric if (__input.peek() != ':') 382*0fca6ea1SDimitry Andric return __negative ? -__result : __result; 383*0fca6ea1SDimitry Andric 384*0fca6ea1SDimitry Andric __input.get(); 385*0fca6ea1SDimitry Andric __result += seconds(__parse_integral(__input, true)); 386*0fca6ea1SDimitry Andric if (__input.peek() != '.') 387*0fca6ea1SDimitry Andric return __negative ? -__result : __result; 388*0fca6ea1SDimitry Andric 389*0fca6ea1SDimitry Andric __input.get(); 390*0fca6ea1SDimitry Andric (void)__parse_integral(__input, true); // Truncate the digits. 391*0fca6ea1SDimitry Andric 392*0fca6ea1SDimitry Andric return __negative ? -__result : __result; 393*0fca6ea1SDimitry Andric } 394*0fca6ea1SDimitry Andric 395*0fca6ea1SDimitry Andric [[nodiscard]] static __tz::__clock __parse_clock(istream& __input) { 396*0fca6ea1SDimitry Andric switch (__input.get()) { // case sensitive 397*0fca6ea1SDimitry Andric case 'w': 398*0fca6ea1SDimitry Andric return __tz::__clock::__local; 399*0fca6ea1SDimitry Andric case 's': 400*0fca6ea1SDimitry Andric return __tz::__clock::__standard; 401*0fca6ea1SDimitry Andric 402*0fca6ea1SDimitry Andric case 'u': 403*0fca6ea1SDimitry Andric case 'g': 404*0fca6ea1SDimitry Andric case 'z': 405*0fca6ea1SDimitry Andric return __tz::__clock::__universal; 406*0fca6ea1SDimitry Andric } 407*0fca6ea1SDimitry Andric 408*0fca6ea1SDimitry Andric __input.unget(); 409*0fca6ea1SDimitry Andric return __tz::__clock::__local; 410*0fca6ea1SDimitry Andric } 411*0fca6ea1SDimitry Andric 412*0fca6ea1SDimitry Andric [[nodiscard]] static bool __parse_dst(istream& __input, seconds __offset) { 413*0fca6ea1SDimitry Andric switch (__input.get()) { // case sensitive 414*0fca6ea1SDimitry Andric case 's': 415*0fca6ea1SDimitry Andric return false; 416*0fca6ea1SDimitry Andric 417*0fca6ea1SDimitry Andric case 'd': 418*0fca6ea1SDimitry Andric return true; 419*0fca6ea1SDimitry Andric } 420*0fca6ea1SDimitry Andric 421*0fca6ea1SDimitry Andric __input.unget(); 422*0fca6ea1SDimitry Andric return __offset != 0s; 423*0fca6ea1SDimitry Andric } 424*0fca6ea1SDimitry Andric 425*0fca6ea1SDimitry Andric [[nodiscard]] static __tz::__at __parse_at(istream& __input) { 426*0fca6ea1SDimitry Andric return {__parse_duration(__input), __parse_clock(__input)}; 427*0fca6ea1SDimitry Andric } 428*0fca6ea1SDimitry Andric 429*0fca6ea1SDimitry Andric [[nodiscard]] static __tz::__save __parse_save(istream& __input) { 430*0fca6ea1SDimitry Andric seconds __time = chrono::__parse_duration(__input); 431*0fca6ea1SDimitry Andric return {__time, chrono::__parse_dst(__input, __time)}; 432*0fca6ea1SDimitry Andric } 433*0fca6ea1SDimitry Andric 434*0fca6ea1SDimitry Andric [[nodiscard]] static string __parse_letters(istream& __input) { 435*0fca6ea1SDimitry Andric string __result = __parse_string(__input); 436*0fca6ea1SDimitry Andric // Canonicalize "-" to "" since they are equivalent in the specification. 437*0fca6ea1SDimitry Andric return __result != "-" ? __result : ""; 438*0fca6ea1SDimitry Andric } 439*0fca6ea1SDimitry Andric 440*0fca6ea1SDimitry Andric [[nodiscard]] static __tz::__continuation::__rules_t __parse_rules(istream& __input) { 441*0fca6ea1SDimitry Andric int __c = __input.peek(); 442*0fca6ea1SDimitry Andric // A single - is not a SAVE but a special case. 443*0fca6ea1SDimitry Andric if (__c == '-') { 444*0fca6ea1SDimitry Andric __input.get(); 445*0fca6ea1SDimitry Andric if (chrono::__is_whitespace(__input.peek())) 446*0fca6ea1SDimitry Andric return monostate{}; 447*0fca6ea1SDimitry Andric __input.unget(); 448*0fca6ea1SDimitry Andric return chrono::__parse_save(__input); 449*0fca6ea1SDimitry Andric } 450*0fca6ea1SDimitry Andric 451*0fca6ea1SDimitry Andric if (std::isdigit(__c) || __c == '+') 452*0fca6ea1SDimitry Andric return chrono::__parse_save(__input); 453*0fca6ea1SDimitry Andric 454*0fca6ea1SDimitry Andric return chrono::__parse_string(__input); 455*0fca6ea1SDimitry Andric } 456*0fca6ea1SDimitry Andric 457*0fca6ea1SDimitry Andric [[nodiscard]] static __tz::__continuation __parse_continuation(__tz::__rules_storage_type& __rules, istream& __input) { 458*0fca6ea1SDimitry Andric __tz::__continuation __result; 459*0fca6ea1SDimitry Andric 460*0fca6ea1SDimitry Andric __result.__rule_database_ = std::addressof(__rules); 461*0fca6ea1SDimitry Andric 462*0fca6ea1SDimitry Andric // Note STDOFF is specified as 463*0fca6ea1SDimitry Andric // This field has the same format as the AT and SAVE fields of rule lines; 464*0fca6ea1SDimitry Andric // These fields have different suffix letters, these letters seem 465*0fca6ea1SDimitry Andric // not to be used so do not allow any of them. 466*0fca6ea1SDimitry Andric 467*0fca6ea1SDimitry Andric __result.__stdoff = chrono::__parse_duration(__input); 468*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 469*0fca6ea1SDimitry Andric __result.__rules = chrono::__parse_rules(__input); 470*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 471*0fca6ea1SDimitry Andric __result.__format = chrono::__parse_string(__input); 472*0fca6ea1SDimitry Andric chrono::__skip_optional_whitespace(__input); 473*0fca6ea1SDimitry Andric 474*0fca6ea1SDimitry Andric if (chrono::__is_eol(__input.peek())) 475*0fca6ea1SDimitry Andric return __result; 476*0fca6ea1SDimitry Andric __result.__year = chrono::__parse_year(__input); 477*0fca6ea1SDimitry Andric chrono::__skip_optional_whitespace(__input); 478*0fca6ea1SDimitry Andric 479*0fca6ea1SDimitry Andric if (chrono::__is_eol(__input.peek())) 480*0fca6ea1SDimitry Andric return __result; 481*0fca6ea1SDimitry Andric __result.__in = chrono::__parse_month(__input); 482*0fca6ea1SDimitry Andric chrono::__skip_optional_whitespace(__input); 483*0fca6ea1SDimitry Andric 484*0fca6ea1SDimitry Andric if (chrono::__is_eol(__input.peek())) 485*0fca6ea1SDimitry Andric return __result; 486*0fca6ea1SDimitry Andric __result.__on = chrono::__parse_on(__input); 487*0fca6ea1SDimitry Andric chrono::__skip_optional_whitespace(__input); 488*0fca6ea1SDimitry Andric 489*0fca6ea1SDimitry Andric if (chrono::__is_eol(__input.peek())) 490*0fca6ea1SDimitry Andric return __result; 491*0fca6ea1SDimitry Andric __result.__at = __parse_at(__input); 492*0fca6ea1SDimitry Andric 493*0fca6ea1SDimitry Andric return __result; 494*0fca6ea1SDimitry Andric } 495*0fca6ea1SDimitry Andric 496*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 497*0fca6ea1SDimitry Andric // Time Zone Database entries 498*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 499*0fca6ea1SDimitry Andric 500*0fca6ea1SDimitry Andric static string __parse_version(istream& __input) { 501*0fca6ea1SDimitry Andric // The first line in tzdata.zi contains 502*0fca6ea1SDimitry Andric // # version YYYYw 503*0fca6ea1SDimitry Andric // The parser expects this pattern 504*0fca6ea1SDimitry Andric // #\s*version\s*\(.*) 505*0fca6ea1SDimitry Andric // This part is not documented. 506*0fca6ea1SDimitry Andric chrono::__matches(__input, '#'); 507*0fca6ea1SDimitry Andric chrono::__skip_optional_whitespace(__input); 508*0fca6ea1SDimitry Andric chrono::__matches(__input, "version"); 509*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 510*0fca6ea1SDimitry Andric return chrono::__parse_string(__input); 511*0fca6ea1SDimitry Andric } 512*0fca6ea1SDimitry Andric 513*0fca6ea1SDimitry Andric [[nodiscard]] 514*0fca6ea1SDimitry Andric static __tz::__rule& __create_entry(__tz::__rules_storage_type& __rules, const string& __name) { 515*0fca6ea1SDimitry Andric auto __result = [&]() -> __tz::__rule& { 516*0fca6ea1SDimitry Andric auto& __rule = __rules.emplace_back(__name, vector<__tz::__rule>{}); 517*0fca6ea1SDimitry Andric return __rule.second.emplace_back(); 518*0fca6ea1SDimitry Andric }; 519*0fca6ea1SDimitry Andric 520*0fca6ea1SDimitry Andric if (__rules.empty()) 521*0fca6ea1SDimitry Andric return __result(); 522*0fca6ea1SDimitry Andric 523*0fca6ea1SDimitry Andric // Typically rules are in contiguous order in the database. 524*0fca6ea1SDimitry Andric // But there are exceptions, some rules are interleaved. 525*0fca6ea1SDimitry Andric if (__rules.back().first == __name) 526*0fca6ea1SDimitry Andric return __rules.back().second.emplace_back(); 527*0fca6ea1SDimitry Andric 528*0fca6ea1SDimitry Andric if (auto __it = ranges::find(__rules, __name, [](const auto& __r) { return __r.first; }); 529*0fca6ea1SDimitry Andric __it != ranges::end(__rules)) 530*0fca6ea1SDimitry Andric return __it->second.emplace_back(); 531*0fca6ea1SDimitry Andric 532*0fca6ea1SDimitry Andric return __result(); 533*0fca6ea1SDimitry Andric } 534*0fca6ea1SDimitry Andric 535*0fca6ea1SDimitry Andric static void __parse_rule(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) { 536*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 537*0fca6ea1SDimitry Andric string __name = chrono::__parse_string(__input); 538*0fca6ea1SDimitry Andric 539*0fca6ea1SDimitry Andric __tz::__rule& __rule = __create_entry(__rules, __name); 540*0fca6ea1SDimitry Andric 541*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 542*0fca6ea1SDimitry Andric __rule.__from = chrono::__parse_year(__input); 543*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 544*0fca6ea1SDimitry Andric __rule.__to = chrono::__parse_to(__input, __rule.__from); 545*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 546*0fca6ea1SDimitry Andric chrono::__matches(__input, '-'); 547*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 548*0fca6ea1SDimitry Andric __rule.__in = chrono::__parse_month(__input); 549*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 550*0fca6ea1SDimitry Andric __rule.__on = chrono::__parse_on(__input); 551*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 552*0fca6ea1SDimitry Andric __rule.__at = __parse_at(__input); 553*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 554*0fca6ea1SDimitry Andric __rule.__save = __parse_save(__input); 555*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 556*0fca6ea1SDimitry Andric __rule.__letters = chrono::__parse_letters(__input); 557*0fca6ea1SDimitry Andric chrono::__skip_line(__input); 558*0fca6ea1SDimitry Andric } 559*0fca6ea1SDimitry Andric 560*0fca6ea1SDimitry Andric static void __parse_zone(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) { 561*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 562*0fca6ea1SDimitry Andric auto __p = std::make_unique<time_zone::__impl>(chrono::__parse_string(__input), __rules); 563*0fca6ea1SDimitry Andric vector<__tz::__continuation>& __continuations = __p->__continuations(); 564*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 565*0fca6ea1SDimitry Andric 566*0fca6ea1SDimitry Andric do { 567*0fca6ea1SDimitry Andric // The first line must be valid, continuations are optional. 568*0fca6ea1SDimitry Andric __continuations.emplace_back(__parse_continuation(__rules, __input)); 569*0fca6ea1SDimitry Andric chrono::__skip_line(__input); 570*0fca6ea1SDimitry Andric chrono::__skip_optional_whitespace(__input); 571*0fca6ea1SDimitry Andric } while (std::isdigit(__input.peek()) || __input.peek() == '-'); 572*0fca6ea1SDimitry Andric 573*0fca6ea1SDimitry Andric __tzdb.zones.emplace_back(time_zone::__create(std::move(__p))); 574*0fca6ea1SDimitry Andric } 575*0fca6ea1SDimitry Andric 576*0fca6ea1SDimitry Andric static void __parse_link(tzdb& __tzdb, istream& __input) { 577*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 578*0fca6ea1SDimitry Andric string __target = chrono::__parse_string(__input); 579*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 580*0fca6ea1SDimitry Andric string __name = chrono::__parse_string(__input); 581*0fca6ea1SDimitry Andric chrono::__skip_line(__input); 582*0fca6ea1SDimitry Andric 583*0fca6ea1SDimitry Andric __tzdb.links.emplace_back(std::__private_constructor_tag{}, std::move(__name), std::move(__target)); 584*0fca6ea1SDimitry Andric } 585*0fca6ea1SDimitry Andric 586*0fca6ea1SDimitry Andric static void __parse_tzdata(tzdb& __db, __tz::__rules_storage_type& __rules, istream& __input) { 587*0fca6ea1SDimitry Andric while (true) { 588*0fca6ea1SDimitry Andric int __c = std::tolower(__input.get()); 589*0fca6ea1SDimitry Andric 590*0fca6ea1SDimitry Andric switch (__c) { 591*0fca6ea1SDimitry Andric case istream::traits_type::eof(): 592*0fca6ea1SDimitry Andric return; 593*0fca6ea1SDimitry Andric 594*0fca6ea1SDimitry Andric case ' ': 595*0fca6ea1SDimitry Andric case '\t': 596*0fca6ea1SDimitry Andric case '\n': 597*0fca6ea1SDimitry Andric break; 598*0fca6ea1SDimitry Andric 599*0fca6ea1SDimitry Andric case '#': 600*0fca6ea1SDimitry Andric chrono::__skip_line(__input); 601*0fca6ea1SDimitry Andric break; 602*0fca6ea1SDimitry Andric 603*0fca6ea1SDimitry Andric case 'r': 604*0fca6ea1SDimitry Andric chrono::__skip(__input, "ule"); 605*0fca6ea1SDimitry Andric chrono::__parse_rule(__db, __rules, __input); 606*0fca6ea1SDimitry Andric break; 607*0fca6ea1SDimitry Andric 608*0fca6ea1SDimitry Andric case 'z': 609*0fca6ea1SDimitry Andric chrono::__skip(__input, "one"); 610*0fca6ea1SDimitry Andric chrono::__parse_zone(__db, __rules, __input); 611*0fca6ea1SDimitry Andric break; 612*0fca6ea1SDimitry Andric 613*0fca6ea1SDimitry Andric case 'l': 614*0fca6ea1SDimitry Andric chrono::__skip(__input, "ink"); 615*0fca6ea1SDimitry Andric chrono::__parse_link(__db, __input); 616*0fca6ea1SDimitry Andric break; 617*0fca6ea1SDimitry Andric 618*0fca6ea1SDimitry Andric default: 619*0fca6ea1SDimitry Andric std::__throw_runtime_error("corrupt tzdb: unexpected input"); 620*0fca6ea1SDimitry Andric } 621*0fca6ea1SDimitry Andric } 622*0fca6ea1SDimitry Andric } 623*0fca6ea1SDimitry Andric 624*0fca6ea1SDimitry Andric static void __parse_leap_seconds(vector<leap_second>& __leap_seconds, istream&& __input) { 625*0fca6ea1SDimitry Andric // The file stores dates since 1 January 1900, 00:00:00, we want 626*0fca6ea1SDimitry Andric // seconds since 1 January 1970. 627*0fca6ea1SDimitry Andric constexpr auto __offset = sys_days{1970y / January / 1} - sys_days{1900y / January / 1}; 628*0fca6ea1SDimitry Andric 629*0fca6ea1SDimitry Andric struct __entry { 630*0fca6ea1SDimitry Andric sys_seconds __timestamp; 631*0fca6ea1SDimitry Andric seconds __value; 632*0fca6ea1SDimitry Andric }; 633*0fca6ea1SDimitry Andric vector<__entry> __entries; 634*0fca6ea1SDimitry Andric [&] { 635*0fca6ea1SDimitry Andric while (true) { 636*0fca6ea1SDimitry Andric switch (__input.peek()) { 637*0fca6ea1SDimitry Andric case istream::traits_type::eof(): 638*0fca6ea1SDimitry Andric return; 639*0fca6ea1SDimitry Andric 640*0fca6ea1SDimitry Andric case ' ': 641*0fca6ea1SDimitry Andric case '\t': 642*0fca6ea1SDimitry Andric case '\n': 643*0fca6ea1SDimitry Andric __input.get(); 644*0fca6ea1SDimitry Andric continue; 645*0fca6ea1SDimitry Andric 646*0fca6ea1SDimitry Andric case '#': 647*0fca6ea1SDimitry Andric chrono::__skip_line(__input); 648*0fca6ea1SDimitry Andric continue; 649*0fca6ea1SDimitry Andric } 650*0fca6ea1SDimitry Andric 651*0fca6ea1SDimitry Andric sys_seconds __date = sys_seconds{seconds{chrono::__parse_integral(__input, false)}} - __offset; 652*0fca6ea1SDimitry Andric chrono::__skip_mandatory_whitespace(__input); 653*0fca6ea1SDimitry Andric seconds __value{chrono::__parse_integral(__input, false)}; 654*0fca6ea1SDimitry Andric chrono::__skip_line(__input); 655*0fca6ea1SDimitry Andric 656*0fca6ea1SDimitry Andric __entries.emplace_back(__date, __value); 657*0fca6ea1SDimitry Andric } 658*0fca6ea1SDimitry Andric }(); 659*0fca6ea1SDimitry Andric // The Standard requires the leap seconds to be sorted. The file 660*0fca6ea1SDimitry Andric // leap-seconds.list usually provides them in sorted order, but that is not 661*0fca6ea1SDimitry Andric // guaranteed so we ensure it here. 662*0fca6ea1SDimitry Andric ranges::sort(__entries, {}, &__entry::__timestamp); 663*0fca6ea1SDimitry Andric 664*0fca6ea1SDimitry Andric // The database should contain the number of seconds inserted by a leap 665*0fca6ea1SDimitry Andric // second (1 or -1). So the difference between the two elements is stored. 666*0fca6ea1SDimitry Andric // std::ranges::views::adjacent has not been implemented yet. 667*0fca6ea1SDimitry Andric (void)ranges::adjacent_find(__entries, [&](const __entry& __first, const __entry& __second) { 668*0fca6ea1SDimitry Andric __leap_seconds.emplace_back( 669*0fca6ea1SDimitry Andric std::__private_constructor_tag{}, __second.__timestamp, __second.__value - __first.__value); 670*0fca6ea1SDimitry Andric return false; 671*0fca6ea1SDimitry Andric }); 672*0fca6ea1SDimitry Andric } 673*0fca6ea1SDimitry Andric 674*0fca6ea1SDimitry Andric void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) { 675*0fca6ea1SDimitry Andric filesystem::path __root = chrono::__libcpp_tzdb_directory(); 676*0fca6ea1SDimitry Andric ifstream __tzdata{__root / "tzdata.zi"}; 677*0fca6ea1SDimitry Andric 678*0fca6ea1SDimitry Andric __tzdb.version = chrono::__parse_version(__tzdata); 679*0fca6ea1SDimitry Andric chrono::__parse_tzdata(__tzdb, __rules, __tzdata); 680*0fca6ea1SDimitry Andric ranges::sort(__tzdb.zones); 681*0fca6ea1SDimitry Andric ranges::sort(__tzdb.links); 682*0fca6ea1SDimitry Andric ranges::sort(__rules, {}, [](const auto& p) { return p.first; }); 683*0fca6ea1SDimitry Andric 684*0fca6ea1SDimitry Andric // There are two files with the leap second information 685*0fca6ea1SDimitry Andric // - leapseconds as specified by zic 686*0fca6ea1SDimitry Andric // - leap-seconds.list the source data 687*0fca6ea1SDimitry Andric // The latter is much easier to parse, it seems Howard shares that 688*0fca6ea1SDimitry Andric // opinion. 689*0fca6ea1SDimitry Andric chrono::__parse_leap_seconds(__tzdb.leap_seconds, ifstream{__root / "leap-seconds.list"}); 690*0fca6ea1SDimitry Andric } 691*0fca6ea1SDimitry Andric 692*0fca6ea1SDimitry Andric #ifdef _WIN32 693*0fca6ea1SDimitry Andric [[nodiscard]] static const time_zone* __current_zone_windows(const tzdb& tzdb) { 694*0fca6ea1SDimitry Andric // TODO TZDB Implement this on Windows. 695*0fca6ea1SDimitry Andric std::__throw_runtime_error("unknown time zone"); 696*0fca6ea1SDimitry Andric } 697*0fca6ea1SDimitry Andric #else // ifdef _WIN32 698*0fca6ea1SDimitry Andric [[nodiscard]] static const time_zone* __current_zone_posix(const tzdb& tzdb) { 699*0fca6ea1SDimitry Andric // On POSIX systems there are several ways to configure the time zone. 700*0fca6ea1SDimitry Andric // In order of priority they are: 701*0fca6ea1SDimitry Andric // - TZ environment variable 702*0fca6ea1SDimitry Andric // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08 703*0fca6ea1SDimitry Andric // The documentation is unclear whether or not it's allowed to 704*0fca6ea1SDimitry Andric // change time zone information. For example the TZ string 705*0fca6ea1SDimitry Andric // MST7MDT 706*0fca6ea1SDimitry Andric // this is an entry in tzdata.zi. The value 707*0fca6ea1SDimitry Andric // MST 708*0fca6ea1SDimitry Andric // is also an entry. Is it allowed to use the following? 709*0fca6ea1SDimitry Andric // MST-3 710*0fca6ea1SDimitry Andric // Even when this is valid there is no time_zone record in the 711*0fca6ea1SDimitry Andric // database. Since the library would need to return a valid pointer, 712*0fca6ea1SDimitry Andric // this means the library needs to allocate and leak a pointer. 713*0fca6ea1SDimitry Andric // 714*0fca6ea1SDimitry Andric // - The time zone name is the target of the symlink /etc/localtime 715*0fca6ea1SDimitry Andric // relative to /usr/share/zoneinfo/ 716*0fca6ea1SDimitry Andric 717*0fca6ea1SDimitry Andric // The algorithm is like this: 718*0fca6ea1SDimitry Andric // - If the environment variable TZ is set and points to a valid 719*0fca6ea1SDimitry Andric // record use this value. 720*0fca6ea1SDimitry Andric // - Else use the name based on the `/etc/localtime` symlink. 721*0fca6ea1SDimitry Andric 722*0fca6ea1SDimitry Andric if (const char* __tz = getenv("TZ")) 723*0fca6ea1SDimitry Andric if (const time_zone* __result = tzdb.__locate_zone(__tz)) 724*0fca6ea1SDimitry Andric return __result; 725*0fca6ea1SDimitry Andric 726*0fca6ea1SDimitry Andric filesystem::path __path = "/etc/localtime"; 727*0fca6ea1SDimitry Andric if (!filesystem::exists(__path)) 728*0fca6ea1SDimitry Andric std::__throw_runtime_error("tzdb: the symlink '/etc/localtime' does not exist"); 729*0fca6ea1SDimitry Andric 730*0fca6ea1SDimitry Andric if (!filesystem::is_symlink(__path)) 731*0fca6ea1SDimitry Andric std::__throw_runtime_error("tzdb: the path '/etc/localtime' is not a symlink"); 732*0fca6ea1SDimitry Andric 733*0fca6ea1SDimitry Andric filesystem::path __tz = filesystem::read_symlink(__path); 734*0fca6ea1SDimitry Andric // The path may be a relative path, in that case convert it to an absolute 735*0fca6ea1SDimitry Andric // path based on the proper initial directory. 736*0fca6ea1SDimitry Andric if (__tz.is_relative()) 737*0fca6ea1SDimitry Andric __tz = filesystem::canonical("/etc" / __tz); 738*0fca6ea1SDimitry Andric 739*0fca6ea1SDimitry Andric string __name = filesystem::relative(__tz, "/usr/share/zoneinfo/"); 740*0fca6ea1SDimitry Andric if (const time_zone* __result = tzdb.__locate_zone(__name)) 741*0fca6ea1SDimitry Andric return __result; 742*0fca6ea1SDimitry Andric 743*0fca6ea1SDimitry Andric std::__throw_runtime_error(("tzdb: the time zone '" + __name + "' is not found in the database").c_str()); 744*0fca6ea1SDimitry Andric } 745*0fca6ea1SDimitry Andric #endif // ifdef _WIN32 746*0fca6ea1SDimitry Andric 747*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 748*0fca6ea1SDimitry Andric // Public API 749*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 750*0fca6ea1SDimitry Andric 751*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list& get_tzdb_list() { 752*0fca6ea1SDimitry Andric static tzdb_list __result{new tzdb_list::__impl()}; 753*0fca6ea1SDimitry Andric return __result; 754*0fca6ea1SDimitry Andric } 755*0fca6ea1SDimitry Andric 756*0fca6ea1SDimitry Andric [[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const time_zone* tzdb::__current_zone() const { 757*0fca6ea1SDimitry Andric #ifdef _WIN32 758*0fca6ea1SDimitry Andric return chrono::__current_zone_windows(*this); 759*0fca6ea1SDimitry Andric #else 760*0fca6ea1SDimitry Andric return chrono::__current_zone_posix(*this); 761*0fca6ea1SDimitry Andric #endif 762*0fca6ea1SDimitry Andric } 763*0fca6ea1SDimitry Andric 764*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const tzdb& reload_tzdb() { 765*0fca6ea1SDimitry Andric if (chrono::remote_version() == chrono::get_tzdb().version) 766*0fca6ea1SDimitry Andric return chrono::get_tzdb(); 767*0fca6ea1SDimitry Andric 768*0fca6ea1SDimitry Andric return chrono::get_tzdb_list().__implementation().__load(); 769*0fca6ea1SDimitry Andric } 770*0fca6ea1SDimitry Andric 771*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI string remote_version() { 772*0fca6ea1SDimitry Andric filesystem::path __root = chrono::__libcpp_tzdb_directory(); 773*0fca6ea1SDimitry Andric ifstream __tzdata{__root / "tzdata.zi"}; 774*0fca6ea1SDimitry Andric return chrono::__parse_version(__tzdata); 775*0fca6ea1SDimitry Andric } 776*0fca6ea1SDimitry Andric 777*0fca6ea1SDimitry Andric } // namespace chrono 778*0fca6ea1SDimitry Andric 779*0fca6ea1SDimitry Andric _LIBCPP_END_NAMESPACE_STD 780