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 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 // UNSUPPORTED: no-filesystem, no-localization, no-tzdb
11 // REQUIRES: long_tests
12 
13 // XFAIL: libcpp-has-no-experimental-tzdb
14 // XFAIL: availability-tzdb-missing
15 
16 // TODO TZDB Investigate why this fails.
17 // UNSUPPORTED: target={{.*}}
18 
19 // <chrono>
20 
21 // class time_zone;
22 
23 // template <class _Duration>
24 //   local_info get_info(const local_time<_Duration>& time) const;
25 
26 // This test uses the system provided database. This makes the test portable,
27 // but may cause failures when the database information changes. Historic data
28 // may change if new facts are uncovered, future data may change when regions
29 // change their time zone or daylight saving time. Most tests will not look in
30 // the future to attempt to avoid issues. All tests list the data on which they
31 // are based, this makes debugging easier upon failure; including to see whether
32 // the provided data has not been changed.
33 //
34 // The first part of the test is manually crafted, the second part compares the
35 // transitions for all time zones in the database.
36 
37 #include <algorithm>
38 #include <cassert>
39 #include <chrono>
40 #include <format>
41 
42 #include "test_macros.h"
43 #include "assert_macros.h"
44 #include "concat_macros.h"
45 
46 // The year range to validate. The dates used in practice are expected to be
47 // inside the tested range.
48 constexpr std::chrono::year first{1800};
49 constexpr std::chrono::year last{2100};
50 
51 /***** ***** HELPERS ***** *****/
52 
to_sys_seconds(std::chrono::year year,std::chrono::month month,std::chrono::day day,std::chrono::hours h=std::chrono::hours (0),std::chrono::minutes m=std::chrono::minutes{0},std::chrono::seconds s=std::chrono::seconds{0})53 [[nodiscard]] static std::chrono::sys_seconds to_sys_seconds(
54     std::chrono::year year,
55     std::chrono::month month,
56     std::chrono::day day,
57     std::chrono::hours h   = std::chrono::hours(0),
58     std::chrono::minutes m = std::chrono::minutes{0},
59     std::chrono::seconds s = std::chrono::seconds{0}) {
60   std::chrono::year_month_day result{year, month, day};
61 
62   return std::chrono::time_point_cast<std::chrono::seconds>(static_cast<std::chrono::sys_days>(result)) + h + m + s;
63 }
64 
to_local_seconds(std::chrono::year year,std::chrono::month month,std::chrono::day day,std::chrono::hours h=std::chrono::hours (0),std::chrono::minutes m=std::chrono::minutes{0},std::chrono::seconds s=std::chrono::seconds{0})65 [[nodiscard]] static std::chrono::local_seconds to_local_seconds(
66     std::chrono::year year,
67     std::chrono::month month,
68     std::chrono::day day,
69     std::chrono::hours h   = std::chrono::hours(0),
70     std::chrono::minutes m = std::chrono::minutes{0},
71     std::chrono::seconds s = std::chrono::seconds{0}) {
72   std::chrono::year_month_day result{year, month, day};
73 
74   return std::chrono::time_point_cast<std::chrono::seconds>(static_cast<std::chrono::local_days>(result)) + h + m + s;
75 }
76 
assert_equal(const std::chrono::sys_info & lhs,const std::chrono::sys_info & rhs)77 static void assert_equal(const std::chrono::sys_info& lhs, const std::chrono::sys_info& rhs) {
78   TEST_REQUIRE(lhs.begin == rhs.begin,
79                TEST_WRITE_CONCATENATED("\nBegin:\nExpected output ", lhs.begin, "\nActual output   ", rhs.begin, '\n'));
80   TEST_REQUIRE(lhs.end == rhs.end,
81                TEST_WRITE_CONCATENATED("\nEnd:\nExpected output ", lhs.end, "\nActual output   ", rhs.end, '\n'));
82   TEST_REQUIRE(
83       lhs.offset == rhs.offset,
84       TEST_WRITE_CONCATENATED("\nOffset:\nExpected output ", lhs.offset, "\nActual output   ", rhs.offset, '\n'));
85   TEST_REQUIRE(lhs.save == rhs.save,
86                TEST_WRITE_CONCATENATED("\nSave:\nExpected output ", lhs.save, "\nActual output   ", rhs.save, '\n'));
87   TEST_REQUIRE(
88       lhs.abbrev == rhs.abbrev,
89       TEST_WRITE_CONCATENATED("\nAbbrev:\nExpected output ", lhs.abbrev, "\nActual output   ", rhs.abbrev, '\n'));
90 }
91 
assert_equal(const std::chrono::local_info & lhs,const std::chrono::local_info & rhs)92 static void assert_equal(const std::chrono::local_info& lhs, const std::chrono::local_info& rhs) {
93   TEST_REQUIRE(
94       lhs.result == rhs.result,
95       TEST_WRITE_CONCATENATED("\nResult:\nExpected output ", lhs.result, "\nActual output   ", rhs.result, '\n'));
96 
97   assert_equal(lhs.first, rhs.first);
98   assert_equal(lhs.second, rhs.second);
99 }
100 
101 /***** ***** TESTS ***** *****/
102 
test_gmt()103 static void test_gmt() {
104   // Simple zone always valid, no rule entries, lookup using a link.
105   // L Etc/GMT GMT
106   // Z Etc/GMT 0 - GMT
107 
108   using namespace std::literals::chrono_literals;
109   const std::chrono::time_zone* tz = std::chrono::locate_zone("GMT");
110 
111   assert_equal(
112       std::chrono::local_info(
113           std::chrono::local_info::unique,
114           std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), 0s, 0min, "GMT"),
115           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
116       tz->get_info(std::chrono::local_seconds::min()));
117 }
118 
test_local_time_out_of_range()119 static void test_local_time_out_of_range() {
120   // Fixed positive offset
121   // Etc/GMT-1 1 - +01
122 
123   using namespace std::literals::chrono_literals;
124   { // lower bound
125     const std::chrono::time_zone* tz = std::chrono::locate_zone("Etc/GMT-1");
126 
127     assert_equal(
128         std::chrono::local_info(
129             -1,
130             std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), 1h, 0min, "+01"),
131             std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
132         tz->get_info(std::chrono::local_seconds::min()));
133 
134     assert_equal(
135         std::chrono::local_info(
136             -1,
137             std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), 1h, 0min, "+01"),
138             std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
139         tz->get_info(std::chrono::local_seconds::min() + 59min + 59s));
140 
141     assert_equal(
142         std::chrono::local_info(
143             std::chrono::local_info::unique,
144             std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), 1h, 0min, "+01"),
145             std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
146         tz->get_info(std::chrono::local_seconds::min() + 1h));
147   }
148 
149   { // upper bound
150     const std::chrono::time_zone* tz = std::chrono::locate_zone("Etc/GMT+1");
151 
152     assert_equal(
153         std::chrono::local_info(
154             -2,
155             std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), -1h, 0min, "-01"),
156             std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
157         tz->get_info(std::chrono::local_seconds::max() - 1s));
158 
159     assert_equal(
160         std::chrono::local_info(
161             std::chrono::local_info::unique,
162             std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), -1h, 0min, "-01"),
163             std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
164         tz->get_info(std::chrono::local_seconds::max() - 1h - 1s));
165   }
166 }
167 
test_indian_kerguelen()168 static void test_indian_kerguelen() {
169   // One change, no rules, no dst changes.
170 
171   // Z Indian/Kerguelen 0 - -00 1950
172   // 5 - +05
173 
174   using namespace std::literals::chrono_literals;
175   const std::chrono::time_zone* tz = std::chrono::locate_zone("Indian/Kerguelen");
176 
177   assert_equal(
178       std::chrono::local_info(
179           std::chrono::local_info::unique,
180           std::chrono::sys_info(
181               std::chrono::sys_seconds::min(), to_sys_seconds(1950y, std::chrono::January, 1d), 0s, 0min, "-00"),
182           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
183       tz->get_info(std::chrono::local_seconds::min()));
184 
185   assert_equal(
186       std::chrono::local_info(
187           std::chrono::local_info::nonexistent,
188           std::chrono::sys_info(
189               std::chrono::sys_seconds::min(), to_sys_seconds(1950y, std::chrono::January, 1d), 0s, 0min, "-00"),
190           std::chrono::sys_info(
191               to_sys_seconds(1950y, std::chrono::January, 1d), std::chrono::sys_seconds::max(), 5h, 0min, "+05")),
192       tz->get_info(to_local_seconds(1950y, std::chrono::January, 1d)));
193 
194   assert_equal(
195       std::chrono::local_info(
196           std::chrono::local_info::unique,
197           std::chrono::sys_info(
198               to_sys_seconds(1950y, std::chrono::January, 1d), std::chrono::sys_seconds::max(), 5h, 0min, "+05"),
199           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
200       tz->get_info(to_local_seconds(1950y, std::chrono::January, 1d, 5h)));
201 
202   assert_equal(
203       std::chrono::local_info(
204           std::chrono::local_info::unique,
205           std::chrono::sys_info(
206               to_sys_seconds(1950y, std::chrono::January, 1d), std::chrono::sys_seconds::max(), 5h, 0min, "+05"),
207           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
208       tz->get_info(std::chrono::local_seconds::max() - 1s));
209 }
210 
test_antarctica_rothera()211 static void test_antarctica_rothera() {
212   // One change, no rules, no dst changes
213 
214   // Z Antarctica/Rothera 0 - -00 1976 D
215   // -3 - -03
216 
217   using namespace std::literals::chrono_literals;
218   const std::chrono::time_zone* tz = std::chrono::locate_zone("Antarctica/Rothera");
219 
220   assert_equal(
221       std::chrono::local_info(
222           std::chrono::local_info::unique,
223           std::chrono::sys_info(
224               std::chrono::sys_seconds::min(), to_sys_seconds(1976y, std::chrono::December, 1d), 0s, 0min, "-00"),
225           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
226       tz->get_info(std::chrono::local_seconds::min()));
227 
228   assert_equal(
229       std::chrono::local_info(
230           std::chrono::local_info::unique,
231           std::chrono::sys_info(
232               std::chrono::sys_seconds::min(), to_sys_seconds(1976y, std::chrono::December, 1d), 0s, 0min, "-00"),
233           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
234       tz->get_info(to_local_seconds(1976y, std::chrono::November, 30d, 20h, 59min, 59s)));
235 
236   assert_equal(
237       std::chrono::local_info(
238           std::chrono::local_info::ambiguous,
239           std::chrono::sys_info(
240               std::chrono::sys_seconds::min(), to_sys_seconds(1976y, std::chrono::December, 1d), 0s, 0min, "-00"),
241           std::chrono::sys_info(
242               to_sys_seconds(1976y, std::chrono::December, 1d), std::chrono::sys_seconds::max(), -3h, 0min, "-03")),
243       tz->get_info(to_local_seconds(1976y, std::chrono::November, 30d, 21h)));
244 
245   assert_equal(
246       std::chrono::local_info(
247           std::chrono::local_info::ambiguous,
248           std::chrono::sys_info(
249               std::chrono::sys_seconds::min(), to_sys_seconds(1976y, std::chrono::December, 1d), 0s, 0min, "-00"),
250           std::chrono::sys_info(
251               to_sys_seconds(1976y, std::chrono::December, 1d), std::chrono::sys_seconds::max(), -3h, 0min, "-03")),
252       tz->get_info(to_local_seconds(1976y, std::chrono::November, 30d, 23h, 59min, 59s)));
253 
254   assert_equal(
255       std::chrono::local_info(
256           std::chrono::local_info::unique,
257           std::chrono::sys_info(
258               to_sys_seconds(1976y, std::chrono::December, 1d), std::chrono::sys_seconds::max(), -3h, 0min, "-03"),
259           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
260       tz->get_info(to_local_seconds(1976y, std::chrono::December, 1d)));
261 
262   assert_equal(
263       std::chrono::local_info(
264           std::chrono::local_info::unique,
265           std::chrono::sys_info(
266               to_sys_seconds(1976y, std::chrono::December, 1d), std::chrono::sys_seconds::max(), -3h, 0min, "-03"),
267           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
268       tz->get_info(std::chrono::local_seconds::max() - 3h - 1s));
269 
270   assert_equal(
271       std::chrono::local_info(
272           -2,
273           std::chrono::sys_info(
274               to_sys_seconds(1976y, std::chrono::December, 1d), std::chrono::sys_seconds::max(), -3h, 0min, "-03"),
275           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
276       tz->get_info(std::chrono::local_seconds::max() - 1s));
277 }
278 
test_asia_hong_kong()279 static void test_asia_hong_kong() {
280   // A more typical entry, first some hard-coded entires and then at the
281   // end a rules based entry. This rule is valid for its entire period
282   //
283   // Z Asia/Hong_Kong 7:36:42 - LMT 1904 O 30 0:36:42
284   // 8 - HKT 1941 Jun 15 3
285   // 8 1 HKST 1941 O 1 4
286   // 8 0:30 HKWT 1941 D 25
287   // 9 - JST 1945 N 18 2
288   // 8 HK HK%sT
289   //
290   // R HK 1946 o - Ap 21 0 1 S
291   // R HK 1946 o - D 1 3:30s 0 -
292   // R HK 1947 o - Ap 13 3:30s 1 S
293   // R HK 1947 o - N 30 3:30s 0 -
294   // R HK 1948 o - May 2 3:30s 1 S
295   // R HK 1948 1952 - O Su>=28 3:30s 0 -
296   // R HK 1949 1953 - Ap Su>=1 3:30 1 S
297   // R HK 1953 1964 - O Su>=31 3:30 0 -
298   // R HK 1954 1964 - Mar Su>=18 3:30 1 S
299   // R HK 1965 1976 - Ap Su>=16 3:30 1 S
300   // R HK 1965 1976 - O Su>=16 3:30 0 -
301   // R HK 1973 o - D 30 3:30 1 S
302   // R HK 1979 o - May 13 3:30 1 S
303   // R HK 1979 o - O 21 3:30 0 -
304 
305   using namespace std::literals::chrono_literals;
306   const std::chrono::time_zone* tz = std::chrono::locate_zone("Asia/Hong_Kong");
307 
308   assert_equal(
309       std::chrono::local_info(
310           -1,
311           std::chrono::sys_info(
312               std::chrono::sys_seconds::min(),
313               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
314               7h + 36min + 42s,
315               0min,
316               "LMT"),
317           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
318       tz->get_info(std::chrono::local_seconds::min()));
319 
320   assert_equal(
321       std::chrono::local_info(
322           -1,
323           std::chrono::sys_info(
324               std::chrono::sys_seconds::min(),
325               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
326               7h + 36min + 42s,
327               0min,
328               "LMT"),
329           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
330       tz->get_info(std::chrono::local_seconds::min() + 7h + 36min + 41s));
331 
332   assert_equal(
333       std::chrono::local_info(
334           std::chrono::local_info::unique,
335           std::chrono::sys_info(
336               std::chrono::sys_seconds::min(),
337               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
338               7h + 36min + 42s,
339               0min,
340               "LMT"),
341           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
342       tz->get_info(std::chrono::local_seconds::min() + 7h + 36min + 42s));
343 
344   assert_equal(
345       std::chrono::local_info(
346           std::chrono::local_info::unique,
347           std::chrono::sys_info(
348               std::chrono::sys_seconds::min(),
349               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
350               7h + 36min + 42s,
351               0min,
352               "LMT"),
353           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
354       tz->get_info(to_local_seconds(1904y, std::chrono::October, 30d, 0h, 36min, 41s)));
355 
356   assert_equal(
357       std::chrono::local_info(
358           std::chrono::local_info::nonexistent,
359           std::chrono::sys_info(
360               std::chrono::sys_seconds::min(),
361               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
362               7h + 36min + 42s,
363               0min,
364               "LMT"),
365           std::chrono::sys_info(
366               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
367               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
368               8h,
369               0min,
370               "HKT")),
371       tz->get_info(to_local_seconds(1904y, std::chrono::October, 30d, 0h, 36min, 42s)));
372 
373   assert_equal(
374       std::chrono::local_info(
375           std::chrono::local_info::nonexistent,
376           std::chrono::sys_info(
377               std::chrono::sys_seconds::min(),
378               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
379               7h + 36min + 42s,
380               0min,
381               "LMT"),
382           std::chrono::sys_info(
383               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
384               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
385               8h,
386               0min,
387               "HKT")),
388       tz->get_info(to_local_seconds(1904y, std::chrono::October, 30d, 0h, 59min, 59s)));
389 
390   assert_equal(
391       std::chrono::local_info(
392           std::chrono::local_info::unique,
393           std::chrono::sys_info(
394               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
395               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
396               8h,
397               0min,
398               "HKT"),
399           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
400       tz->get_info(to_local_seconds(1904y, std::chrono::October, 30d, 1h)));
401 
402   assert_equal(
403       std::chrono::local_info(
404           std::chrono::local_info::unique,
405           std::chrono::sys_info(
406               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
407               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
408               8h,
409               0min,
410               "HKT"),
411           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
412       tz->get_info(to_local_seconds(1941y, std::chrono::June, 15d, 2h, 59min, 59s)));
413 
414   assert_equal(
415       std::chrono::local_info(
416           std::chrono::local_info::nonexistent,
417           std::chrono::sys_info(
418               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
419               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
420               8h,
421               0min,
422               "HKT"),
423           std::chrono::sys_info(
424               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
425               to_sys_seconds(1941y, std::chrono::September, 30d, 19h),
426               9h,
427               60min,
428               "HKST")),
429       tz->get_info(to_local_seconds(1941y, std::chrono::June, 15d, 3h)));
430 
431   assert_equal(
432       std::chrono::local_info(
433           std::chrono::local_info::nonexistent,
434           std::chrono::sys_info(
435               to_sys_seconds(1904y, std::chrono::October, 29d, 17h),
436               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
437               8h,
438               0min,
439               "HKT"),
440           std::chrono::sys_info(
441               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
442               to_sys_seconds(1941y, std::chrono::September, 30d, 19h),
443               9h,
444               60min,
445               "HKST")),
446       tz->get_info(to_local_seconds(1941y, std::chrono::June, 15d, 3h, 59min, 59s)));
447 
448   assert_equal(
449       std::chrono::local_info(
450           std::chrono::local_info::unique,
451           std::chrono::sys_info(
452               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
453               to_sys_seconds(1941y, std::chrono::September, 30d, 19h),
454               9h,
455               60min,
456               "HKST"),
457           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
458       tz->get_info(to_local_seconds(1941y, std::chrono::June, 15d, 4h)));
459 
460   assert_equal(
461       std::chrono::local_info(
462           std::chrono::local_info::unique,
463           std::chrono::sys_info(
464               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
465               to_sys_seconds(1941y, std::chrono::September, 30d, 19h),
466               9h,
467               60min,
468               "HKST"),
469           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
470       tz->get_info(to_local_seconds(1941y, std::chrono::October, 1d, 3h, 29min, 29s)));
471 
472   assert_equal(
473       std::chrono::local_info(
474           std::chrono::local_info::ambiguous,
475           std::chrono::sys_info(
476               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
477               to_sys_seconds(1941y, std::chrono::September, 30d, 19h),
478               9h,
479               60min,
480               "HKST"),
481           std::chrono::sys_info(
482               to_sys_seconds(1941y, std::chrono::September, 30d, 19h),
483               to_sys_seconds(1941y, std::chrono::December, 24d, 15h, 30min),
484               8h + 30min,
485               30min,
486               "HKWT")),
487       tz->get_info(to_local_seconds(1941y, std::chrono::October, 1d, 3h, 30min)));
488 
489   assert_equal(
490       std::chrono::local_info(
491           std::chrono::local_info::ambiguous,
492           std::chrono::sys_info(
493               to_sys_seconds(1941y, std::chrono::June, 14d, 19h),
494               to_sys_seconds(1941y, std::chrono::September, 30d, 19h),
495               9h,
496               60min,
497               "HKST"),
498           std::chrono::sys_info(
499               to_sys_seconds(1941y, std::chrono::September, 30d, 19h),
500               to_sys_seconds(1941y, std::chrono::December, 24d, 15h, 30min),
501               8h + 30min,
502               30min,
503               "HKWT")),
504       tz->get_info(to_local_seconds(1941y, std::chrono::October, 1d, 3h, 59min, 59s)));
505 
506   assert_equal(
507       std::chrono::local_info(
508           std::chrono::local_info::unique,
509           std::chrono::sys_info(
510               to_sys_seconds(1941y, std::chrono::September, 30d, 19h),
511               to_sys_seconds(1941y, std::chrono::December, 24d, 15h, 30min),
512               8h + 30min,
513               30min,
514               "HKWT"),
515           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
516       tz->get_info(to_local_seconds(1941y, std::chrono::October, 1d, 4h)));
517 }
518 
test_europe_berlin()519 static void test_europe_berlin() {
520   // A more typical entry, first some hard-coded entires and then at the
521   // end a rules based entry. This rule is valid for its entire period
522   //
523 
524   // Z Europe/Berlin 0:53:28 - LMT 1893 Ap
525   // 1 c CE%sT 1945 May 24 2
526   // 1 So CE%sT 1946
527   // 1 DE CE%sT 1980
528   // 1 E CE%sT
529   //
530   // R c 1916 o - Ap 30 23 1 S
531   // R c 1916 o - O 1 1 0 -
532   // R c 1917 1918 - Ap M>=15 2s 1 S
533   // R c 1917 1918 - S M>=15 2s 0 -
534   // R c 1940 o - Ap 1 2s 1 S
535   // R c 1942 o - N 2 2s 0 -
536   // R c 1943 o - Mar 29 2s 1 S
537   // R c 1943 o - O 4 2s 0 -
538   // R c 1944 1945 - Ap M>=1 2s 1 S
539   // R c 1944 o - O 2 2s 0 -
540   // R c 1945 o - S 16 2s 0 -
541   // R c 1977 1980 - Ap Su>=1 2s 1 S
542   // R c 1977 o - S lastSu 2s 0 -
543   // R c 1978 o - O 1 2s 0 -
544   // R c 1979 1995 - S lastSu 2s 0 -
545   // R c 1981 ma - Mar lastSu 2s 1 S
546   // R c 1996 ma - O lastSu 2s 0 -
547   //
548   // R So 1945 o - May 24 2 2 M
549   // R So 1945 o - S 24 3 1 S
550   // R So 1945 o - N 18 2s 0 -
551   //
552   // R DE 1946 o - Ap 14 2s 1 S
553   // R DE 1946 o - O 7 2s 0 -
554   // R DE 1947 1949 - O Su>=1 2s 0 -
555   // R DE 1947 o - Ap 6 3s 1 S
556   // R DE 1947 o - May 11 2s 2 M
557   // R DE 1947 o - Jun 29 3 1 S
558   // R DE 1948 o - Ap 18 2s 1 S
559   // R DE 1949 o - Ap 10 2s 1 S
560   //
561   // R E 1977 1980 - Ap Su>=1 1u 1 S
562   // R E 1977 o - S lastSu 1u 0 -
563   // R E 1978 o - O 1 1u 0 -
564   // R E 1979 1995 - S lastSu 1u 0 -
565   // R E 1981 ma - Mar lastSu 1u 1 S
566   // R E 1996 ma - O lastSu 1u 0 -
567   //
568   // Note the European Union decided to stop the seasonal change in
569   // 2021. In 2023 seasonal changes are still in effect.
570 
571   using namespace std::literals::chrono_literals;
572   const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Berlin");
573 
574   assert_equal(
575       std::chrono::local_info(
576           -1,
577           std::chrono::sys_info(
578               std::chrono::sys_seconds::min(),
579               to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 32s),
580               53min + 28s,
581               0min,
582               "LMT"),
583           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
584       tz->get_info(std::chrono::local_seconds::min()));
585 
586   assert_equal(
587       std::chrono::local_info(
588           -1,
589           std::chrono::sys_info(
590               std::chrono::sys_seconds::min(),
591               to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 32s),
592               53min + 28s,
593               0min,
594               "LMT"),
595           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
596       tz->get_info(std::chrono::local_seconds::min() + 53min + 27s));
597 
598   assert_equal(
599       std::chrono::local_info(
600           std::chrono::local_info::unique,
601           std::chrono::sys_info(
602               std::chrono::sys_seconds::min(),
603               to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 32s),
604               53min + 28s,
605               0min,
606               "LMT"),
607           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
608       tz->get_info(std::chrono::local_seconds::min() + 53min + 28s));
609 
610   assert_equal(
611       std::chrono::local_info(
612           std::chrono::local_info::unique,
613           std::chrono::sys_info(
614               std::chrono::sys_seconds::min(),
615               to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 32s),
616               53min + 28s,
617               0min,
618               "LMT"),
619           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
620       tz->get_info(to_local_seconds(1893y, std::chrono::March, 31d, 23h, 59min, 59s)));
621 
622   assert_equal(
623       std::chrono::local_info(
624           std::chrono::local_info::unique,
625           std::chrono::sys_info(
626               to_sys_seconds(1946y, std::chrono::October, 7d, 1h),
627               to_sys_seconds(1947y, std::chrono::April, 6d, 2h),
628               1h,
629               0min,
630               "CET"),
631           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
632       tz->get_info(to_local_seconds(1947y, std::chrono::April, 6d, 2h, 59min, 59s)));
633 
634   assert_equal(
635       std::chrono::local_info(
636           std::chrono::local_info::nonexistent,
637           std::chrono::sys_info(
638               to_sys_seconds(1946y, std::chrono::October, 7d, 1h),
639               to_sys_seconds(1947y, std::chrono::April, 6d, 2h),
640               1h,
641               0min,
642               "CET"),
643           std::chrono::sys_info(
644               to_sys_seconds(1947y, std::chrono::April, 6d, 2h),
645               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
646               2h,
647               60min,
648               "CEST")),
649       tz->get_info(to_local_seconds(1947y, std::chrono::April, 6d, 3h)));
650 
651   assert_equal(
652       std::chrono::local_info(
653           std::chrono::local_info::nonexistent,
654           std::chrono::sys_info(
655               to_sys_seconds(1946y, std::chrono::October, 7d, 1h),
656               to_sys_seconds(1947y, std::chrono::April, 6d, 2h),
657               1h,
658               0min,
659               "CET"),
660           std::chrono::sys_info(
661               to_sys_seconds(1947y, std::chrono::April, 6d, 2h),
662               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
663               2h,
664               60min,
665               "CEST")),
666       tz->get_info(to_local_seconds(1947y, std::chrono::April, 6d, 3h, 59min, 59s)));
667 
668   assert_equal(
669       std::chrono::local_info(
670           std::chrono::local_info::unique,
671           std::chrono::sys_info(
672               to_sys_seconds(1947y, std::chrono::April, 6d, 2h),
673               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
674               2h,
675               60min,
676               "CEST"),
677           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
678       tz->get_info(to_local_seconds(1947y, std::chrono::April, 6d, 4h)));
679 
680   assert_equal(
681       std::chrono::local_info(
682           std::chrono::local_info::unique,
683           std::chrono::sys_info(
684               to_sys_seconds(1947y, std::chrono::April, 6d, 2h),
685               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
686               2h,
687               60min,
688               "CEST"),
689           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
690       tz->get_info(to_local_seconds(1947y, std::chrono::May, 11d, 2h, 59min, 59s)));
691 
692   assert_equal(
693       std::chrono::local_info(
694           std::chrono::local_info::nonexistent,
695           std::chrono::sys_info(
696               to_sys_seconds(1947y, std::chrono::April, 6d, 2h),
697               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
698               2h,
699               60min,
700               "CEST"),
701           std::chrono::sys_info(
702               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
703               to_sys_seconds(1947y, std::chrono::June, 29d),
704               3h,
705               120min,
706               "CEMT")),
707       tz->get_info(to_local_seconds(1947y, std::chrono::May, 11d, 3h)));
708 
709   assert_equal(
710       std::chrono::local_info(
711           std::chrono::local_info::nonexistent,
712           std::chrono::sys_info(
713               to_sys_seconds(1947y, std::chrono::April, 6d, 2h),
714               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
715               2h,
716               60min,
717               "CEST"),
718           std::chrono::sys_info(
719               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
720               to_sys_seconds(1947y, std::chrono::June, 29d),
721               3h,
722               120min,
723               "CEMT")),
724       tz->get_info(to_local_seconds(1947y, std::chrono::May, 11d, 3h, 59min, 59s)));
725 
726   assert_equal(
727       std::chrono::local_info(
728           std::chrono::local_info::unique,
729           std::chrono::sys_info(
730               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
731               to_sys_seconds(1947y, std::chrono::June, 29d),
732               3h,
733               120min,
734               "CEMT"),
735           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
736       tz->get_info(to_local_seconds(1947y, std::chrono::May, 11d, 4h)));
737 
738   assert_equal(
739       std::chrono::local_info(
740           std::chrono::local_info::unique,
741           std::chrono::sys_info(
742               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
743               to_sys_seconds(1947y, std::chrono::June, 29d),
744               3h,
745               120min,
746               "CEMT"),
747           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
748       tz->get_info(to_local_seconds(1947y, std::chrono::June, 29d, 1h, 59min, 59s)));
749 
750   assert_equal(
751       std::chrono::local_info(
752           std::chrono::local_info::ambiguous,
753           std::chrono::sys_info(
754               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
755               to_sys_seconds(1947y, std::chrono::June, 29d),
756               3h,
757               120min,
758               "CEMT"),
759           std::chrono::sys_info(
760               to_sys_seconds(1947y, std::chrono::June, 29d),
761               to_sys_seconds(1947y, std::chrono::October, 5d, 1h),
762               2h,
763               60min,
764               "CEST")),
765       tz->get_info(to_local_seconds(1947y, std::chrono::June, 29d, 2h)));
766 
767   assert_equal(
768       std::chrono::local_info(
769           std::chrono::local_info::ambiguous,
770           std::chrono::sys_info(
771               to_sys_seconds(1947y, std::chrono::May, 11d, 1h),
772               to_sys_seconds(1947y, std::chrono::June, 29d),
773               3h,
774               120min,
775               "CEMT"),
776           std::chrono::sys_info(
777               to_sys_seconds(1947y, std::chrono::June, 29d),
778               to_sys_seconds(1947y, std::chrono::October, 5d, 1h),
779               2h,
780               60min,
781               "CEST")),
782       tz->get_info(to_local_seconds(1947y, std::chrono::June, 29d, 2h, 59min, 59s)));
783 
784   assert_equal(
785       std::chrono::local_info(
786           std::chrono::local_info::unique,
787           std::chrono::sys_info(
788               to_sys_seconds(1947y, std::chrono::June, 29d),
789               to_sys_seconds(1947y, std::chrono::October, 5d, 1h),
790               2h,
791               60min,
792               "CEST"),
793           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
794       tz->get_info(to_local_seconds(1947y, std::chrono::June, 29d, 3h)));
795 
796   assert_equal(
797       std::chrono::local_info(
798           std::chrono::local_info::unique,
799           std::chrono::sys_info(
800               to_sys_seconds(1947y, std::chrono::June, 29d),
801               to_sys_seconds(1947y, std::chrono::October, 5d, 1h),
802               2h,
803               60min,
804               "CEST"),
805           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
806       tz->get_info(to_local_seconds(1947y, std::chrono::October, 5d, 1h, 59min, 59s)));
807 
808   assert_equal(
809       std::chrono::local_info(
810           std::chrono::local_info::ambiguous,
811           std::chrono::sys_info(
812               to_sys_seconds(1947y, std::chrono::June, 29d),
813               to_sys_seconds(1947y, std::chrono::October, 5d, 1h),
814               2h,
815               60min,
816               "CEST"),
817           std::chrono::sys_info(
818               to_sys_seconds(1947y, std::chrono::October, 5d, 1h),
819               to_sys_seconds(1948y, std::chrono::April, 18d, 1h),
820               1h,
821               0min,
822               "CET")),
823       tz->get_info(to_local_seconds(1947y, std::chrono::October, 5d, 2h)));
824 
825   assert_equal(
826       std::chrono::local_info(
827           std::chrono::local_info::ambiguous,
828           std::chrono::sys_info(
829               to_sys_seconds(1947y, std::chrono::June, 29d),
830               to_sys_seconds(1947y, std::chrono::October, 5d, 1h),
831               2h,
832               60min,
833               "CEST"),
834           std::chrono::sys_info(
835               to_sys_seconds(1947y, std::chrono::October, 5d, 1h),
836               to_sys_seconds(1948y, std::chrono::April, 18d, 1h),
837               1h,
838               0min,
839               "CET")),
840       tz->get_info(to_local_seconds(1947y, std::chrono::October, 5d, 2h, 59min, 59s)));
841 
842   assert_equal(
843       std::chrono::local_info(
844           std::chrono::local_info::unique,
845           std::chrono::sys_info(
846               to_sys_seconds(1947y, std::chrono::October, 5d, 1h),
847               to_sys_seconds(1948y, std::chrono::April, 18d, 1h),
848               1h,
849               0min,
850               "CET"),
851           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
852       tz->get_info(to_local_seconds(1947y, std::chrono::October, 5d, 3h)));
853 }
854 
test_europe_dublin()855 static void test_europe_dublin() {
856   // Z Europe/Dublin -0:25:21 - LMT 1880 Au 2
857   // -0:25:21 - DMT 1916 May 21 2s
858   // -0:25:21 1 IST 1916 O 1 2s
859   // 0 G %s 1921 D 6
860   // ...
861   //
862   // R G 1916 o - May 21 2s 1 BST
863   // R G 1916 o - O 1 2s 0 GMT
864   // R G 1917 o - Ap 8 2s 1 BST
865   // ...
866 
867   using namespace std::literals::chrono_literals;
868   const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Dublin");
869 
870   assert_equal(
871       std::chrono::local_info(
872           std::chrono::local_info::unique,
873           std::chrono::sys_info(
874               std::chrono::sys_seconds::min(),
875               to_sys_seconds(1880y, std::chrono::August, 2d, 0h, 25min, 21s),
876               -(25min + 21s),
877               0min,
878               "LMT"),
879           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
880       tz->get_info(std::chrono::local_seconds::min()));
881 
882   assert_equal(
883       std::chrono::local_info(
884           std::chrono::local_info::unique,
885           std::chrono::sys_info(
886               std::chrono::sys_seconds::min(),
887               to_sys_seconds(1880y, std::chrono::August, 2d, 0h, 25min, 21s),
888               -(25min + 21s),
889               0min,
890               "LMT"),
891           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
892       tz->get_info(to_local_seconds(1880y, std::chrono::August, 1d, 23h, 59min, 59s)));
893 
894   assert_equal(
895       std::chrono::local_info(
896           std::chrono::local_info::unique,
897           std::chrono::sys_info(
898               to_sys_seconds(1880y, std::chrono::August, 2d, 0h, 25min, 21s),
899               to_sys_seconds(1916y, std::chrono::May, 21d, 2h, 25min, 21s),
900               -(25min + 21s),
901               0min,
902               "DMT"),
903           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
904       tz->get_info(to_local_seconds(1880y, std::chrono::August, 2d)));
905 
906   assert_equal(
907       std::chrono::local_info(
908           std::chrono::local_info::unique,
909           std::chrono::sys_info(
910               to_sys_seconds(1880y, std::chrono::August, 2d, 0h, 25min, 21s),
911               to_sys_seconds(1916y, std::chrono::May, 21d, 2h, 25min, 21s),
912               -(25min + 21s),
913               0min,
914               "DMT"),
915           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
916       tz->get_info(to_local_seconds(1916y, std::chrono::May, 21d, 1h, 59min, 59s)));
917 
918   assert_equal(
919       std::chrono::local_info(
920           std::chrono::local_info::nonexistent,
921           std::chrono::sys_info(
922               to_sys_seconds(1880y, std::chrono::August, 2d, 0h, 25min, 21s),
923               to_sys_seconds(1916y, std::chrono::May, 21d, 2h, 25min, 21s),
924               -(25min + 21s),
925               0min,
926               "DMT"),
927           std::chrono::sys_info(
928               to_sys_seconds(1916y, std::chrono::May, 21d, 2h, 25min, 21s),
929               to_sys_seconds(1916y, std::chrono::October, 1d, 02h, 25min, 21s),
930               34min + 39s,
931               60min,
932               "IST")),
933       tz->get_info(to_local_seconds(1916y, std::chrono::May, 21d, 2h)));
934 
935   assert_equal(
936       std::chrono::local_info(
937           std::chrono::local_info::nonexistent,
938           std::chrono::sys_info(
939               to_sys_seconds(1880y, std::chrono::August, 2d, 0h, 25min, 21s),
940               to_sys_seconds(1916y, std::chrono::May, 21d, 2h, 25min, 21s),
941               -(25min + 21s),
942               0min,
943               "DMT"),
944           std::chrono::sys_info(
945               to_sys_seconds(1916y, std::chrono::May, 21d, 2h, 25min, 21s),
946               to_sys_seconds(1916y, std::chrono::October, 1d, 02h, 25min, 21s),
947               34min + 39s,
948               60min,
949               "IST")),
950       tz->get_info(to_local_seconds(1916y, std::chrono::May, 21d, 2h, 59min, 59s)));
951 
952   assert_equal(
953       std::chrono::local_info(
954           std::chrono::local_info::unique,
955           std::chrono::sys_info(
956               to_sys_seconds(1916y, std::chrono::May, 21d, 2h, 25min, 21s),
957               to_sys_seconds(1916y, std::chrono::October, 1d, 02h, 25min, 21s),
958               34min + 39s,
959               60min,
960               "IST"),
961           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
962       tz->get_info(to_local_seconds(1916y, std::chrono::May, 21d, 6h)));
963 
964   assert_equal(
965       std::chrono::local_info(
966           std::chrono::local_info::unique,
967           std::chrono::sys_info(
968               to_sys_seconds(1916y, std::chrono::May, 21d, 2h, 25min, 21s),
969               to_sys_seconds(1916y, std::chrono::October, 1d, 02h, 25min, 21s),
970               34min + 39s,
971               60min,
972               "IST"),
973           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
974       tz->get_info(to_local_seconds(1916y, std::chrono::October, 1d, 2h, 25min, 20s)));
975 
976   assert_equal(
977       std::chrono::local_info(
978           std::chrono::local_info::ambiguous,
979           std::chrono::sys_info(
980               to_sys_seconds(1916y, std::chrono::May, 21d, 2h, 25min, 21s),
981               to_sys_seconds(1916y, std::chrono::October, 1d, 02h, 25min, 21s),
982               34min + 39s,
983               60min,
984               "IST"),
985           std::chrono::sys_info(
986               to_sys_seconds(1916y, std::chrono::October, 1d, 02h, 25min, 21s),
987               to_sys_seconds(1917y, std::chrono::April, 8d, 2h),
988               0s,
989               0min,
990               "GMT")),
991       tz->get_info(to_local_seconds(1916y, std::chrono::October, 1d, 2h, 59min, 59s)));
992 
993   assert_equal(
994       std::chrono::local_info(
995           std::chrono::local_info::unique,
996           std::chrono::sys_info(
997               to_sys_seconds(1916y, std::chrono::October, 1d, 02h, 25min, 21s),
998               to_sys_seconds(1917y, std::chrono::April, 8d, 2h),
999               0s,
1000               0min,
1001               "GMT"),
1002           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1003       tz->get_info(to_local_seconds(1916y, std::chrono::October, 1d, 3h)));
1004 
1005   assert_equal(
1006       std::chrono::local_info(
1007           std::chrono::local_info::unique,
1008           std::chrono::sys_info(
1009               to_sys_seconds(1916y, std::chrono::October, 1d, 02h, 25min, 21s),
1010               to_sys_seconds(1917y, std::chrono::April, 8d, 2h),
1011               0s,
1012               0min,
1013               "GMT"),
1014           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1015       tz->get_info(to_local_seconds(1917y, std::chrono::April, 8d, 1h, 59min, 59s)));
1016 }
1017 
test_america_st_johns()1018 static void test_america_st_johns() {
1019   // A more typical entry,
1020   // Uses letters both when DST is ative and not and has multiple
1021   // letters. Uses negetive offsets.
1022   // Switches several times between their own and Canadian rules
1023   // Switches the stdoff from -3:30:52 to -3:30 while observing the same rule
1024 
1025   // Z America/St_Johns -3:30:52 - LMT 1884
1026   // -3:30:52 j N%sT 1918
1027   // -3:30:52 C N%sT 1919
1028   // ...
1029   //
1030   // R j 1917 o - Ap 8 2 1 D
1031   // R j 1917 o - S 17 2 0 S
1032   // R j 1919 o - May 5 23 1 D
1033   // R j 1919 o - Au 12 23 0 S
1034   // R j 1920 1935 - May Su>=1 23 1 D
1035   // ...
1036   //
1037   // R C 1918 o - Ap 14 2 1 D
1038   // R C 1918 o - O 27 2 0 S
1039   // R C 1942 o - F 9 2 1 W
1040   // ...
1041 
1042   using namespace std::literals::chrono_literals;
1043   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/St_Johns");
1044 
1045   assert_equal(
1046       std::chrono::local_info(
1047           std::chrono::local_info::unique,
1048           std::chrono::sys_info(
1049               std::chrono::sys_seconds::min(),
1050               to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s),
1051               -(3h + 30min + 52s),
1052               0min,
1053               "LMT"),
1054           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1055       tz->get_info(std::chrono::local_seconds::min()));
1056 
1057   assert_equal(
1058       std::chrono::local_info(
1059           std::chrono::local_info::unique,
1060           std::chrono::sys_info(
1061               std::chrono::sys_seconds::min(),
1062               to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s),
1063               -(3h + 30min + 52s),
1064               0min,
1065               "LMT"),
1066           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1067       tz->get_info(to_local_seconds(1883y, std::chrono::December, 31d, 23h, 59min, 59s)));
1068 
1069   assert_equal(
1070       std::chrono::local_info(
1071           std::chrono::local_info::unique,
1072           std::chrono::sys_info(
1073               to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s),
1074               to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s),
1075               -(3h + 30min + 52s),
1076               0min,
1077               "NST"),
1078           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1079       tz->get_info(to_local_seconds(1884y, std::chrono::January, 1d)));
1080 
1081   assert_equal(
1082       std::chrono::local_info(
1083           std::chrono::local_info::unique,
1084           std::chrono::sys_info(
1085               to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s),
1086               to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s),
1087               -(3h + 30min + 52s),
1088               0min,
1089               "NST"),
1090           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1091       tz->get_info(to_local_seconds(1917y, std::chrono::April, 8d, 1h, 59min, 59s)));
1092 
1093   assert_equal(
1094       std::chrono::local_info(
1095           std::chrono::local_info::nonexistent,
1096           std::chrono::sys_info(
1097               to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s),
1098               to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s),
1099               -(3h + 30min + 52s),
1100               0min,
1101               "NST"),
1102           std::chrono::sys_info(
1103               to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s),
1104               to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 52s),
1105               -(2h + 30min + 52s),
1106               60min,
1107               "NDT")),
1108       tz->get_info(to_local_seconds(1917y, std::chrono::April, 8d, 2h)));
1109 
1110   assert_equal(
1111       std::chrono::local_info(
1112           std::chrono::local_info::nonexistent,
1113           std::chrono::sys_info(
1114               to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s),
1115               to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s),
1116               -(3h + 30min + 52s),
1117               0min,
1118               "NST"),
1119           std::chrono::sys_info(
1120               to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s),
1121               to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 52s),
1122               -(2h + 30min + 52s),
1123               60min,
1124               "NDT")),
1125       tz->get_info(to_local_seconds(1917y, std::chrono::April, 8d, 2h, 59min, 59s)));
1126 
1127   assert_equal(
1128       std::chrono::local_info(
1129           std::chrono::local_info::unique,
1130           std::chrono::sys_info(
1131               to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s),
1132               to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 52s),
1133               -(2h + 30min + 52s),
1134               60min,
1135               "NDT"),
1136           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1137       tz->get_info(to_local_seconds(1917y, std::chrono::April, 8d, 3h)));
1138 
1139   assert_equal(
1140       std::chrono::local_info(
1141           std::chrono::local_info::unique,
1142           std::chrono::sys_info(
1143               to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s),
1144               to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 52s),
1145               -(2h + 30min + 52s),
1146               60min,
1147               "NDT"),
1148           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1149       tz->get_info(to_local_seconds(1917y, std::chrono::September, 17d, 0h, 59min, 59s)));
1150 
1151   assert_equal(
1152       std::chrono::local_info(
1153           std::chrono::local_info::ambiguous,
1154           std::chrono::sys_info(
1155               to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s),
1156               to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 52s),
1157               -(2h + 30min + 52s),
1158               60min,
1159               "NDT"),
1160           std::chrono::sys_info(
1161               to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 52s),
1162               to_sys_seconds(1918y, std::chrono::April, 14d, 5h, 30min, 52s),
1163               -(3h + 30min + 52s),
1164               0min,
1165               "NST")),
1166       tz->get_info(to_local_seconds(1917y, std::chrono::September, 17d, 1h)));
1167 
1168   assert_equal(
1169       std::chrono::local_info(
1170           std::chrono::local_info::ambiguous,
1171           std::chrono::sys_info(
1172               to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s),
1173               to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 52s),
1174               -(2h + 30min + 52s),
1175               60min,
1176               "NDT"),
1177           std::chrono::sys_info(
1178               to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 52s),
1179               to_sys_seconds(1918y, std::chrono::April, 14d, 5h, 30min, 52s),
1180               -(3h + 30min + 52s),
1181               0min,
1182               "NST")),
1183       tz->get_info(to_local_seconds(1917y, std::chrono::September, 17d, 1h, 59min, 59s)));
1184 
1185   assert_equal(
1186       std::chrono::local_info(
1187           std::chrono::local_info::unique,
1188           std::chrono::sys_info(
1189               to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 52s),
1190               to_sys_seconds(1918y, std::chrono::April, 14d, 5h, 30min, 52s),
1191               -(3h + 30min + 52s),
1192               0min,
1193               "NST"),
1194           std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1195       tz->get_info(to_local_seconds(1917y, std::chrono::September, 17d, 2h)));
1196 }
1197 
validate_transitions(const std::chrono::time_zone & zone)1198 static void validate_transitions(const std::chrono::time_zone& zone) {
1199   using namespace std::literals::chrono_literals;
1200 
1201   constexpr auto begin = std::chrono::time_point_cast<std::chrono::seconds>(
1202       static_cast<std::chrono::sys_days>(std::chrono::year_month_day{first, std::chrono::January, 1d}));
1203   constexpr auto end = std::chrono::time_point_cast<std::chrono::seconds>(
1204       static_cast<std::chrono::sys_days>(std::chrono::year_month_day{last, std::chrono::January, 1d}));
1205 
1206   // Builds the set of sys_info objects for the selected time range.
1207   std::vector<std::chrono::sys_info> input;
1208   std::chrono::sys_seconds s = begin;
1209   do {
1210     input.emplace_back(zone.get_info(s));
1211     s = input.back().end;
1212   } while (s < end);
1213 
1214   for (auto previous = input.begin(), next = previous + 1; next != input.end(); ++previous, ++next) {
1215     // Now iterates to all adjacent objects.
1216     // For every transition gets the locate time of the
1217     // - end of the first          (a)
1218     // - the start if the second   (b)
1219     // Depending on the difference between 'a' and 'b' different tests are done.
1220     std::chrono::local_seconds end_previous{previous->end.time_since_epoch() + previous->offset};
1221     std::chrono::local_seconds begin_next{next->begin.time_since_epoch() + next->offset};
1222 
1223     if (end_previous == begin_next) {
1224       // unique transition
1225       // a |------------|
1226       // b              |----------|
1227       //                T
1228       assert_equal(std::chrono::local_info(
1229                        std::chrono::local_info::unique,
1230                        *previous,
1231                        std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1232                    zone.get_info(end_previous - 1s));
1233 
1234       assert_equal(std::chrono::local_info(
1235                        std::chrono::local_info::unique,
1236                        *next,
1237                        std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1238                    zone.get_info(begin_next));
1239 
1240     } else if (end_previous < begin_next) {
1241       // non-existent transition
1242       // a |------------|
1243       // b                 |----------|
1244       //                T  T
1245       assert_equal(std::chrono::local_info(
1246                        std::chrono::local_info::unique,
1247                        *previous,
1248                        std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1249                    zone.get_info(end_previous - 1s));
1250 
1251       assert_equal(std::chrono::local_info(std::chrono::local_info::nonexistent, *previous, *next),
1252                    zone.get_info(end_previous));
1253 
1254       assert_equal(std::chrono::local_info(std::chrono::local_info::nonexistent, *previous, *next),
1255                    zone.get_info(begin_next - 1s));
1256 
1257       assert_equal(std::chrono::local_info(
1258                        std::chrono::local_info::unique,
1259                        *next,
1260                        std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1261                    zone.get_info(begin_next));
1262 
1263     } else {
1264       // ambiguous transition
1265       // a |------------|
1266       // b           |----------|
1267       //             T  T
1268       assert_equal(std::chrono::local_info(
1269                        std::chrono::local_info::unique,
1270                        *previous,
1271                        std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1272                    zone.get_info(begin_next - 1s));
1273 
1274       assert_equal(std::chrono::local_info(std::chrono::local_info::ambiguous, *previous, *next),
1275                    zone.get_info(begin_next));
1276 
1277       assert_equal(std::chrono::local_info(std::chrono::local_info::ambiguous, *previous, *next),
1278                    zone.get_info(end_previous - 1s));
1279 
1280       assert_equal(std::chrono::local_info(
1281                        std::chrono::local_info::unique,
1282                        *next,
1283                        std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
1284                    zone.get_info(end_previous));
1285     }
1286   }
1287 }
1288 
main(int,const char **)1289 int main(int, const char**) {
1290   test_gmt();
1291   test_local_time_out_of_range();
1292   test_indian_kerguelen();
1293   test_antarctica_rothera();
1294 
1295   test_asia_hong_kong();
1296   test_europe_berlin();
1297   test_europe_dublin();
1298   test_america_st_johns();
1299 
1300   const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
1301   for (const auto& zone : tzdb.zones) {
1302     validate_transitions(zone);
1303   }
1304 
1305   return 0;
1306 }
1307