• 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(_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 namespace absl {
31 ABSL_NAMESPACE_BEGIN
32 namespace time_internal {
33 namespace cctz {
34 
35 namespace {
36 
37 #if defined(_WIN32) || defined(_WIN64)
38 // Uses the globals: '_timezone', '_dstbias' and '_tzname'.
tm_gmtoff(const std::tm & tm)39 auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) {
40   const bool is_dst = tm.tm_isdst > 0;
41   return _timezone + (is_dst ? _dstbias : 0);
42 }
tm_zone(const std::tm & tm)43 auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) {
44   const bool is_dst = tm.tm_isdst > 0;
45   return _tzname[is_dst];
46 }
47 #elif defined(__sun)
48 // Uses the globals: 'timezone', 'altzone' and 'tzname'.
49 auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) {
50   const bool is_dst = tm.tm_isdst > 0;
51   return is_dst ? altzone : timezone;
52 }
53 auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
54   const bool is_dst = tm.tm_isdst > 0;
55   return tzname[is_dst];
56 }
57 #elif defined(__native_client__) || defined(__myriad2__) || \
58     defined(__EMSCRIPTEN__)
59 // Uses the globals: 'timezone' and 'tzname'.
60 auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) {
61   const bool is_dst = tm.tm_isdst > 0;
62   return _timezone + (is_dst ? 60 * 60 : 0);
63 }
64 auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
65   const bool is_dst = tm.tm_isdst > 0;
66   return tzname[is_dst];
67 }
68 #else
69 // Adapt to different spellings of the struct std::tm extension fields.
70 #if defined(tm_gmtoff)
71 auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) {
72   return tm.tm_gmtoff;
73 }
74 #elif defined(__tm_gmtoff)
75 auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) {
76   return tm.__tm_gmtoff;
77 }
78 #else
79 template <typename T>
80 auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) {
81   return tm.tm_gmtoff;
82 }
83 template <typename T>
84 auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) {
85   return tm.__tm_gmtoff;
86 }
87 #endif  // tm_gmtoff
88 #if defined(tm_zone)
89 auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { return tm.tm_zone; }
90 #elif defined(__tm_zone)
91 auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) {
92   return tm.__tm_zone;
93 }
94 #else
95 template <typename T>
96 auto tm_zone(const T& tm) -> decltype(tm.tm_zone) {
97   return tm.tm_zone;
98 }
99 template <typename T>
100 auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) {
101   return tm.__tm_zone;
102 }
103 #endif  // tm_zone
104 #endif
105 
gm_time(const std::time_t * timep,std::tm * result)106 inline std::tm* gm_time(const std::time_t* timep, std::tm* result) {
107 #if defined(_WIN32) || defined(_WIN64)
108   return gmtime_s(result, timep) ? nullptr : result;
109 #else
110   return gmtime_r(timep, result);
111 #endif
112 }
113 
local_time(const std::time_t * timep,std::tm * result)114 inline std::tm* local_time(const std::time_t* timep, std::tm* result) {
115 #if defined(_WIN32) || defined(_WIN64)
116   return localtime_s(result, timep) ? nullptr : result;
117 #else
118   return localtime_r(timep, result);
119 #endif
120 }
121 
122 // Converts a civil second and "dst" flag into a time_t and UTC offset.
123 // Returns false if time_t cannot represent the requested civil second.
124 // 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)125 bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) {
126   std::tm tm;
127   tm.tm_year = static_cast<int>(cs.year() - year_t{1900});
128   tm.tm_mon = cs.month() - 1;
129   tm.tm_mday = cs.day();
130   tm.tm_hour = cs.hour();
131   tm.tm_min = cs.minute();
132   tm.tm_sec = cs.second();
133   tm.tm_isdst = is_dst;
134   *t = std::mktime(&tm);
135   if (*t == std::time_t{-1}) {
136     std::tm tm2;
137     const std::tm* tmp = local_time(t, &tm2);
138     if (tmp == nullptr || tmp->tm_year != tm.tm_year ||
139         tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday ||
140         tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min ||
141         tmp->tm_sec != tm.tm_sec) {
142       // A true error (not just one second before the epoch).
143       return false;
144     }
145   }
146   *off = static_cast<int>(tm_gmtoff(tm));
147   return true;
148 }
149 
150 // Find the least time_t in [lo:hi] where local time matches offset, given:
151 // (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)152 std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
153   std::tm tm;
154   while (lo + 1 != hi) {
155     const std::time_t mid = lo + (hi - lo) / 2;
156     std::tm* tmp = local_time(&mid, &tm);
157     if (tmp != nullptr) {
158       if (tm_gmtoff(*tmp) == offset) {
159         hi = mid;
160       } else {
161         lo = mid;
162       }
163     } else {
164       // If std::tm cannot hold some result we resort to a linear search,
165       // ignoring all failed conversions.  Slow, but never really happens.
166       while (++lo != hi) {
167         tmp = local_time(&lo, &tm);
168         if (tmp != nullptr) {
169           if (tm_gmtoff(*tmp) == offset) break;
170         }
171       }
172       return lo;
173     }
174   }
175   return hi;
176 }
177 
178 }  // namespace
179 
TimeZoneLibC(const std::string & name)180 TimeZoneLibC::TimeZoneLibC(const std::string& name)
181     : local_(name == "localtime") {}
182 
BreakTime(const time_point<seconds> & tp) const183 time_zone::absolute_lookup TimeZoneLibC::BreakTime(
184     const time_point<seconds>& tp) const {
185   time_zone::absolute_lookup al;
186   al.offset = 0;
187   al.is_dst = false;
188   al.abbr = "-00";
189 
190   const std::int_fast64_t s = ToUnixSeconds(tp);
191 
192   // If std::time_t cannot hold the input we saturate the output.
193   if (s < std::numeric_limits<std::time_t>::min()) {
194     al.cs = civil_second::min();
195     return al;
196   }
197   if (s > std::numeric_limits<std::time_t>::max()) {
198     al.cs = civil_second::max();
199     return al;
200   }
201 
202   const std::time_t t = static_cast<std::time_t>(s);
203   std::tm tm;
204   std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm);
205 
206   // If std::tm cannot hold the result we saturate the output.
207   if (tmp == nullptr) {
208     al.cs = (s < 0) ? civil_second::min() : civil_second::max();
209     return al;
210   }
211 
212   const year_t year = tmp->tm_year + year_t{1900};
213   al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour,
214                        tmp->tm_min, tmp->tm_sec);
215   al.offset = static_cast<int>(tm_gmtoff(*tmp));
216   al.abbr = local_ ? tm_zone(*tmp) : "UTC";  // as expected by cctz
217   al.is_dst = tmp->tm_isdst > 0;
218   return al;
219 }
220 
MakeTime(const civil_second & cs) const221 time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
222   if (!local_) {
223     // If time_point<seconds> cannot hold the result we saturate.
224     static const civil_second min_tp_cs =
225         civil_second() + ToUnixSeconds(time_point<seconds>::min());
226     static const civil_second max_tp_cs =
227         civil_second() + ToUnixSeconds(time_point<seconds>::max());
228     const time_point<seconds> tp = (cs < min_tp_cs) ? time_point<seconds>::min()
229                                    : (cs > max_tp_cs)
230                                        ? time_point<seconds>::max()
231                                        : FromUnixSeconds(cs - civil_second());
232     return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
233   }
234 
235   // If tm_year cannot hold the requested year we saturate the result.
236   if (cs.year() < 0) {
237     if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
238       const time_point<seconds> tp = time_point<seconds>::min();
239       return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
240     }
241   } else {
242     if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
243       const time_point<seconds> tp = time_point<seconds>::max();
244       return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
245     }
246   }
247 
248   // We probe with "is_dst" values of 0 and 1 to try to distinguish unique
249   // civil seconds from skipped or repeated ones.  This is not always possible
250   // however, as the "dst" flag does not change over some offset transitions.
251   // We are also subject to the vagaries of mktime() implementations.
252   std::time_t t0, t1;
253   int offset0, offset1;
254   if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) {
255     if (t0 == t1) {
256       // The civil time was singular (pre == trans == post).
257       const time_point<seconds> tp = FromUnixSeconds(t0);
258       return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
259     }
260 
261     if (t0 > t1) {
262       std::swap(t0, t1);
263       std::swap(offset0, offset1);
264     }
265     const std::time_t tt = find_trans(t0, t1, offset1);
266     const time_point<seconds> trans = FromUnixSeconds(tt);
267 
268     if (offset0 < offset1) {
269       // The civil time did not exist (pre >= trans > post).
270       const time_point<seconds> pre = FromUnixSeconds(t1);
271       const time_point<seconds> post = FromUnixSeconds(t0);
272       return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
273     }
274 
275     // The civil time was ambiguous (pre < trans <= post).
276     const time_point<seconds> pre = FromUnixSeconds(t0);
277     const time_point<seconds> post = FromUnixSeconds(t1);
278     return {time_zone::civil_lookup::REPEATED, pre, trans, post};
279   }
280 
281   // make_time() failed somehow so we saturate the result.
282   const time_point<seconds> tp = (cs < civil_second())
283                                      ? time_point<seconds>::min()
284                                      : time_point<seconds>::max();
285   return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
286 }
287 
NextTransition(const time_point<seconds> &,time_zone::civil_transition *) const288 bool TimeZoneLibC::NextTransition(const time_point<seconds>&,
289                                   time_zone::civil_transition*) const {
290   return false;
291 }
292 
PrevTransition(const time_point<seconds> &,time_zone::civil_transition *) const293 bool TimeZoneLibC::PrevTransition(const time_point<seconds>&,
294                                   time_zone::civil_transition*) const {
295   return false;
296 }
297 
Version() const298 std::string TimeZoneLibC::Version() const {
299   return std::string();  // unknown
300 }
301 
Description() const302 std::string TimeZoneLibC::Description() const {
303   return local_ ? "localtime" : "UTC";
304 }
305 
306 }  // namespace cctz
307 }  // namespace time_internal
308 ABSL_NAMESPACE_END
309 }  // namespace absl
310