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