Coverage Report

Created: 2023-09-07 15:06

/src/firebase-ios-sdk/build/external/src/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc
Line
Count
Source (jump to first uncovered line)
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'.
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
}
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
0
auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) {
87
0
  return tm.tm_gmtoff;
88
0
}
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
0
auto tm_zone(const T& tm) -> decltype(tm.tm_zone) {
103
0
  return tm.tm_zone;
104
0
}
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
112
0
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
0
  return gmtime_r(timep, result);
117
0
#endif
118
0
}
119
120
0
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
0
  return localtime_r(timep, result);
125
0
#endif
126
0
}
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.
131
0
bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) {
132
0
  std::tm tm;
133
0
  tm.tm_year = static_cast<int>(cs.year() - year_t{1900});
134
0
  tm.tm_mon = cs.month() - 1;
135
0
  tm.tm_mday = cs.day();
136
0
  tm.tm_hour = cs.hour();
137
0
  tm.tm_min = cs.minute();
138
0
  tm.tm_sec = cs.second();
139
0
  tm.tm_isdst = is_dst;
140
0
  *t = std::mktime(&tm);
141
0
  if (*t == std::time_t{-1}) {
142
0
    std::tm tm2;
143
0
    const std::tm* tmp = local_time(t, &tm2);
144
0
    if (tmp == nullptr || tmp->tm_year != tm.tm_year ||
145
0
        tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday ||
146
0
        tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min ||
147
0
        tmp->tm_sec != tm.tm_sec) {
148
      // A true error (not just one second before the epoch).
149
0
      return false;
150
0
    }
151
0
  }
152
0
  *off = static_cast<int>(tm_gmtoff(tm));
153
0
  return true;
154
0
}
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.
158
0
std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
159
0
  std::tm tm;
160
0
  while (lo + 1 != hi) {
161
0
    const std::time_t mid = lo + (hi - lo) / 2;
162
0
    std::tm* tmp = local_time(&mid, &tm);
163
0
    if (tmp != nullptr) {
164
0
      if (tm_gmtoff(*tmp) == offset) {
165
0
        hi = mid;
166
0
      } else {
167
0
        lo = mid;
168
0
      }
169
0
    } 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
0
      while (++lo != hi) {
173
0
        tmp = local_time(&lo, &tm);
174
0
        if (tmp != nullptr) {
175
0
          if (tm_gmtoff(*tmp) == offset) break;
176
0
        }
177
0
      }
178
0
      return lo;
179
0
    }
180
0
  }
181
0
  return hi;
182
0
}
183
184
}  // namespace
185
186
TimeZoneLibC::TimeZoneLibC(const std::string& name)
187
0
    : local_(name == "localtime") {}
188
189
time_zone::absolute_lookup TimeZoneLibC::BreakTime(
190
0
    const time_point<seconds>& tp) const {
191
0
  time_zone::absolute_lookup al;
192
0
  al.offset = 0;
193
0
  al.is_dst = false;
194
0
  al.abbr = "-00";
195
196
0
  const std::int_fast64_t s = ToUnixSeconds(tp);
197
198
  // If std::time_t cannot hold the input we saturate the output.
199
0
  if (s < std::numeric_limits<std::time_t>::min()) {
200
0
    al.cs = civil_second::min();
201
0
    return al;
202
0
  }
203
0
  if (s > std::numeric_limits<std::time_t>::max()) {
204
0
    al.cs = civil_second::max();
205
0
    return al;
206
0
  }
207
208
0
  const std::time_t t = static_cast<std::time_t>(s);
209
0
  std::tm tm;
210
0
  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
0
  if (tmp == nullptr) {
214
0
    al.cs = (s < 0) ? civil_second::min() : civil_second::max();
215
0
    return al;
216
0
  }
217
218
0
  const year_t year = tmp->tm_year + year_t{1900};
219
0
  al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour,
220
0
                       tmp->tm_min, tmp->tm_sec);
221
0
  al.offset = static_cast<int>(tm_gmtoff(*tmp));
222
0
  al.abbr = local_ ? tm_zone(*tmp) : "UTC";  // as expected by cctz
223
0
  al.is_dst = tmp->tm_isdst > 0;
224
0
  return al;
225
0
}
226
227
0
time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
228
0
  if (!local_) {
229
    // If time_point<seconds> cannot hold the result we saturate.
230
0
    static const civil_second min_tp_cs =
231
0
        civil_second() + ToUnixSeconds(time_point<seconds>::min());
232
0
    static const civil_second max_tp_cs =
233
0
        civil_second() + ToUnixSeconds(time_point<seconds>::max());
234
0
    const time_point<seconds> tp = (cs < min_tp_cs) ? time_point<seconds>::min()
235
0
                                   : (cs > max_tp_cs)
236
0
                                       ? time_point<seconds>::max()
237
0
                                       : FromUnixSeconds(cs - civil_second());
238
0
    return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
239
0
  }
240
241
  // If tm_year cannot hold the requested year we saturate the result.
242
0
  if (cs.year() < 0) {
243
0
    if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
244
0
      const time_point<seconds> tp = time_point<seconds>::min();
245
0
      return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
246
0
    }
247
0
  } else {
248
0
    if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
249
0
      const time_point<seconds> tp = time_point<seconds>::max();
250
0
      return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
251
0
    }
252
0
  }
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
0
  std::time_t t0, t1;
259
0
  int offset0, offset1;
260
0
  if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) {
261
0
    if (t0 == t1) {
262
      // The civil time was singular (pre == trans == post).
263
0
      const time_point<seconds> tp = FromUnixSeconds(t0);
264
0
      return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
265
0
    }
266
267
0
    if (t0 > t1) {
268
0
      std::swap(t0, t1);
269
0
      std::swap(offset0, offset1);
270
0
    }
271
0
    const std::time_t tt = find_trans(t0, t1, offset1);
272
0
    const time_point<seconds> trans = FromUnixSeconds(tt);
273
274
0
    if (offset0 < offset1) {
275
      // The civil time did not exist (pre >= trans > post).
276
0
      const time_point<seconds> pre = FromUnixSeconds(t1);
277
0
      const time_point<seconds> post = FromUnixSeconds(t0);
278
0
      return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
279
0
    }
280
281
    // The civil time was ambiguous (pre < trans <= post).
282
0
    const time_point<seconds> pre = FromUnixSeconds(t0);
283
0
    const time_point<seconds> post = FromUnixSeconds(t1);
284
0
    return {time_zone::civil_lookup::REPEATED, pre, trans, post};
285
0
  }
286
287
  // make_time() failed somehow so we saturate the result.
288
0
  const time_point<seconds> tp = (cs < civil_second())
289
0
                                     ? time_point<seconds>::min()
290
0
                                     : time_point<seconds>::max();
291
0
  return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
292
0
}
293
294
bool TimeZoneLibC::NextTransition(const time_point<seconds>&,
295
0
                                  time_zone::civil_transition*) const {
296
0
  return false;
297
0
}
298
299
bool TimeZoneLibC::PrevTransition(const time_point<seconds>&,
300
0
                                  time_zone::civil_transition*) const {
301
0
  return false;
302
0
}
303
304
0
std::string TimeZoneLibC::Version() const {
305
0
  return std::string();  // unknown
306
0
}
307
308
0
std::string TimeZoneLibC::Description() const {
309
0
  return local_ ? "localtime" : "UTC";
310
0
}
311
312
}  // namespace cctz
313
}  // namespace time_internal
314
ABSL_NAMESPACE_END
315
}  // namespace absl