1 | // -*- C++ -*- |
2 | //===----------------------------------------------------------------------===// |
3 | // |
4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
5 | // See https://llvm.org/LICENSE.txt for license information. |
6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
7 | // |
8 | //===----------------------------------------------------------------------===// |
9 | |
10 | // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html |
11 | |
12 | #ifndef _LIBCPP___CHRONO_ZONED_TIME_H |
13 | #define _LIBCPP___CHRONO_ZONED_TIME_H |
14 | |
15 | #include <version> |
16 | // Enable the contents of the header only when libc++ was built with experimental features enabled. |
17 | #if _LIBCPP_HAS_EXPERIMENTAL_TZDB |
18 | |
19 | # include <__chrono/calendar.h> |
20 | # include <__chrono/duration.h> |
21 | # include <__chrono/sys_info.h> |
22 | # include <__chrono/system_clock.h> |
23 | # include <__chrono/time_zone.h> |
24 | # include <__chrono/tzdb_list.h> |
25 | # include <__concepts/constructible.h> |
26 | # include <__config> |
27 | # include <__type_traits/common_type.h> |
28 | # include <__type_traits/conditional.h> |
29 | # include <__type_traits/remove_cvref.h> |
30 | # include <__utility/declval.h> |
31 | # include <__utility/move.h> |
32 | # include <string_view> |
33 | |
34 | # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) |
35 | # pragma GCC system_header |
36 | # endif |
37 | |
38 | _LIBCPP_PUSH_MACROS |
39 | # include <__undef_macros> |
40 | |
41 | _LIBCPP_BEGIN_NAMESPACE_STD |
42 | |
43 | # if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION |
44 | |
45 | namespace chrono { |
46 | |
47 | template <class> |
48 | struct zoned_traits {}; |
49 | |
50 | template <> |
51 | struct zoned_traits<const time_zone*> { |
52 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static const time_zone* default_zone() { return chrono::locate_zone(name: "UTC" ); } |
53 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static const time_zone* locate_zone(string_view __name) { |
54 | return chrono::locate_zone(__name); |
55 | } |
56 | }; |
57 | |
58 | template <class _Duration, class _TimeZonePtr = const time_zone*> |
59 | class zoned_time { |
60 | // [time.zone.zonedtime.ctor]/2 |
61 | static_assert(__is_duration_v<_Duration>, |
62 | "the program is ill-formed since _Duration is not a specialization of std::chrono::duration" ); |
63 | |
64 | // The wording uses the constraints like |
65 | // constructible_from<zoned_time, decltype(__traits::locate_zone(string_view{}))> |
66 | // Using these constraints in the code causes the compiler to give an |
67 | // error that the constraint depends on itself. To avoid that issue use |
68 | // the fact it is possible to create this object from a _TimeZonePtr. |
69 | using __traits _LIBCPP_NODEBUG = zoned_traits<_TimeZonePtr>; |
70 | |
71 | public: |
72 | using duration = common_type_t<_Duration, seconds>; |
73 | |
74 | _LIBCPP_HIDE_FROM_ABI zoned_time() |
75 | requires requires { __traits::default_zone(); } |
76 | : __zone_{__traits::default_zone()}, __tp_{} {} |
77 | |
78 | _LIBCPP_HIDE_FROM_ABI zoned_time(const zoned_time&) = default; |
79 | _LIBCPP_HIDE_FROM_ABI zoned_time& operator=(const zoned_time&) = default; |
80 | |
81 | _LIBCPP_HIDE_FROM_ABI zoned_time(const sys_time<_Duration>& __tp) |
82 | requires requires { __traits::default_zone(); } |
83 | : __zone_{__traits::default_zone()}, __tp_{__tp} {} |
84 | |
85 | _LIBCPP_HIDE_FROM_ABI explicit zoned_time(_TimeZonePtr __zone) : __zone_{std::move(__zone)}, __tp_{} {} |
86 | |
87 | _LIBCPP_HIDE_FROM_ABI explicit zoned_time(string_view __name) |
88 | requires(requires { __traits::locate_zone(string_view{}); } && |
89 | constructible_from<_TimeZonePtr, decltype(__traits::locate_zone(string_view{}))>) |
90 | : __zone_{__traits::locate_zone(__name)}, __tp_{} {} |
91 | |
92 | template <class _Duration2> |
93 | _LIBCPP_HIDE_FROM_ABI zoned_time(const zoned_time<_Duration2, _TimeZonePtr>& __zt) |
94 | requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>> |
95 | : __zone_{__zt.get_time_zone()}, __tp_{__zt.get_sys_time()} {} |
96 | |
97 | _LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const sys_time<_Duration>& __tp) |
98 | : __zone_{std::move(__zone)}, __tp_{__tp} {} |
99 | |
100 | _LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const sys_time<_Duration>& __tp) |
101 | requires requires { _TimeZonePtr{__traits::locate_zone(string_view{})}; } |
102 | : zoned_time{__traits::locate_zone(__name), __tp} {} |
103 | |
104 | _LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const local_time<_Duration>& __tp) |
105 | requires(is_convertible_v<decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{})), |
106 | sys_time<duration>>) |
107 | : __zone_{std::move(__zone)}, __tp_{__zone_->to_sys(__tp)} {} |
108 | |
109 | _LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const local_time<_Duration>& __tp) |
110 | requires(requires { |
111 | _TimeZonePtr{__traits::locate_zone(string_view{})}; |
112 | } && is_convertible_v<decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{})), |
113 | sys_time<duration>>) |
114 | : zoned_time{__traits::locate_zone(__name), __tp} {} |
115 | |
116 | _LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const local_time<_Duration>& __tp, choose __c) |
117 | requires(is_convertible_v< |
118 | decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{}, choose::earliest)), |
119 | sys_time<duration>>) |
120 | : __zone_{std::move(__zone)}, __tp_{__zone_->to_sys(__tp, __c)} {} |
121 | |
122 | _LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const local_time<_Duration>& __tp, choose __c) |
123 | requires(requires { |
124 | _TimeZonePtr{__traits::locate_zone(string_view{})}; |
125 | } && is_convertible_v<decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{}, choose::earliest)), |
126 | sys_time<duration>>) |
127 | : zoned_time{__traits::locate_zone(__name), __tp, __c} {} |
128 | |
129 | template <class _Duration2, class _TimeZonePtr2> |
130 | _LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const zoned_time<_Duration2, _TimeZonePtr2>& __zt) |
131 | requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>> |
132 | : __zone_{std::move(__zone)}, __tp_{__zt.get_sys_time()} {} |
133 | |
134 | // per wording choose has no effect |
135 | template <class _Duration2, class _TimeZonePtr2> |
136 | _LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const zoned_time<_Duration2, _TimeZonePtr2>& __zt, choose) |
137 | requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>> |
138 | : __zone_{std::move(__zone)}, __tp_{__zt.get_sys_time()} {} |
139 | |
140 | template <class _Duration2, class _TimeZonePtr2> |
141 | _LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const zoned_time<_Duration2, _TimeZonePtr2>& __zt) |
142 | requires(requires { |
143 | _TimeZonePtr{__traits::locate_zone(string_view{})}; |
144 | } && is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>) |
145 | : zoned_time{__traits::locate_zone(__name), __zt} {} |
146 | |
147 | template <class _Duration2, class _TimeZonePtr2> |
148 | _LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const zoned_time<_Duration2, _TimeZonePtr2>& __zt, choose __c) |
149 | requires(requires { |
150 | _TimeZonePtr{__traits::locate_zone(string_view{})}; |
151 | } && is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>) |
152 | : zoned_time{__traits::locate_zone(__name), __zt, __c} {} |
153 | |
154 | _LIBCPP_HIDE_FROM_ABI zoned_time& operator=(const sys_time<_Duration>& __tp) { |
155 | __tp_ = __tp; |
156 | return *this; |
157 | } |
158 | |
159 | _LIBCPP_HIDE_FROM_ABI zoned_time& operator=(const local_time<_Duration>& __tp) { |
160 | // TODO TZDB This seems wrong. |
161 | // Assigning a non-existent or ambiguous time will throw and not satisfy |
162 | // the post condition. This seems quite odd; I constructed an object with |
163 | // choose::earliest and that choice is not respected. |
164 | // what did LEWG do with this. |
165 | // MSVC STL and libstdc++ behave the same |
166 | __tp_ = __zone_->to_sys(__tp); |
167 | return *this; |
168 | } |
169 | |
170 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI operator sys_time<duration>() const { return get_sys_time(); } |
171 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit operator local_time<duration>() const { return get_local_time(); } |
172 | |
173 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _TimeZonePtr get_time_zone() const { return __zone_; } |
174 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI local_time<duration> get_local_time() const { return __zone_->to_local(__tp_); } |
175 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_time<duration> get_sys_time() const { return __tp_; } |
176 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_info get_info() const { return __zone_->get_info(__tp_); } |
177 | |
178 | private: |
179 | _TimeZonePtr __zone_; |
180 | sys_time<duration> __tp_; |
181 | }; |
182 | |
183 | zoned_time() -> zoned_time<seconds>; |
184 | |
185 | template <class _Duration> |
186 | zoned_time(sys_time<_Duration>) -> zoned_time<common_type_t<_Duration, seconds>>; |
187 | |
188 | template <class _TimeZonePtrOrName> |
189 | using __time_zone_representation _LIBCPP_NODEBUG = |
190 | conditional_t<is_convertible_v<_TimeZonePtrOrName, string_view>, |
191 | const time_zone*, |
192 | remove_cvref_t<_TimeZonePtrOrName>>; |
193 | |
194 | template <class _TimeZonePtrOrName> |
195 | zoned_time(_TimeZonePtrOrName&&) -> zoned_time<seconds, __time_zone_representation<_TimeZonePtrOrName>>; |
196 | |
197 | template <class _TimeZonePtrOrName, class _Duration> |
198 | zoned_time(_TimeZonePtrOrName&&, sys_time<_Duration>) |
199 | -> zoned_time<common_type_t<_Duration, seconds>, __time_zone_representation<_TimeZonePtrOrName>>; |
200 | |
201 | template <class _TimeZonePtrOrName, class _Duration> |
202 | zoned_time(_TimeZonePtrOrName&&, local_time<_Duration>, choose = choose::earliest) |
203 | -> zoned_time<common_type_t<_Duration, seconds>, __time_zone_representation<_TimeZonePtrOrName>>; |
204 | |
205 | template <class _Duration, class _TimeZonePtrOrName, class _TimeZonePtr2> |
206 | zoned_time(_TimeZonePtrOrName&&, zoned_time<_Duration, _TimeZonePtr2>, choose = choose::earliest) |
207 | -> zoned_time<common_type_t<_Duration, seconds>, __time_zone_representation<_TimeZonePtrOrName>>; |
208 | |
209 | using zoned_seconds = zoned_time<seconds>; |
210 | |
211 | template <class _Duration1, class _Duration2, class _TimeZonePtr> |
212 | _LIBCPP_HIDE_FROM_ABI bool |
213 | operator==(const zoned_time<_Duration1, _TimeZonePtr>& __lhs, const zoned_time<_Duration2, _TimeZonePtr>& __rhs) { |
214 | return __lhs.get_time_zone() == __rhs.get_time_zone() && __lhs.get_sys_time() == __rhs.get_sys_time(); |
215 | } |
216 | |
217 | } // namespace chrono |
218 | |
219 | # endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && |
220 | // _LIBCPP_HAS_LOCALIZATION |
221 | |
222 | _LIBCPP_END_NAMESPACE_STD |
223 | |
224 | _LIBCPP_POP_MACROS |
225 | |
226 | #endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB |
227 | |
228 | #endif // _LIBCPP___CHRONO_ZONED_TIME_H |
229 | |