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.
34static 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.
65static 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
941986-03-30 02:00:00 CET and
951986-03-30 03:00:00 CEST which are both equivalent to
961986-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
1081986-03-30 02:00:00 CET and
1091986-03-30 03:00:00 CEST which are both equivalent to
1101986-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
1221986-03-30 02:00:00 CET and
1231986-03-30 03:00:00 CEST which are both equivalent to
1241986-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
1361986-03-30 02:00:00 CET and
1371986-03-30 03:00:00 CEST which are both equivalent to
1381986-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.
149static 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
1781986-09-28 02:30:00.000000000 CEST == 1986-09-28 00:30:00.000000000 UTC or
1791986-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
1911986-09-28 02:30:00.000000 CEST == 1986-09-28 00:30:00.000000 UTC or
1921986-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
2041986-09-28 02:30:00.000 CEST == 1986-09-28 00:30:00.000 UTC or
2051986-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
2171986-09-28 02:30:00 CEST == 1986-09-28 00:30:00 UTC or
2181986-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.
231int main(int, char**) {
232 test_unique();
233 test_nonexistent();
234 test_ambiguous();
235
236 return 0;
237}
238

source code of libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/to_sys.pass.cpp