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


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:19:58