1 // Copyright 2016 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #if defined(_WIN32) || defined(_WIN64)
16 #define _CRT_SECURE_NO_WARNINGS 1
17 #endif
18
19 #include "time_zone_libc.h"
20
21 #include <chrono>
22 #include <ctime>
23 #include <limits>
24 #include <utility>
25
26 #include "absl/base/config.h"
27 #include "absl/time/internal/cctz/include/cctz/civil_time.h"
28 #include "absl/time/internal/cctz/include/cctz/time_zone.h"
29
30 #if defined(_AIX)
31 extern "C" {
32 extern long altzone;
33 }
34 #endif
35
36 namespace absl {
37 ABSL_NAMESPACE_BEGIN
38 namespace time_internal {
39 namespace cctz {
40
41 namespace {
42
43 #if defined(_WIN32) || defined(_WIN64)
44 // Uses the globals: '_timezone', '_dstbias' and '_tzname'.
tm_gmtoff(const std::tm & tm)45 auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) {
46 const bool is_dst = tm.tm_isdst > 0;
47 return _timezone + (is_dst ? _dstbias : 0);
48 }
tm_zone(const std::tm & tm)49 auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) {
50 const bool is_dst = tm.tm_isdst > 0;
51 return _tzname[is_dst];
52 }
53 #elif defined(__sun) || defined(_AIX)
54 // Uses the globals: 'timezone', 'altzone' and 'tzname'.
55 auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) {
56 const bool is_dst = tm.tm_isdst > 0;
57 return is_dst ? altzone : timezone;
58 }
59 auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
60 const bool is_dst = tm.tm_isdst > 0;
61 return tzname[is_dst];
62 }
63 #elif defined(__native_client__) || defined(__myriad2__) || \
64 defined(__EMSCRIPTEN__)
65 // Uses the globals: 'timezone' and 'tzname'.
66 auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) {
67 const bool is_dst = tm.tm_isdst > 0;
68 return _timezone + (is_dst ? 60 * 60 : 0);
69 }
70 auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
71 const bool is_dst = tm.tm_isdst > 0;
72 return tzname[is_dst];
73 }
74 #else
75 // Adapt to different spellings of the struct std::tm extension fields.
76 #if defined(tm_gmtoff)
77 auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) {
78 return tm.tm_gmtoff;
79 }
80 #elif defined(__tm_gmtoff)
81 auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) {
82 return tm.__tm_gmtoff;
83 }
84 #else
85 template <typename T>
86 auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) {
87 return tm.tm_gmtoff;
88 }
89 template <typename T>
90 auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) {
91 return tm.__tm_gmtoff;
92 }
93 #endif // tm_gmtoff
94 #if defined(tm_zone)
95 auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { return tm.tm_zone; }
96 #elif defined(__tm_zone)
97 auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) {
98 return tm.__tm_zone;
99 }
100 #else
101 template <typename T>
102 auto tm_zone(const T& tm) -> decltype(tm.tm_zone) {
103 return tm.tm_zone;
104 }
105 template <typename T>
106 auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) {
107 return tm.__tm_zone;
108 }
109 #endif // tm_zone
110 #endif
111
gm_time(const std::time_t * timep,std::tm * result)112 inline std::tm* gm_time(const std::time_t* timep, std::tm* result) {
113 #if defined(_WIN32) || defined(_WIN64)
114 return gmtime_s(result, timep) ? nullptr : result;
115 #else
116 return gmtime_r(timep, result);
117 #endif
118 }
119
local_time(const std::time_t * timep,std::tm * result)120 inline std::tm* local_time(const std::time_t* timep, std::tm* result) {
121 #if defined(_WIN32) || defined(_WIN64)
122 return localtime_s(result, timep) ? nullptr : result;
123 #else
124 return localtime_r(timep, result);
125 #endif
126 }
127
128 // Converts a civil second and "dst" flag into a time_t and UTC offset.
129 // Returns false if time_t cannot represent the requested civil second.
130 // Caller must have already checked that cs.year() will fit into a tm_year.
make_time(const civil_second & cs,int is_dst,std::time_t * t,int * off)131 bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) {
132 std::tm tm;
133 tm.tm_year = static_cast<int>(cs.year() - year_t{1900});
134 tm.tm_mon = cs.month() - 1;
135 tm.tm_mday = cs.day();
136 tm.tm_hour = cs.hour();
137 tm.tm_min = cs.minute();
138 tm.tm_sec = cs.second();
139 tm.tm_isdst = is_dst;
140 *t = std::mktime(&tm);
141 if (*t == std::time_t{-1}) {
142 std::tm tm2;
143 const std::tm* tmp = local_time(t, &tm2);
144 if (tmp == nullptr || tmp->tm_year != tm.tm_year ||
145 tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday ||
146 tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min ||
147 tmp->tm_sec != tm.tm_sec) {
148 // A true error (not just one second before the epoch).
149 return false;
150 }
151 }
152 *off = static_cast<int>(tm_gmtoff(tm));
153 return true;
154 }
155
156 // Find the least time_t in [lo:hi] where local time matches offset, given:
157 // (1) lo doesn't match, (2) hi does, and (3) there is only one transition.
find_trans(std::time_t lo,std::time_t hi,int offset)158 std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
159 std::tm tm;
160 while (lo + 1 != hi) {
161 const std::time_t mid = lo + (hi - lo) / 2;
162 std::tm* tmp = local_time(&mid, &tm);
163 if (tmp != nullptr) {
164 if (tm_gmtoff(*tmp) == offset) {
165 hi = mid;
166 } else {
167 lo = mid;
168 }
169 } else {
170 // If std::tm cannot hold some result we resort to a linear search,
171 // ignoring all failed conversions. Slow, but never really happens.
172 while (++lo != hi) {
173 tmp = local_time(&lo, &tm);
174 if (tmp != nullptr) {
175 if (tm_gmtoff(*tmp) == offset) break;
176 }
177 }
178 return lo;
179 }
180 }
181 return hi;
182 }
183
184 } // namespace
185
TimeZoneLibC(const std::string & name)186 TimeZoneLibC::TimeZoneLibC(const std::string& name)
187 : local_(name == "localtime") {}
188
BreakTime(const time_point<seconds> & tp) const189 time_zone::absolute_lookup TimeZoneLibC::BreakTime(
190 const time_point<seconds>& tp) const {
191 time_zone::absolute_lookup al;
192 al.offset = 0;
193 al.is_dst = false;
194 al.abbr = "-00";
195
196 const std::int_fast64_t s = ToUnixSeconds(tp);
197
198 // If std::time_t cannot hold the input we saturate the output.
199 if (s < std::numeric_limits<std::time_t>::min()) {
200 al.cs = civil_second::min();
201 return al;
202 }
203 if (s > std::numeric_limits<std::time_t>::max()) {
204 al.cs = civil_second::max();
205 return al;
206 }
207
208 const std::time_t t = static_cast<std::time_t>(s);
209 std::tm tm;
210 std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm);
211
212 // If std::tm cannot hold the result we saturate the output.
213 if (tmp == nullptr) {
214 al.cs = (s < 0) ? civil_second::min() : civil_second::max();
215 return al;
216 }
217
218 const year_t year = tmp->tm_year + year_t{1900};
219 al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour,
220 tmp->tm_min, tmp->tm_sec);
221 al.offset = static_cast<int>(tm_gmtoff(*tmp));
222 al.abbr = local_ ? tm_zone(*tmp) : "UTC"; // as expected by cctz
223 al.is_dst = tmp->tm_isdst > 0;
224 return al;
225 }
226
MakeTime(const civil_second & cs) const227 time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
228 if (!local_) {
229 // If time_point<seconds> cannot hold the result we saturate.
230 static const civil_second min_tp_cs =
231 civil_second() + ToUnixSeconds(time_point<seconds>::min());
232 static const civil_second max_tp_cs =
233 civil_second() + ToUnixSeconds(time_point<seconds>::max());
234 const time_point<seconds> tp = (cs < min_tp_cs) ? time_point<seconds>::min()
235 : (cs > max_tp_cs)
236 ? time_point<seconds>::max()
237 : FromUnixSeconds(cs - civil_second());
238 return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
239 }
240
241 // If tm_year cannot hold the requested year we saturate the result.
242 if (cs.year() < 0) {
243 if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
244 const time_point<seconds> tp = time_point<seconds>::min();
245 return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
246 }
247 } else {
248 if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
249 const time_point<seconds> tp = time_point<seconds>::max();
250 return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
251 }
252 }
253
254 // We probe with "is_dst" values of 0 and 1 to try to distinguish unique
255 // civil seconds from skipped or repeated ones. This is not always possible
256 // however, as the "dst" flag does not change over some offset transitions.
257 // We are also subject to the vagaries of mktime() implementations.
258 std::time_t t0, t1;
259 int offset0, offset1;
260 if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) {
261 if (t0 == t1) {
262 // The civil time was singular (pre == trans == post).
263 const time_point<seconds> tp = FromUnixSeconds(t0);
264 return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
265 }
266
267 if (t0 > t1) {
268 std::swap(t0, t1);
269 std::swap(offset0, offset1);
270 }
271 const std::time_t tt = find_trans(t0, t1, offset1);
272 const time_point<seconds> trans = FromUnixSeconds(tt);
273
274 if (offset0 < offset1) {
275 // The civil time did not exist (pre >= trans > post).
276 const time_point<seconds> pre = FromUnixSeconds(t1);
277 const time_point<seconds> post = FromUnixSeconds(t0);
278 return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
279 }
280
281 // The civil time was ambiguous (pre < trans <= post).
282 const time_point<seconds> pre = FromUnixSeconds(t0);
283 const time_point<seconds> post = FromUnixSeconds(t1);
284 return {time_zone::civil_lookup::REPEATED, pre, trans, post};
285 }
286
287 // make_time() failed somehow so we saturate the result.
288 const time_point<seconds> tp = (cs < civil_second())
289 ? time_point<seconds>::min()
290 : time_point<seconds>::max();
291 return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
292 }
293
NextTransition(const time_point<seconds> &,time_zone::civil_transition *) const294 bool TimeZoneLibC::NextTransition(const time_point<seconds>&,
295 time_zone::civil_transition*) const {
296 return false;
297 }
298
PrevTransition(const time_point<seconds> &,time_zone::civil_transition *) const299 bool TimeZoneLibC::PrevTransition(const time_point<seconds>&,
300 time_zone::civil_transition*) const {
301 return false;
302 }
303
Version() const304 std::string TimeZoneLibC::Version() const {
305 return std::string(); // unknown
306 }
307
Description() const308 std::string TimeZoneLibC::Description() const {
309 return local_ ? "localtime" : "UTC";
310 }
311
312 } // namespace cctz
313 } // namespace time_internal
314 ABSL_NAMESPACE_END
315 } // namespace absl
316