xref: /freebsd/contrib/llvm-project/libcxx/include/__chrono/time_zone.h (revision 47ef2a131091508e049ab10cad7f91a3c1342cd9)
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_TIME_ZONE_H
13 #define _LIBCPP___CHRONO_TIME_ZONE_H
14 
15 #include <version>
16 // Enable the contents of the header only when libc++ was built with experimental features enabled.
17 #if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
18 
19 #  include <__chrono/calendar.h>
20 #  include <__chrono/duration.h>
21 #  include <__chrono/exception.h>
22 #  include <__chrono/local_info.h>
23 #  include <__chrono/sys_info.h>
24 #  include <__chrono/system_clock.h>
25 #  include <__compare/strong_order.h>
26 #  include <__config>
27 #  include <__memory/unique_ptr.h>
28 #  include <__type_traits/common_type.h>
29 #  include <string_view>
30 
31 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
32 #    pragma GCC system_header
33 #  endif
34 
35 _LIBCPP_PUSH_MACROS
36 #  include <__undef_macros>
37 
38 _LIBCPP_BEGIN_NAMESPACE_STD
39 
40 #  if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&   \
41       !defined(_LIBCPP_HAS_NO_LOCALIZATION)
42 
43 namespace chrono {
44 
45 enum class choose { earliest, latest };
46 
47 class _LIBCPP_AVAILABILITY_TZDB time_zone {
48   _LIBCPP_HIDE_FROM_ABI time_zone() = default;
49 
50 public:
51   class __impl; // public so it can be used by make_unique.
52 
53   // The "constructor".
54   //
55   // The default constructor is private to avoid the constructor from being
56   // part of the ABI. Instead use an __ugly_named function as an ABI interface,
57   // since that gives us the ability to change it in the future.
58   [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI static time_zone __create(unique_ptr<__impl>&& __p);
59 
60   _LIBCPP_EXPORTED_FROM_ABI ~time_zone();
61 
62   _LIBCPP_HIDE_FROM_ABI time_zone(time_zone&&)            = default;
63   _LIBCPP_HIDE_FROM_ABI time_zone& operator=(time_zone&&) = default;
64 
65   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string_view name() const noexcept { return __name(); }
66 
67   template <class _Duration>
68   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_info get_info(const sys_time<_Duration>& __time) const {
69     return __get_info(chrono::time_point_cast<seconds>(__time));
70   }
71 
72   template <class _Duration>
73   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI local_info get_info(const local_time<_Duration>& __time) const {
74     return __get_info(chrono::time_point_cast<seconds>(__time));
75   }
76 
77   // We don't apply nodiscard here since this function throws on many inputs,
78   // so it could be used as a validation.
79   template <class _Duration>
80   _LIBCPP_HIDE_FROM_ABI sys_time<common_type_t<_Duration, seconds>> to_sys(const local_time<_Duration>& __time) const {
81     local_info __info = get_info(__time);
82     switch (__info.result) {
83     case local_info::unique:
84       return sys_time<common_type_t<_Duration, seconds>>{__time.time_since_epoch() - __info.first.offset};
85 
86     case local_info::nonexistent:
87       chrono::__throw_nonexistent_local_time(__time, __info);
88 
89     case local_info::ambiguous:
90       chrono::__throw_ambiguous_local_time(__time, __info);
91     }
92 
93     // TODO TZDB The Standard does not specify anything in these cases.
94     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
95         __info.result != -1, "cannot convert the local time; it would be before the minimum system clock value");
96     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
97         __info.result != -2, "cannot convert the local time; it would be after the maximum system clock value");
98 
99     return {};
100   }
101 
102   template <class _Duration>
103   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_time<common_type_t<_Duration, seconds>>
104   to_sys(const local_time<_Duration>& __time, choose __z) const {
105     local_info __info = get_info(__time);
106     switch (__info.result) {
107     case local_info::unique:
108     case local_info::nonexistent: // first and second are the same
109       return sys_time<common_type_t<_Duration, seconds>>{__time.time_since_epoch() - __info.first.offset};
110 
111     case local_info::ambiguous:
112       switch (__z) {
113       case choose::earliest:
114         return sys_time<common_type_t<_Duration, seconds>>{__time.time_since_epoch() - __info.first.offset};
115 
116       case choose::latest:
117         return sys_time<common_type_t<_Duration, seconds>>{__time.time_since_epoch() - __info.second.offset};
118 
119         // Note a value out of bounds is not specified.
120       }
121     }
122 
123     // TODO TZDB The standard does not specify anything in these cases.
124     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
125         __info.result != -1, "cannot convert the local time; it would be before the minimum system clock value");
126     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
127         __info.result != -2, "cannot convert the local time; it would be after the maximum system clock value");
128 
129     return {};
130   }
131 
132   template <class _Duration>
133   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI local_time<common_type_t<_Duration, seconds>>
134   to_local(const sys_time<_Duration>& __time) const {
135     using _Dp = common_type_t<_Duration, seconds>;
136 
137     sys_info __info = get_info(__time);
138 
139     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
140         __info.offset >= chrono::seconds{0} || __time.time_since_epoch() >= _Dp::min() - __info.offset,
141         "cannot convert the system time; it would be before the minimum local clock value");
142 
143     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
144         __info.offset <= chrono::seconds{0} || __time.time_since_epoch() <= _Dp::max() - __info.offset,
145         "cannot convert the system time; it would be after the maximum local clock value");
146 
147     return local_time<_Dp>{__time.time_since_epoch() + __info.offset};
148   }
149 
150   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const __impl& __implementation() const noexcept { return *__impl_; }
151 
152 private:
153   [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string_view __name() const noexcept;
154 
155   [[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI sys_info __get_info(sys_seconds __time) const;
156   [[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI local_info __get_info(local_seconds __time) const;
157 
158   unique_ptr<__impl> __impl_;
159 };
160 
161 [[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline bool
162 operator==(const time_zone& __x, const time_zone& __y) noexcept {
163   return __x.name() == __y.name();
164 }
165 
166 [[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline strong_ordering
167 operator<=>(const time_zone& __x, const time_zone& __y) noexcept {
168   return __x.name() <=> __y.name();
169 }
170 
171 } // namespace chrono
172 
173 #  endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM)
174          // && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
175 
176 _LIBCPP_END_NAMESPACE_STD
177 
178 _LIBCPP_POP_MACROS
179 
180 #endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
181 
182 #endif // _LIBCPP___CHRONO_TIME_ZONE_H
183