Commit 1285ca4b by Abseil Team Committed by Copybara-Service

Import of CCTZ from GitHub.

PiperOrigin-RevId: 538241594
Change-Id: Ie6f0d913bcf07dea2f33e47198ba952b3800d70e
parent 66406fdf
...@@ -26,6 +26,8 @@ namespace cctz { ...@@ -26,6 +26,8 @@ namespace cctz {
std::unique_ptr<TimeZoneIf> TimeZoneIf::Load(const std::string& name) { std::unique_ptr<TimeZoneIf> TimeZoneIf::Load(const std::string& name) {
// Support "libc:localtime" and "libc:*" to access the legacy // Support "libc:localtime" and "libc:*" to access the legacy
// localtime and UTC support respectively from the C library. // localtime and UTC support respectively from the C library.
// NOTE: The "libc:*" zones are internal, test-only interfaces, and
// are subject to change/removal without notice. Do not use them.
if (name.compare(0, 5, "libc:") == 0) { if (name.compare(0, 5, "libc:") == 0) {
return std::unique_ptr<TimeZoneIf>(new TimeZoneLibC(name.substr(5))); return std::unique_ptr<TimeZoneIf>(new TimeZoneLibC(name.substr(5)));
} }
......
...@@ -125,31 +125,30 @@ inline std::tm* local_time(const std::time_t* timep, std::tm* result) { ...@@ -125,31 +125,30 @@ inline std::tm* local_time(const std::time_t* timep, std::tm* result) {
#endif #endif
} }
// Converts a civil second and "dst" flag into a time_t and UTC offset. // Converts a civil second and "dst" flag into a time_t and a struct tm.
// Returns false if time_t cannot represent the requested civil second. // 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. // 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) { bool make_time(const civil_second& cs, int is_dst, std::time_t* t,
std::tm tm; std::tm* tm) {
tm.tm_year = static_cast<int>(cs.year() - year_t{1900}); tm->tm_year = static_cast<int>(cs.year() - year_t{1900});
tm.tm_mon = cs.month() - 1; tm->tm_mon = cs.month() - 1;
tm.tm_mday = cs.day(); tm->tm_mday = cs.day();
tm.tm_hour = cs.hour(); tm->tm_hour = cs.hour();
tm.tm_min = cs.minute(); tm->tm_min = cs.minute();
tm.tm_sec = cs.second(); tm->tm_sec = cs.second();
tm.tm_isdst = is_dst; tm->tm_isdst = is_dst;
*t = std::mktime(&tm); *t = std::mktime(tm);
if (*t == std::time_t{-1}) { if (*t == std::time_t{-1}) {
std::tm tm2; std::tm tm2;
const std::tm* tmp = local_time(t, &tm2); const std::tm* tmp = local_time(t, &tm2);
if (tmp == nullptr || tmp->tm_year != tm.tm_year || if (tmp == nullptr || tmp->tm_year != tm->tm_year ||
tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday || 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_hour != tm->tm_hour || tmp->tm_min != tm->tm_min ||
tmp->tm_sec != tm.tm_sec) { tmp->tm_sec != tm->tm_sec) {
// A true error (not just one second before the epoch). // A true error (not just one second before the epoch).
return false; return false;
} }
} }
*off = static_cast<int>(tm_gmtoff(tm));
return true; return true;
} }
...@@ -254,33 +253,37 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { ...@@ -254,33 +253,37 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
// We probe with "is_dst" values of 0 and 1 to try to distinguish unique // 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 // civil seconds from skipped or repeated ones. This is not always possible
// however, as the "dst" flag does not change over some offset transitions. // however, as the "dst" flag does not change over some offset transitions.
// We are also subject to the vagaries of mktime() implementations. // We are also subject to the vagaries of mktime() implementations. For
// example, some implementations treat "tm_isdst" as a demand (useless),
// and some as a disambiguator (useful).
std::time_t t0, t1; std::time_t t0, t1;
int offset0, offset1; std::tm tm0, tm1;
if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) { if (make_time(cs, 0, &t0, &tm0) && make_time(cs, 1, &t1, &tm1)) {
if (t0 == t1) { if (tm0.tm_isdst == tm1.tm_isdst) {
// The civil time was singular (pre == trans == post). // The civil time was singular (pre == trans == post).
const time_point<seconds> tp = FromUnixSeconds(t0); const time_point<seconds> tp = FromUnixSeconds(tm0.tm_isdst ? t1 : t0);
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
} }
if (t0 > t1) { int offset = tm_gmtoff(tm0);
if (t0 < t1) { // negative DST
std::swap(t0, t1); std::swap(t0, t1);
std::swap(offset0, offset1); offset = tm_gmtoff(tm1);
} }
const std::time_t tt = find_trans(t0, t1, offset1);
const std::time_t tt = find_trans(t1, t0, offset);
const time_point<seconds> trans = FromUnixSeconds(tt); const time_point<seconds> trans = FromUnixSeconds(tt);
if (offset0 < offset1) { if (tm0.tm_isdst) {
// The civil time did not exist (pre >= trans > post). // The civil time did not exist (pre >= trans > post).
const time_point<seconds> pre = FromUnixSeconds(t1); const time_point<seconds> pre = FromUnixSeconds(t0);
const time_point<seconds> post = FromUnixSeconds(t0); const time_point<seconds> post = FromUnixSeconds(t1);
return {time_zone::civil_lookup::SKIPPED, pre, trans, post}; return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
} }
// The civil time was ambiguous (pre < trans <= post). // The civil time was ambiguous (pre < trans <= post).
const time_point<seconds> pre = FromUnixSeconds(t0); const time_point<seconds> pre = FromUnixSeconds(t1);
const time_point<seconds> post = FromUnixSeconds(t1); const time_point<seconds> post = FromUnixSeconds(t0);
return {time_zone::civil_lookup::REPEATED, pre, trans, post}; return {time_zone::civil_lookup::REPEATED, pre, trans, post};
} }
......
...@@ -1034,16 +1034,16 @@ TEST(MakeTime, SysSecondsLimits) { ...@@ -1034,16 +1034,16 @@ TEST(MakeTime, SysSecondsLimits) {
const time_zone cut = LoadZone("libc:UTC"); const time_zone cut = LoadZone("libc:UTC");
const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900; const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900;
tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), cut); tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), cut);
#if defined(__FreeBSD__) || defined(__OpenBSD__) #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
// The BSD gmtime_r() fails on extreme positive tm_year values. // Some gmtime_r() impls fail on extreme positive values.
#else #else
EXPECT_EQ("2147485547-12-31T23:59:59+00:00", EXPECT_EQ("2147485547-12-31T23:59:59+00:00",
absl::time_internal::cctz::format(RFC3339, tp, cut)); absl::time_internal::cctz::format(RFC3339, tp, cut));
#endif #endif
const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900; const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900;
tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut); tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut);
#if defined(__Fuchsia__) #if defined(__Fuchsia__) || defined(__EMSCRIPTEN__)
// Fuchsia's gmtime_r() fails on extreme negative values (fxbug.dev/78527). // Some gmtime_r() impls fail on extreme negative values (fxbug.dev/78527).
#else #else
EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", EXPECT_EQ("-2147481748-01-01T00:00:00+00:00",
absl::time_internal::cctz::format(RFC3339, tp, cut)); absl::time_internal::cctz::format(RFC3339, tp, cut));
...@@ -1072,7 +1072,7 @@ TEST(MakeTime, LocalTimeLibC) { ...@@ -1072,7 +1072,7 @@ TEST(MakeTime, LocalTimeLibC) {
tp = zi.lookup(transition.to).trans) { tp = zi.lookup(transition.to).trans) {
const auto fcl = zi.lookup(transition.from); const auto fcl = zi.lookup(transition.from);
const auto tcl = zi.lookup(transition.to); const auto tcl = zi.lookup(transition.to);
civil_second cs; // compare cs in zi and lc civil_second cs, us; // compare cs and us in zi and lc
if (fcl.kind == time_zone::civil_lookup::UNIQUE) { if (fcl.kind == time_zone::civil_lookup::UNIQUE) {
if (tcl.kind == time_zone::civil_lookup::UNIQUE) { if (tcl.kind == time_zone::civil_lookup::UNIQUE) {
// Both unique; must be an is_dst or abbr change. // Both unique; must be an is_dst or abbr change.
...@@ -1088,12 +1088,14 @@ TEST(MakeTime, LocalTimeLibC) { ...@@ -1088,12 +1088,14 @@ TEST(MakeTime, LocalTimeLibC) {
} }
ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind); ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind);
cs = transition.to; cs = transition.to;
us = transition.from;
} else { } else {
ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind); ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind);
ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind); ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind);
cs = transition.from; cs = transition.from;
us = transition.to;
} }
if (cs.year() > 2037) break; // limit test time (and to 32-bit time_t) if (us.year() > 2037) break; // limit test time (and to 32-bit time_t)
const auto cl_zi = zi.lookup(cs); const auto cl_zi = zi.lookup(cs);
if (zi.lookup(cl_zi.pre).is_dst == zi.lookup(cl_zi.post).is_dst) { if (zi.lookup(cl_zi.pre).is_dst == zi.lookup(cl_zi.post).is_dst) {
// The "libc" implementation cannot correctly classify transitions // The "libc" implementation cannot correctly classify transitions
...@@ -1125,6 +1127,13 @@ TEST(MakeTime, LocalTimeLibC) { ...@@ -1125,6 +1127,13 @@ TEST(MakeTime, LocalTimeLibC) {
EXPECT_EQ(cl_zi.pre, cl_lc.pre); EXPECT_EQ(cl_zi.pre, cl_lc.pre);
EXPECT_EQ(cl_zi.trans, cl_lc.trans); EXPECT_EQ(cl_zi.trans, cl_lc.trans);
EXPECT_EQ(cl_zi.post, cl_lc.post); EXPECT_EQ(cl_zi.post, cl_lc.post);
const auto ucl_zi = zi.lookup(us);
const auto ucl_lc = lc.lookup(us);
SCOPED_TRACE(testing::Message() << "For " << us << " in " << *np);
EXPECT_EQ(ucl_zi.kind, ucl_lc.kind);
EXPECT_EQ(ucl_zi.pre, ucl_lc.pre);
EXPECT_EQ(ucl_zi.trans, ucl_lc.trans);
EXPECT_EQ(ucl_zi.post, ucl_lc.post);
} }
} }
if (ep == nullptr) { if (ep == nullptr) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment