//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: no-filesystem, no-localization, no-tzdb // XFAIL: libcpp-has-no-experimental-tzdb // XFAIL: availability-tzdb-missing // // class time_zone; // template // sys_time> // to_sys(const local_time& tp) const; #include #include #include #include #include "test_macros.h" #include "assert_macros.h" #include "concat_macros.h" // Tests unique conversions. To make sure the test is does not depend on changes // in the database it uses a time zone with a fixed offset. static void test_unique() { using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Etc/GMT+1"); assert(tz->to_sys(std::chrono::local_time{-1ns}) == std::chrono::sys_time{-1ns + 1h}); assert(tz->to_sys(std::chrono::local_time{0us}) == std::chrono::sys_time{1h}); assert(tz->to_sys(std::chrono::local_time{ (std::chrono::sys_days{std::chrono::January / 1 / -21970}).time_since_epoch()}) == std::chrono::sys_time{ (std::chrono::sys_days{std::chrono::January / 1 / -21970}).time_since_epoch() + 1h}); // sys_time> is seconds for the larger types assert(tz->to_sys(std::chrono::local_time{ (std::chrono::sys_days{std::chrono::January / 1 / 21970}).time_since_epoch()}) == std::chrono::sys_time{ (std::chrono::sys_days{std::chrono::January / 1 / 21970}).time_since_epoch() + 1h}); assert(tz->to_sys(std::chrono::local_time{}) == std::chrono::sys_time{ (std::chrono::sys_days{std::chrono::January / 1 / 1970}).time_since_epoch() + 1h}); // Note months and years can not be streamed; thus the function cannot be // instantiated for these types. (Even when there is no exception thrown.) } // Tests non-existant conversions. static void test_nonexistent() { #ifndef TEST_HAS_NO_EXCEPTIONS using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Berlin"); // Z Europe/Berlin 0:53:28 - LMT 1893 Ap // ... // 1 DE CE%sT 1980 // 1 E CE%sT // // ... // R E 1981 ma - Mar lastSu 1u 1 S // R E 1996 ma - O lastSu 1u 0 - // Pick an historic date where it's well known what the time zone rules were. // This makes it unlikely updates to the database change these rules. std::chrono::local_time time{ (std::chrono::sys_days{std::chrono::March / 30 / 1986} + 2h + 30min).time_since_epoch()}; // Validates whether the database did not change. std::chrono::local_info info = tz->get_info(time); assert(info.result == std::chrono::local_info::nonexistent); TEST_VALIDATE_EXCEPTION( std::chrono::nonexistent_local_time, [&]([[maybe_unused]] const std::chrono::nonexistent_local_time& e) { [[maybe_unused]] std::string_view what = R"(1986-03-30 02:30:00.000000000 is in a gap between 1986-03-30 02:00:00 CET and 1986-03-30 03:00:00 CEST which are both equivalent to 1986-03-30 01:00:00 UTC)"; TEST_LIBCPP_REQUIRE( e.what() == what, TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n')); }, tz->to_sys(time + 0ns)); TEST_VALIDATE_EXCEPTION( std::chrono::nonexistent_local_time, [&]([[maybe_unused]] const std::chrono::nonexistent_local_time& e) { [[maybe_unused]] std::string_view what = R"(1986-03-30 02:30:00.000000 is in a gap between 1986-03-30 02:00:00 CET and 1986-03-30 03:00:00 CEST which are both equivalent to 1986-03-30 01:00:00 UTC)"; TEST_LIBCPP_REQUIRE( e.what() == what, TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n')); }, tz->to_sys(time + 0us)); TEST_VALIDATE_EXCEPTION( std::chrono::nonexistent_local_time, [&]([[maybe_unused]] const std::chrono::nonexistent_local_time& e) { [[maybe_unused]] std::string_view what = R"(1986-03-30 02:30:00.000 is in a gap between 1986-03-30 02:00:00 CET and 1986-03-30 03:00:00 CEST which are both equivalent to 1986-03-30 01:00:00 UTC)"; TEST_LIBCPP_REQUIRE( e.what() == what, TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n')); }, tz->to_sys(time + 0ms)); TEST_VALIDATE_EXCEPTION( std::chrono::nonexistent_local_time, [&]([[maybe_unused]] const std::chrono::nonexistent_local_time& e) { [[maybe_unused]] std::string_view what = R"(1986-03-30 02:30:00 is in a gap between 1986-03-30 02:00:00 CET and 1986-03-30 03:00:00 CEST which are both equivalent to 1986-03-30 01:00:00 UTC)"; TEST_LIBCPP_REQUIRE( e.what() == what, TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n')); }, tz->to_sys(time + 0s)); #endif // TEST_HAS_NO_EXCEPTIONS } // Tests ambiguous conversions. static void test_ambiguous() { #ifndef TEST_HAS_NO_EXCEPTIONS using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Berlin"); // Z Europe/Berlin 0:53:28 - LMT 1893 Ap // ... // 1 DE CE%sT 1980 // 1 E CE%sT // // ... // R E 1981 ma - Mar lastSu 1u 1 S // R E 1996 ma - O lastSu 1u 0 - // Pick an historic date where it's well known what the time zone rules were. // This makes it unlikely updates to the database change these rules. std::chrono::local_time time{ (std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h + 30min).time_since_epoch()}; // Validates whether the database did not change. std::chrono::local_info info = tz->get_info(time); assert(info.result == std::chrono::local_info::ambiguous); TEST_VALIDATE_EXCEPTION( std::chrono::ambiguous_local_time, [&]([[maybe_unused]] const std::chrono::ambiguous_local_time& e) { [[maybe_unused]] std::string_view what = R"(1986-09-28 02:30:00.000000000 is ambiguous. It could be 1986-09-28 02:30:00.000000000 CEST == 1986-09-28 00:30:00.000000000 UTC or 1986-09-28 02:30:00.000000000 CET == 1986-09-28 01:30:00.000000000 UTC)"; TEST_LIBCPP_REQUIRE( e.what() == what, TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n')); }, tz->to_sys(time + 0ns)); TEST_VALIDATE_EXCEPTION( std::chrono::ambiguous_local_time, [&]([[maybe_unused]] const std::chrono::ambiguous_local_time& e) { [[maybe_unused]] std::string_view what = R"(1986-09-28 02:30:00.000000 is ambiguous. It could be 1986-09-28 02:30:00.000000 CEST == 1986-09-28 00:30:00.000000 UTC or 1986-09-28 02:30:00.000000 CET == 1986-09-28 01:30:00.000000 UTC)"; TEST_LIBCPP_REQUIRE( e.what() == what, TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n')); }, tz->to_sys(time + 0us)); TEST_VALIDATE_EXCEPTION( std::chrono::ambiguous_local_time, [&]([[maybe_unused]] const std::chrono::ambiguous_local_time& e) { [[maybe_unused]] std::string_view what = R"(1986-09-28 02:30:00.000 is ambiguous. It could be 1986-09-28 02:30:00.000 CEST == 1986-09-28 00:30:00.000 UTC or 1986-09-28 02:30:00.000 CET == 1986-09-28 01:30:00.000 UTC)"; TEST_LIBCPP_REQUIRE( e.what() == what, TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n')); }, tz->to_sys(time + 0ms)); TEST_VALIDATE_EXCEPTION( std::chrono::ambiguous_local_time, [&]([[maybe_unused]] const std::chrono::ambiguous_local_time& e) { [[maybe_unused]] std::string_view what = R"(1986-09-28 02:30:00 is ambiguous. It could be 1986-09-28 02:30:00 CEST == 1986-09-28 00:30:00 UTC or 1986-09-28 02:30:00 CET == 1986-09-28 01:30:00 UTC)"; TEST_LIBCPP_REQUIRE( e.what() == what, TEST_WRITE_CONCATENATED("Expected exception\n", what, "\n\nActual exception\n", e.what(), '\n')); }, tz->to_sys(time + 0s)); #endif // TEST_HAS_NO_EXCEPTIONS } // This test does the basic validations of this function. The library function // uses `local_info get_info(const local_time& tp)` as implementation // detail. The get_info function does extensive testing of the data. int main(int, char**) { test_unique(); test_nonexistent(); test_ambiguous(); return 0; }