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