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 // TODO TZDB review the test based on review comments in
10 // https://github.com/llvm/llvm-project/pull/85619
11 
12 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23, c++26
13 // UNSUPPORTED: no-filesystem, no-localization, no-tzdb
14 
15 // XFAIL: libcpp-has-no-experimental-tzdb
16 // XFAIL: availability-tzdb-missing
17 
18 // <chrono>
19 
20 // class time_zone;
21 
22 // template <class _Duration>
23 //   sys_info get_info(const sys_time<_Duration>& time) const;
24 
25 // This test uses the system provided database. This makes the test portable,
26 // but may cause failures when the database information changes. Historic data
27 // may change if new facts are uncovered, future data may change when regions
28 // change their time zone or daylight saving time. Most tests will not look in
29 // the future to attempt to avoid issues. All tests list the data on which they
30 // are based, this makes debugging easier upon failure; including to see whether
31 // the provided data has not been changed
32 //
33 //
34 // The data in the tests can be validated by using the zdump tool. For
35 // example
36 //   zdump -v Asia/Hong_Kong
37 // show all transistions in the Hong Kong time zone. Or
38 //   zdump -c1970,1980 -v Asia/Hong_Kong
39 // shows all transitions in Hong Kong between 1970 and 1980.
40 
41 #include <algorithm>
42 #include <cassert>
43 #include <chrono>
44 #include <format>
45 
46 #include "test_macros.h"
47 #include "assert_macros.h"
48 #include "concat_macros.h"
49 
50 /***** ***** HELPERS ***** *****/
51 
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})52 [[nodiscard]] static std::chrono::sys_seconds to_sys_seconds(
53     std::chrono::year year,
54     std::chrono::month month,
55     std::chrono::day day,
56     std::chrono::hours h   = std::chrono::hours(0),
57     std::chrono::minutes m = std::chrono::minutes{0},
58     std::chrono::seconds s = std::chrono::seconds{0}) {
59   std::chrono::year_month_day result{year, month, day};
60 
61   return std::chrono::time_point_cast<std::chrono::seconds>(static_cast<std::chrono::sys_days>(result)) + h + m + s;
62 }
63 
assert_equal(const std::chrono::sys_info & lhs,const std::chrono::sys_info & rhs)64 static void assert_equal(const std::chrono::sys_info& lhs, const std::chrono::sys_info& rhs) {
65   TEST_REQUIRE(lhs.begin == rhs.begin,
66                TEST_WRITE_CONCATENATED("\nBegin:\nExpected output ", lhs.begin, "\nActual output   ", rhs.begin, '\n'));
67   TEST_REQUIRE(lhs.end == rhs.end,
68                TEST_WRITE_CONCATENATED("\nEnd:\nExpected output ", lhs.end, "\nActual output   ", rhs.end, '\n'));
69   TEST_REQUIRE(
70       lhs.offset == rhs.offset,
71       TEST_WRITE_CONCATENATED("\nOffset:\nExpected output ", lhs.offset, "\nActual output   ", rhs.offset, '\n'));
72   TEST_REQUIRE(lhs.save == rhs.save,
73                TEST_WRITE_CONCATENATED("\nSave:\nExpected output ", lhs.save, "\nActual output   ", rhs.save, '\n'));
74   TEST_REQUIRE(
75       lhs.abbrev == rhs.abbrev,
76       TEST_WRITE_CONCATENATED("\nAbbrev:\nExpected output ", lhs.abbrev, "\nActual output   ", rhs.abbrev, '\n'));
77 }
78 
assert_equal(std::string_view expected,const std::chrono::sys_info & value)79 static void assert_equal(std::string_view expected, const std::chrono::sys_info& value) {
80   // Note the output of operator<< is implementation defined, use this
81   // format to keep the test portable.
82   std::string result = std::format(
83       "[{}, {}) {:%T} {:%Q%q} {}",
84       value.begin,
85       value.end,
86       std::chrono::hh_mm_ss{value.offset},
87       value.save,
88       value.abbrev);
89 
90   TEST_REQUIRE(expected == result,
91                TEST_WRITE_CONCATENATED("\nExpected output ", expected, "\nActual output   ", result, '\n'));
92 }
93 
94 static void
assert_range(std::string_view expected,const std::chrono::sys_info & begin,const std::chrono::sys_info & end)95 assert_range(std::string_view expected, const std::chrono::sys_info& begin, const std::chrono::sys_info& end) {
96   assert_equal(expected, begin);
97   assert_equal(expected, end);
98 }
99 
assert_cycle(std::string_view expected_1,const std::chrono::sys_info & begin_1,const std::chrono::sys_info & end_1,std::string_view expected_2,const std::chrono::sys_info & begin_2,const std::chrono::sys_info & end_2)100 static void assert_cycle(
101     std::string_view expected_1,
102     const std::chrono::sys_info& begin_1,
103     const std::chrono::sys_info& end_1,
104     std::string_view expected_2,
105     const std::chrono::sys_info& begin_2,
106     const std::chrono::sys_info& end_2
107 
108 ) {
109   assert_range(expected_1, begin_1, end_1);
110   assert_range(expected_2, begin_2, end_2);
111 }
112 
113 /***** ***** TESTS ***** *****/
114 
test_gmt()115 static void test_gmt() {
116   // Simple zone always valid, no rule entries, lookup using a link.
117   // L Etc/GMT GMT
118   // Z Etc/GMT 0 - GMT
119 
120   const std::chrono::time_zone* tz = std::chrono::locate_zone("GMT");
121 
122   assert_equal(
123       std::chrono::sys_info(
124           std::chrono::sys_seconds::min(),
125           std::chrono::sys_seconds::max(),
126           std::chrono::seconds(0),
127           std::chrono::minutes(0),
128           "GMT"),
129       tz->get_info(std::chrono::sys_seconds::min()));
130   assert_equal(
131       std::chrono::sys_info(
132           std::chrono::sys_seconds::min(),
133           std::chrono::sys_seconds::max(),
134           std::chrono::seconds(0),
135           std::chrono::minutes(0),
136           "GMT"),
137       tz->get_info(std::chrono::sys_seconds(std::chrono::seconds{0})));
138 
139   assert_equal(
140       std::chrono::sys_info(
141           std::chrono::sys_seconds::min(),
142           std::chrono::sys_seconds::max(),
143           std::chrono::seconds(0),
144           std::chrono::minutes(0),
145           "GMT"),
146       tz->get_info(std::chrono::sys_seconds::max() - std::chrono::seconds{1})); // max is not valid
147 }
148 
test_durations()149 static void test_durations() {
150   // Doesn't test a location, instead tests whether different duration
151   // specializations work.
152   const std::chrono::time_zone* tz = std::chrono::locate_zone("GMT");
153 
154   // Using the GMT zone means every call gives the same result.
155   std::chrono::sys_info expected(
156       std::chrono::sys_seconds::min(),
157       std::chrono::sys_seconds::max(),
158       std::chrono::seconds(0),
159       std::chrono::minutes(0),
160       "GMT");
161 
162   assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::nanoseconds>{}));
163   assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::microseconds>{}));
164   assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::milliseconds>{}));
165   assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::seconds>{}));
166   assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::minutes>{}));
167   assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::minutes>{}));
168   assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::hours>{}));
169   assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::days>{}));
170   assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::weeks>{}));
171   assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::months>{}));
172   assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::years>{}));
173 }
174 
test_antarctica_syowa()175 static void test_antarctica_syowa() {
176   // One change, no rules, no dst changes
177   // This change uses an ON field with a day number
178   //
179   // There don't seem to be rule-less zones that use last day or a
180   // contrained day
181 
182   // Z Antarctica/Syowa 0 - -00 1957 Ja 29
183   // 3 - +03
184 
185   const std::chrono::time_zone* tz = std::chrono::locate_zone("Antarctica/Syowa");
186 
187   std::chrono::sys_seconds transition =
188       to_sys_seconds(std::chrono::year(1957), std::chrono::January, std::chrono::day(29));
189 
190   assert_equal(
191       std::chrono::sys_info(
192           std::chrono::sys_seconds::min(), //
193           transition,                      //
194           std::chrono::seconds(0),         //
195           std::chrono::minutes(0),         //
196           "-00"),                          //
197       tz->get_info(std::chrono::sys_seconds::min()));
198 
199   assert_equal(
200       std::chrono::sys_info(
201           std::chrono::sys_seconds::min(), //
202           transition,                      //
203           std::chrono::seconds(0),         //
204           std::chrono::minutes(0),         //
205           "-00"),                          //
206       tz->get_info(transition - std::chrono::seconds(1)));
207 
208   assert_equal(
209       std::chrono::sys_info(
210           transition,                      //
211           std::chrono::sys_seconds::max(), //
212           std::chrono::hours(3),           //
213           std::chrono::minutes(0),         //
214           "+03"),                          //
215       tz->get_info(transition));
216 }
217 
test_asia_hong_kong()218 static void test_asia_hong_kong() {
219   // A more typical entry, first some hard-coded entires and then at the
220   // end a rules based entry. This rule is valid for its entire period
221   //
222   // Z Asia/Hong_Kong 7:36:42 - LMT 1904 O 30 0:36:42
223   // 8 - HKT 1941 Jun 15 3
224   // 8 1 HKST 1941 O 1 4
225   // 8 0:30 HKWT 1941 D 25
226   // 9 - JST 1945 N 18 2
227   // 8 HK HK%sT
228   //
229   // R HK 1946 o - Ap 21 0 1 S
230   // R HK 1946 o - D 1 3:30s 0 -
231   // R HK 1947 o - Ap 13 3:30s 1 S
232   // R HK 1947 o - N 30 3:30s 0 -
233   // R HK 1948 o - May 2 3:30s 1 S
234   // R HK 1948 1952 - O Su>=28 3:30s 0 -
235   // R HK 1949 1953 - Ap Su>=1 3:30 1 S
236   // R HK 1953 1964 - O Su>=31 3:30 0 -
237   // R HK 1954 1964 - Mar Su>=18 3:30 1 S
238   // R HK 1965 1976 - Ap Su>=16 3:30 1 S
239   // R HK 1965 1976 - O Su>=16 3:30 0 -
240   // R HK 1973 o - D 30 3:30 1 S
241   // R HK 1979 o - May 13 3:30 1 S
242   // R HK 1979 o - O 21 3:30 0 -
243 
244   using namespace std::literals::chrono_literals;
245   const std::chrono::time_zone* tz = std::chrono::locate_zone("Asia/Hong_Kong");
246 
247   assert_equal(
248       std::chrono::sys_info(
249           std::chrono::sys_seconds::min(),
250           to_sys_seconds(1904y, std::chrono::October, 29d, 17h), // 7:36:42 - LMT 1904 O 30 0:36:42
251           7h + 36min + 42s,
252           0min,
253           "LMT"),
254       tz->get_info(std::chrono::sys_seconds::min()));
255 
256   assert_equal(
257       std::chrono::sys_info(
258           std::chrono::sys_seconds::min(),
259           to_sys_seconds(1904y, std::chrono::October, 29d, 17h), // 7:36:42 - LMT 1904 O 30 0:36:42
260           7h + 36min + 42s,
261           0min,
262           "LMT"),
263       tz->get_info(to_sys_seconds(1904y, std::chrono::October, 29d, 16h, 59min, 59s)));
264 
265   assert_range("[1904-10-29 17:00:00, 1941-06-14 19:00:00) 08:00:00 0min HKT", // 8 - HKT 1941 Jun 15 3
266                tz->get_info(to_sys_seconds(1904y, std::chrono::October, 29d, 17h)),
267                tz->get_info(to_sys_seconds(1941y, std::chrono::June, 14d, 18h, 59min, 59s)));
268 
269   assert_range("[1941-06-14 19:00:00, 1941-09-30 19:00:00) 09:00:00 60min HKST", // 8 1 HKST 1941 O 1 4
270                tz->get_info(to_sys_seconds(1941y, std::chrono::June, 14d, 19h)),
271                tz->get_info(to_sys_seconds(1941y, std::chrono::September, 30d, 18h, 59min, 59s)));
272 
273   assert_range("[1941-09-30 19:00:00, 1941-12-24 15:30:00) 08:30:00 30min HKWT", // 8 0:30 HKWT 1941 D 25
274                tz->get_info(to_sys_seconds(1941y, std::chrono::September, 30d, 19h)),
275                tz->get_info(to_sys_seconds(1941y, std::chrono::December, 24d, 15h, 29min, 59s)));
276 
277   assert_range("[1941-12-24 15:30:00, 1945-11-17 17:00:00) 09:00:00 0min JST", // 9 - JST 1945 N 18 2
278                tz->get_info(to_sys_seconds(1941y, std::chrono::December, 24d, 15h, 30min)),
279                tz->get_info(to_sys_seconds(1945y, std::chrono::November, 17d, 16h, 59min, 59s)));
280 
281   assert_range("[1945-11-17 17:00:00, 1946-04-20 16:00:00) 08:00:00 0min HKT", // 8 HK%sT
282                tz->get_info(to_sys_seconds(1945y, std::chrono::November, 17d, 17h)),
283                tz->get_info(to_sys_seconds(1946y, std::chrono::April, 20d, 15h, 59min, 59s)));
284 
285   assert_cycle( // 8 HK%sT
286       "[1946-04-20 16:00:00, 1946-11-30 19:30:00) 09:00:00 60min HKST",
287       tz->get_info(to_sys_seconds(1946y, std::chrono::April, 20d, 16h)),                // 1946 o Ap 21 0 1 S
288       tz->get_info(to_sys_seconds(1946y, std::chrono::November, 30d, 19h, 29min, 59s)), // 1946 o D 1 3:30s 0 -
289       "[1946-11-30 19:30:00, 1947-04-12 19:30:00) 08:00:00 0min HKT",
290       tz->get_info(to_sys_seconds(1946y, std::chrono::November, 30d, 19h, 30min)),    // 1946 o D 1 3:30s 0 -
291       tz->get_info(to_sys_seconds(1947y, std::chrono::April, 12d, 19h, 29min, 59s))); // 1947 o Ap 13 3:30s 1 S
292 
293   assert_cycle( // 8 HK%sT
294       "[1947-04-12 19:30:00, 1947-11-29 19:30:00) 09:00:00 60min HKST",
295       tz->get_info(to_sys_seconds(1947y, std::chrono::April, 12d, 19h, 30min)),         // 1947 o Ap 13 3:30s 1 S
296       tz->get_info(to_sys_seconds(1947y, std::chrono::November, 29d, 19h, 29min, 59s)), // 1947 o N 30 3:30s 0 -
297       "[1947-11-29 19:30:00, 1948-05-01 19:30:00) 08:00:00 0min HKT",
298       tz->get_info(to_sys_seconds(1947y, std::chrono::November, 29d, 19h, 30min)), // 1947 o N 30 3:30s 0 -
299       tz->get_info(to_sys_seconds(1948y, std::chrono::May, 1d, 19h, 29min, 59s))); // 1948 o May 2 3:30s 1 S
300 
301   assert_cycle( // 8 HK%sT
302       "[1948-05-01 19:30:00, 1948-10-30 19:30:00) 09:00:00 60min HKST",
303       tz->get_info(to_sys_seconds(1948y, std::chrono::May, 1d, 19h, 30min)),           // 1948 o May 2 3:30s 1 S
304       tz->get_info(to_sys_seconds(1948y, std::chrono::October, 30d, 19h, 29min, 59s)), // 1948 1952 O Su>=28 3:30s 0 -
305       "[1948-10-30 19:30:00, 1949-04-02 19:30:00) 08:00:00 0min HKT",
306       tz->get_info(to_sys_seconds(1948y, std::chrono::October, 30d, 19h, 30min)),    // 1948 1952 O Su>=28 3:30s 0 -
307       tz->get_info(to_sys_seconds(1949y, std::chrono::April, 2d, 19h, 29min, 59s))); // 1949 1953 Ap Su>=1 3:30 1 S
308 
309   assert_cycle( // 8 HK%sT
310       "[1949-04-02 19:30:00, 1949-10-29 19:30:00) 09:00:00 60min HKST",
311       tz->get_info(to_sys_seconds(1949y, std::chrono::April, 2d, 19h, 30min)),         // 1949 1953 Ap Su>=1 3:30 1 S
312       tz->get_info(to_sys_seconds(1949y, std::chrono::October, 29d, 19h, 29min, 59s)), // 1948 1952 O Su>=28 3:30s 0
313       "[1949-10-29 19:30:00, 1950-04-01 19:30:00) 08:00:00 0min HKT",
314       tz->get_info(to_sys_seconds(1949y, std::chrono::October, 29d, 19h, 30min)),    // 1948 1952 O Su>=28 3:30s 0
315       tz->get_info(to_sys_seconds(1950y, std::chrono::April, 1d, 19h, 29min, 59s))); // 1949 1953 Ap Su>=1 3:30 1 S
316 
317   assert_range(
318       "[1953-10-31 18:30:00, 1954-03-20 19:30:00) 08:00:00 0min HKT",
319       tz->get_info(to_sys_seconds(1953y, std::chrono::October, 31d, 18h, 30min)),     // 1953 1964 - O Su>=31 3:30 0 -
320       tz->get_info(to_sys_seconds(1954y, std::chrono::March, 20d, 19h, 29min, 59s))); // 1954 1964 - Mar Su>=18 3:30 1 S
321 
322   assert_cycle( // 8 HK%sT
323       "[1953-04-04 19:30:00, 1953-10-31 18:30:00) 09:00:00 60min HKST",
324       tz->get_info(to_sys_seconds(1953y, std::chrono::April, 4d, 19h, 30min)),         // 1949 1953 Ap Su>=1 3:30 1 S
325       tz->get_info(to_sys_seconds(1953y, std::chrono::October, 31d, 18h, 29min, 59s)), // 1953 1964 - O Su>=31 3:30 0 -
326       "[1953-10-31 18:30:00, 1954-03-20 19:30:00) 08:00:00 0min HKT",
327       tz->get_info(to_sys_seconds(1953y, std::chrono::October, 31d, 18h, 30min)),     // 1953 1964 - O Su>=31 3:30 0 -
328       tz->get_info(to_sys_seconds(1954y, std::chrono::March, 20d, 19h, 29min, 59s))); // 1954 1964 - Mar Su>=18 3:30 1 S
329 
330   assert_cycle( // 8 HK%sT
331       "[1972-04-15 19:30:00, 1972-10-21 18:30:00) 09:00:00 60min HKST",
332       tz->get_info(to_sys_seconds(1972y, std::chrono::April, 19d, 19h, 30min)),        // 1965 1976 - Ap Su>=16 3:30 1 S
333       tz->get_info(to_sys_seconds(1972y, std::chrono::October, 21d, 18h, 29min, 59s)), // 1965 1976 - O Su>=16 3:30 0 -
334       "[1972-10-21 18:30:00, 1973-04-21 19:30:00) 08:00:00 0min HKT",
335       tz->get_info(to_sys_seconds(1972y, std::chrono::October, 21d, 18h, 30min)),     // 1965 1976 - O Su>=16 3:30 0 -
336       tz->get_info(to_sys_seconds(1973y, std::chrono::April, 21d, 19h, 29min, 59s))); // 1965 1976 - Ap Su>=16 3:30 1 S
337 
338   assert_range( // 8 HK%sT
339       "[1973-04-21 19:30:00, 1973-10-20 18:30:00) 09:00:00 60min HKST",
340       tz->get_info(to_sys_seconds(1973y, std::chrono::April, 21d, 19h, 30min)), // 1965 1976 - Ap Su>=16 3:30 1 S
341       tz->get_info(to_sys_seconds(1973y, std::chrono::October, 20d, 18h, 29min, 59s))); // 1965 1976 - O Su>=16 3:30 0 -
342 
343   assert_range( // 8 HK%sT, test "1973 o - D 30 3:30 1 S"
344       "[1973-10-20 18:30:00, 1973-12-29 19:30:00) 08:00:00 0min HKT",
345       tz->get_info(to_sys_seconds(1973y, std::chrono::October, 20d, 18h, 30min)),        // 1965 1976 - O Su>=16 3:30
346       tz->get_info(to_sys_seconds(1973y, std::chrono::December, 29d, 19h, 29min, 59s))); // 1973 o - D 30 3:30 1 S
347 
348   assert_range( // 8 HK%sT
349       "[1973-12-29 19:30:00, 1974-10-19 18:30:00) 09:00:00 60min HKST",
350       tz->get_info(to_sys_seconds(1973y, std::chrono::December, 29d, 19h, 30min)),      // 1973 o - D 30 3:30 1 S
351       tz->get_info(to_sys_seconds(1974y, std::chrono::October, 19d, 18h, 29min, 59s))); // 1965 1976 - O Su>=16 3:30
352 
353   assert_range( // 8 HK%sT, between 1973 and 1979 no rule is active so falls back to default
354       "[1976-04-17 19:30:00, 1976-10-16 18:30:00) 09:00:00 60min HKST",
355       tz->get_info(to_sys_seconds(1976y, std::chrono::April, 17d, 19h, 30min)), // 1965 1976 - Ap Su>=16 3:30 1 S
356       tz->get_info(to_sys_seconds(1976y, std::chrono::October, 16d, 18h, 29min, 59s))); // 1965 1976 - O Su>=16 3:30 0 -
357 
358   assert_range( // 8 HK%sT, between 1973 and 1979 no rule is active so falls back to default
359       "[1976-10-16 18:30:00, 1979-05-12 19:30:00) 08:00:00 0min HKT",
360       tz->get_info(to_sys_seconds(1976y, std::chrono::October, 16d, 18h, 30min)),   // 1965 1976 - O Su>=16 3:30 0 -
361       tz->get_info(to_sys_seconds(1979y, std::chrono::May, 12d, 19h, 29min, 59s))); // 1979 o - May 13 3:30 1 S
362 
363   assert_range( // 8 HK%sT
364       "[1979-05-12 19:30:00, 1979-10-20 18:30:00) 09:00:00 60min HKST",
365       tz->get_info(to_sys_seconds(1979y, std::chrono::May, 12d, 19h, 30min)),           // 1979 o - May 13 3:30 1 S
366       tz->get_info(to_sys_seconds(1979y, std::chrono::October, 20d, 18h, 29min, 59s))); // 1979 o - O 21 3:30 0 -
367 
368   assert_equal(
369       std::chrono::sys_info(
370           to_sys_seconds(1979y, std::chrono::October, 20d, 18h, 30min),
371           std::chrono::sys_seconds::max(),
372           8h,
373           std::chrono::minutes(0),
374           "HKT"),
375       tz->get_info(to_sys_seconds(1979y, std::chrono::October, 20d, 18h, 30min)));
376 
377   assert_equal(
378       std::chrono::sys_info(
379           to_sys_seconds(1979y, std::chrono::October, 20d, 18h, 30min),
380           std::chrono::sys_seconds::max(),
381           8h,
382           std::chrono::minutes(0),
383           "HKT"),
384       tz->get_info(std::chrono::sys_seconds::max() - std::chrono::seconds{1})); // max is not valid
385 }
386 
test_europe_berlin()387 static void test_europe_berlin() {
388   // A more typical entry, first some hard-coded entires and then at the
389   // end a rules based entry. This rule is valid for its entire period
390   //
391 
392   // Z Europe/Berlin 0:53:28 - LMT 1893 Ap
393   // 1 c CE%sT 1945 May 24 2
394   // 1 So CE%sT 1946
395   // 1 DE CE%sT 1980
396   // 1 E CE%sT
397   //
398   // R c 1916 o - Ap 30 23 1 S
399   // R c 1916 o - O 1 1 0 -
400   // R c 1917 1918 - Ap M>=15 2s 1 S
401   // R c 1917 1918 - S M>=15 2s 0 -
402   // R c 1940 o - Ap 1 2s 1 S
403   // R c 1942 o - N 2 2s 0 -
404   // R c 1943 o - Mar 29 2s 1 S
405   // R c 1943 o - O 4 2s 0 -
406   // R c 1944 1945 - Ap M>=1 2s 1 S
407   // R c 1944 o - O 2 2s 0 -
408   // R c 1945 o - S 16 2s 0 -
409   // R c 1977 1980 - Ap Su>=1 2s 1 S
410   // R c 1977 o - S lastSu 2s 0 -
411   // R c 1978 o - O 1 2s 0 -
412   // R c 1979 1995 - S lastSu 2s 0 -
413   // R c 1981 ma - Mar lastSu 2s 1 S
414   // R c 1996 ma - O lastSu 2s 0 -
415   //
416   // R So 1945 o - May 24 2 2 M
417   // R So 1945 o - S 24 3 1 S
418   // R So 1945 o - N 18 2s 0 -
419   //
420   // R DE 1946 o - Ap 14 2s 1 S
421   // R DE 1946 o - O 7 2s 0 -
422   // R DE 1947 1949 - O Su>=1 2s 0 -
423   // R DE 1947 o - Ap 6 3s 1 S
424   // R DE 1947 o - May 11 2s 2 M
425   // R DE 1947 o - Jun 29 3 1 S
426   // R DE 1948 o - Ap 18 2s 1 S
427   // R DE 1949 o - Ap 10 2s 1 S
428   //
429   // R E 1977 1980 - Ap Su>=1 1u 1 S
430   // R E 1977 o - S lastSu 1u 0 -
431   // R E 1978 o - O 1 1u 0 -
432   // R E 1979 1995 - S lastSu 1u 0 -
433   // R E 1981 ma - Mar lastSu 1u 1 S
434   // R E 1996 ma - O lastSu 1u 0 -
435   //
436   // Note the European Union decided to stop the seasonal change in
437   // 2021. In 2023 seasonal changes are still in effect.
438 
439   using namespace std::literals::chrono_literals;
440   const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Berlin");
441 
442   assert_equal(
443       std::chrono::sys_info(
444           std::chrono::sys_seconds::min(),
445           to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 32s), // 0:53:28 - LMT 1893 Ap
446           53min + 28s,
447           0min,
448           "LMT"),
449       tz->get_info(std::chrono::sys_seconds::min()));
450 
451   assert_equal(
452       std::chrono::sys_info(
453           std::chrono::sys_seconds::min(),
454           to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 32s), // 0:53:28 - LMT 1893 Ap
455           53min + 28s,
456           0min,
457           "LMT"),
458       tz->get_info(to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 31s)));
459 
460   assert_range(
461       // 1 CE%sT before 1916 o - Ap 30 23 1 S
462       "[1893-03-31 23:06:32, 1916-04-30 22:00:00) 01:00:00 0min CET",
463       tz->get_info(to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 32s)),
464       tz->get_info(to_sys_seconds(1916y, std::chrono::April, 30d, 21h, 59min, 59s)));
465 
466   assert_cycle(
467       // 1 CE%sT
468       "[1916-04-30 22:00:00, 1916-09-30 23:00:00) 02:00:00 60min CEST",
469       tz->get_info(to_sys_seconds(1916y, std::chrono::April, 30d, 22h)),                 // 1916 o - Ap 30 23 1 S
470       tz->get_info(to_sys_seconds(1916y, std::chrono::September, 30d, 22h, 59min, 59s)), // o - O 1 1 0 -
471       "[1916-09-30 23:00:00, 1917-04-16 01:00:00) 01:00:00 0min CET",
472       tz->get_info(to_sys_seconds(1916y, std::chrono::September, 30d, 23h)),         // o - O 1 1 0 -
473       tz->get_info(to_sys_seconds(1917y, std::chrono::April, 16d, 0h, 59min, 59s))); // 1917 1918 - Ap M>=15 2s 1 S
474 
475   assert_cycle(
476       // 1 CE%sT
477       "[1917-04-16 01:00:00, 1917-09-17 01:00:00) 02:00:00 60min CEST",
478       tz->get_info(to_sys_seconds(1917y, std::chrono::April, 16d, 1h)),                 // 1917 1918 Ap M>=15 2s 1 S
479       tz->get_info(to_sys_seconds(1917y, std::chrono::September, 17d, 0h, 59min, 59s)), // 1917 1918 S M>=15 2s 0 -
480       "[1917-09-17 01:00:00, 1918-04-15 01:00:00) 01:00:00 0min CET",
481       tz->get_info(to_sys_seconds(1917y, std::chrono::September, 17d, 1h)),          // 1917 1918 S M>=15 2s 0 -
482       tz->get_info(to_sys_seconds(1918y, std::chrono::April, 15d, 0h, 59min, 59s))); // 1917 1918 Ap M>=15 2s 1 S
483 
484   assert_cycle(
485       // 1 CE%sT (The cycle is more than 1 year)
486       "[1918-04-15 01:00:00, 1918-09-16 01:00:00) 02:00:00 60min CEST",
487       tz->get_info(to_sys_seconds(1918y, std::chrono::April, 15d, 1h)),                 // 1917 1918 Ap M>=15 2s 1 S
488       tz->get_info(to_sys_seconds(1918y, std::chrono::September, 16d, 0h, 59min, 59s)), // 1917 1918 S M>=15 2s 0 -
489       "[1918-09-16 01:00:00, 1940-04-01 01:00:00) 01:00:00 0min CET",
490       tz->get_info(to_sys_seconds(1918y, std::chrono::September, 16d, 1h)),         // 1917 1918 S M>=15 2s 0 -
491       tz->get_info(to_sys_seconds(1940y, std::chrono::April, 1d, 0h, 59min, 59s))); // 1940 o Ap 1 2s 1 S
492 
493   assert_cycle(
494       // 1 CE%sT (The cycle is more than 1 year)
495       "[1940-04-01 01:00:00, 1942-11-02 01:00:00) 02:00:00 60min CEST",
496       tz->get_info(to_sys_seconds(1940y, std::chrono::April, 1d, 1h)),                // 1940 o Ap 1 2s 1 S
497       tz->get_info(to_sys_seconds(1942y, std::chrono::November, 2d, 0h, 59min, 59s)), // 1942 o N 2 2s 0 -
498       "[1942-11-02 01:00:00, 1943-03-29 01:00:00) 01:00:00 0min CET",
499       tz->get_info(to_sys_seconds(1942y, std::chrono::November, 2d, 1h)),            // 1942 o N 2 2s 0 -
500       tz->get_info(to_sys_seconds(1943y, std::chrono::March, 29d, 0h, 59min, 59s))); // 1943 o Mar 29 2s 1 S
501 
502   assert_range(
503       // Here the zone changes from c (C-Eur) to So (SovietZone).
504       // The rule c ends on 1945-09-16, instead it ends at the zone change date/time
505       // There is a tricky part in the time
506       // "1 c CE%sT" has an offset of 1 at the moment the rule
507       // ends there is a save of 60 minutes. This means the
508       // local offset to UTC is 2 hours. The rule ends at
509       // 1945-05-24 02:00:00 local time, which is
510       // 1945-05-24 00:00:00 UTC.
511       "[1945-04-02 01:00:00, 1945-05-24 00:00:00) 02:00:00 60min CEST",
512       tz->get_info(to_sys_seconds(1945y, std::chrono::April, 2d, 1h)),              // 1 CE%sT & 1945 Ap M>=1 2s 1 S
513       tz->get_info(to_sys_seconds(1945y, std::chrono::May, 23d, 23h, 59min, 59s))); // 1 c CE%sT & 1945 May 24 2
514 
515   assert_range( // --
516       "[1945-05-24 00:00:00, 1945-09-24 00:00:00) 03:00:00 120min CEMT",
517       tz->get_info(to_sys_seconds(1945y, std::chrono::May, 24d)),                         // 1 c CE%sT & 1945 May 24 2
518       tz->get_info(to_sys_seconds(1945y, std::chrono::September, 23d, 23h, 59min, 59s))); // 1945 o S 24 3 1 S
519 
520   assert_range(
521       // 1 c CE%sT 1945 May 24 2
522       "[1945-09-24 00:00:00, 1945-11-18 01:00:00) 02:00:00 60min CEST",
523       tz->get_info(to_sys_seconds(1945y, std::chrono::September, 24d)),                 // 1945 o S 24 3 1 S
524       tz->get_info(to_sys_seconds(1945y, std::chrono::November, 18d, 0h, 59min, 59s))); // 1945 o N 18 2s 0 -
525   assert_range(                                                                         // --
526                                                                                         // Merges 2 continuations
527       "[1945-11-18 01:00:00, 1946-04-14 01:00:00) 01:00:00 0min CET",
528       tz->get_info(to_sys_seconds(1945y, std::chrono::November, 18d, 1h)),           // 1 c CE%sT & 1945 o N 18 2s 0 -
529       tz->get_info(to_sys_seconds(1946y, std::chrono::April, 14d, 0h, 59min, 59s))); // 1 So CE%sT & 1946 o Ap 14 2s 1 S
530 
531   assert_range(
532       // 1 DE CE%sT 1980
533       "[1946-04-14 01:00:00, 1946-10-07 01:00:00) 02:00:00 60min CEST",
534       tz->get_info(to_sys_seconds(1946y, std::chrono::April, 14d, 1h)),               // 1946 o Ap 14 2s 1 S
535       tz->get_info(to_sys_seconds(1946y, std::chrono::October, 7d, 0h, 59min, 59s))); // 1946 o O 7 2s 0 -
536 
537   // Note 1947 is an interesting year with 4 rules
538   // R DE 1947 1949 - O Su>=1 2s 0 -
539   // R DE 1947 o - Ap 6 3s 1 S
540   // R DE 1947 o - May 11 2s 2 M
541   // R DE 1947 o - Jun 29 3 1 S
542   assert_range(
543       // 1 DE CE%sT 1980
544       "[1946-10-07 01:00:00, 1947-04-06 02:00:00) 01:00:00 0min CET",
545       tz->get_info(to_sys_seconds(1946y, std::chrono::October, 7d, 1h)),            // 1946 o O 7 2s 0 -
546       tz->get_info(to_sys_seconds(1947y, std::chrono::April, 6d, 1h, 59min, 59s))); // 1947 o Ap 6 3s 1 S
547 
548   assert_range(
549       // 1 DE CE%sT 1980
550       "[1947-04-06 02:00:00, 1947-05-11 01:00:00) 02:00:00 60min CEST",
551       tz->get_info(to_sys_seconds(1947y, std::chrono::April, 6d, 2h)),             // 1947 o Ap 6 3s 1 S
552       tz->get_info(to_sys_seconds(1947y, std::chrono::May, 11d, 0h, 59min, 59s))); // 1947 o May 11 2s 2 M
553 
554   assert_range(
555       // 1 DE CE%sT 1980
556       "[1947-05-11 01:00:00, 1947-06-29 00:00:00) 03:00:00 120min CEMT",
557       tz->get_info(to_sys_seconds(1947y, std::chrono::May, 11d, 1h)),                // 1947 o May 11 2s 2 M
558       tz->get_info(to_sys_seconds(1947y, std::chrono::June, 28d, 23h, 59min, 59s))); // 1947 o Jun 29 3 1 S
559 
560   assert_cycle(
561       // 1 DE CE%sT 1980
562       "[1947-06-29 00:00:00, 1947-10-05 01:00:00) 02:00:00 60min CEST",
563       tz->get_info(to_sys_seconds(1947y, std::chrono::June, 29d)),                   // 1947 o Jun 29 3 1 S
564       tz->get_info(to_sys_seconds(1947y, std::chrono::October, 5d, 0h, 59min, 59s)), // 1947 1949 O Su>=1 2s 0 -
565       "[1947-10-05 01:00:00, 1948-04-18 01:00:00) 01:00:00 0min CET",
566       tz->get_info(to_sys_seconds(1947y, std::chrono::October, 5d, 1h)),             // 1947 1949 O Su>=1 2s 0 -
567       tz->get_info(to_sys_seconds(1948y, std::chrono::April, 18d, 0h, 59min, 59s))); // 1948 o Ap 18 2s 1 S
568 
569   assert_cycle(
570       // 1 DE CE%sT 1980
571       "[1948-04-18 01:00:00, 1948-10-03 01:00:00) 02:00:00 60min CEST",
572       tz->get_info(to_sys_seconds(1948y, std::chrono::April, 18d, 1h)),              // 1948 o Ap 18 2s 1 S
573       tz->get_info(to_sys_seconds(1948y, std::chrono::October, 3d, 0h, 59min, 59s)), // 1947 1949 O Su>=1 2s 0 -
574       "[1948-10-03 01:00:00, 1949-04-10 01:00:00) 01:00:00 0min CET",
575       tz->get_info(to_sys_seconds(1948y, std::chrono::October, 3d, 1h)),             // 1947 1949 O Su>=1 2s 0 -
576       tz->get_info(to_sys_seconds(1949y, std::chrono::April, 10d, 0h, 59min, 59s))); // 1949 o Ap 10 2s 1 S
577 
578   assert_cycle( // Note the end time is  in a different continuation.
579       "[1949-04-10 01:00:00, 1949-10-02 01:00:00) 02:00:00 60min CEST",              // 1 DE CE%sT 1980
580       tz->get_info(to_sys_seconds(1949y, std::chrono::April, 10d, 1h)),              //  1949 o Ap 10 2s 1 S
581       tz->get_info(to_sys_seconds(1949y, std::chrono::October, 2d, 0h, 59min, 59s)), //  1947 1949 O Su>=1 2s 0 -
582       "[1949-10-02 01:00:00, 1980-04-06 01:00:00) 01:00:00 0min CET",
583       tz->get_info(to_sys_seconds(1949y, std::chrono::October, 2d, 1h)),   //  1947 1949 O Su>=1 2s 0 -
584       tz->get_info(                                                        // 1 E CE%sT
585           to_sys_seconds(1980y, std::chrono::April, 6d, 0h, 59min, 59s))); //  1977 1980 Ap Su>=1 1u 1 S
586 
587   assert_cycle(
588       // 1 E CE%sT
589       "[2020-03-29 01:00:00, 2020-10-25 01:00:00) 02:00:00 60min CEST",
590       tz->get_info(to_sys_seconds(2020y, std::chrono::March, 29d, 1h)),               // 1981 ma Mar lastSu 1u 1 S
591       tz->get_info(to_sys_seconds(2020y, std::chrono::October, 25d, 0h, 59min, 59s)), // 1996 ma O lastSu 1u 0 -
592       "[2020-10-25 01:00:00, 2021-03-28 01:00:00) 01:00:00 0min CET",
593       tz->get_info(to_sys_seconds(2020y, std::chrono::October, 25d, 1h)),            // 1996 ma O lastSu 1u 0 -
594       tz->get_info(to_sys_seconds(2021y, std::chrono::March, 28d, 0h, 59min, 59s))); // 1981 ma Mar lastSu 1u 1 S
595 
596   assert_cycle(
597       // 1 E CE%sT
598       "[2021-03-28 01:00:00, 2021-10-31 01:00:00) 02:00:00 60min CEST",
599       tz->get_info(to_sys_seconds(2021y, std::chrono::March, 28d, 1h)),               // 1981 ma Mar lastSu 1u 1 S
600       tz->get_info(to_sys_seconds(2021y, std::chrono::October, 31d, 0h, 59min, 59s)), // 1996 ma O lastSu 1u 0 -
601       "[2021-10-31 01:00:00, 2022-03-27 01:00:00) 01:00:00 0min CET",
602       tz->get_info(to_sys_seconds(2021y, std::chrono::October, 31d, 1h)),            // 1996 ma O lastSu 1u 0 -
603       tz->get_info(to_sys_seconds(2022y, std::chrono::March, 27d, 0h, 59min, 59s))); // 1981 ma Mar lastSu 1u 1 S
604 }
605 
test_america_st_johns()606 static void test_america_st_johns() {
607   // A more typical entry,
608   // Uses letters both when DST is ative and not and has multiple
609   // letters. Uses negetive offsets.
610   // Switches several times between their own and Canadian rules
611   // Switches the stdoff from -3:30:52 to -3:30 while observing the same rule
612 
613   // Z America/St_Johns -3:30:52 - LMT 1884
614   // -3:30:52 j N%sT 1918
615   // -3:30:52 C N%sT 1919
616   // -3:30:52 j N%sT 1935 Mar 30
617   // -3:30 j N%sT 1942 May 11
618   // -3:30 C N%sT 1946
619   // -3:30 j N%sT 2011 N
620   // -3:30 C N%sT
621   //
622   // R j 1917 o - Ap 8 2 1 D
623   // R j 1917 o - S 17 2 0 S
624   // R j 1919 o - May 5 23 1 D
625   // R j 1919 o - Au 12 23 0 S
626   // R j 1920 1935 - May Su>=1 23 1 D
627   // R j 1920 1935 - O lastSu 23 0 S
628   // R j 1936 1941 - May M>=9 0 1 D
629   // R j 1936 1941 - O M>=2 0 0 S
630   // R j 1946 1950 - May Su>=8 2 1 D
631   // R j 1946 1950 - O Su>=2 2 0 S
632   // R j 1951 1986 - Ap lastSu 2 1 D
633   // R j 1951 1959 - S lastSu 2 0 S
634   // R j 1960 1986 - O lastSu 2 0 S
635   // R j 1987 o - Ap Su>=1 0:1 1 D
636   // R j 1987 2006 - O lastSu 0:1 0 S
637   // R j 1988 o - Ap Su>=1 0:1 2 DD
638   // R j 1989 2006 - Ap Su>=1 0:1 1 D
639   // R j 2007 2011 - Mar Su>=8 0:1 1 D
640   // R j 2007 2010 - N Su>=1 0:1 0 S
641   //
642   // R C 1918 o - Ap 14 2 1 D
643   // R C 1918 o - O 27 2 0 S
644   // R C 1942 o - F 9 2 1 W
645   // R C 1945 o - Au 14 23u 1 P
646   // R C 1945 o - S 30 2 0 S
647   // R C 1974 1986 - Ap lastSu 2 1 D
648   // R C 1974 2006 - O lastSu 2 0 S
649   // R C 1987 2006 - Ap Su>=1 2 1 D
650   // R C 2007 ma - Mar Su>=8 2 1 D
651   // R C 2007 ma - N Su>=1 2 0 S
652 
653   using namespace std::literals::chrono_literals;
654   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/St_Johns");
655 
656   assert_equal( // --
657       std::chrono::sys_info(
658           std::chrono::sys_seconds::min(),
659           to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s), // -3:30:52 - LMT 1884
660           -(3h + 30min + 52s),
661           0min,
662           "LMT"),
663       tz->get_info(std::chrono::sys_seconds::min()));
664 
665   assert_equal( // --
666       std::chrono::sys_info(
667           std::chrono::sys_seconds::min(),
668           to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s), // -3:30:52 - LMT 1884
669           -(3h + 30min + 52s),
670           0min,
671           "LMT"),
672       tz->get_info(to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 51s)));
673 
674   assert_range( // -3:30:52 j N%sT 1918
675       "[1884-01-01 03:30:52, 1917-04-08 05:30:52) -03:30:52 0min NST",
676       tz->get_info(to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s)), // no rule active
677       tz->get_info(to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 51s)));  // 1917 o Ap 8 2 1 D
678 
679   assert_range( // -3:30:52 j N%sT 1918
680       "[1917-04-08 05:30:52, 1917-09-17 04:30:52) -02:30:52 60min NDT",
681       tz->get_info(to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s)),       // 1917 o Ap 8 2 1 D
682       tz->get_info(to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 51s))); // 1917 o S 17 2 0 S
683 
684   assert_range("[1917-09-17 04:30:52, 1918-04-14 05:30:52) -03:30:52 0min NST",
685                tz->get_info(                                                            // -3:30:52 j N%sT 1918
686                    to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 52s)), //   1917 o S 17 2 0 S
687                tz->get_info(                                                            // -3:30:52 C N%sT 1919
688                    to_sys_seconds(1918y, std::chrono::April, 14d, 5h, 30min, 51s)));    //   1918 o Ap 14 2 1 D
689 
690   assert_range( // -3:30:52 C N%sT 1919
691       "[1918-04-14 05:30:52, 1918-10-27 04:30:52) -02:30:52 60min NDT",
692       tz->get_info(to_sys_seconds(1918y, std::chrono::April, 14d, 5h, 30min, 52s)),    // 1918 o Ap 14 2 1 D
693       tz->get_info(to_sys_seconds(1918y, std::chrono::October, 27d, 4h, 30min, 51s))); // 1918 o O 27 2 0 S
694 
695   assert_range("[1918-10-27 04:30:52, 1919-05-06 02:30:52) -03:30:52 0min NST",
696                tz->get_info(                                                          // -3:30:52 C N%sT 1919
697                    to_sys_seconds(1918y, std::chrono::October, 27d, 4h, 30min, 52s)), //   1918 o O 27 2 0 S
698                tz->get_info(                                                          // -3:30:52 j N%sT 1935 Mar 30
699                    to_sys_seconds(1919y, std::chrono::May, 6d, 2h, 30min, 51s)));     //   1919 o May 5 23 1 D
700 
701   assert_range( // -3:30:52 j N%sT 1935 Mar 30
702       "[1934-10-29 01:30:52, 1935-03-30 03:30:52) -03:30:52 0min NST",
703       tz->get_info(to_sys_seconds(1934y, std::chrono::October, 29d, 1h, 30min, 52s)), // 1920 1935 O lastSu 23 0 S
704       tz->get_info(to_sys_seconds(1935y, std::chrono::March, 30d, 3h, 30min, 51s)));  // 1920 1935 May Su>=1 23 1 D
705 
706   assert_range( // -3:30 j N%sT 1942 May 11
707                 // Changed the stdoff while the same rule remains active.
708       "[1935-03-30 03:30:52, 1935-05-06 02:30:00) -03:30:00 0min NST",
709       tz->get_info(to_sys_seconds(1935y, std::chrono::March, 30d, 3h, 30min, 52s)), // 1920 1935 O lastSu 23 0 S
710       tz->get_info(to_sys_seconds(1935y, std::chrono::May, 6d, 2h, 29min, 59s)));   // 1920 1935 May Su>=1 23 1 D
711 
712   assert_range( // -3:30 j N%sT 1942 May 11
713       "[1935-05-06 02:30:00, 1935-10-28 01:30:00) -02:30:00 60min NDT",
714       tz->get_info(to_sys_seconds(1935y, std::chrono::May, 6d, 2h, 30min, 0s)),        // 1920 1935 May Su>=1 23 1 D
715       tz->get_info(to_sys_seconds(1935y, std::chrono::October, 28d, 1h, 29min, 59s))); // 1920 1935 O lastSu 23 0 S
716 
717   assert_range( // -3:30 j N%sT 1942 May 11
718       "[1941-10-06 02:30:00, 1942-05-11 03:30:00) -03:30:00 0min NST",
719       tz->get_info(to_sys_seconds(1941y, std::chrono::October, 6d, 2h, 30min, 0s)), // 1936 1941 O M>=2 0 0 S
720       tz->get_info(to_sys_seconds(1942y, std::chrono::May, 11d, 3h, 29min, 59s)));  // 1946 1950 May Su>=8 2 1 D
721 
722   assert_range( // -3:30 C N%sT 1946
723       "[1942-05-11 03:30:00, 1945-08-14 23:00:00) -02:30:00 60min NWT",
724       tz->get_info(to_sys_seconds(1942y, std::chrono::May, 11d, 3h, 30min, 0s)),       // 1942 o F 9 2 1 W
725       tz->get_info(to_sys_seconds(1945y, std::chrono::August, 14d, 22h, 59min, 59s))); // 1945 o Au 14 23u 1 P
726 
727   assert_range( // -3:30 C N%sT 1946
728       "[1945-08-14 23:00:00, 1945-09-30 04:30:00) -02:30:00 60min NPT",
729       tz->get_info(to_sys_seconds(1945y, std::chrono::August, 14d, 23h, 0min, 0s)),      // 1945 o Au 14 23u 1 P
730       tz->get_info(to_sys_seconds(1945y, std::chrono::September, 30d, 4h, 29min, 59s))); // 1945 o S 30 2 0 S
731 
732   assert_range(
733       "[1945-09-30 04:30:00, 1946-05-12 05:30:00) -03:30:00 0min NST",
734       tz->get_info(
735           to_sys_seconds(1945y, std::chrono::September, 30d, 4h, 30min, 0s)), // -3:30 C N%sT 1946 & 945 o S 30 2 0 S
736       tz->get_info(to_sys_seconds(
737           1946y, std::chrono::May, 12d, 5h, 29min, 59s))); // -3:30 j N%sT 2011 N & 1946 1950 May Su>=8 2 1 D
738 
739   assert_range( // -3:30 j N%sT 2011 N
740       "[1988-04-03 03:31:00, 1988-10-30 01:31:00) -01:30:00 120min NDDT",
741       tz->get_info(to_sys_seconds(1988y, std::chrono::April, 3d, 3h, 31min, 0s)),      // 1988 o Ap Su>=1 0:1 2 DD
742       tz->get_info(to_sys_seconds(1988y, std::chrono::October, 30d, 1h, 30min, 59s))); // 1987 2006 O lastSu 0:1 0 S
743 
744   assert_range("[2011-03-13 03:31:00, 2011-11-06 04:30:00) -02:30:00 60min NDT",
745                tz->get_info(                                                            // -3:30 j N%sT 2011 N
746                    to_sys_seconds(2011y, std::chrono::March, 13d, 3h, 31min, 0s)),      //   2007 2011 Mar Su>=8 0:1 1 D
747                tz->get_info(                                                            // -3:30 C N%sT
748                    to_sys_seconds(2011y, std::chrono::November, 6d, 04h, 29min, 59s))); //   2007 ma N Su>=1 2 0 S
749 }
750 
test_get_at_standard_time_universal()751 static void test_get_at_standard_time_universal() {
752   // Z Asia/Barnaul 5:35 - LMT 1919 D 10
753   // ...
754   // 7 R +07/+08 1995 May 28
755   // 6 R +06/+07 2011 Mar 27 2s
756   // ...
757   //
758   // ...
759   // R R 1985 2010 - Mar lastSu 2s 1 S
760   // R R 1996 2010 - O lastSu 2s 0 -
761 
762   using namespace std::literals::chrono_literals;
763   const std::chrono::time_zone* tz = std::chrono::locate_zone("Asia/Barnaul");
764 
765   assert_equal(
766       std::chrono::sys_info(
767           to_sys_seconds(2010y, std::chrono::October, 30d, 20h),
768           to_sys_seconds(2011y, std::chrono::March, 26d, 20h),
769           6h,
770           0min,
771           "+06"),
772       tz->get_info(to_sys_seconds(2010y, std::chrono::October, 31d, 10h)));
773 }
774 
test_get_at_standard_time_standard()775 static void test_get_at_standard_time_standard() {
776   // Z Africa/Bissau -1:2:20 - LMT 1912 Ja 1 1u
777   using namespace std::literals::chrono_literals;
778   const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Bissau");
779 
780   assert_equal(
781       std::chrono::sys_info(
782           std::chrono::sys_seconds::min(),
783           to_sys_seconds(1912y, std::chrono::January, 1d, 1h),
784           -(1h + 2min + 20s),
785           0min,
786           "LMT"),
787       tz->get_info(std::chrono::sys_seconds::min()));
788 }
789 
test_get_at_save_universal()790 static void test_get_at_save_universal() {
791   // Z America/Tijuana -7:48:4 - LMT 1922 Ja 1 0:11:56
792   // -7 - MST 1924
793   // -8 - PST 1927 Jun 10 23
794   // -7 - MST 1930 N 15
795   // -8 - PST 1931 Ap
796   // -8 1 PDT 1931 S 30
797   // -8 - PST 1942 Ap 24
798   // -8 1 PWT 1945 Au 14 23u
799   // ...
800 
801   using namespace std::literals::chrono_literals;
802   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Tijuana");
803 
804   assert_equal(
805       std::chrono::sys_info(
806           to_sys_seconds(1942y, std::chrono::April, 24d, 8h),
807           to_sys_seconds(1945y, std::chrono::August, 14d, 23h),
808           -7h,
809           60min,
810           "PWT"),
811       tz->get_info(to_sys_seconds(1942y, std::chrono::April, 24d, 8h)));
812 }
813 
test_get_at_rule_standard()814 static void test_get_at_rule_standard() {
815   // Z Antarctica/Macquarie 0 - -00 1899 N
816   // 10 - AEST 1916 O 1 2
817   // 10 1 AEDT 1917 F
818   // 10 AU AE%sT 1919 Ap 1 0s
819   // ...
820   //
821   // R AU 1917 o - Ja 1 2s 1 D
822   // R AU 1917 o - Mar lastSu 2s 0 S
823   // R AU 1942 o - Ja 1 2s 1 D
824   // ...
825 
826   using namespace std::literals::chrono_literals;
827   const std::chrono::time_zone* tz = std::chrono::locate_zone("Antarctica/Macquarie");
828 
829   // Another rule where the S propagates?
830   assert_equal(
831       std::chrono::sys_info(
832           to_sys_seconds(1916y, std::chrono::September, 30d, 16h),
833           to_sys_seconds(1917y, std::chrono::March, 24d, 16h),
834           11h,
835           60min,
836           "AEDT"),
837       tz->get_info(to_sys_seconds(1916y, std::chrono::September, 30d, 16h)));
838 }
839 
test_get_at_rule_universal()840 static void test_get_at_rule_universal() {
841   // Z America/Nuuk -3:26:56 - LMT 1916 Jul 28
842   // -3 - -03 1980 Ap 6 2
843   // -3 E -03/-02 2023 O 29 1u
844   // -2 E -02/-01
845   //
846   // R E 1977 1980 - Ap Su>=1 1u 1 S
847   // R E 1977 o - S lastSu 1u 0 -
848   // R E 1978 o - O 1 1u 0 -
849   // R E 1979 1995 - S lastSu 1u 0 -
850   // R E 1981 ma - Mar lastSu 1u 1 S
851   // R E 1996 ma - O lastSu 1u 0 -
852 
853   using namespace std::literals::chrono_literals;
854   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Nuuk");
855 
856   assert_equal(
857       std::chrono::sys_info(
858           to_sys_seconds(1980y, std::chrono::April, 6d, 5h),
859           to_sys_seconds(1980y, std::chrono::September, 28d, 1h),
860           -2h,
861           60min,
862           "-02"),
863       tz->get_info(to_sys_seconds(1980y, std::chrono::April, 6d, 5h)));
864 }
865 
test_format_with_alternatives_west()866 static void test_format_with_alternatives_west() {
867   // Z America/Nuuk -3:26:56 - LMT 1916 Jul 28
868   // -3 - -03 1980 Ap 6 2
869   // -3 E -03/-02 2023 O 29 1u
870   // -2 E -02/-01
871   //
872   // ...
873   // R E 1981 ma - Mar lastSu 1u 1 S
874   // R E 1996 ma - O lastSu 1u 0 -
875 
876   using namespace std::literals::chrono_literals;
877   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Nuuk");
878 
879   assert_cycle( // -3 E -03/-02
880       "[2019-10-27 01:00:00, 2020-03-29 01:00:00) -03:00:00 0min -03",
881       tz->get_info(to_sys_seconds(2019y, std::chrono::October, 27d, 1h)),           // 1981 ma Mar lastSu 1u 1 S
882       tz->get_info(to_sys_seconds(2020y, std::chrono::March, 29d, 0h, 59min, 59s)), // 1996 ma O lastSu 1u 0 -
883       "[2020-03-29 01:00:00, 2020-10-25 01:00:00) -02:00:00 60min -02",
884       tz->get_info(to_sys_seconds(2020y, std::chrono::March, 29d, 1h)),                // 1996 ma O lastSu 1u 0 -
885       tz->get_info(to_sys_seconds(2020y, std::chrono::October, 25d, 0h, 59min, 59s))); // 1981 ma Mar lastSu 1u 1 S
886 }
887 
test_format_with_alternatives_east()888 static void test_format_with_alternatives_east() {
889   // Z Asia/Barnaul 5:35 - LMT 1919 D 10
890   // ...
891   // 6 R +06/+07 2011 Mar 27 2s
892   // ...
893   //
894   // ...
895   // R R 1985 2010 - Mar lastSu 2s 1 S
896   // R R 1996 2010 - O lastSu 2s 0 -
897 
898   using namespace std::literals::chrono_literals;
899   const std::chrono::time_zone* tz = std::chrono::locate_zone("Asia/Barnaul");
900 
901   assert_cycle( // 6 R +06/+07 2011 Mar 27 2s
902       "[2000-03-25 20:00:00, 2000-10-28 20:00:00) 07:00:00 60min +07",
903       tz->get_info(to_sys_seconds(2000y, std::chrono::March, 25d, 20h)),               // 1985 2010 Mar lastSu 2s 1 S
904       tz->get_info(to_sys_seconds(2000y, std::chrono::October, 28d, 19h, 59min, 59s)), // 1996 2010 O lastSu 2s 0 -
905       "[2000-10-28 20:00:00, 2001-03-24 20:00:00) 06:00:00 0min +06",
906       tz->get_info(to_sys_seconds(2000y, std::chrono::October, 28d, 20h)),            // 1996 2010 O lastSu 2s 0 -
907       tz->get_info(to_sys_seconds(2001y, std::chrono::March, 24d, 19h, 59min, 59s))); // 1985 2010 Mar lastSu 2s 1 S
908 }
909 
test_africa_algiers()910 static void test_africa_algiers() {
911   using namespace std::literals::chrono_literals;
912   const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Algiers");
913 
914   assert_equal(
915       std::chrono::sys_info(
916           to_sys_seconds(1977y, std::chrono::October, 20d, 23h),
917           to_sys_seconds(1978y, std::chrono::March, 24d),
918           1h,
919           std::chrono::minutes(0),
920           "CET"),
921       tz->get_info(to_sys_seconds(1977y, std::chrono::October, 20d, 23h)));
922 
923   assert_range("[1977-05-06 00:00:00, 1977-10-20 23:00:00) 01:00:00 60min WEST", // 0 d WE%sT 1977 O 21
924                tz->get_info(to_sys_seconds(1977y, std::chrono::May, 6d)),
925                tz->get_info(to_sys_seconds(1977y, std::chrono::October, 20d, 22h, 59min, 59s)));
926 
927   assert_range("[1977-10-20 23:00:00, 1978-03-24 00:00:00) 01:00:00 0min CET", // 1 d CE%sT 1979 O 26
928                tz->get_info(to_sys_seconds(1977y, std::chrono::October, 20d, 23h)),
929                tz->get_info(to_sys_seconds(1978y, std::chrono::March, 23d, 23h, 59min, 59s)));
930 }
931 
test_africa_casablanca()932 static void test_africa_casablanca() {
933   // Z Africa/Casablanca -0:30:20 - LMT 1913 O 26
934   // 0 M +00/+01 1984 Mar 16
935   // 1 - +01 1986
936   // 0 M +00/+01 2018 O 28 3
937   // 1 M +01/+00
938   //
939   // ...
940   // R M 2013 2018 - O lastSu 3 0 -
941   // R M 2014 2018 - Mar lastSu 2 1 -
942   // R M 2014 o - Jun 28 3 0 -
943   // R M 2014 o - Au 2 2 1 -
944   // R M 2015 o - Jun 14 3 0 -
945   // R M 2015 o - Jul 19 2 1 -
946   // R M 2016 o - Jun 5 3 0 -
947   // R M 2016 o - Jul 10 2 1 -
948   // R M 2017 o - May 21 3 0 -
949   // R M 2017 o - Jul 2 2 1 -
950   // R M 2018 o - May 13 3 0 -
951   // R M 2018 o - Jun 17 2 1 -
952   // R M 2019 o - May 5 3 -1 -
953   // R M 2019 o - Jun 9 2 0 -
954   // R M 2020 o - Ap 19 3 -1 -
955   // ...
956 
957   using namespace std::literals::chrono_literals;
958   const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Casablanca");
959 
960   assert_range("[2018-06-17 02:00:00, 2018-10-28 02:00:00) 01:00:00 60min +01",
961                tz->get_info(to_sys_seconds(2018y, std::chrono::June, 17d, 2h)),
962                tz->get_info(to_sys_seconds(2018y, std::chrono::October, 28d, 1h, 59min, 59s)));
963 
964   assert_range("[2018-10-28 02:00:00, 2019-05-05 02:00:00) 01:00:00 0min +01",
965                tz->get_info( // 1 M +01/+00 & R M 2018 o - Jun 17 2 1 -
966                    to_sys_seconds(2018y, std::chrono::October, 28d, 2h)),
967                tz->get_info( // 1 M +01/+00 & R M 2019 o - May 5 3 -1 -
968                    to_sys_seconds(2019y, std::chrono::May, 5d, 1h, 59min, 59s)));
969 
970   // 1 M +01/+00
971   // Note the SAVE contains a negative value
972   assert_range("[2019-05-05 02:00:00, 2019-06-09 02:00:00) 00:00:00 -60min +00",
973                tz->get_info(to_sys_seconds(2019y, std::chrono::May, 5d, 2h)),               // R M 2019 o - May 5 3 -1 -
974                tz->get_info(to_sys_seconds(2019y, std::chrono::June, 9d, 1h, 59min, 59s))); // R M 2019 o - Jun 9 2 0 -
975 
976   assert_range("[2019-06-09 02:00:00, 2020-04-19 02:00:00) 01:00:00 0min +01",
977                tz->get_info( // 1 M +01/+00 & R M 2019 o - Jun 9 2 0 -
978                    to_sys_seconds(2019y, std::chrono::June, 9d, 2h)),
979                tz->get_info( // 1 M +01/+00 & R M 2020 o - Ap 19 3 -1 -
980                    to_sys_seconds(2020y, std::chrono::April, 19d, 1h, 59min, 59s))); //
981 }
982 
test_africa_ceuta()983 static void test_africa_ceuta() {
984   // Z Africa/Ceuta -0:21:16 - LMT 1900 D 31 23:38:44
985   // 0 - WET 1918 May 6 23
986   // 0 1 WEST 1918 O 7 23
987   // 0 - WET 1924
988   // 0 s WE%sT 1929
989   // 0 - WET 1967
990   // 0 Sp WE%sT 1984 Mar 16
991   // 1 - CET 1986
992   // 1 E CE%sT
993   //
994   // ...
995   // R s 1926 o - Ap 17 23 1 S
996   // R s 1926 1929 - O Sa>=1 24s 0 -
997   // R s 1927 o - Ap 9 23 1 S
998   // R s 1928 o - Ap 15 0 1 S
999   // R s 1929 o - Ap 20 23 1 S
1000   // R s 1937 o - Jun 16 23 1 S
1001   // ...
1002   //
1003   // R Sp 1967 o - Jun 3 12 1 S
1004   // R Sp 1967 o - O 1 0 0 -
1005   // R Sp 1974 o - Jun 24 0 1 S
1006   // R Sp 1974 o - S 1 0 0 -
1007   // R Sp 1976 1977 - May 1 0 1 S
1008   // R Sp 1976 o - Au 1 0 0 -
1009   // R Sp 1977 o - S 28 0 0 -
1010   // R Sp 1978 o - Jun 1 0 1 S
1011   // R Sp 1978 o - Au 4 0 0 -
1012 
1013   using namespace std::literals::chrono_literals;
1014   const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Ceuta");
1015 
1016   assert_range(
1017 
1018       "[1928-10-07 00:00:00, 1967-06-03 12:00:00) 00:00:00 0min WET",
1019       tz->get_info(to_sys_seconds(1928y, std::chrono::October, 7d)),       // 0 s WE%sT 1929 & 1926 1929 O Sa>=1 24s 0 -
1020       tz->get_info(                                                        // No transitions in "0 - WET 1967"
1021           to_sys_seconds(1967y, std::chrono::June, 3d, 11h, 59min, 59s))); // 0 - WET 1967 & 1967 o Jun 3 12 1 S
1022 }
1023 
test_africa_freetown()1024 static void test_africa_freetown() {
1025   // Z Africa/Freetown -0:53 - LMT 1882
1026   // -0:53 - FMT 1913 Jul
1027   // -1 SL %s 1939 S 5
1028   // -1 - -01 1941 D 6 24
1029   // 0 - GMT
1030   //
1031   // R SL 1932 o - D 1 0 0:20 -0040
1032   // R SL 1933 1938 - Mar 31 24 0 -01
1033   // R SL 1933 1939 - Au 31 24 0:20 -0040
1034   // R SL 1939 o - May 31 24 0 -01
1035 
1036   using namespace std::literals::chrono_literals;
1037   const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Freetown");
1038 
1039   // When a continuation has a named rule, the tranisition time determined by
1040   // the active rule can be wrong. The next continuation may set the clock to an
1041   // earlier time. This is tested for San Luis. This tests the rule is not used
1042   // when the rule is not a named rule.
1043   //
1044   // Fixes:
1045   //   Expected output [1882-01-01 00:53:00, 1913-07-01 00:53:00) -00:53:00 0min FMT
1046   //   Actual output   [1882-01-01 00:53:00, 1913-07-01 00:46:00) -00:53:00 0min FMT
1047 
1048   assert_range("[1882-01-01 00:53:00, 1913-07-01 00:53:00) -00:53:00 0min FMT",
1049                tz->get_info(to_sys_seconds(1882y, std::chrono::January, 1d, 0h, 53min)), // -0:53 - FMT 1913 Jul
1050                tz->get_info( // -1 SL %s 1939 S 5 & before first rule
1051                    to_sys_seconds(1913y, std::chrono::July, 1d, 0h, 52min, 59s)));
1052 
1053   // Tests whether the "-1 SL %s 1939 S 5" until gets the proper local time
1054   // adjustment.
1055   assert_range("[1939-09-01 01:00:00, 1939-09-05 00:40:00) -00:40:00 20min -0040",
1056                tz->get_info( // -1 SL %s 1939 S 5 & R SL 1933 1939 - Au 31 24 0:20 -0040
1057                    to_sys_seconds(1939y, std::chrono::September, 1d, 1h)),
1058                tz->get_info( // -1 - -01 1941 D 6 24
1059                    to_sys_seconds(1939y, std::chrono::September, 5d, 0h, 39min, 59s)));
1060 }
1061 
test_africa_windhoek()1062 static void test_africa_windhoek() {
1063   // Tests the LETTER/S used before the first rule per
1064   // https://data.iana.org/time-zones/tz-how-to.html
1065   //   If switching to a named rule before any transition has happened,
1066   //   assume standard time (SAVE zero), and use the LETTER data from
1067   //   the earliest transition with a SAVE of zero.
1068 
1069   // Z Africa/Windhoek 1:8:24 - LMT 1892 F 8
1070   // 1:30 - +0130 1903 Mar
1071   // 2 - SAST 1942 S 20 2
1072   // 2 1 SAST 1943 Mar 21 2
1073   // 2 - SAST 1990 Mar 21
1074   // 2 NA %s
1075   //
1076   // R NA 1994 o - Mar 21 0 -1 WAT
1077   // R NA 1994 2017 - S Su>=1 2 0 CAT
1078   // R NA 1995 2017 - Ap Su>=1 2 -1 WAT
1079 
1080   using namespace std::literals::chrono_literals;
1081   const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Windhoek");
1082 
1083   assert_range( // 2 - EET 2012 N 10 2
1084       "[1990-03-20 22:00:00, 1994-03-20 22:00:00) 02:00:00 0min CAT",
1085       tz->get_info(to_sys_seconds(1990y, std::chrono::March, 20d, 22h)),
1086       tz->get_info(to_sys_seconds(1994y, std::chrono::March, 20d, 21h, 59min, 59s)));
1087 }
1088 
test_america_adak()1089 static void test_america_adak() {
1090   // Z America/Adak 12:13:22 - LMT 1867 O 19 12:44:35
1091   // ...
1092   // -11 u B%sT 1983 O 30 2
1093   // -10 u AH%sT 1983 N 30
1094   // -10 u H%sT
1095   //
1096   // ...
1097   // R u 1945 o - S 30 2 0 S
1098   // R u 1967 2006 - O lastSu 2 0 S
1099   // R u 1967 1973 - Ap lastSu 2 1 D
1100   // R u 1974 o - Ja 6 2 1 D
1101   // R u 1975 o - F lastSu 2 1 D
1102   // R u 1976 1986 - Ap lastSu 2 1 D
1103   // R u 1987 2006 - Ap Su>=1 2 1 D
1104   // ...
1105 
1106   using namespace std::literals::chrono_literals;
1107   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Adak");
1108 
1109   assert_range( // 2 - EET 2012 N 10 2
1110       "[1983-10-30 12:00:00, 1983-11-30 10:00:00) -10:00:00 0min AHST",
1111       tz->get_info(to_sys_seconds(1983y, std::chrono::October, 30d, 12h)),              // -11 u B%sT 1983 O 30 2
1112       tz->get_info(to_sys_seconds(1983y, std::chrono::November, 30d, 9h, 59min, 59s))); // -10 u AH%sT 1983 N 30
1113 }
1114 
test_america_auncion()1115 static void test_america_auncion() {
1116   // R y 2013 ma - Mar Su>=22 0 0 -
1117   // Z America/Asuncion -3:50:40 - LMT 1890
1118   // -3:50:40 - AMT 1931 O 10
1119   // -4 - -04 1972 O
1120   // -3 - -03 1974 Ap
1121   // -4 y -04/-03
1122   //
1123   // R y 1975 1988 - O 1 0 1 -
1124   // R y 1975 1978 - Mar 1 0 0 -
1125   // R y 1979 1991 - Ap 1 0 0 -
1126   // ...
1127 
1128   using namespace std::literals::chrono_literals;
1129   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Asuncion");
1130 
1131   assert_range("[1974-04-01 03:00:00, 1975-10-01 04:00:00) -04:00:00 0min -04",
1132                tz->get_info(to_sys_seconds(1974y, std::chrono::April, 1d, 3h)),
1133                tz->get_info(to_sys_seconds(1975y, std::chrono::October, 1d, 3h, 59min, 59s)));
1134 
1135   assert_range("[1975-10-01 04:00:00, 1976-03-01 03:00:00) -03:00:00 60min -03",
1136                tz->get_info(to_sys_seconds(1975y, std::chrono::October, 1d, 4h)),
1137                tz->get_info(to_sys_seconds(1976y, std::chrono::March, 1d, 2h, 59min, 59s)));
1138 }
1139 
test_america_ciudad_juarez()1140 static void test_america_ciudad_juarez() {
1141   // Z America/Ciudad_Juarez -7:5:56 - LMT 1922 Ja 1 7u
1142   // -7 - MST 1927 Jun 10 23
1143   // -6 - CST 1930 N 15
1144   // -7 m MST 1932 Ap
1145   // -6 - CST 1996
1146   // -6 m C%sT 1998
1147   // ...
1148   //
1149   // R m 1939 o - F 5 0 1 D
1150   // R m 1939 o - Jun 25 0 0 S
1151   // R m 1940 o - D 9 0 1 D
1152   // R m 1941 o - Ap 1 0 0 S
1153   // R m 1943 o - D 16 0 1 W
1154   // R m 1944 o - May 1 0 0 S
1155   // R m 1950 o - F 12 0 1 D
1156   // R m 1950 o - Jul 30 0 0 S
1157   // R m 1996 2000 - Ap Su>=1 2 1 D
1158   // R m 1996 2000 - O lastSu 2 0 S
1159   // ...
1160 
1161   using namespace std::literals::chrono_literals;
1162   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Ciudad_Juarez");
1163 
1164   // 1996 has a similar issue, instead of __time the __until end before
1165   // the first rule in 1939. Between the two usages of RULE Mexico
1166   // a different continuation RULE is active
1167   assert_range("[1996-04-07 08:00:00, 1996-10-27 07:00:00) -05:00:00 60min CDT",
1168                tz->get_info(to_sys_seconds(1996y, std::chrono::April, 7d, 8h)),
1169                tz->get_info(to_sys_seconds(1996y, std::chrono::October, 27d, 6h, 59min, 59s)));
1170 }
1171 
test_america_argentina_buenos_aires()1172 static void test_america_argentina_buenos_aires() {
1173   // Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31
1174   // -4:16:48 - CMT 1920 May
1175   // -4 - -04 1930 D
1176   // -4 A -04/-03 1969 O 5
1177   // -3 A -03/-02 1999 O 3
1178   // -4 A -04/-03 2000 Mar 3
1179   // -3 A -03/-02
1180   //
1181   // ...
1182   // R A 1989 1992 - O Su>=15 0 1 -
1183   // R A 1999 o - O Su>=1 0 1 -
1184   // R A 2000 o - Mar 3 0 0 -
1185   // R A 2007 o - D 30 0 1 -
1186   // ...
1187 
1188   // The 1999 switch uses the same rule, but with a different stdoff.
1189   //   R A 1999 o - O Su>=1 0 1 -
1190   //     stdoff -3 -> 1999-10-03 03:00:00
1191   //     stdoff -4 -> 1999-10-03 04:00:00
1192   // This generates an invalid entry and this is evaluated as a transition.
1193   // Looking at the zdump like output in libc++ this generates jumps in
1194   // the UTC time
1195 
1196   using namespace std::literals::chrono_literals;
1197   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Argentina/Buenos_Aires");
1198 
1199   assert_range("[1999-10-03 03:00:00, 2000-03-03 03:00:00) -03:00:00 60min -03",
1200                tz->get_info(to_sys_seconds(1999y, std::chrono::October, 3d, 3h)),
1201                tz->get_info(to_sys_seconds(2000y, std::chrono::March, 3d, 2h, 59min, 59s)));
1202   assert_range("[2000-03-03 03:00:00, 2007-12-30 03:00:00) -03:00:00 0min -03",
1203                tz->get_info(to_sys_seconds(2000y, std::chrono::March, 3d, 3h)),
1204                tz->get_info(to_sys_seconds(2007y, std::chrono::December, 30d, 2h, 59min, 59s)));
1205 }
1206 
test_america_argentina_la_rioja()1207 static void test_america_argentina_la_rioja() {
1208   // Z America/Argentina/La_Rioja -4:27:24 - LMT 1894 O 31
1209   // ...
1210   // -4 A -04/-03 1969 O 5
1211   // -3 A -03/-02 1991 Mar
1212   // -4 - -04 1991 May 7
1213   // -3 A -03/-02 1999 O 3
1214   // ...
1215   //
1216   // ...
1217   // R A 1988 o - D 1 0 1 -
1218   // R A 1989 1993 - Mar Su>=1 0 0 -
1219   // R A 1989 1992 - O Su>=15 0 1 -
1220   // R A 1999 o - O Su>=1 0 1 -
1221   // ...
1222 
1223   using namespace std::literals::chrono_literals;
1224   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Argentina/La_Rioja");
1225 
1226   assert_range("[1990-10-21 03:00:00, 1991-03-01 02:00:00) -02:00:00 60min -02",
1227                tz->get_info(to_sys_seconds(1990y, std::chrono::October, 21d, 3h)),
1228                tz->get_info(to_sys_seconds(1991y, std::chrono::March, 1d, 1h, 59min, 59s)));
1229 }
1230 
test_america_argentina_san_luis()1231 static void test_america_argentina_san_luis() {
1232   // Z America/Argentina/San_Luis -4:25:24 - LMT 1894 O 31
1233   // ...
1234   // -4 A -04/-03 1969 O 5
1235   // -3 A -03/-02 1990
1236   // -3 1 -02 1990 Mar 14
1237   // -4 - -04 1990 O 15
1238   // -4 1 -03 1991 Mar
1239   // -4 - -04 1991 Jun
1240   // -3 - -03 1999 O 3
1241   // -4 1 -03 2000 Mar 3
1242   // -4 - -04 2004 Jul 25
1243   // -3 A -03/-02 2008 Ja 21
1244   // -4 Sa -04/-03 2009 O 11
1245   // -3 - -03
1246   //
1247   // ...
1248   // R A 1988 o - D 1 0 1 -
1249   // R A 1989 1993 - Mar Su>=1 0 0 -
1250   // R A 1989 1992 - O Su>=15 0 1 -
1251   // R A 1999 o - O Su>=1 0 1 -
1252   // R A 2000 o - Mar 3 0 0 -
1253   // R A 2007 o - D 30 0 1 -
1254   // R A 2008 2009 - Mar Su>=15 0 0 -
1255   // R A 2008 o - O Su>=15 0 1 -
1256   //
1257   // R Sa 2008 2009 - Mar Su>=8 0 0 -
1258   // R Sa 2007 2008 - O Su>=8 0 1 -
1259 
1260   using namespace std::literals::chrono_literals;
1261   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Argentina/San_Luis");
1262 
1263   assert_range("[1989-10-15 03:00:00, 1990-03-14 02:00:00) -02:00:00 60min -02",
1264                tz->get_info( // -3 A -03/-02 1990 & R A 1989 1992 - O Su>=15 0 1 -
1265                    to_sys_seconds(1989y, std::chrono::October, 15d, 3h)),
1266                tz->get_info( // UNTIL -3 1 -02 1990 Mar 14
1267                    to_sys_seconds(1990y, std::chrono::March, 14d, 1h, 59min, 59s)));
1268 
1269   assert_range("[2008-01-21 02:00:00, 2008-03-09 03:00:00) -03:00:00 60min -03",
1270                tz->get_info(to_sys_seconds(2008y, std::chrono::January, 21d, 2h)),
1271                tz->get_info(to_sys_seconds(2008y, std::chrono::March, 9d, 2h, 59min, 59s)));
1272 }
1273 
test_america_indiana_knox()1274 static void test_america_indiana_knox() {
1275   // Z America/Indiana/Knox -5:46:30 - LMT 1883 N 18 12:13:30
1276   // -6 u C%sT 1947
1277   // -6 St C%sT 1962 Ap 29 2
1278   // -5 - EST 1963 O 27 2
1279   // -6 u C%sT 1991 O 27 2
1280   // -5 - EST 2006 Ap 2 2
1281   // -6 u C%sT
1282   //
1283   // ...
1284   // R u 1976 1986 - Ap lastSu 2 1 D
1285   // R u 1987 2006 - Ap Su>=1 2 1 D
1286   // R u 2007 ma - Mar Su>=8 2 1 D
1287   // R u 2007 ma - N Su>=1 2 0 S
1288 
1289   using namespace std::literals::chrono_literals;
1290   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Indiana/Knox");
1291 
1292   // The continuations
1293   // -5 - EST
1294   // -6 u C%sT
1295   // have different offsets. The start time of the first active rule in
1296   // RULE u should use the offset at the end of -5 - EST.
1297   assert_range("[2006-04-02 07:00:00, 2006-10-29 07:00:00) -05:00:00 60min CDT",
1298                tz->get_info(to_sys_seconds(2006y, std::chrono::April, 2d, 7h)),
1299                tz->get_info(to_sys_seconds(2006y, std::chrono::October, 29d, 6h, 59min, 59s)));
1300 }
1301 
test_america_punta_arenas()1302 static void test_america_punta_arenas() {
1303   // Z America/Punta_Arenas -4:43:40 - LMT 1890
1304   // ...
1305   // -4 - -04 1919 Jul
1306   // -4:42:45 - SMT 1927 S
1307   // -5 x -05/-04 1932 S
1308   // ...
1309   //
1310   // R x 1927 1931 - S 1 0 1 -
1311   // R x 1928 1932 - Ap 1 0 0 -
1312   // ...
1313 
1314   using namespace std::literals::chrono_literals;
1315   const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Punta_Arenas");
1316 
1317   assert_equal(
1318       std::chrono::sys_info(
1319           to_sys_seconds(1927y, std::chrono::September, 1d, 4h, 42min, 45s),
1320           to_sys_seconds(1928y, std::chrono::April, 1d, 4h),
1321           -4h,
1322           60min,
1323           "-04"),
1324       tz->get_info(to_sys_seconds(1927y, std::chrono::September, 1d, 4h, 42min, 45s)));
1325 
1326   assert_equal(
1327       std::chrono::sys_info(
1328           to_sys_seconds(1927y, std::chrono::September, 1d, 4h, 42min, 45s),
1329           to_sys_seconds(1928y, std::chrono::April, 1d, 4h),
1330           -4h,
1331           60min,
1332           "-04"),
1333       tz->get_info(to_sys_seconds(1928y, std::chrono::April, 1d, 3h, 59min, 59s)));
1334 }
1335 
test_europ_ljubljana()1336 static void test_europ_ljubljana() {
1337   // Z Europe/Ljubljana 0:58:4 - LMT 1884
1338   // 1 - CET 1941 Ap 18 23
1339   // 1 c CE%sT 1945 May 8 2s
1340   // 1 1 CEST 1945 S 16 2s
1341   // 1 - CET 1982 N 27
1342   // 1 E CE%sT
1343   //
1344   // ...
1345   // R c 1943 o - O 4 2s 0 -
1346   // R c 1944 1945 - Ap M>=1 2s 1 S
1347   // R c 1944 o - O 2 2s 0 -
1348   // R c 1945 o - S 16 2s 0 -
1349   // R c 1977 1980 - Ap Su>=1 2s 1 S
1350   // ...
1351 
1352   using namespace std::literals::chrono_literals;
1353   const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Ljubljana");
1354 
1355   assert_equal(
1356       std::chrono::sys_info(
1357           to_sys_seconds(1945y, std::chrono::April, 2d, 1h),
1358           to_sys_seconds(1945y, std::chrono::September, 16d, 1h),
1359           2h,
1360           60min,
1361           "CEST"),
1362       tz->get_info(to_sys_seconds(1945y, std::chrono::April, 2d, 1h)));
1363 
1364   assert_equal(
1365       std::chrono::sys_info(
1366           to_sys_seconds(1945y, std::chrono::April, 2d, 1h),
1367           to_sys_seconds(1945y, std::chrono::September, 16d, 1h),
1368           2h,
1369           60min,
1370           "CEST"),
1371       tz->get_info(to_sys_seconds(1945y, std::chrono::September, 16d, 0h, 59min, 59s)));
1372 }
1373 
main(int,const char **)1374 int main(int, const char**) {
1375   // Basic tests
1376   test_gmt();
1377   test_durations();
1378   test_antarctica_syowa();
1379   test_asia_hong_kong();
1380   test_europe_berlin();
1381 
1382   test_america_st_johns();
1383 
1384   // Small tests for not-yet tested conditions
1385   test_get_at_standard_time_universal();
1386   test_get_at_standard_time_standard();
1387   test_get_at_save_universal();
1388   test_get_at_rule_standard();
1389   test_get_at_rule_universal();
1390 
1391   test_format_with_alternatives_west();
1392   test_format_with_alternatives_east();
1393 
1394   // Tests based on bugs found
1395   test_africa_algiers();
1396   test_africa_casablanca();
1397   test_africa_ceuta();
1398   test_africa_freetown();
1399   test_africa_windhoek();
1400   test_america_adak();
1401   test_america_argentina_buenos_aires();
1402   test_america_argentina_la_rioja();
1403   test_america_argentina_san_luis();
1404   test_america_auncion();
1405   test_america_ciudad_juarez();
1406   test_america_indiana_knox();
1407 
1408   // Reverse search bugs
1409   test_america_punta_arenas();
1410   test_europ_ljubljana();
1411 
1412   return 0;
1413 }
1414