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