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(_CRT_SECURE_NO_WARNINGS) && defined(_WIN32)
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 #elif defined(__VXWORKS__)
75 // Uses the globals: 'timezone' and 'tzname'.
76 auto tm_gmtoff(const std::tm& tm) -> decltype(timezone + 0) {
77 const bool is_dst = tm.tm_isdst > 0;
78 return timezone + (is_dst ? 60 * 60 : 0);
79 }
80 auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
81 const bool is_dst = tm.tm_isdst > 0;
82 return tzname[is_dst];
83 }
84 #else
85 // Adapt to different spellings of the struct std::tm extension fields.
86 #if defined(tm_gmtoff)
87 auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) {
88 return tm.tm_gmtoff;
89 }
90 #elif defined(__tm_gmtoff)
91 auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) {
92 return tm.__tm_gmtoff;
93 }
94 #else
95 template <typename T>
96 auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) {
97 return tm.tm_gmtoff;
98 }
99 template <typename T>
100 auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) {
101 return tm.__tm_gmtoff;
102 }
103 #endif // tm_gmtoff
104 #if defined(tm_zone)
105 auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { return tm.tm_zone; }
106 #elif defined(__tm_zone)
107 auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) {
108 return tm.__tm_zone;
109 }
110 #else
111 template <typename T>
112 auto tm_zone(const T& tm) -> decltype(tm.tm_zone) {
113 return tm.tm_zone;
114 }
115 template <typename T>
116 auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) {
117 return tm.__tm_zone;
118 }
119 #endif // tm_zone
120 #endif
121 using tm_gmtoff_t = decltype(tm_gmtoff(std::tm{}));
122
gm_time(const std::time_t * timep,std::tm * result)123 inline std::tm* gm_time(const std::time_t* timep, std::tm* result) {
124 #if defined(_WIN32) || defined(_WIN64)
125 return gmtime_s(result, timep) ? nullptr : result;
126 #else
127 return gmtime_r(timep, result);
128 #endif
129 }
130
local_time(const std::time_t * timep,std::tm * result)131 inline std::tm* local_time(const std::time_t* timep, std::tm* result) {
132 #if defined(_WIN32) || defined(_WIN64)
133 return localtime_s(result, timep) ? nullptr : result;
134 #else
135 return localtime_r(timep, result);
136 #endif
137 }
138
139 // Converts a civil second and "dst" flag into a time_t and a struct tm.
140 // Returns false if time_t cannot represent the requested civil second.
141 // 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,std::tm * tm)142 bool make_time(const civil_second& cs, int is_dst, std::time_t* t,
143 std::tm* tm) {
144 tm->tm_year = static_cast<int>(cs.year() - year_t{1900});
145 tm->tm_mon = cs.month() - 1;
146 tm->tm_mday = cs.day();
147 tm->tm_hour = cs.hour();
148 tm->tm_min = cs.minute();
149 tm->tm_sec = cs.second();
150 tm->tm_isdst = is_dst;
151 *t = std::mktime(tm);
152 if (*t == std::time_t{-1}) {
153 std::tm tm2;
154 const std::tm* tmp = local_time(t, &tm2);
155 if (tmp == nullptr || tmp->tm_year != tm->tm_year ||
156 tmp->tm_mon != tm->tm_mon || tmp->tm_mday != tm->tm_mday ||
157 tmp->tm_hour != tm->tm_hour || tmp->tm_min != tm->tm_min ||
158 tmp->tm_sec != tm->tm_sec) {
159 // A true error (not just one second before the epoch).
160 return false;
161 }
162 }
163 return true;
164 }
165
166 // Find the least time_t in [lo:hi] where local time matches offset, given:
167 // (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,tm_gmtoff_t offset)168 std::time_t find_trans(std::time_t lo, std::time_t hi, tm_gmtoff_t offset) {
169 std::tm tm;
170 while (lo + 1 != hi) {
171 const std::time_t mid = lo + (hi - lo) / 2;
172 std::tm* tmp = local_time(&mid, &tm);
173 if (tmp != nullptr) {
174 if (tm_gmtoff(*tmp) == offset) {
175 hi = mid;
176 } else {
177 lo = mid;
178 }
179 } else {
180 // If std::tm cannot hold some result we resort to a linear search,
181 // ignoring all failed conversions. Slow, but never really happens.
182 while (++lo != hi) {
183 tmp = local_time(&lo, &tm);
184 if (tmp != nullptr) {
185 if (tm_gmtoff(*tmp) == offset) break;
186 }
187 }
188 return lo;
189 }
190 }
191 return hi;
192 }
193
194 } // namespace
195
Make(const std::string & name)196 std::unique_ptr<TimeZoneLibC> TimeZoneLibC::Make(const std::string& name) {
197 return std::unique_ptr<TimeZoneLibC>(new TimeZoneLibC(name));
198 }
199
BreakTime(const time_point<seconds> & tp) const200 time_zone::absolute_lookup TimeZoneLibC::BreakTime(
201 const time_point<seconds>& tp) const {
202 time_zone::absolute_lookup al;
203 al.offset = 0;
204 al.is_dst = false;
205 al.abbr = "-00";
206
207 const std::int_fast64_t s = ToUnixSeconds(tp);
208
209 // If std::time_t cannot hold the input we saturate the output.
210 if (s < std::numeric_limits<std::time_t>::min()) {
211 al.cs = civil_second::min();
212 return al;
213 }
214 if (s > std::numeric_limits<std::time_t>::max()) {
215 al.cs = civil_second::max();
216 return al;
217 }
218
219 const std::time_t t = static_cast<std::time_t>(s);
220 std::tm tm;
221 std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm);
222
223 // If std::tm cannot hold the result we saturate the output.
224 if (tmp == nullptr) {
225 al.cs = (s < 0) ? civil_second::min() : civil_second::max();
226 return al;
227 }
228
229 const year_t year = tmp->tm_year + year_t{1900};
230 al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour,
231 tmp->tm_min, tmp->tm_sec);
232 al.offset = static_cast<int>(tm_gmtoff(*tmp));
233 al.abbr = local_ ? tm_zone(*tmp) : "UTC"; // as expected by cctz
234 al.is_dst = tmp->tm_isdst > 0;
235 return al;
236 }
237
MakeTime(const civil_second & cs) const238 time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
239 if (!local_) {
240 // If time_point<seconds> cannot hold the result we saturate.
241 static const civil_second min_tp_cs =
242 civil_second() + ToUnixSeconds(time_point<seconds>::min());
243 static const civil_second max_tp_cs =
244 civil_second() + ToUnixSeconds(time_point<seconds>::max());
245 const time_point<seconds> tp = (cs < min_tp_cs) ? time_point<seconds>::min()
246 : (cs > max_tp_cs)
247 ? time_point<seconds>::max()
248 : FromUnixSeconds(cs - civil_second());
249 return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
250 }
251
252 // If tm_year cannot hold the requested year we saturate the result.
253 if (cs.year() < 0) {
254 if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
255 const time_point<seconds> tp = time_point<seconds>::min();
256 return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
257 }
258 } else {
259 if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
260 const time_point<seconds> tp = time_point<seconds>::max();
261 return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
262 }
263 }
264
265 // We probe with "is_dst" values of 0 and 1 to try to distinguish unique
266 // civil seconds from skipped or repeated ones. This is not always possible
267 // however, as the "dst" flag does not change over some offset transitions.
268 // We are also subject to the vagaries of mktime() implementations. For
269 // example, some implementations treat "tm_isdst" as a demand (useless),
270 // and some as a disambiguator (useful).
271 std::time_t t0, t1;
272 std::tm tm0, tm1;
273 if (make_time(cs, 0, &t0, &tm0) && make_time(cs, 1, &t1, &tm1)) {
274 if (tm0.tm_isdst == tm1.tm_isdst) {
275 // The civil time was singular (pre == trans == post).
276 const time_point<seconds> tp = FromUnixSeconds(tm0.tm_isdst ? t1 : t0);
277 return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
278 }
279
280 tm_gmtoff_t offset = tm_gmtoff(tm0);
281 if (t0 < t1) { // negative DST
282 std::swap(t0, t1);
283 offset = tm_gmtoff(tm1);
284 }
285
286 const std::time_t tt = find_trans(t1, t0, offset);
287 const time_point<seconds> trans = FromUnixSeconds(tt);
288
289 if (tm0.tm_isdst) {
290 // The civil time did not exist (pre >= trans > post).
291 const time_point<seconds> pre = FromUnixSeconds(t0);
292 const time_point<seconds> post = FromUnixSeconds(t1);
293 return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
294 }
295
296 // The civil time was ambiguous (pre < trans <= post).
297 const time_point<seconds> pre = FromUnixSeconds(t1);
298 const time_point<seconds> post = FromUnixSeconds(t0);
299 return {time_zone::civil_lookup::REPEATED, pre, trans, post};
300 }
301
302 // make_time() failed somehow so we saturate the result.
303 const time_point<seconds> tp = (cs < civil_second())
304 ? time_point<seconds>::min()
305 : time_point<seconds>::max();
306 return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
307 }
308
NextTransition(const time_point<seconds> &,time_zone::civil_transition *) const309 bool TimeZoneLibC::NextTransition(const time_point<seconds>&,
310 time_zone::civil_transition*) const {
311 return false;
312 }
313
PrevTransition(const time_point<seconds> &,time_zone::civil_transition *) const314 bool TimeZoneLibC::PrevTransition(const time_point<seconds>&,
315 time_zone::civil_transition*) const {
316 return false;
317 }
318
Version() const319 std::string TimeZoneLibC::Version() const {
320 return std::string(); // unknown
321 }
322
Description() const323 std::string TimeZoneLibC::Description() const {
324 return local_ ? "localtime" : "UTC";
325 }
326
TimeZoneLibC(const std::string & name)327 TimeZoneLibC::TimeZoneLibC(const std::string& name)
328 : local_(name == "localtime") {}
329
330 } // namespace cctz
331 } // namespace time_internal
332 ABSL_NAMESPACE_END
333 } // namespace absl
334