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, choose z) const;
22 
23 #include <chrono>
24 #include <format>
25 #include <cassert>
26 #include <string_view>
27 
28 #include "test_macros.h"
29 
30 // Tests unique conversions. To make sure the test is does not depend on changes
31 // in the database it uses a time zone with a fixed offset.
test_unique()32 static void test_unique() {
33   using namespace std::literals::chrono_literals;
34 
35   const std::chrono::time_zone* tz = std::chrono::locate_zone("Etc/GMT+1");
36 
37   assert(tz->to_sys(std::chrono::local_time<std::chrono::nanoseconds>{-1ns}, std::chrono::choose::earliest) ==
38          std::chrono::sys_time<std::chrono::nanoseconds>{-1ns + 1h});
39 
40   assert(tz->to_sys(std::chrono::local_time<std::chrono::microseconds>{0us}, std::chrono::choose::latest) ==
41          std::chrono::sys_time<std::chrono::microseconds>{1h});
42 
43   assert(tz->to_sys(
44              std::chrono::local_time<std::chrono::seconds>{
45                  (std::chrono::sys_days{std::chrono::January / 1 / -21970}).time_since_epoch()},
46              std::chrono::choose::earliest) ==
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(
52              std::chrono::local_time<std::chrono::days>{
53                  (std::chrono::sys_days{std::chrono::January / 1 / 21970}).time_since_epoch()},
54              std::chrono::choose::latest) ==
55          std::chrono::sys_time<std::chrono::seconds>{
56              (std::chrono::sys_days{std::chrono::January / 1 / 21970}).time_since_epoch() + 1h});
57 
58   assert(tz->to_sys(std::chrono::local_time<std::chrono::weeks>{}, std::chrono::choose::earliest) ==
59          std::chrono::sys_time<std::chrono::seconds>{
60              (std::chrono::sys_days{std::chrono::January / 1 / 1970}).time_since_epoch() + 1h});
61 
62   // Note months and years cannot be streamed; however these functions don't
63   // throw an exception and thus can be used.
64   assert(tz->to_sys(std::chrono::local_time<std::chrono::months>{}, std::chrono::choose::latest) ==
65          std::chrono::sys_time<std::chrono::seconds>{
66              (std::chrono::sys_days{std::chrono::January / 1 / 1970}).time_since_epoch() + 1h});
67 
68   assert(tz->to_sys(std::chrono::local_time<std::chrono::years>{}, std::chrono::choose::earliest) ==
69          std::chrono::sys_time<std::chrono::seconds>{
70              (std::chrono::sys_days{std::chrono::January / 1 / 1970}).time_since_epoch() + 1h});
71 }
72 
73 // Tests non-existant conversions.
test_nonexistent()74 static void test_nonexistent() {
75   using namespace std::literals::chrono_literals;
76 
77   const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Berlin");
78 
79   // Z Europe/Berlin 0:53:28 - LMT 1893 Ap
80   // ...
81   // 1 DE CE%sT 1980
82   // 1 E CE%sT
83   //
84   // ...
85   // R E 1981 ma - Mar lastSu 1u 1 S
86   // R E 1996 ma - O lastSu 1u 0 -
87 
88   // Pick an historic date where it's well known what the time zone rules were.
89   // This makes it unlikely updates to the database change these rules.
90   std::chrono::local_time<std::chrono::seconds> time{
91       (std::chrono::sys_days{std::chrono::March / 30 / 1986} + 2h + 30min).time_since_epoch()};
92 
93   std::chrono::sys_seconds expected{time.time_since_epoch() - 1h};
94 
95   // Validates whether the database did not change.
96   std::chrono::local_info info = tz->get_info(time);
97   assert(info.result == std::chrono::local_info::nonexistent);
98 
99   assert(tz->to_sys(time + 0ns, std::chrono::choose::earliest) == expected);
100   assert(tz->to_sys(time + 0us, std::chrono::choose::latest) == expected);
101   assert(tz->to_sys(time + 0ms, std::chrono::choose::earliest) == expected);
102   assert(tz->to_sys(time + 0s, std::chrono::choose::latest) == expected);
103 }
104 
105 // Tests ambiguous conversions.
test_ambiguous()106 static void test_ambiguous() {
107   using namespace std::literals::chrono_literals;
108 
109   const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Berlin");
110 
111   // Z Europe/Berlin 0:53:28 - LMT 1893 Ap
112   // ...
113   // 1 DE CE%sT 1980
114   // 1 E CE%sT
115   //
116   // ...
117   // R E 1981 ma - Mar lastSu 1u 1 S
118   // R E 1996 ma - O lastSu 1u 0 -
119 
120   // Pick an historic date where it's well known what the time zone rules were.
121   // This makes it unlikely updates to the database change these rules.
122   std::chrono::local_time<std::chrono::seconds> time{
123       (std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h + 30min).time_since_epoch()};
124 
125   std::chrono::sys_seconds earlier{time.time_since_epoch() - 2h};
126   std::chrono::sys_seconds later{time.time_since_epoch() - 1h};
127 
128   // Validates whether the database did not change.
129   std::chrono::local_info info = tz->get_info(time);
130   assert(info.result == std::chrono::local_info::ambiguous);
131 
132   assert(tz->to_sys(time + 0ns, std::chrono::choose::earliest) == earlier);
133   assert(tz->to_sys(time + 0us, std::chrono::choose::latest) == later);
134   assert(tz->to_sys(time + 0ms, std::chrono::choose::earliest) == earlier);
135   assert(tz->to_sys(time + 0s, std::chrono::choose::latest) == later);
136 }
137 
138 // This test does the basic validations of this function. The library function
139 // uses `local_info get_info(const local_time<Duration>& tp)` as implementation
140 // detail. The get_info function does extensive testing of the data.
main(int,char **)141 int main(int, char**) {
142   test_unique();
143   test_nonexistent();
144   test_ambiguous();
145 
146   return 0;
147 }
148