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