merge(3p/absl): subtree merge of Abseil up to e19260f

... notably, this includes Abseil's own StatusOr type, which
conflicted with our implementation (that was taken from TensorFlow).

Change-Id: Ie7d6764b64055caaeb8dc7b6b9d066291e6b538f
This commit is contained in:
Vincent Ambo 2020-11-21 14:43:54 +01:00
parent cc27324d02
commit 082c006c04
854 changed files with 11260 additions and 5296 deletions

View file

@ -16,7 +16,7 @@ load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
package(features = ["-parse_headers"])
licenses(["notice"]) # Apache License
licenses(["notice"])
filegroup(
name = "zoneinfo",
@ -92,6 +92,11 @@ cc_library(
### tests
test_suite(
name = "all_tests",
visibility = ["//visibility:public"],
)
cc_test(
name = "civil_time_test",
size = "small",

View file

@ -416,16 +416,10 @@ class civil_time {
// Assigning arithmetic.
CONSTEXPR_M civil_time& operator+=(diff_t n) noexcept {
f_ = step(T{}, f_, n);
return *this;
return *this = *this + n;
}
CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept {
if (n != (std::numeric_limits<diff_t>::min)()) {
f_ = step(T{}, f_, -n);
} else {
f_ = step(T{}, step(T{}, f_, -(n + 1)), 1);
}
return *this;
return *this = *this - n;
}
CONSTEXPR_M civil_time& operator++() noexcept { return *this += 1; }
CONSTEXPR_M civil_time operator++(int) noexcept {
@ -442,13 +436,15 @@ class civil_time {
// Binary arithmetic operators.
friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept {
return a += n;
return civil_time(step(T{}, a.f_, n));
}
friend CONSTEXPR_F civil_time operator+(diff_t n, civil_time a) noexcept {
return a += n;
return a + n;
}
friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept {
return a -= n;
return n != (std::numeric_limits<diff_t>::min)()
? civil_time(step(T{}, a.f_, -n))
: civil_time(step(T{}, step(T{}, a.f_, -(n + 1)), 1));
}
friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept {
return difference(T{}, lhs.f_, rhs.f_);

View file

@ -654,14 +654,23 @@ const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
}
// Sets year, tm_mon and tm_mday given the year, week_num, and tm_wday,
// and the day on which weeks are defined to start.
void FromWeek(int week_num, weekday week_start, year_t* year, std::tm* tm) {
// and the day on which weeks are defined to start. Returns false if year
// would need to move outside its bounds.
bool FromWeek(int week_num, weekday week_start, year_t* year, std::tm* tm) {
const civil_year y(*year % 400);
civil_day cd = prev_weekday(y, week_start); // week 0
cd = next_weekday(cd - 1, FromTmWday(tm->tm_wday)) + (week_num * 7);
*year += cd.year() - y.year();
if (const year_t shift = cd.year() - y.year()) {
if (shift > 0) {
if (*year > std::numeric_limits<year_t>::max() - shift) return false;
} else {
if (*year < std::numeric_limits<year_t>::min() - shift) return false;
}
*year += shift;
}
tm->tm_mon = cd.month() - 1;
tm->tm_mday = cd.day();
return true;
}
} // namespace
@ -965,7 +974,12 @@ bool parse(const std::string& format, const std::string& input,
}
// Compute year, tm.tm_mon and tm.tm_mday if we parsed a week number.
if (week_num != -1) FromWeek(week_num, week_start, &year, &tm);
if (week_num != -1) {
if (!FromWeek(week_num, week_start, &year, &tm)) {
if (err != nullptr) *err = "Out-of-range field";
return false;
}
}
const int month = tm.tm_mon + 1;
civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);

View file

@ -1481,6 +1481,11 @@ TEST(Parse, WeekYearShift) {
EXPECT_EQ(exp, tp);
EXPECT_TRUE(parse("%Y-%W-%w", "2020-52-5", utc, &tp));
EXPECT_EQ(exp, tp);
// Slipping into the previous/following calendar years should fail when
// we're already at the extremes.
EXPECT_FALSE(parse("%Y-%U-%u", "-9223372036854775808-0-7", utc, &tp));
EXPECT_FALSE(parse("%Y-%U-%u", "9223372036854775807-53-7", utc, &tp));
}
TEST(Parse, MaxRange) {

View file

@ -40,7 +40,6 @@
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
@ -83,6 +82,27 @@ const std::int_least32_t kSecsPerYear[2] = {
366 * kSecsPerDay,
};
// Convert a cctz::weekday to a POSIX TZ weekday number (0==Sun, ..., 6=Sat).
inline int ToPosixWeekday(weekday wd) {
switch (wd) {
case weekday::sunday:
return 0;
case weekday::monday:
return 1;
case weekday::tuesday:
return 2;
case weekday::wednesday:
return 3;
case weekday::thursday:
return 4;
case weekday::friday:
return 5;
case weekday::saturday:
return 6;
}
return 0; /*NOTREACHED*/
}
// Single-byte, unsigned numeric values are encoded directly.
inline std::uint_fast8_t Decode8(const char* cp) {
return static_cast<std::uint_fast8_t>(*cp) & 0xff;
@ -188,15 +208,13 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
tt.is_dst = false;
tt.abbr_index = 0;
// We temporarily add some redundant, contemporary (2013 through 2023)
// We temporarily add some redundant, contemporary (2015 through 2025)
// transitions for performance reasons. See TimeZoneInfo::LocalTime().
// TODO: Fix the performance issue and remove the extra transitions.
transitions_.clear();
transitions_.reserve(12);
for (const std::int_fast64_t unix_time : {
-(1LL << 59), // BIG_BANG
1356998400LL, // 2013-01-01T00:00:00+00:00
1388534400LL, // 2014-01-01T00:00:00+00:00
-(1LL << 59), // a "first half" transition
1420070400LL, // 2015-01-01T00:00:00+00:00
1451606400LL, // 2016-01-01T00:00:00+00:00
1483228800LL, // 2017-01-01T00:00:00+00:00
@ -206,7 +224,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
1609459200LL, // 2021-01-01T00:00:00+00:00
1640995200LL, // 2022-01-01T00:00:00+00:00
1672531200LL, // 2023-01-01T00:00:00+00:00
2147483647LL, // 2^31 - 1
1704067200LL, // 2024-01-01T00:00:00+00:00
1735689600LL, // 2025-01-01T00:00:00+00:00
}) {
Transition& tr(*transitions_.emplace(transitions_.end()));
tr.unix_time = unix_time;
@ -217,8 +236,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
default_transition_type_ = 0;
abbreviations_ = FixedOffsetToAbbr(offset);
abbreviations_.append(1, '\0'); // add NUL
future_spec_.clear(); // never needed for a fixed-offset zone
abbreviations_.append(1, '\0');
future_spec_.clear(); // never needed for a fixed-offset zone
extended_ = false;
tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
@ -259,21 +278,6 @@ std::size_t TimeZoneInfo::Header::DataLength(std::size_t time_len) const {
return len;
}
// Check that the TransitionType has the expected offset/is_dst/abbreviation.
void TimeZoneInfo::CheckTransition(const std::string& name,
const TransitionType& tt,
std::int_fast32_t offset, bool is_dst,
const std::string& abbr) const {
if (tt.utc_offset != offset || tt.is_dst != is_dst ||
&abbreviations_[tt.abbr_index] != abbr) {
std::clog << name << ": Transition"
<< " offset=" << tt.utc_offset << "/"
<< (tt.is_dst ? "DST" : "STD")
<< "/abbr=" << &abbreviations_[tt.abbr_index]
<< " does not match POSIX spec '" << future_spec_ << "'\n";
}
}
// zic(8) can generate no-op transitions when a zone changes rules at an
// instant when there is actually no discontinuity. So we check whether
// two transitions have equivalent types (same offset/is_dst/abbr).
@ -282,117 +286,108 @@ bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index,
if (tt1_index == tt2_index) return true;
const TransitionType& tt1(transition_types_[tt1_index]);
const TransitionType& tt2(transition_types_[tt2_index]);
if (tt1.is_dst != tt2.is_dst) return false;
if (tt1.utc_offset != tt2.utc_offset) return false;
if (tt1.is_dst != tt2.is_dst) return false;
if (tt1.abbr_index != tt2.abbr_index) return false;
return true;
}
// Find/make a transition type with these attributes.
bool TimeZoneInfo::GetTransitionType(std::int_fast32_t utc_offset, bool is_dst,
const std::string& abbr,
std::uint_least8_t* index) {
std::size_t type_index = 0;
std::size_t abbr_index = abbreviations_.size();
for (; type_index != transition_types_.size(); ++type_index) {
const TransitionType& tt(transition_types_[type_index]);
const char* tt_abbr = &abbreviations_[tt.abbr_index];
if (tt_abbr == abbr) abbr_index = tt.abbr_index;
if (tt.utc_offset == utc_offset && tt.is_dst == is_dst) {
if (abbr_index == tt.abbr_index) break; // reuse
}
}
if (type_index > 255 || abbr_index > 255) {
// No index space (8 bits) available for a new type or abbreviation.
return false;
}
if (type_index == transition_types_.size()) {
TransitionType& tt(*transition_types_.emplace(transition_types_.end()));
tt.utc_offset = static_cast<std::int_least32_t>(utc_offset);
tt.is_dst = is_dst;
if (abbr_index == abbreviations_.size()) {
abbreviations_.append(abbr);
abbreviations_.append(1, '\0');
}
tt.abbr_index = static_cast<std::uint_least8_t>(abbr_index);
}
*index = static_cast<std::uint_least8_t>(type_index);
return true;
}
// Use the POSIX-TZ-environment-variable-style string to handle times
// in years after the last transition stored in the zoneinfo data.
void TimeZoneInfo::ExtendTransitions(const std::string& name,
const Header& hdr) {
bool TimeZoneInfo::ExtendTransitions() {
extended_ = false;
bool extending = !future_spec_.empty();
if (future_spec_.empty()) return true; // last transition prevails
PosixTimeZone posix;
if (extending && !ParsePosixSpec(future_spec_, &posix)) {
std::clog << name << ": Failed to parse '" << future_spec_ << "'\n";
extending = false;
if (!ParsePosixSpec(future_spec_, &posix)) return false;
// Find transition type for the future std specification.
std::uint_least8_t std_ti;
if (!GetTransitionType(posix.std_offset, false, posix.std_abbr, &std_ti))
return false;
if (posix.dst_abbr.empty()) { // std only
// The future specification should match the last transition, and
// that means that handling the future will fall out naturally.
return EquivTransitions(transitions_.back().type_index, std_ti);
}
if (extending && posix.dst_abbr.empty()) { // std only
// The future specification should match the last/default transition,
// and that means that handling the future will fall out naturally.
std::uint_fast8_t index = default_transition_type_;
if (hdr.timecnt != 0) index = transitions_[hdr.timecnt - 1].type_index;
const TransitionType& tt(transition_types_[index]);
CheckTransition(name, tt, posix.std_offset, false, posix.std_abbr);
extending = false;
}
if (extending && hdr.timecnt < 2) {
std::clog << name << ": Too few transitions for POSIX spec\n";
extending = false;
}
if (!extending) {
// Ensure that there is always a transition in the second half of the
// time line (the BIG_BANG transition is in the first half) so that the
// signed difference between a civil_second and the civil_second of its
// previous transition is always representable, without overflow.
const Transition& last(transitions_.back());
if (last.unix_time < 0) {
const std::uint_fast8_t type_index = last.type_index;
Transition& tr(*transitions_.emplace(transitions_.end()));
tr.unix_time = 2147483647; // 2038-01-19T03:14:07+00:00
tr.type_index = type_index;
}
return; // last transition wins
}
// Find transition type for the future dst specification.
std::uint_least8_t dst_ti;
if (!GetTransitionType(posix.dst_offset, true, posix.dst_abbr, &dst_ti))
return false;
// Extend the transitions for an additional 400 years using the
// future specification. Years beyond those can be handled by
// mapping back to a cycle-equivalent year within that range.
// zic(8) should probably do this so that we don't have to.
// TODO: Reduce the extension by the number of compatible
// transitions already in place.
transitions_.reserve(hdr.timecnt + 400 * 2 + 1);
transitions_.resize(hdr.timecnt + 400 * 2);
// We may need two additional transitions for the current year.
transitions_.reserve(transitions_.size() + 400 * 2 + 2);
extended_ = true;
// The future specification should match the last two transitions,
// and those transitions should have different is_dst flags. Note
// that nothing says the UTC offset used by the is_dst transition
// must be greater than that used by the !is_dst transition. (See
// Europe/Dublin, for example.)
const Transition* tr0 = &transitions_[hdr.timecnt - 1];
const Transition* tr1 = &transitions_[hdr.timecnt - 2];
const TransitionType* tt0 = &transition_types_[tr0->type_index];
const TransitionType* tt1 = &transition_types_[tr1->type_index];
const TransitionType& dst(tt0->is_dst ? *tt0 : *tt1);
const TransitionType& std(tt0->is_dst ? *tt1 : *tt0);
CheckTransition(name, dst, posix.dst_offset, true, posix.dst_abbr);
CheckTransition(name, std, posix.std_offset, false, posix.std_abbr);
// Add the transitions to tr1 and back to tr0 for each extra year.
last_year_ = LocalTime(tr0->unix_time, *tt0).cs.year();
const Transition& last(transitions_.back());
const std::int_fast64_t last_time = last.unix_time;
const TransitionType& last_tt(transition_types_[last.type_index]);
last_year_ = LocalTime(last_time, last_tt).cs.year();
bool leap_year = IsLeap(last_year_);
const civil_day jan1(last_year_, 1, 1);
std::int_fast64_t jan1_time = civil_second(jan1) - civil_second();
int jan1_weekday = (static_cast<int>(get_weekday(jan1)) + 1) % 7;
Transition* tr = &transitions_[hdr.timecnt]; // next trans to fill
if (LocalTime(tr1->unix_time, *tt1).cs.year() != last_year_) {
// Add a single extra transition to align to a calendar year.
transitions_.resize(transitions_.size() + 1);
assert(tr == &transitions_[hdr.timecnt]); // no reallocation
const PosixTransition& pt1(tt0->is_dst ? posix.dst_end : posix.dst_start);
std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1);
tr->unix_time = jan1_time + tr1_offset - tt0->utc_offset;
tr++->type_index = tr1->type_index;
tr0 = &transitions_[hdr.timecnt];
tr1 = &transitions_[hdr.timecnt - 1];
tt0 = &transition_types_[tr0->type_index];
tt1 = &transition_types_[tr1->type_index];
}
const PosixTransition& pt1(tt0->is_dst ? posix.dst_end : posix.dst_start);
const PosixTransition& pt0(tt0->is_dst ? posix.dst_start : posix.dst_end);
for (const year_t limit = last_year_ + 400; last_year_ < limit;) {
last_year_ += 1; // an additional year of generated transitions
const civil_second jan1(last_year_);
std::int_fast64_t jan1_time = jan1 - civil_second();
int jan1_weekday = ToPosixWeekday(get_weekday(jan1));
Transition dst = {0, dst_ti, civil_second(), civil_second()};
Transition std = {0, std_ti, civil_second(), civil_second()};
for (const year_t limit = last_year_ + 400;; ++last_year_) {
auto dst_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_start);
auto std_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_end);
dst.unix_time = jan1_time + dst_trans_off - posix.std_offset;
std.unix_time = jan1_time + std_trans_off - posix.dst_offset;
const auto* ta = dst.unix_time < std.unix_time ? &dst : &std;
const auto* tb = dst.unix_time < std.unix_time ? &std : &dst;
if (last_time < tb->unix_time) {
if (last_time < ta->unix_time) transitions_.push_back(*ta);
transitions_.push_back(*tb);
}
if (last_year_ == limit) break;
jan1_time += kSecsPerYear[leap_year];
jan1_weekday = (jan1_weekday + kDaysPerYear[leap_year]) % 7;
leap_year = !leap_year && IsLeap(last_year_);
std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1);
tr->unix_time = jan1_time + tr1_offset - tt0->utc_offset;
tr++->type_index = tr1->type_index;
std::int_fast64_t tr0_offset = TransOffset(leap_year, jan1_weekday, pt0);
tr->unix_time = jan1_time + tr0_offset - tt1->utc_offset;
tr++->type_index = tr0->type_index;
leap_year = !leap_year && IsLeap(last_year_ + 1);
}
assert(tr == &transitions_[0] + transitions_.size());
return true;
}
bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
bool TimeZoneInfo::Load(ZoneInfoSource* zip) {
// Read and validate the header.
tzhead tzh;
if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false;
@ -430,7 +425,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
const char* bp = tbuf.data();
// Decode and validate the transitions.
transitions_.reserve(hdr.timecnt + 2); // We might add a couple.
transitions_.reserve(hdr.timecnt + 2);
transitions_.resize(hdr.timecnt);
for (std::size_t i = 0; i != hdr.timecnt; ++i) {
transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp);
@ -449,6 +444,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
}
// Decode and validate the transition types.
transition_types_.reserve(hdr.typecnt + 2);
transition_types_.resize(hdr.typecnt);
for (std::size_t i = 0; i != hdr.typecnt; ++i) {
transition_types_[i].utc_offset =
@ -475,6 +471,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
}
// Copy all the abbreviations.
abbreviations_.reserve(hdr.charcnt + 10);
abbreviations_.assign(bp, hdr.charcnt);
bp += hdr.charcnt;
@ -525,19 +522,29 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
transitions_.resize(hdr.timecnt);
// Ensure that there is always a transition in the first half of the
// time line (the second half is handled in ExtendTransitions()) so that
// the signed difference between a civil_second and the civil_second of
// its previous transition is always representable, without overflow.
// A contemporary zic will usually have already done this for us.
// time line (the second half is handled below) so that the signed
// difference between a civil_second and the civil_second of its
// previous transition is always representable, without overflow.
if (transitions_.empty() || transitions_.front().unix_time >= 0) {
Transition& tr(*transitions_.emplace(transitions_.begin()));
tr.unix_time = -(1LL << 59); // see tz/zic.c "BIG_BANG"
tr.unix_time = -(1LL << 59); // -18267312070-10-26T17:01:52+00:00
tr.type_index = default_transition_type_;
hdr.timecnt += 1;
}
// Extend the transitions using the future specification.
ExtendTransitions(name, hdr);
if (!ExtendTransitions()) return false;
// Ensure that there is always a transition in the second half of the
// time line (the first half is handled above) so that the signed
// difference between a civil_second and the civil_second of its
// previous transition is always representable, without overflow.
const Transition& last(transitions_.back());
if (last.unix_time < 0) {
const std::uint_fast8_t type_index = last.type_index;
Transition& tr(*transitions_.emplace(transitions_.end()));
tr.unix_time = 2147483647; // 2038-01-19T03:14:07+00:00
tr.type_index = type_index;
}
// Compute the local civil time for each transition and the preceding
// second. These will be used for reverse conversions in MakeTime().
@ -718,12 +725,12 @@ bool TimeZoneInfo::Load(const std::string& name) {
// Find and use a ZoneInfoSource to load the named zone.
auto zip = cctz_extension::zone_info_source_factory(
name, [](const std::string& name) -> std::unique_ptr<ZoneInfoSource> {
if (auto zip = FileZoneInfoSource::Open(name)) return zip;
if (auto zip = AndroidZoneInfoSource::Open(name)) return zip;
name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> {
if (auto z = FileZoneInfoSource::Open(n)) return z;
if (auto z = AndroidZoneInfoSource::Open(n)) return z;
return nullptr;
});
return zip != nullptr && Load(name, zip.get());
return zip != nullptr && Load(zip.get());
}
// BreakTime() translation for a particular transition type.
@ -897,8 +904,8 @@ bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp,
const Transition* begin = &transitions_[0];
const Transition* end = begin + transitions_.size();
if (begin->unix_time <= -(1LL << 59)) {
// Do not report the BIG_BANG found in recent zoneinfo data as it is
// really a sentinel, not a transition. See tz/zic.c.
// Do not report the BIG_BANG found in some zoneinfo data as it is
// really a sentinel, not a transition. See pre-2018f tz/zic.c.
++begin;
}
std::int_fast64_t unix_time = ToUnixSeconds(tp);
@ -923,8 +930,8 @@ bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp,
const Transition* begin = &transitions_[0];
const Transition* end = begin + transitions_.size();
if (begin->unix_time <= -(1LL << 59)) {
// Do not report the BIG_BANG found in recent zoneinfo data as it is
// really a sentinel, not a transition. See tz/zic.c.
// Do not report the BIG_BANG found in some zoneinfo data as it is
// really a sentinel, not a transition. See pre-2018f tz/zic.c.
++begin;
}
std::int_fast64_t unix_time = ToUnixSeconds(tp);

View file

@ -95,15 +95,14 @@ class TimeZoneInfo : public TimeZoneIf {
std::size_t DataLength(std::size_t time_len) const;
};
void CheckTransition(const std::string& name, const TransitionType& tt,
std::int_fast32_t offset, bool is_dst,
const std::string& abbr) const;
bool GetTransitionType(std::int_fast32_t utc_offset, bool is_dst,
const std::string& abbr, std::uint_least8_t* index);
bool EquivTransitions(std::uint_fast8_t tt1_index,
std::uint_fast8_t tt2_index) const;
void ExtendTransitions(const std::string& name, const Header& hdr);
bool ExtendTransitions();
bool ResetToBuiltinUTC(const seconds& offset);
bool Load(const std::string& name, ZoneInfoSource* zip);
bool Load(ZoneInfoSource* zip);
// Helpers for BreakTime() and MakeTime().
time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,

View file

@ -27,6 +27,12 @@
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#if defined(_AIX)
extern "C" {
extern long altzone;
}
#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace time_internal {
@ -44,7 +50,7 @@ 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)
#elif defined(__sun) || defined(_AIX)
// Uses the globals: 'timezone', 'altzone' and 'tzname'.
auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) {
const bool is_dst = tm.tm_isdst > 0;
@ -153,7 +159,8 @@ 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)) {
std::tm* tmp = local_time(&mid, &tm);
if (tmp != nullptr) {
if (tm_gmtoff(*tmp) == offset) {
hi = mid;
} else {
@ -163,7 +170,8 @@ std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
// 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)) {
tmp = local_time(&lo, &tm);
if (tmp != nullptr) {
if (tm_gmtoff(*tmp) == offset) break;
}
}
@ -223,11 +231,10 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
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());
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};
}

View file

@ -1004,13 +1004,17 @@ TEST(MakeTime, SysSecondsLimits) {
#if defined(_WIN32) || defined(_WIN64)
// localtime_s() and gmtime_s() don't believe in years outside [1970:3000].
#else
const time_zone utc = LoadZone("libc:UTC");
const time_zone cut = LoadZone("libc:UTC");
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), utc);
EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, utc));
tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), cut);
#if defined(__FreeBSD__) || defined(__OpenBSD__)
// The BSD gmtime_r() fails on extreme positive tm_year values.
#else
EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, cut));
#endif
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), utc);
EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, utc));
tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut);
EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut));
#endif
}
}

View file

@ -1 +1 @@
2020a
2020d

Some files were not shown because too many files have changed in this diff Show more