xref: /llvm-project/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/to_sys.pass.cpp (revision b27360c346a529affde40150f46b967b616a2144)
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-experimental-tzdb
13 // XFAIL: availability-tzdb-missing
14 
15 // <chrono>
16 
17 // class time_zone;
18 
19 // template <class _Duration>
20 // sys_time<common_type_t<Duration, seconds>>
21 //   to_sys(const local_time<Duration>& tp) const;
22 
23 #include <chrono>
24 #include <format>
25 #include <cassert>
26 #include <string_view>
27 
28 #include "test_macros.h"
29 #include "assert_macros.h"
30 #include "concat_macros.h"
31 
32 // Tests unique conversions. To make sure the test is does not depend on changes
33 // in the database it uses a time zone with a fixed offset.
34 static void test_unique() {
35   using namespace std::literals::chrono_literals;
36 
37   const std::chrono::time_zone* tz = std::chrono::locate_zone("Etc/GMT+1");
38 
39   assert(tz->to_sys(std::chrono::local_time<std::chrono::nanoseconds>{-1ns}) ==
40          std::chrono::sys_time<std::chrono::nanoseconds>{-1ns + 1h});
41 
42   assert(tz->to_sys(std::chrono::local_time<std::chrono::microseconds>{0us}) ==
43          std::chrono::sys_time<std::chrono::microseconds>{1h});
44 
45   assert(tz->to_sys(std::chrono::local_time<std::chrono::seconds>{
46              (std::chrono::sys_days{std::chrono::January / 1 / -21970}).time_since_epoch()}) ==
47          std::chrono::sys_time<std::chrono::seconds>{
48              (std::chrono::sys_days{std::chrono::January / 1 / -21970}).time_since_epoch() + 1h});
49 
50   // sys_time<common_type_t<Duration, seconds>> is seconds for the larger types
51   assert(tz->to_sys(std::chrono::local_time<std::chrono::days>{
52              (std::chrono::sys_days{std::chrono::January / 1 / 21970}).time_since_epoch()}) ==
53          std::chrono::sys_time<std::chrono::seconds>{
54              (std::chrono::sys_days{std::chrono::January / 1 / 21970}).time_since_epoch() + 1h});
55 
56   assert(tz->to_sys(std::chrono::local_time<std::chrono::weeks>{}) ==
57          std::chrono::sys_time<std::chrono::seconds>{
58              (std::chrono::sys_days{std::chrono::January / 1 / 1970}).time_since_epoch() + 1h});
59 
60   // Note months and years can not be streamed; thus the function cannot be
61   // instantiated for these types. (Even when there is no exception thrown.)
62 }
63 
64 // Tests non-existant conversions.
65 static void test_nonexistent() {
66 #ifndef TEST_HAS_NO_EXCEPTIONS
67   using namespace std::literals::chrono_literals;
68 
69   const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Berlin");
70 
71   // Z Europe/Berlin 0:53:28 - LMT 1893 Ap
72   // ...
73   // 1 DE CE%sT 1980
74   // 1 E CE%sT
75   //
76   // ...
77   // R E 1981 ma - Mar lastSu 1u 1 S
78   // R E 1996 ma - O lastSu 1u 0 -
79 
80   // Pick an historic date where it's well known what the time zone rules were.
81   // This makes it unlikely updates to the database change these rules.
82   std::chrono::local_time<std::chrono::seconds> time{
83       (std::chrono::sys_days{std::chrono::March / 30 / 1986} + 2h + 30min).time_since_epoch()};
84 
85   // Validates whether the database did not change.
86   std::chrono::local_info info = tz->get_info(time);
87   assert(info.result == std::chrono::local_info::nonexistent);
88 
89   TEST_VALIDATE_EXCEPTION(
90       std::chrono::nonexistent_local_time,
91       [&]([[maybe_unused]] const std::chrono::nonexistent_local_time& e) {
92         [[maybe_unused]] std::string_view what =
93             R"(1986-03-30 02:30:00.000000000 is in a gap between
94 1986-03-30 02:00:00 CET and
95 1986-03-30 03:00:00 CEST which are both equivalent to
96 1986-03-30 01:00:00 UTC)";
97         TEST_LIBCPP_REQUIRE(
98             e.what() == what,
99             TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n'));
100       },
101       tz->to_sys(time + 0ns));
102 
103   TEST_VALIDATE_EXCEPTION(
104       std::chrono::nonexistent_local_time,
105       [&]([[maybe_unused]] const std::chrono::nonexistent_local_time& e) {
106         [[maybe_unused]] std::string_view what =
107             R"(1986-03-30 02:30:00.000000 is in a gap between
108 1986-03-30 02:00:00 CET and
109 1986-03-30 03:00:00 CEST which are both equivalent to
110 1986-03-30 01:00:00 UTC)";
111         TEST_LIBCPP_REQUIRE(
112             e.what() == what,
113             TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n'));
114       },
115       tz->to_sys(time + 0us));
116 
117   TEST_VALIDATE_EXCEPTION(
118       std::chrono::nonexistent_local_time,
119       [&]([[maybe_unused]] const std::chrono::nonexistent_local_time& e) {
120         [[maybe_unused]] std::string_view what =
121             R"(1986-03-30 02:30:00.000 is in a gap between
122 1986-03-30 02:00:00 CET and
123 1986-03-30 03:00:00 CEST which are both equivalent to
124 1986-03-30 01:00:00 UTC)";
125         TEST_LIBCPP_REQUIRE(
126             e.what() == what,
127             TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n'));
128       },
129       tz->to_sys(time + 0ms));
130 
131   TEST_VALIDATE_EXCEPTION(
132       std::chrono::nonexistent_local_time,
133       [&]([[maybe_unused]] const std::chrono::nonexistent_local_time& e) {
134         [[maybe_unused]] std::string_view what =
135             R"(1986-03-30 02:30:00 is in a gap between
136 1986-03-30 02:00:00 CET and
137 1986-03-30 03:00:00 CEST which are both equivalent to
138 1986-03-30 01:00:00 UTC)";
139         TEST_LIBCPP_REQUIRE(
140             e.what() == what,
141             TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n'));
142       },
143       tz->to_sys(time + 0s));
144 
145 #endif // TEST_HAS_NO_EXCEPTIONS
146 }
147 
148 // Tests ambiguous conversions.
149 static void test_ambiguous() {
150 #ifndef TEST_HAS_NO_EXCEPTIONS
151   using namespace std::literals::chrono_literals;
152 
153   const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Berlin");
154 
155   // Z Europe/Berlin 0:53:28 - LMT 1893 Ap
156   // ...
157   // 1 DE CE%sT 1980
158   // 1 E CE%sT
159   //
160   // ...
161   // R E 1981 ma - Mar lastSu 1u 1 S
162   // R E 1996 ma - O lastSu 1u 0 -
163 
164   // Pick an historic date where it's well known what the time zone rules were.
165   // This makes it unlikely updates to the database change these rules.
166   std::chrono::local_time<std::chrono::seconds> time{
167       (std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h + 30min).time_since_epoch()};
168 
169   // Validates whether the database did not change.
170   std::chrono::local_info info = tz->get_info(time);
171   assert(info.result == std::chrono::local_info::ambiguous);
172 
173   TEST_VALIDATE_EXCEPTION(
174       std::chrono::ambiguous_local_time,
175       [&]([[maybe_unused]] const std::chrono::ambiguous_local_time& e) {
176         [[maybe_unused]] std::string_view what =
177             R"(1986-09-28 02:30:00.000000000 is ambiguous.  It could be
178 1986-09-28 02:30:00.000000000 CEST == 1986-09-28 00:30:00.000000000 UTC or
179 1986-09-28 02:30:00.000000000 CET == 1986-09-28 01:30:00.000000000 UTC)";
180         TEST_LIBCPP_REQUIRE(
181             e.what() == what,
182             TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n'));
183       },
184       tz->to_sys(time + 0ns));
185 
186   TEST_VALIDATE_EXCEPTION(
187       std::chrono::ambiguous_local_time,
188       [&]([[maybe_unused]] const std::chrono::ambiguous_local_time& e) {
189         [[maybe_unused]] std::string_view what =
190             R"(1986-09-28 02:30:00.000000 is ambiguous.  It could be
191 1986-09-28 02:30:00.000000 CEST == 1986-09-28 00:30:00.000000 UTC or
192 1986-09-28 02:30:00.000000 CET == 1986-09-28 01:30:00.000000 UTC)";
193         TEST_LIBCPP_REQUIRE(
194             e.what() == what,
195             TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n'));
196       },
197       tz->to_sys(time + 0us));
198 
199   TEST_VALIDATE_EXCEPTION(
200       std::chrono::ambiguous_local_time,
201       [&]([[maybe_unused]] const std::chrono::ambiguous_local_time& e) {
202         [[maybe_unused]] std::string_view what =
203             R"(1986-09-28 02:30:00.000 is ambiguous.  It could be
204 1986-09-28 02:30:00.000 CEST == 1986-09-28 00:30:00.000 UTC or
205 1986-09-28 02:30:00.000 CET == 1986-09-28 01:30:00.000 UTC)";
206         TEST_LIBCPP_REQUIRE(
207             e.what() == what,
208             TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n'));
209       },
210       tz->to_sys(time + 0ms));
211 
212   TEST_VALIDATE_EXCEPTION(
213       std::chrono::ambiguous_local_time,
214       [&]([[maybe_unused]] const std::chrono::ambiguous_local_time& e) {
215         [[maybe_unused]] std::string_view what =
216             R"(1986-09-28 02:30:00 is ambiguous.  It could be
217 1986-09-28 02:30:00 CEST == 1986-09-28 00:30:00 UTC or
218 1986-09-28 02:30:00 CET == 1986-09-28 01:30:00 UTC)";
219         TEST_LIBCPP_REQUIRE(
220             e.what() == what,
221             TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n'));
222       },
223       tz->to_sys(time + 0s));
224 
225 #endif // TEST_HAS_NO_EXCEPTIONS
226 }
227 
228 // This test does the basic validations of this function. The library function
229 // uses `local_info get_info(const local_time<Duration>& tp)` as implementation
230 // detail. The get_info function does extensive testing of the data.
231 int main(int, char**) {
232   test_unique();
233   test_nonexistent();
234   test_ambiguous();
235 
236   return 0;
237 }
238