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 <chrono>
12#include <filesystem>
13#include <fstream>
14#include <stdexcept>
15#include <string>
16
17// Contains a parser for the IANA time zone data files.
18//
19// These files can be found at https://data.iana.org/time-zones/ and are in the
20// public domain. Information regarding the input can be found at
21// https://data.iana.org/time-zones/tz-how-to.html and
22// https://man7.org/linux/man-pages/man8/zic.8.html.
23//
24// As indicated at https://howardhinnant.github.io/date/tz.html#Installation
25// For Windows another file seems to be required
26// https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml
27// This file seems to contain the mapping of Windows time zone name to IANA
28// time zone names.
29//
30// However this article mentions another way to do the mapping on Windows
31// https://devblogs.microsoft.com/oldnewthing/20210527-00/?p=105255
32// This requires Windows 10 Version 1903, which was released in May of 2019
33// and considered end of life in December 2020
34// https://learn.microsoft.com/en-us/lifecycle/announcements/windows-10-1903-end-of-servicing
35//
36// TODO TZDB Implement the Windows mapping in tzdb::current_zone
37
38_LIBCPP_BEGIN_NAMESPACE_STD
39
40namespace chrono {
41
42// This function is weak so it can be overriden in the tests. The
43// declaration is in the test header test/support/test_tzdb.h
44_LIBCPP_WEAK string_view __libcpp_tzdb_directory() {
45#if defined(__linux__)
46 return "/usr/share/zoneinfo/";
47#else
48# error "unknown path to the IANA Time Zone Database"
49#endif
50}
51
52[[nodiscard]] static bool __is_whitespace(int __c) { return __c == ' ' || __c == '\t'; }
53
54static void __skip_optional_whitespace(istream& __input) {
55 while (chrono::__is_whitespace(c: __input.peek()))
56 __input.get();
57}
58
59static void __skip_mandatory_whitespace(istream& __input) {
60 if (!chrono::__is_whitespace(c: __input.get()))
61 std::__throw_runtime_error("corrupt tzdb: expected whitespace");
62
63 chrono::__skip_optional_whitespace(__input);
64}
65
66static void __matches(istream& __input, char __expected) {
67 if (std::tolower(__input.get()) != __expected)
68 std::__throw_runtime_error((string("corrupt tzdb: expected character '") + __expected + '\'').c_str());
69}
70
71static void __matches(istream& __input, string_view __expected) {
72 for (auto __c : __expected)
73 if (std::tolower(__input.get()) != __c)
74 std::__throw_runtime_error((string("corrupt tzdb: expected string '") + string(__expected) + '\'').c_str());
75}
76
77[[nodiscard]] static string __parse_string(istream& __input) {
78 string __result;
79 while (true) {
80 int __c = __input.get();
81 switch (__c) {
82 case ' ':
83 case '\t':
84 case '\n':
85 __input.unget();
86 [[fallthrough]];
87 case istream::traits_type::eof():
88 if (__result.empty())
89 std::__throw_runtime_error("corrupt tzdb: expected a string");
90
91 return __result;
92
93 default:
94 __result.push_back(__c);
95 }
96 }
97}
98
99static string __parse_version(istream& __input) {
100 // The first line in tzdata.zi contains
101 // # version YYYYw
102 // The parser expects this pattern
103 // #\s*version\s*\(.*)
104 // This part is not documented.
105 chrono::__matches(__input, '#');
106 chrono::__skip_optional_whitespace(__input);
107 chrono::__matches(__input, "version");
108 chrono::__skip_mandatory_whitespace(__input);
109 return chrono::__parse_string(__input);
110}
111
112static tzdb __make_tzdb() {
113 tzdb __result;
114
115 filesystem::path __root = chrono::__libcpp_tzdb_directory();
116 ifstream __tzdata{__root / "tzdata.zi"};
117
118 __result.version = chrono::__parse_version(__tzdata);
119 return __result;
120}
121
122//===----------------------------------------------------------------------===//
123// Public API
124//===----------------------------------------------------------------------===//
125
126_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list& get_tzdb_list() {
127 static tzdb_list __result{chrono::__make_tzdb()};
128 return __result;
129}
130
131_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const tzdb& reload_tzdb() {
132 if (chrono::remote_version() == chrono::get_tzdb().version)
133 return chrono::get_tzdb();
134
135 return chrono::get_tzdb_list().__emplace_front(chrono::__make_tzdb());
136}
137
138_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI string remote_version() {
139 filesystem::path __root = chrono::__libcpp_tzdb_directory();
140 ifstream __tzdata{__root / "tzdata.zi"};
141 return chrono::__parse_version(__tzdata);
142}
143
144} // namespace chrono
145
146_LIBCPP_END_NAMESPACE_STD
147

source code of libcxx/src/tz.cpp