• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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