1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 // UNSUPPORTED: no-filesystem, no-localization, no-tzdb
11
12 // XFAIL: libcpp-has-no-incomplete-tzdb
13 // XFAIL: availability-tzdb-missing
14
15 // <chrono>
16
17 // Tests the IANA database zones parsing and operations.
18 // This is not part of the public tzdb interface.
19 // The test uses private implementation headers.
20 // ADDITIONAL_COMPILE_FLAGS: -I %S/../../../../../src/include
21
22 #include <cassert>
23 #include <chrono>
24 #include <fstream>
25 #include <string>
26 #include <string_view>
27 #include <variant>
28
29 #include "assert_macros.h"
30 #include "concat_macros.h"
31 #include "filesystem_test_helper.h"
32 #include "test_tzdb.h"
33
34 // headers in the dylib
35 #include "tzdb/types_private.h"
36 #include "tzdb/tzdb_private.h"
37 #include "tzdb/time_zone_private.h"
38
39 scoped_test_env env;
40 [[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo");
41 const std::filesystem::path file = env.create_file("zoneinfo/tzdata.zi");
42
__libcpp_tzdb_directory()43 std::string_view std::chrono::__libcpp_tzdb_directory() {
44 static std::string result = dir.string();
45 return result;
46 }
47
write(std::string_view input)48 static void write(std::string_view input) {
49 static int version = 0;
50
51 std::ofstream f{file};
52 f << "# version " << version++ << '\n';
53 f.write(input.data(), input.size());
54 }
55
parse(std::string_view input)56 static const std::chrono::tzdb& parse(std::string_view input) {
57 write(input);
58 return std::chrono::reload_tzdb();
59 }
60
continuations(const std::chrono::time_zone & time_zone)61 static const std::vector<std::chrono::__tz::__continuation>& continuations(const std::chrono::time_zone& time_zone) {
62 return time_zone.__implementation().__continuations();
63 }
64
test_exception(std::string_view input,std::string_view what)65 static void test_exception(std::string_view input, [[maybe_unused]] std::string_view what) {
66 write(input);
67
68 TEST_VALIDATE_EXCEPTION(
69 std::runtime_error,
70 [&]([[maybe_unused]] const std::runtime_error& e) {
71 TEST_LIBCPP_REQUIRE(
72 e.what() == what,
73 TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
74 },
75 TEST_IGNORE_NODISCARD std::chrono::reload_tzdb());
76 }
77
test_invalid()78 static void test_invalid() {
79 test_exception("Z", "corrupt tzdb: expected whitespace");
80
81 test_exception("Z ", "corrupt tzdb: expected a string");
82
83 test_exception("Z n", "corrupt tzdb: expected whitespace");
84
85 test_exception("Z n ", "corrupt tzdb: expected a digit");
86 test_exception("Z n x", "corrupt tzdb: expected a digit");
87 test_exception("Z n +", "corrupt tzdb: expected a digit");
88
89 test_exception("Z n 0", "corrupt tzdb: expected whitespace");
90
91 test_exception("Z n 0 ", "corrupt tzdb: expected a string");
92
93 test_exception("Z n 0 r", "corrupt tzdb: expected whitespace");
94
95 test_exception("Z n 0 r ", "corrupt tzdb: expected a string");
96 }
97
test_name()98 static void test_name() {
99 const std::chrono::tzdb& result = parse(
100 R"(
101 Z n 0 r f
102 )");
103 assert(result.zones.size() == 1);
104 assert(result.zones[0].name() == "n");
105 }
106
test_stdoff()107 static void test_stdoff() {
108 const std::chrono::tzdb& result = parse(
109 R"(
110 # Based on the examples in the man page.
111 # Note the input is not expected to have fractional seconds, they are truncated.
112 Zo na 2 r f
113 Zon nb 2:00 r f
114 Zone nc 01:28:14 r f
115 zONE nd 00:19:32.10 r f
116 zoNe ne 12:00 r f
117 Z nf 15:00 r f
118 Z ng 24:00 r f
119 Z nh 260:00 r f
120 Z ni -2:30 r f
121 Z nj - r f
122 )");
123
124 assert(result.zones.size() == 10);
125 for (std::size_t i = 0; i < result.zones.size(); ++i)
126 assert(continuations(result.zones[0]).size() == 1);
127
128 assert(continuations(result.zones[0])[0].__stdoff == std::chrono::hours(2));
129 assert(continuations(result.zones[1])[0].__stdoff == std::chrono::hours(2));
130 assert(continuations(result.zones[2])[0].__stdoff ==
131 std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
132 assert(continuations(result.zones[3])[0].__stdoff == std::chrono::minutes(19) + std::chrono::seconds(32));
133 assert(continuations(result.zones[4])[0].__stdoff == std::chrono::hours(12));
134 assert(continuations(result.zones[5])[0].__stdoff == std::chrono::hours(15));
135 assert(continuations(result.zones[6])[0].__stdoff == std::chrono::hours(24));
136 assert(continuations(result.zones[7])[0].__stdoff == std::chrono::hours(260));
137 assert(continuations(result.zones[8])[0].__stdoff == -(std::chrono::hours(2) + std::chrono::minutes(30)));
138 assert(continuations(result.zones[9])[0].__stdoff == std::chrono::hours(0)); // The man page expresses it in hours
139 }
140
test_rules()141 static void test_rules() {
142 const std::chrono::tzdb& result = parse(
143 R"(
144 Z na 0 - f
145 Z nb 0 r f
146 Z nc 0 2d f
147 Z nd 0 2:00s f
148 Z ne 0 0 f
149 Z nf 0 0:00:01 f
150 Z ng 0 -0:00:01 f
151 )");
152
153 assert(result.zones.size() == 7);
154 for (std::size_t i = 0; i < result.zones.size(); ++i)
155 assert(continuations(result.zones[0]).size() == 1);
156
157 assert(std::holds_alternative<std::monostate>(continuations(result.zones[0])[0].__rules));
158 assert(std::get<std::string>(continuations(result.zones[1])[0].__rules) == "r");
159
160 assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[2])[0].__rules).__time ==
161 std::chrono::hours(2));
162 assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[2])[0].__rules).__is_dst == true);
163
164 assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[3])[0].__rules).__time ==
165 std::chrono::hours(2));
166 assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[3])[0].__rules).__is_dst == false);
167
168 assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[4])[0].__rules).__time ==
169 std::chrono::hours(0));
170 assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[4])[0].__rules).__is_dst == false);
171
172 assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[5])[0].__rules).__time ==
173 std::chrono::seconds(1));
174 assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[5])[0].__rules).__is_dst == true);
175
176 assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[6])[0].__rules).__time ==
177 -std::chrono::seconds(1));
178 assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[6])[0].__rules).__is_dst == true);
179 }
180
test_format()181 static void test_format() {
182 const std::chrono::tzdb& result = parse(
183 R"(
184 Z n 0 r f
185 )");
186 assert(result.zones.size() == 1);
187 assert(continuations(result.zones[0]).size() == 1);
188 assert(continuations(result.zones[0])[0].__format == "f");
189 }
190
test_until()191 static void test_until() {
192 const std::chrono::tzdb& result = parse(
193 R"(
194 Z na 0 r f
195 Z nb 0 r f 1000
196 Z nc 0 r f -1000 N
197 Z nd 0 r f ma S 31
198 Z ne 0 r f 0 jA LASTw
199 Z nf 0 r f -42 jUN m<=1
200 Z ng 0 r f 42 jul Su>=12
201 Z nh 0 r f 42 JUl 1 2w
202 Z ni 0 r f 42 July 1 01:28:14u
203 Z nj 0 r f 42 Jul 1 -
204 )");
205 assert(result.zones.size() == 10);
206 for (std::size_t i = 0; i < result.zones.size(); ++i)
207 assert(continuations(result.zones[0]).size() == 1);
208
209 std::chrono::__tz::__constrained_weekday r;
210
211 assert(continuations(result.zones[0])[0].__year == std::chrono::year::min());
212 assert(continuations(result.zones[0])[0].__in == std::chrono::January);
213 assert(std::get<std::chrono::day>(continuations(result.zones[0])[0].__on) == std::chrono::day(1));
214 assert(continuations(result.zones[0])[0].__at.__time == std::chrono::seconds(0));
215 assert(continuations(result.zones[0])[0].__at.__clock == std::chrono::__tz::__clock::__local);
216
217 assert(continuations(result.zones[1])[0].__year == std::chrono::year(1000));
218 assert(continuations(result.zones[1])[0].__in == std::chrono::January);
219 assert(std::get<std::chrono::day>(continuations(result.zones[1])[0].__on) == std::chrono::day(1));
220 assert(continuations(result.zones[1])[0].__at.__time == std::chrono::seconds(0));
221 assert(continuations(result.zones[1])[0].__at.__clock == std::chrono::__tz::__clock::__local);
222
223 assert(continuations(result.zones[2])[0].__year == std::chrono::year(-1000));
224 assert(continuations(result.zones[2])[0].__in == std::chrono::November);
225 assert(std::get<std::chrono::day>(continuations(result.zones[2])[0].__on) == std::chrono::day(1));
226 assert(continuations(result.zones[2])[0].__at.__time == std::chrono::seconds(0));
227 assert(continuations(result.zones[2])[0].__at.__clock == std::chrono::__tz::__clock::__local);
228
229 assert(continuations(result.zones[3])[0].__year == std::chrono::year::max());
230 assert(continuations(result.zones[3])[0].__in == std::chrono::September);
231 assert(std::get<std::chrono::day>(continuations(result.zones[3])[0].__on) == std::chrono::day(31));
232 assert(continuations(result.zones[3])[0].__at.__time == std::chrono::seconds(0));
233 assert(continuations(result.zones[3])[0].__at.__clock == std::chrono::__tz::__clock::__local);
234
235 assert(continuations(result.zones[4])[0].__year == std::chrono::year(0));
236 assert(continuations(result.zones[4])[0].__in == std::chrono::January);
237 assert(std::get<std::chrono::weekday_last>(continuations(result.zones[4])[0].__on) ==
238 std::chrono::weekday_last{std::chrono::Wednesday});
239 assert(continuations(result.zones[4])[0].__at.__time == std::chrono::seconds(0));
240 assert(continuations(result.zones[4])[0].__at.__clock == std::chrono::__tz::__clock::__local);
241
242 assert(continuations(result.zones[5])[0].__year == std::chrono::year(-42));
243 assert(continuations(result.zones[5])[0].__in == std::chrono::June);
244 r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[5])[0].__on);
245 assert(r.__weekday == std::chrono::Monday);
246 assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
247 assert(r.__day == std::chrono::day(1));
248 assert(continuations(result.zones[5])[0].__at.__time == std::chrono::seconds(0));
249 assert(continuations(result.zones[5])[0].__at.__clock == std::chrono::__tz::__clock::__local);
250
251 assert(continuations(result.zones[6])[0].__year == std::chrono::year(42));
252 assert(continuations(result.zones[6])[0].__in == std::chrono::July);
253 r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[6])[0].__on);
254 assert(r.__weekday == std::chrono::Sunday);
255 assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
256 assert(r.__day == std::chrono::day(12));
257 assert(continuations(result.zones[6])[0].__at.__time == std::chrono::seconds(0));
258 assert(continuations(result.zones[6])[0].__at.__clock == std::chrono::__tz::__clock::__local);
259
260 assert(continuations(result.zones[7])[0].__year == std::chrono::year(42));
261 assert(continuations(result.zones[7])[0].__in == std::chrono::July);
262 assert(std::get<std::chrono::day>(continuations(result.zones[7])[0].__on) == std::chrono::day(1));
263 assert(continuations(result.zones[7])[0].__at.__time == std::chrono::hours(2));
264 assert(continuations(result.zones[7])[0].__at.__clock == std::chrono::__tz::__clock::__local);
265
266 assert(continuations(result.zones[8])[0].__year == std::chrono::year(42));
267 assert(continuations(result.zones[8])[0].__in == std::chrono::July);
268 assert(std::get<std::chrono::day>(continuations(result.zones[8])[0].__on) == std::chrono::day(1));
269 assert(continuations(result.zones[8])[0].__at.__time ==
270 std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
271 assert(continuations(result.zones[8])[0].__at.__clock == std::chrono::__tz::__clock::__universal);
272
273 assert(continuations(result.zones[9])[0].__year == std::chrono::year(42));
274 assert(continuations(result.zones[9])[0].__in == std::chrono::July);
275 assert(std::get<std::chrono::day>(continuations(result.zones[9])[0].__on) == std::chrono::day(1));
276 assert(continuations(result.zones[9])[0].__at.__time == std::chrono::hours(0)); // The man page expresses it in hours
277 assert(continuations(result.zones[9])[0].__at.__clock == std::chrono::__tz::__clock::__local);
278 }
279
test_continuation()280 static void test_continuation() {
281 const std::chrono::tzdb& result = parse(
282 R"(
283 Z na 0 r f
284 0 r f 1000
285 0 r f -1000 N
286 0 r f ma S 31
287 0 r f 0 Ja lastW
288 0 r f -42 Jun M<=1
289 0 r f 42 Jul Su>=12
290 0 r f 42 Jul 1 2w
291 0 r f 42 Jul 1 01:28:14u
292 0 r f 42 Jul 1 -
293 )");
294
295 assert(result.zones.size() == 1);
296 assert(continuations(result.zones[0]).size() == 10);
297
298 std::chrono::__tz::__constrained_weekday r;
299
300 assert(continuations(result.zones[0])[0].__year == std::chrono::year::min());
301 assert(continuations(result.zones[0])[0].__in == std::chrono::January);
302 assert(std::get<std::chrono::day>(continuations(result.zones[0])[0].__on) == std::chrono::day(1));
303 assert(continuations(result.zones[0])[0].__at.__time == std::chrono::seconds(0));
304 assert(continuations(result.zones[0])[0].__at.__clock == std::chrono::__tz::__clock::__local);
305
306 assert(continuations(result.zones[0])[1].__year == std::chrono::year(1000));
307 assert(continuations(result.zones[0])[1].__in == std::chrono::January);
308 assert(std::get<std::chrono::day>(continuations(result.zones[0])[1].__on) == std::chrono::day(1));
309 assert(continuations(result.zones[0])[1].__at.__time == std::chrono::seconds(0));
310 assert(continuations(result.zones[0])[1].__at.__clock == std::chrono::__tz::__clock::__local);
311
312 assert(continuations(result.zones[0])[2].__year == std::chrono::year(-1000));
313 assert(continuations(result.zones[0])[2].__in == std::chrono::November);
314 assert(std::get<std::chrono::day>(continuations(result.zones[0])[2].__on) == std::chrono::day(1));
315 assert(continuations(result.zones[0])[2].__at.__time == std::chrono::seconds(0));
316 assert(continuations(result.zones[0])[2].__at.__clock == std::chrono::__tz::__clock::__local);
317
318 assert(continuations(result.zones[0])[3].__year == std::chrono::year::max());
319 assert(continuations(result.zones[0])[3].__in == std::chrono::September);
320 assert(std::get<std::chrono::day>(continuations(result.zones[0])[3].__on) == std::chrono::day(31));
321 assert(continuations(result.zones[0])[3].__at.__time == std::chrono::seconds(0));
322 assert(continuations(result.zones[0])[3].__at.__clock == std::chrono::__tz::__clock::__local);
323
324 assert(continuations(result.zones[0])[4].__year == std::chrono::year(0));
325 assert(continuations(result.zones[0])[4].__in == std::chrono::January);
326 assert(std::get<std::chrono::weekday_last>(continuations(result.zones[0])[4].__on) ==
327 std::chrono::weekday_last{std::chrono::Wednesday});
328 assert(continuations(result.zones[0])[4].__at.__time == std::chrono::seconds(0));
329 assert(continuations(result.zones[0])[4].__at.__clock == std::chrono::__tz::__clock::__local);
330
331 assert(continuations(result.zones[0])[5].__year == std::chrono::year(-42));
332 assert(continuations(result.zones[0])[5].__in == std::chrono::June);
333 r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[0])[5].__on);
334 assert(r.__weekday == std::chrono::Monday);
335 assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
336 assert(r.__day == std::chrono::day(1));
337 assert(continuations(result.zones[0])[5].__at.__time == std::chrono::seconds(0));
338 assert(continuations(result.zones[0])[5].__at.__clock == std::chrono::__tz::__clock::__local);
339
340 assert(continuations(result.zones[0])[6].__year == std::chrono::year(42));
341 assert(continuations(result.zones[0])[6].__in == std::chrono::July);
342 r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[0])[6].__on);
343 assert(r.__weekday == std::chrono::Sunday);
344 assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
345 assert(r.__day == std::chrono::day(12));
346 assert(continuations(result.zones[0])[6].__at.__time == std::chrono::seconds(0));
347 assert(continuations(result.zones[0])[6].__at.__clock == std::chrono::__tz::__clock::__local);
348
349 assert(continuations(result.zones[0])[7].__year == std::chrono::year(42));
350 assert(continuations(result.zones[0])[7].__in == std::chrono::July);
351 assert(std::get<std::chrono::day>(continuations(result.zones[0])[7].__on) == std::chrono::day(1));
352 assert(continuations(result.zones[0])[7].__at.__time == std::chrono::hours(2));
353 assert(continuations(result.zones[0])[7].__at.__clock == std::chrono::__tz::__clock::__local);
354
355 assert(continuations(result.zones[0])[8].__year == std::chrono::year(42));
356 assert(continuations(result.zones[0])[8].__in == std::chrono::July);
357 assert(std::get<std::chrono::day>(continuations(result.zones[0])[8].__on) == std::chrono::day(1));
358 assert(continuations(result.zones[0])[8].__at.__time ==
359 std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
360 assert(continuations(result.zones[0])[8].__at.__clock == std::chrono::__tz::__clock::__universal);
361
362 assert(continuations(result.zones[0])[9].__year == std::chrono::year(42));
363 assert(continuations(result.zones[0])[9].__in == std::chrono::July);
364 assert(std::get<std::chrono::day>(continuations(result.zones[0])[9].__on) == std::chrono::day(1));
365 assert(continuations(result.zones[0])[9].__at.__time == std::chrono::hours(0)); // The man page expresses it in hours
366 assert(continuations(result.zones[0])[9].__at.__clock == std::chrono::__tz::__clock::__local);
367 }
368
main(int,const char **)369 int main(int, const char**) {
370 test_invalid();
371 test_name();
372 test_stdoff();
373 test_rules();
374 test_format();
375 test_until();
376
377 test_continuation();
378
379 return 0;
380 }
381