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
__libcpp_tzdb_directory()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
__is_whitespace(int __c)62*0fca6ea1SDimitry Andric [[nodiscard]] static bool __is_whitespace(int __c) { return __c == ' ' || __c == '\t'; }
63*0fca6ea1SDimitry Andric
__skip_optional_whitespace(istream & __input)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
__skip_mandatory_whitespace(istream & __input)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
__is_eol(int __c)76*0fca6ea1SDimitry Andric [[nodiscard]] static bool __is_eol(int __c) { return __c == '\n' || __c == std::char_traits<char>::eof(); }
77*0fca6ea1SDimitry Andric
__skip_line(istream & __input)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
__skip(istream & __input,char __suffix)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
__skip(istream & __input,string_view __suffix)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
__matches(istream & __input,char __expected)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
__matches(istream & __input,string_view __expected)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
__parse_string(istream & __input)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
__parse_integral(istream & __input,bool __leading_zero_allowed)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
__parse_day(istream & __input)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
__parse_weekday(istream & __input)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
__parse_month(istream & __input)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
__parse_year_value(istream & __input)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
__parse_year(istream & __input)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
__parse_to(istream & __input,year __only)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
__parse_comparison(istream & __input)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
__parse_on(istream & __input)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
__parse_duration(istream & __input)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
__parse_clock(istream & __input)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
__parse_dst(istream & __input,seconds __offset)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
__parse_at(istream & __input)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
__parse_save(istream & __input)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
__parse_letters(istream & __input)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
__parse_rules(istream & __input)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
__parse_continuation(__tz::__rules_storage_type & __rules,istream & __input)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
__parse_version(istream & __input)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]]
__create_entry(__tz::__rules_storage_type & __rules,const string & __name)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
__parse_rule(tzdb & __tzdb,__tz::__rules_storage_type & __rules,istream & __input)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
__parse_zone(tzdb & __tzdb,__tz::__rules_storage_type & __rules,istream & __input)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
__parse_link(tzdb & __tzdb,istream & __input)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
__parse_tzdata(tzdb & __db,__tz::__rules_storage_type & __rules,istream & __input)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
__parse_leap_seconds(vector<leap_second> & __leap_seconds,istream && __input)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
__init_tzdb(tzdb & __tzdb,__tz::__rules_storage_type & __rules)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
__current_zone_windows(const tzdb & tzdb)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
__current_zone_posix(const tzdb & tzdb)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
get_tzdb_list()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
__current_zone() const756*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
reload_tzdb()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
remote_version()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