238 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710// UNSUPPORTED: no-filesystem, no-localization, no-tzdb11 12// XFAIL: libcpp-has-no-experimental-tzdb13// XFAIL: availability-tzdb-missing14 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 changes33// 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 types51 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 be61 // 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_EXCEPTIONS67 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 Ap72 // ...73 // 1 DE CE%sT 198074 // 1 E CE%sT75 //76 // ...77 // R E 1981 ma - Mar lastSu 1u 1 S78 // 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 between941986-03-30 02:00:00 CET and951986-03-30 03:00:00 CEST which are both equivalent to961986-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 between1081986-03-30 02:00:00 CET and1091986-03-30 03:00:00 CEST which are both equivalent to1101986-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 between1221986-03-30 02:00:00 CET and1231986-03-30 03:00:00 CEST which are both equivalent to1241986-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 between1361986-03-30 02:00:00 CET and1371986-03-30 03:00:00 CEST which are both equivalent to1381986-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_EXCEPTIONS146}147 148// Tests ambiguous conversions.149static void test_ambiguous() {150#ifndef TEST_HAS_NO_EXCEPTIONS151 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 Ap156 // ...157 // 1 DE CE%sT 1980158 // 1 E CE%sT159 //160 // ...161 // R E 1981 ma - Mar lastSu 1u 1 S162 // 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 be1781986-09-28 02:30:00.000000000 CEST == 1986-09-28 00:30:00.000000000 UTC or1791986-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 be1911986-09-28 02:30:00.000000 CEST == 1986-09-28 00:30:00.000000 UTC or1921986-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 be2041986-09-28 02:30:00.000 CEST == 1986-09-28 00:30:00.000 UTC or2051986-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 be2171986-09-28 02:30:00 CEST == 1986-09-28 00:30:00 UTC or2181986-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_EXCEPTIONS226}227 228// This test does the basic validations of this function. The library function229// uses `local_info get_info(const local_time<Duration>& tp)` as implementation230// 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