brintos

brintos / llvm-project-archived public Read only

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