-- 4a21ad4ffa957d28b770de8717289fab7410f567 by Gennadiy Rozental <rogeeff@google.com>: Internal cleanup PiperOrigin-RevId: 252366381 -- b6b0f25439549c54f1537a16625be1fecd3c7d8c by Xiaoyi Zhang <zhangxy@google.com>: Fix C4245 compiler warning of visual studio. This allows using abseil headers in code requiring strict warning settings. This is an import of https://github.com/abseil/abseil-cpp/pull/321. PiperOrigin-RevId: 252101240 -- 0543b7861b658a5a665298e1d868e29968ff7b27 by CJ Johnson <johnsoncj@google.com>: Adds new benchmarks for the constructors of InlinedVector PiperOrigin-RevId: 251905349 -- c65a08507917e9f8f6450b8beb235fe1426d7954 by CJ Johnson <johnsoncj@google.com>: Updates the InlinedVector BatchedBenchmark abstractions to 1) provide the index of the instance back to the prepare and test functions so that callers may perform extra work on local state with a unique per-instance ID and 2) reduce the number of manually written calls to BENCHMARK_TEMPLATE. PiperOrigin-RevId: 251895546 -- 99a1ae2d786b80096172f6e018711e15c0c750b9 by Samuel Benzaquen <sbenza@google.com>: Fix ambiguous construction problem in absl::variant<> to make in line with std::variant. ImaginaryFun is hiding duplicate objects instead of causing ambiguity. Add a second unique argument to make sure all overloads exist in the final overload set. PiperOrigin-RevId: 251860694 -- b54d0a12673be6ebb6e77e24a556ce9b758b3a7e by Abseil Team <absl-team@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 251739183 -- f51b115e0dc3fc9a9c9c20b33a1f27027a700d48 by Abseil Team <absl-team@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 251686812 -- 30e868049282dc6a6fc77d923ca7d2a5d35a1658 by Xiaoyi Zhang <zhangxy@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 251652119 GitOrigin-RevId: 4a21ad4ffa957d28b770de8717289fab7410f567 Change-Id: I7171cb613793fa90e0eb0143b65ec8264a2a84db
307 lines
9.8 KiB
C++
307 lines
9.8 KiB
C++
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
#define _CRT_SECURE_NO_WARNINGS 1
|
|
#endif
|
|
|
|
#include "time_zone_libc.h"
|
|
|
|
#include <chrono>
|
|
#include <ctime>
|
|
#include <limits>
|
|
#include <utility>
|
|
|
|
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
|
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
|
|
|
namespace absl {
|
|
namespace time_internal {
|
|
namespace cctz {
|
|
|
|
namespace {
|
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
// Uses the globals: '_timezone', '_dstbias' and '_tzname'.
|
|
auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) {
|
|
const bool is_dst = tm.tm_isdst > 0;
|
|
return _timezone + (is_dst ? _dstbias : 0);
|
|
}
|
|
auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) {
|
|
const bool is_dst = tm.tm_isdst > 0;
|
|
return _tzname[is_dst];
|
|
}
|
|
#elif defined(__sun)
|
|
// Uses the globals: 'timezone', 'altzone' and 'tzname'.
|
|
auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) {
|
|
const bool is_dst = tm.tm_isdst > 0;
|
|
return is_dst ? altzone : timezone;
|
|
}
|
|
auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
|
|
const bool is_dst = tm.tm_isdst > 0;
|
|
return tzname[is_dst];
|
|
}
|
|
#elif defined(__native_client__) || defined(__myriad2__) || \
|
|
defined(__EMSCRIPTEN__)
|
|
// Uses the globals: 'timezone' and 'tzname'.
|
|
auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) {
|
|
const bool is_dst = tm.tm_isdst > 0;
|
|
return _timezone + (is_dst ? 60 * 60 : 0);
|
|
}
|
|
auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
|
|
const bool is_dst = tm.tm_isdst > 0;
|
|
return tzname[is_dst];
|
|
}
|
|
#else
|
|
// Adapt to different spellings of the struct std::tm extension fields.
|
|
#if defined(tm_gmtoff)
|
|
auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) {
|
|
return tm.tm_gmtoff;
|
|
}
|
|
#elif defined(__tm_gmtoff)
|
|
auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) {
|
|
return tm.__tm_gmtoff;
|
|
}
|
|
#else
|
|
template <typename T>
|
|
auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) {
|
|
return tm.tm_gmtoff;
|
|
}
|
|
template <typename T>
|
|
auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) {
|
|
return tm.__tm_gmtoff;
|
|
}
|
|
#endif // tm_gmtoff
|
|
#if defined(tm_zone)
|
|
auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) {
|
|
return tm.tm_zone;
|
|
}
|
|
#elif defined(__tm_zone)
|
|
auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) {
|
|
return tm.__tm_zone;
|
|
}
|
|
#else
|
|
template <typename T>
|
|
auto tm_zone(const T& tm) -> decltype(tm.tm_zone) {
|
|
return tm.tm_zone;
|
|
}
|
|
template <typename T>
|
|
auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) {
|
|
return tm.__tm_zone;
|
|
}
|
|
#endif // tm_zone
|
|
#endif
|
|
|
|
inline std::tm* gm_time(const std::time_t *timep, std::tm *result) {
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
return gmtime_s(result, timep) ? nullptr : result;
|
|
#else
|
|
return gmtime_r(timep, result);
|
|
#endif
|
|
}
|
|
|
|
inline std::tm* local_time(const std::time_t *timep, std::tm *result) {
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
return localtime_s(result, timep) ? nullptr : result;
|
|
#else
|
|
return localtime_r(timep, result);
|
|
#endif
|
|
}
|
|
|
|
// Converts a civil second and "dst" flag into a time_t and UTC offset.
|
|
// Returns false if time_t cannot represent the requested civil second.
|
|
// Caller must have already checked that cs.year() will fit into a tm_year.
|
|
bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) {
|
|
std::tm tm;
|
|
tm.tm_year = static_cast<int>(cs.year() - year_t{1900});
|
|
tm.tm_mon = cs.month() - 1;
|
|
tm.tm_mday = cs.day();
|
|
tm.tm_hour = cs.hour();
|
|
tm.tm_min = cs.minute();
|
|
tm.tm_sec = cs.second();
|
|
tm.tm_isdst = is_dst;
|
|
*t = std::mktime(&tm);
|
|
if (*t == std::time_t{-1}) {
|
|
std::tm tm2;
|
|
const std::tm* tmp = local_time(t, &tm2);
|
|
if (tmp == nullptr || tmp->tm_year != tm.tm_year ||
|
|
tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday ||
|
|
tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min ||
|
|
tmp->tm_sec != tm.tm_sec) {
|
|
// A true error (not just one second before the epoch).
|
|
return false;
|
|
}
|
|
}
|
|
*off = static_cast<int>(tm_gmtoff(tm));
|
|
return true;
|
|
}
|
|
|
|
// Find the least time_t in [lo:hi] where local time matches offset, given:
|
|
// (1) lo doesn't match, (2) hi does, and (3) there is only one transition.
|
|
std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
|
|
std::tm tm;
|
|
while (lo + 1 != hi) {
|
|
const std::time_t mid = lo + (hi - lo) / 2;
|
|
if (std::tm* tmp = local_time(&mid, &tm)) {
|
|
if (tm_gmtoff(*tmp) == offset) {
|
|
hi = mid;
|
|
} else {
|
|
lo = mid;
|
|
}
|
|
} else {
|
|
// If std::tm cannot hold some result we resort to a linear search,
|
|
// ignoring all failed conversions. Slow, but never really happens.
|
|
while (++lo != hi) {
|
|
if (std::tm* tmp = local_time(&lo, &tm)) {
|
|
if (tm_gmtoff(*tmp) == offset) break;
|
|
}
|
|
}
|
|
return lo;
|
|
}
|
|
}
|
|
return hi;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TimeZoneLibC::TimeZoneLibC(const std::string& name)
|
|
: local_(name == "localtime") {}
|
|
|
|
time_zone::absolute_lookup TimeZoneLibC::BreakTime(
|
|
const time_point<seconds>& tp) const {
|
|
time_zone::absolute_lookup al;
|
|
al.offset = 0;
|
|
al.is_dst = false;
|
|
al.abbr = "-00";
|
|
|
|
const std::int_fast64_t s = ToUnixSeconds(tp);
|
|
|
|
// If std::time_t cannot hold the input we saturate the output.
|
|
if (s < std::numeric_limits<std::time_t>::min()) {
|
|
al.cs = civil_second::min();
|
|
return al;
|
|
}
|
|
if (s > std::numeric_limits<std::time_t>::max()) {
|
|
al.cs = civil_second::max();
|
|
return al;
|
|
}
|
|
|
|
const std::time_t t = static_cast<std::time_t>(s);
|
|
std::tm tm;
|
|
std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm);
|
|
|
|
// If std::tm cannot hold the result we saturate the output.
|
|
if (tmp == nullptr) {
|
|
al.cs = (s < 0) ? civil_second::min() : civil_second::max();
|
|
return al;
|
|
}
|
|
|
|
const year_t year = tmp->tm_year + year_t{1900};
|
|
al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday,
|
|
tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
|
|
al.offset = static_cast<int>(tm_gmtoff(*tmp));
|
|
al.abbr = local_ ? tm_zone(*tmp) : "UTC"; // as expected by cctz
|
|
al.is_dst = tmp->tm_isdst > 0;
|
|
return al;
|
|
}
|
|
|
|
time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
|
|
if (!local_) {
|
|
// If time_point<seconds> cannot hold the result we saturate.
|
|
static const civil_second min_tp_cs =
|
|
civil_second() + ToUnixSeconds(time_point<seconds>::min());
|
|
static const civil_second max_tp_cs =
|
|
civil_second() + ToUnixSeconds(time_point<seconds>::max());
|
|
const time_point<seconds> tp =
|
|
(cs < min_tp_cs)
|
|
? time_point<seconds>::min()
|
|
: (cs > max_tp_cs) ? time_point<seconds>::max()
|
|
: FromUnixSeconds(cs - civil_second());
|
|
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
|
}
|
|
|
|
// If tm_year cannot hold the requested year we saturate the result.
|
|
if (cs.year() < 0) {
|
|
if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
|
|
const time_point<seconds> tp = time_point<seconds>::min();
|
|
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
|
}
|
|
} else {
|
|
if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
|
|
const time_point<seconds> tp = time_point<seconds>::max();
|
|
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
|
}
|
|
}
|
|
|
|
// We probe with "is_dst" values of 0 and 1 to try to distinguish unique
|
|
// civil seconds from skipped or repeated ones. This is not always possible
|
|
// however, as the "dst" flag does not change over some offset transitions.
|
|
// We are also subject to the vagaries of mktime() implementations.
|
|
std::time_t t0, t1;
|
|
int offset0, offset1;
|
|
if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) {
|
|
if (t0 == t1) {
|
|
// The civil time was singular (pre == trans == post).
|
|
const time_point<seconds> tp = FromUnixSeconds(t0);
|
|
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
|
}
|
|
|
|
if (t0 > t1) {
|
|
std::swap(t0, t1);
|
|
std::swap(offset0, offset1);
|
|
}
|
|
const std::time_t tt = find_trans(t0, t1, offset1);
|
|
const time_point<seconds> trans = FromUnixSeconds(tt);
|
|
|
|
if (offset0 < offset1) {
|
|
// The civil time did not exist (pre >= trans > post).
|
|
const time_point<seconds> pre = FromUnixSeconds(t1);
|
|
const time_point<seconds> post = FromUnixSeconds(t0);
|
|
return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
|
|
}
|
|
|
|
// The civil time was ambiguous (pre < trans <= post).
|
|
const time_point<seconds> pre = FromUnixSeconds(t0);
|
|
const time_point<seconds> post = FromUnixSeconds(t1);
|
|
return {time_zone::civil_lookup::REPEATED, pre, trans, post};
|
|
}
|
|
|
|
// make_time() failed somehow so we saturate the result.
|
|
const time_point<seconds> tp = (cs < civil_second())
|
|
? time_point<seconds>::min()
|
|
: time_point<seconds>::max();
|
|
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
|
}
|
|
|
|
bool TimeZoneLibC::NextTransition(const time_point<seconds>&,
|
|
time_zone::civil_transition*) const {
|
|
return false;
|
|
}
|
|
|
|
bool TimeZoneLibC::PrevTransition(const time_point<seconds>&,
|
|
time_zone::civil_transition*) const {
|
|
return false;
|
|
}
|
|
|
|
std::string TimeZoneLibC::Version() const {
|
|
return std::string(); // unknown
|
|
}
|
|
|
|
std::string TimeZoneLibC::Description() const {
|
|
return local_ ? "localtime" : "UTC";
|
|
}
|
|
|
|
} // namespace cctz
|
|
} // namespace time_internal
|
|
} // namespace absl
|