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