Commit 134496a3 by Abseil Team Committed by Titus Winters

Export of internal Abseil changes.

--
aa9e2bff92652605b8244677058be787c872f99c by Abseil Team <absl-team@google.com>:

Import of CCTZ from GitHub.

PiperOrigin-RevId: 202702969

--
d26c857c203589892a84bc44d789f2a15a60f234 by Abseil Team <absl-team@google.com>:

Cleans up the FixedArray code (formatting, renames, etc) without changing the functionality

PiperOrigin-RevId: 202538159
GitOrigin-RevId: aa9e2bff92652605b8244677058be787c872f99c
Change-Id: I6561257232c6cc8e1cbf51d7e26bae5f8760551e
parent ba8d6cf0
...@@ -119,7 +119,7 @@ class time_zone { ...@@ -119,7 +119,7 @@ class time_zone {
// of the given civil-time argument, and the pre, trans, and post // of the given civil-time argument, and the pre, trans, and post
// members will give the absolute time answers using the pre-transition // members will give the absolute time answers using the pre-transition
// offset, the transition point itself, and the post-transition offset, // offset, the transition point itself, and the post-transition offset,
// respectively (all three times are equal if kind == UNIQUE). If any // respectively (all three times are equal if kind == UNIQUE). If any
// of these three absolute times is outside the representable range of a // of these three absolute times is outside the representable range of a
// time_point<seconds> the field is set to its maximum/minimum value. // time_point<seconds> the field is set to its maximum/minimum value.
// //
...@@ -159,17 +159,79 @@ class time_zone { ...@@ -159,17 +159,79 @@ class time_zone {
}; };
civil_lookup lookup(const civil_second& cs) const; civil_lookup lookup(const civil_second& cs) const;
// Finds the time of the next/previous offset change in this time zone.
//
// By definition, next_transition(tp, &trans) returns false when tp has
// its maximum value, and prev_transition(tp, &trans) returns false
// when tp has its minimum value. If the zone has no transitions, the
// result will also be false no matter what the argument.
//
// Otherwise, when tp has its minimum value, next_transition(tp, &trans)
// returns true and sets trans to the first recorded transition. Chains
// of calls to next_transition()/prev_transition() will eventually return
// false, but it is unspecified exactly when next_transition(tp, &trans)
// jumps to false, or what time is set by prev_transition(tp, &trans) for
// a very distant tp.
//
// Note: Enumeration of time-zone transitions is for informational purposes
// only. Modern time-related code should not care about when offset changes
// occur.
//
// Example:
// cctz::time_zone nyc;
// if (!cctz::load_time_zone("America/New_York", &nyc)) { ... }
// const auto now = std::chrono::system_clock::now();
// auto tp = cctz::time_point<cctz::seconds>::min();
// cctz::time_zone::civil_transition trans;
// while (tp <= now && nyc.next_transition(tp, &trans)) {
// // transition: trans.from -> trans.to
// tp = nyc.lookup(trans.to).trans;
// }
struct civil_transition {
civil_second from; // the civil time we jump from
civil_second to; // the civil time we jump to
};
bool next_transition(const time_point<seconds>& tp,
civil_transition* trans) const;
template <typename D>
bool next_transition(const time_point<D>& tp,
civil_transition* trans) const {
return next_transition(detail::split_seconds(tp).first, trans);
}
bool prev_transition(const time_point<seconds>& tp,
civil_transition* trans) const;
template <typename D>
bool prev_transition(const time_point<D>& tp,
civil_transition* trans) const {
return prev_transition(detail::split_seconds(tp).first, trans);
}
// version() and description() provide additional information about the
// time zone. The content of each of the returned strings is unspecified,
// however, when the IANA Time Zone Database is the underlying data source
// the version() std::string will be in the familar form (e.g, "2018e") or
// empty when unavailable.
//
// Note: These functions are for informational or testing purposes only.
std::string version() const; // empty when unknown
std::string description() const;
// Relational operators.
friend bool operator==(time_zone lhs, time_zone rhs) {
return &lhs.effective_impl() == &rhs.effective_impl();
}
friend bool operator!=(time_zone lhs, time_zone rhs) {
return !(lhs == rhs);
}
class Impl; class Impl;
private: private:
explicit time_zone(const Impl* impl) : impl_(impl) {} explicit time_zone(const Impl* impl) : impl_(impl) {}
const Impl& effective_impl() const; // handles implicit UTC
const Impl* impl_; const Impl* impl_;
}; };
// Relational operators.
bool operator==(time_zone lhs, time_zone rhs);
inline bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); }
// Loads the named time zone. May perform I/O on the initial load. // Loads the named time zone. May perform I/O on the initial load.
// If the name is invalid, or some other kind of error occurs, returns // If the name is invalid, or some other kind of error occurs, returns
// false and "*tz" is set to the UTC time zone. // false and "*tz" is set to the UTC time zone.
...@@ -184,6 +246,7 @@ time_zone utc_time_zone(); ...@@ -184,6 +246,7 @@ time_zone utc_time_zone();
time_zone fixed_time_zone(const seconds& offset); time_zone fixed_time_zone(const seconds& offset);
// Returns a time zone representing the local time zone. Falls back to UTC. // Returns a time zone representing the local time zone. Falls back to UTC.
// Note: local_time_zone.name() may only be something like "localtime".
time_zone local_time_zone(); time_zone local_time_zone();
// Returns the civil time (cctz::civil_second) within the given time zone at // Returns the civil time (cctz::civil_second) within the given time zone at
...@@ -227,7 +290,7 @@ bool parse(const std::string&, const std::string&, const time_zone&, ...@@ -227,7 +290,7 @@ bool parse(const std::string&, const std::string&, const time_zone&,
// - %E*f - Fractional seconds with full precision (a literal '*') // - %E*f - Fractional seconds with full precision (a literal '*')
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
// //
// Note that %E0S behaves like %S, and %E0f produces no characters. In // Note that %E0S behaves like %S, and %E0f produces no characters. In
// contrast %E*f always produces at least one digit, which may be '0'. // contrast %E*f always produces at least one digit, which may be '0'.
// //
// Note that %Y produces as many characters as it takes to fully render the // Note that %Y produces as many characters as it takes to fully render the
...@@ -254,7 +317,7 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp, ...@@ -254,7 +317,7 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp,
// Parses an input std::string according to the provided format std::string and // Parses an input std::string according to the provided format std::string and
// returns the corresponding time_point. Uses strftime()-like formatting // returns the corresponding time_point. Uses strftime()-like formatting
// options, with the same extensions as cctz::format(), but with the // options, with the same extensions as cctz::format(), but with the
// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez // exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez
// and %E*z also accept the same inputs. // and %E*z also accept the same inputs.
// //
// %Y consumes as many numeric characters as it can, so the matching data // %Y consumes as many numeric characters as it can, so the matching data
......
...@@ -31,6 +31,11 @@ class ZoneInfoSource { ...@@ -31,6 +31,11 @@ class ZoneInfoSource {
virtual std::size_t Read(void* ptr, std::size_t size) = 0; // like fread() virtual std::size_t Read(void* ptr, std::size_t size) = 0; // like fread()
virtual int Skip(std::size_t offset) = 0; // like fseek() virtual int Skip(std::size_t offset) = 0; // like fseek()
// Until the zoneinfo data supports versioning information, we provide
// a way for a ZoneInfoSource to indicate it out-of-band. The default
// implementation returns an empty std::string.
virtual std::string Version() const;
}; };
} // namespace cctz } // namespace cctz
......
...@@ -754,23 +754,21 @@ void BM_Zone_LoadAllTimeZonesCached(benchmark::State& state) { ...@@ -754,23 +754,21 @@ void BM_Zone_LoadAllTimeZonesCached(benchmark::State& state) {
} }
BENCHMARK(BM_Zone_LoadAllTimeZonesCached); BENCHMARK(BM_Zone_LoadAllTimeZonesCached);
void BM_Zone_TimeZoneImplGetImplicit(benchmark::State& state) { void BM_Zone_TimeZoneEqualityImplicit(benchmark::State& state) {
cctz::time_zone tz; // implicit UTC cctz::time_zone tz; // implicit UTC
cctz::time_zone::Impl::get(tz);
while (state.KeepRunning()) { while (state.KeepRunning()) {
cctz::time_zone::Impl::get(tz); benchmark::DoNotOptimize(tz == tz);
} }
} }
BENCHMARK(BM_Zone_TimeZoneImplGetImplicit); BENCHMARK(BM_Zone_TimeZoneEqualityImplicit);
void BM_Zone_TimeZoneImplGetExplicit(benchmark::State& state) { void BM_Zone_TimeZoneEqualityExplicit(benchmark::State& state) {
cctz::time_zone tz = cctz::utc_time_zone(); // explicit UTC cctz::time_zone tz = cctz::utc_time_zone(); // explicit UTC
cctz::time_zone::Impl::get(tz);
while (state.KeepRunning()) { while (state.KeepRunning()) {
cctz::time_zone::Impl::get(tz); benchmark::DoNotOptimize(tz == tz);
} }
} }
BENCHMARK(BM_Zone_TimeZoneImplGetExplicit); BENCHMARK(BM_Zone_TimeZoneEqualityExplicit);
void BM_Zone_UTCTimeZone(benchmark::State& state) { void BM_Zone_UTCTimeZone(benchmark::State& state) {
cctz::time_zone tz; cctz::time_zone tz;
......
...@@ -141,6 +141,9 @@ char* Format02d(char* ep, int v) { ...@@ -141,6 +141,9 @@ char* Format02d(char* ep, int v) {
// Formats a UTC offset, like +00:00. // Formats a UTC offset, like +00:00.
char* FormatOffset(char* ep, int offset, const char* mode) { char* FormatOffset(char* ep, int offset, const char* mode) {
// TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
// generate a "negative zero" when we're formatting a zero offset
// as the result of a failed load_time_zone().
char sign = '+'; char sign = '+';
if (offset < 0) { if (offset < 0) {
offset = -offset; // bounded by 24h so no overflow offset = -offset; // bounded by 24h so no overflow
......
...@@ -64,6 +64,17 @@ void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt, ...@@ -64,6 +64,17 @@ void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt,
EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz)); EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz));
} }
// These tests sometimes run on platforms that have zoneinfo data so old
// that the transition we are attempting to check does not exist, most
// notably Android emulators. Fortunately, AndroidZoneInfoSource supports
// time_zone::version() so, in cases where we've learned that it matters,
// we can make the check conditionally.
int VersionCmp(time_zone tz, const std::string& target) {
std::string version = tz.version();
if (version.empty() && !target.empty()) return 1; // unknown > known
return version.compare(target);
}
} // namespace } // namespace
// //
...@@ -453,8 +464,8 @@ TEST(Format, ExtendedSecondOffset) { ...@@ -453,8 +464,8 @@ TEST(Format, ExtendedSecondOffset) {
EXPECT_TRUE(load_time_zone("America/New_York", &tz)); EXPECT_TRUE(load_time_zone("America/New_York", &tz));
tp = convert(civil_second(1883, 11, 18, 16, 59, 59), utc); tp = convert(civil_second(1883, 11, 18, 16, 59, 59), utc);
if (tz.lookup(tp).offset == -5 * 60 * 60) { if (tz.lookup(tp).offset == -5 * 60 * 60) {
// We're likely dealing with zoneinfo that doesn't support really old // It looks like the tzdata is only 32 bit (probably macOS),
// timestamps, so America/New_York never looks to be on local mean time. // which bottoms out at 1901-12-13T20:45:52+00:00.
} else { } else {
TestFormatSpecifier(tp, tz, "%E*z", "-04:56:02"); TestFormatSpecifier(tp, tz, "%E*z", "-04:56:02");
TestFormatSpecifier(tp, tz, "%Ez", "-04:56"); TestFormatSpecifier(tp, tz, "%Ez", "-04:56");
...@@ -464,12 +475,10 @@ TEST(Format, ExtendedSecondOffset) { ...@@ -464,12 +475,10 @@ TEST(Format, ExtendedSecondOffset) {
EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz)); EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz));
tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc); tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc);
#if defined(__ANDROID__) && __ANDROID_API__ < 25 if (VersionCmp(tz, "2016g") >= 0) {
// Only Android 'N'.1 and beyond have this tz2016g transition. TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19");
#else TestFormatSpecifier(tp, tz, "%Ez", "+04:31");
TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19"); }
TestFormatSpecifier(tp, tz, "%Ez", "+04:31");
#endif
tp += chrono::seconds(1); tp += chrono::seconds(1);
TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00"); TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00");
} }
......
...@@ -41,9 +41,13 @@ class TimeZoneIf { ...@@ -41,9 +41,13 @@ class TimeZoneIf {
virtual time_zone::civil_lookup MakeTime( virtual time_zone::civil_lookup MakeTime(
const civil_second& cs) const = 0; const civil_second& cs) const = 0;
virtual bool NextTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const = 0;
virtual bool PrevTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const = 0;
virtual std::string Version() const = 0;
virtual std::string Description() const = 0; virtual std::string Description() const = 0;
virtual bool NextTransition(time_point<seconds>* tp) const = 0;
virtual bool PrevTransition(time_point<seconds>* tp) const = 0;
protected: protected:
TimeZoneIf() {} TimeZoneIf() {}
......
...@@ -83,15 +83,6 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { ...@@ -83,15 +83,6 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
return impl != utc_impl; return impl != utc_impl;
} }
const time_zone::Impl& time_zone::Impl::get(const time_zone& tz) {
if (tz.impl_ == nullptr) {
// Dereferencing an implicit-UTC time_zone is expected to be
// rare, so we don't mind paying a small synchronization cost.
return *UTCImpl();
}
return *tz.impl_;
}
void time_zone::Impl::ClearTimeZoneMapTestOnly() { void time_zone::Impl::ClearTimeZoneMapTestOnly() {
std::lock_guard<std::mutex> lock(time_zone_mutex); std::lock_guard<std::mutex> lock(time_zone_mutex);
if (time_zone_map != nullptr) { if (time_zone_map != nullptr) {
......
...@@ -37,15 +37,15 @@ class time_zone::Impl { ...@@ -37,15 +37,15 @@ class time_zone::Impl {
// some other kind of error occurs. Note that loading "UTC" never fails. // some other kind of error occurs. Note that loading "UTC" never fails.
static bool LoadTimeZone(const std::string& name, time_zone* tz); static bool LoadTimeZone(const std::string& name, time_zone* tz);
// Dereferences the time_zone to obtain its Impl.
static const time_zone::Impl& get(const time_zone& tz);
// Clears the map of cached time zones. Primarily for use in benchmarks // Clears the map of cached time zones. Primarily for use in benchmarks
// that gauge the performance of loading/parsing the time-zone data. // that gauge the performance of loading/parsing the time-zone data.
static void ClearTimeZoneMapTestOnly(); static void ClearTimeZoneMapTestOnly();
// The primary key is the time-zone ID (e.g., "America/New_York"). // The primary key is the time-zone ID (e.g., "America/New_York").
const std::string& name() const { return name_; } const std::string& Name() const {
// TODO: It would nice if the zoneinfo data included the zone name.
return name_;
}
// Breaks a time_point down to civil-time components in this time zone. // Breaks a time_point down to civil-time components in this time zone.
time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const { time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const {
...@@ -59,28 +59,22 @@ class time_zone::Impl { ...@@ -59,28 +59,22 @@ class time_zone::Impl {
return zone_->MakeTime(cs); return zone_->MakeTime(cs);
} }
// Returns an implementation-specific description of this time zone.
std::string Description() const { return zone_->Description(); }
// Finds the time of the next/previous offset change in this time zone. // Finds the time of the next/previous offset change in this time zone.
// bool NextTransition(const time_point<seconds>& tp,
// By definition, NextTransition(&tp) returns false when tp has its time_zone::civil_transition* trans) const {
// maximum value, and PrevTransition(&tp) returns false when tp has its return zone_->NextTransition(tp, trans);
// mimimum value. If the zone has no transitions, the result will also
// be false no matter what the argument.
//
// Otherwise, when tp has its mimimum value, NextTransition(&tp) returns
// true and sets tp to the first recorded transition. Chains of calls
// to NextTransition()/PrevTransition() will eventually return false,
// but it is unspecified exactly when NextTransition(&tp) jumps to false,
// or what time is set by PrevTransition(&tp) for a very distant tp.
bool NextTransition(time_point<seconds>* tp) const {
return zone_->NextTransition(tp);
} }
bool PrevTransition(time_point<seconds>* tp) const { bool PrevTransition(const time_point<seconds>& tp,
return zone_->PrevTransition(tp); time_zone::civil_transition* trans) const {
return zone_->PrevTransition(tp, trans);
} }
// Returns an implementation-defined version std::string for this time zone.
std::string Version() const { return zone_->Version(); }
// Returns an implementation-defined description of this time zone.
std::string Description() const { return zone_->Description(); }
private: private:
explicit Impl(const std::string& name); explicit Impl(const std::string& name);
static const Impl* UTCImpl(); static const Impl* UTCImpl();
......
...@@ -186,14 +186,13 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { ...@@ -186,14 +186,13 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
tt.is_dst = false; tt.is_dst = false;
tt.abbr_index = 0; tt.abbr_index = 0;
// We temporarily add some redundant, contemporary (2012 through 2021) // We temporarily add some redundant, contemporary (2013 through 2023)
// transitions for performance reasons. See TimeZoneInfo::LocalTime(). // transitions for performance reasons. See TimeZoneInfo::LocalTime().
// TODO: Fix the performance issue and remove the extra transitions. // TODO: Fix the performance issue and remove the extra transitions.
transitions_.clear(); transitions_.clear();
transitions_.reserve(12); transitions_.reserve(12);
for (const std::int_fast64_t unix_time : { for (const std::int_fast64_t unix_time : {
-(1LL << 59), // BIG_BANG -(1LL << 59), // BIG_BANG
1325376000LL, // 2012-01-01T00:00:00+00:00
1356998400LL, // 2013-01-01T00:00:00+00:00 1356998400LL, // 2013-01-01T00:00:00+00:00
1388534400LL, // 2014-01-01T00:00:00+00:00 1388534400LL, // 2014-01-01T00:00:00+00:00
1420070400LL, // 2015-01-01T00:00:00+00:00 1420070400LL, // 2015-01-01T00:00:00+00:00
...@@ -203,6 +202,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { ...@@ -203,6 +202,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
1546300800LL, // 2019-01-01T00:00:00+00:00 1546300800LL, // 2019-01-01T00:00:00+00:00
1577836800LL, // 2020-01-01T00:00:00+00:00 1577836800LL, // 2020-01-01T00:00:00+00:00
1609459200LL, // 2021-01-01T00:00:00+00:00 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 2147483647LL, // 2^31 - 1
}) { }) {
Transition& tr(*transitions_.emplace(transitions_.end())); Transition& tr(*transitions_.emplace(transitions_.end()));
...@@ -519,6 +520,13 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { ...@@ -519,6 +520,13 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
// We don't check for EOF so that we're forwards compatible. // We don't check for EOF so that we're forwards compatible.
// If we did not find version information during the standard loading
// process (as of tzh_version '3' that is unsupported), then ask the
// ZoneInfoSource for any out-of-bound version std::string it may be privy to.
if (version_.empty()) {
version_ = zip->Version();
}
// Trim redundant transitions. zic may have added these to work around // Trim redundant transitions. zic may have added these to work around
// differences between the glibc and reference implementations (see // differences between the glibc and reference implementations (see
// zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071). // zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071).
...@@ -605,6 +613,10 @@ class FileZoneInfoSource : public ZoneInfoSource { ...@@ -605,6 +613,10 @@ class FileZoneInfoSource : public ZoneInfoSource {
if (rc == 0) len_ -= offset; if (rc == 0) len_ -= offset;
return rc; return rc;
} }
std::string Version() const override {
// TODO: It would nice if the zoneinfo data included the tzdb version.
return std::string();
}
protected: protected:
explicit FileZoneInfoSource( explicit FileZoneInfoSource(
...@@ -654,14 +666,15 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open( ...@@ -654,14 +666,15 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length)); return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length));
} }
#if defined(__ANDROID__)
class AndroidZoneInfoSource : public FileZoneInfoSource { class AndroidZoneInfoSource : public FileZoneInfoSource {
public: public:
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name); static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
std::string Version() const override { return version_; }
private: private:
explicit AndroidZoneInfoSource(FILE* fp, std::size_t len) explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers)
: FileZoneInfoSource(fp, len) {} : FileZoneInfoSource(fp, len), version_(vers) {}
std::string version_;
}; };
std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
...@@ -669,6 +682,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( ...@@ -669,6 +682,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
// Use of the "file:" prefix is intended for testing purposes only. // Use of the "file:" prefix is intended for testing purposes only.
if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5)); if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5));
#if defined(__ANDROID__)
// See Android's libc/tzcode/bionic.cpp for additional information. // See Android's libc/tzcode/bionic.cpp for additional information.
for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
"/system/usr/share/zoneinfo/tzdata"}) { "/system/usr/share/zoneinfo/tzdata"}) {
...@@ -678,6 +692,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( ...@@ -678,6 +692,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
char hbuf[24]; // covers header.zonetab_offset too char hbuf[24]; // covers header.zonetab_offset too
if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue; if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
if (strncmp(hbuf, "tzdata", 6) != 0) continue; if (strncmp(hbuf, "tzdata", 6) != 0) continue;
const char* vers = (hbuf[11] == '\0') ? hbuf + 6 : "";
const std::int_fast32_t index_offset = Decode32(hbuf + 12); const std::int_fast32_t index_offset = Decode32(hbuf + 12);
const std::int_fast32_t data_offset = Decode32(hbuf + 16); const std::int_fast32_t data_offset = Decode32(hbuf + 16);
if (index_offset < 0 || data_offset < index_offset) continue; if (index_offset < 0 || data_offset < index_offset) continue;
...@@ -698,13 +713,13 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( ...@@ -698,13 +713,13 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
if (strcmp(name.c_str(), ebuf) == 0) { if (strcmp(name.c_str(), ebuf) == 0) {
if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break; if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break;
return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource( return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource(
fp.release(), static_cast<std::size_t>(length))); fp.release(), static_cast<std::size_t>(length), vers));
} }
} }
} }
#endif // __ANDROID__
return nullptr; return nullptr;
} }
#endif
} // namespace } // namespace
...@@ -722,9 +737,7 @@ bool TimeZoneInfo::Load(const std::string& name) { ...@@ -722,9 +737,7 @@ bool TimeZoneInfo::Load(const std::string& name) {
auto zip = cctz_extension::zone_info_source_factory( auto zip = cctz_extension::zone_info_source_factory(
name, [](const std::string& name) -> std::unique_ptr<ZoneInfoSource> { name, [](const std::string& name) -> std::unique_ptr<ZoneInfoSource> {
if (auto zip = FileZoneInfoSource::Open(name)) return zip; if (auto zip = FileZoneInfoSource::Open(name)) return zip;
#if defined(__ANDROID__)
if (auto zip = AndroidZoneInfoSource::Open(name)) return zip; if (auto zip = AndroidZoneInfoSource::Open(name)) return zip;
#endif
return nullptr; return nullptr;
}); });
return zip != nullptr && Load(name, zip.get()); return zip != nullptr && Load(name, zip.get());
...@@ -885,17 +898,20 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const { ...@@ -885,17 +898,20 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
} }
std::string TimeZoneInfo::Version() const {
return version_;
}
std::string TimeZoneInfo::Description() const { std::string TimeZoneInfo::Description() const {
std::ostringstream oss; std::ostringstream oss;
// TODO: It would nice if the zoneinfo data included the zone name.
// TODO: It would nice if the zoneinfo data included the tzdb version.
oss << "#trans=" << transitions_.size(); oss << "#trans=" << transitions_.size();
oss << " #types=" << transition_types_.size(); oss << " #types=" << transition_types_.size();
oss << " spec='" << future_spec_ << "'"; oss << " spec='" << future_spec_ << "'";
return oss.str(); return oss.str();
} }
bool TimeZoneInfo::NextTransition(time_point<seconds>* tp) const { bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const {
if (transitions_.empty()) return false; if (transitions_.empty()) return false;
const Transition* begin = &transitions_[0]; const Transition* begin = &transitions_[0];
const Transition* end = begin + transitions_.size(); const Transition* end = begin + transitions_.size();
...@@ -904,22 +920,24 @@ bool TimeZoneInfo::NextTransition(time_point<seconds>* tp) const { ...@@ -904,22 +920,24 @@ bool TimeZoneInfo::NextTransition(time_point<seconds>* tp) const {
// really a sentinel, not a transition. See tz/zic.c. // really a sentinel, not a transition. See tz/zic.c.
++begin; ++begin;
} }
std::int_fast64_t unix_time = ToUnixSeconds(*tp); std::int_fast64_t unix_time = ToUnixSeconds(tp);
const Transition target = { unix_time }; const Transition target = { unix_time };
const Transition* tr = std::upper_bound(begin, end, target, const Transition* tr = std::upper_bound(begin, end, target,
Transition::ByUnixTime()); Transition::ByUnixTime());
if (tr != begin) { // skip no-op transitions for (; tr != end; ++tr) { // skip no-op transitions
for (; tr != end; ++tr) { std::uint_fast8_t prev_type_index =
if (!EquivTransitions(tr[-1].type_index, tr[0].type_index)) break; (tr == begin) ? default_transition_type_ : tr[-1].type_index;
} if (!EquivTransitions(prev_type_index, tr[0].type_index)) break;
} }
// When tr == end we return false, ignoring future_spec_. // When tr == end we return false, ignoring future_spec_.
if (tr == end) return false; if (tr == end) return false;
*tp = FromUnixSeconds(tr->unix_time); trans->from = tr->prev_civil_sec + 1;
trans->to = tr->civil_sec;
return true; return true;
} }
bool TimeZoneInfo::PrevTransition(time_point<seconds>* tp) const { bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const {
if (transitions_.empty()) return false; if (transitions_.empty()) return false;
const Transition* begin = &transitions_[0]; const Transition* begin = &transitions_[0];
const Transition* end = begin + transitions_.size(); const Transition* end = begin + transitions_.size();
...@@ -928,11 +946,12 @@ bool TimeZoneInfo::PrevTransition(time_point<seconds>* tp) const { ...@@ -928,11 +946,12 @@ bool TimeZoneInfo::PrevTransition(time_point<seconds>* tp) const {
// really a sentinel, not a transition. See tz/zic.c. // really a sentinel, not a transition. See tz/zic.c.
++begin; ++begin;
} }
std::int_fast64_t unix_time = ToUnixSeconds(*tp); std::int_fast64_t unix_time = ToUnixSeconds(tp);
if (FromUnixSeconds(unix_time) != *tp) { if (FromUnixSeconds(unix_time) != tp) {
if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) { if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) {
if (end == begin) return false; // Ignore future_spec_. if (end == begin) return false; // Ignore future_spec_.
*tp = FromUnixSeconds((--end)->unix_time); trans->from = (--end)->prev_civil_sec + 1;
trans->to = end->civil_sec;
return true; return true;
} }
unix_time += 1; // ceils unix_time += 1; // ceils
...@@ -940,14 +959,15 @@ bool TimeZoneInfo::PrevTransition(time_point<seconds>* tp) const { ...@@ -940,14 +959,15 @@ bool TimeZoneInfo::PrevTransition(time_point<seconds>* tp) const {
const Transition target = { unix_time }; const Transition target = { unix_time };
const Transition* tr = std::lower_bound(begin, end, target, const Transition* tr = std::lower_bound(begin, end, target,
Transition::ByUnixTime()); Transition::ByUnixTime());
if (tr != begin) { // skip no-op transitions for (; tr != begin; --tr) { // skip no-op transitions
for (; tr - 1 != begin; --tr) { std::uint_fast8_t prev_type_index =
if (!EquivTransitions(tr[-2].type_index, tr[-1].type_index)) break; (tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index;
} if (!EquivTransitions(prev_type_index, tr[-1].type_index)) break;
} }
// When tr == end we return the "last" transition, ignoring future_spec_. // When tr == end we return the "last" transition, ignoring future_spec_.
if (tr == begin) return false; if (tr == begin) return false;
*tp = FromUnixSeconds((--tr)->unix_time); trans->from = (--tr)->prev_civil_sec + 1;
trans->to = tr->civil_sec;
return true; return true;
} }
......
...@@ -74,9 +74,12 @@ class TimeZoneInfo : public TimeZoneIf { ...@@ -74,9 +74,12 @@ class TimeZoneInfo : public TimeZoneIf {
const time_point<seconds>& tp) const override; const time_point<seconds>& tp) const override;
time_zone::civil_lookup MakeTime( time_zone::civil_lookup MakeTime(
const civil_second& cs) const override; const civil_second& cs) const override;
bool NextTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const override;
bool PrevTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const override;
std::string Version() const override;
std::string Description() const override; std::string Description() const override;
bool NextTransition(time_point<seconds>* tp) const override;
bool PrevTransition(time_point<seconds>* tp) const override;
private: private:
struct Header { // counts of: struct Header { // counts of:
...@@ -114,6 +117,7 @@ class TimeZoneInfo : public TimeZoneIf { ...@@ -114,6 +117,7 @@ class TimeZoneInfo : public TimeZoneIf {
std::uint_fast8_t default_transition_type_; // for before first transition std::uint_fast8_t default_transition_type_; // for before first transition
std::string abbreviations_; // all the NUL-terminated abbreviations std::string abbreviations_; // all the NUL-terminated abbreviations
std::string version_; // the tzdata version if available
std::string future_spec_; // for after the last zic transition std::string future_spec_; // for after the last zic transition
bool extended_; // future_spec_ was used to generate transitions bool extended_; // future_spec_ was used to generate transitions
year_t last_year_; // the final year of the generated transitions year_t last_year_; // the final year of the generated transitions
......
...@@ -139,16 +139,22 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { ...@@ -139,16 +139,22 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
return cl; return cl;
} }
std::string TimeZoneLibC::Description() const { bool TimeZoneLibC::NextTransition(const time_point<seconds>& tp,
return local_ ? "localtime" : "UTC"; time_zone::civil_transition* trans) const {
return false;
} }
bool TimeZoneLibC::NextTransition(time_point<seconds>* tp) const { bool TimeZoneLibC::PrevTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const {
return false; return false;
} }
bool TimeZoneLibC::PrevTransition(time_point<seconds>* tp) const { std::string TimeZoneLibC::Version() const {
return false; return std::string(); // unknown
}
std::string TimeZoneLibC::Description() const {
return local_ ? "localtime" : "UTC";
} }
} // namespace cctz } // namespace cctz
......
...@@ -35,9 +35,12 @@ class TimeZoneLibC : public TimeZoneIf { ...@@ -35,9 +35,12 @@ class TimeZoneLibC : public TimeZoneIf {
const time_point<seconds>& tp) const override; const time_point<seconds>& tp) const override;
time_zone::civil_lookup MakeTime( time_zone::civil_lookup MakeTime(
const civil_second& cs) const override; const civil_second& cs) const override;
bool NextTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const override;
bool PrevTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const override;
std::string Version() const override;
std::string Description() const override; std::string Description() const override;
bool NextTransition(time_point<seconds>* tp) const override;
bool PrevTransition(time_point<seconds>* tp) const override;
private: private:
const bool local_; // localtime or UTC const bool local_; // localtime or UTC
......
...@@ -61,20 +61,43 @@ int __system_property_get(const char* name, char* value) { ...@@ -61,20 +61,43 @@ int __system_property_get(const char* name, char* value) {
#endif #endif
std::string time_zone::name() const { std::string time_zone::name() const {
return time_zone::Impl::get(*this).name(); return effective_impl().Name();
} }
time_zone::absolute_lookup time_zone::lookup( time_zone::absolute_lookup time_zone::lookup(
const time_point<seconds>& tp) const { const time_point<seconds>& tp) const {
return time_zone::Impl::get(*this).BreakTime(tp); return effective_impl().BreakTime(tp);
} }
time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const { time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
return time_zone::Impl::get(*this).MakeTime(cs); return effective_impl().MakeTime(cs);
} }
bool operator==(time_zone lhs, time_zone rhs) { bool time_zone::next_transition(const time_point<seconds>& tp,
return &time_zone::Impl::get(lhs) == &time_zone::Impl::get(rhs); civil_transition* trans) const {
return effective_impl().NextTransition(tp, trans);
}
bool time_zone::prev_transition(const time_point<seconds>& tp,
civil_transition* trans) const {
return effective_impl().PrevTransition(tp, trans);
}
std::string time_zone::version() const {
return effective_impl().Version();
}
std::string time_zone::description() const {
return effective_impl().Description();
}
const time_zone::Impl& time_zone::effective_impl() const {
if (impl_ == nullptr) {
// Dereferencing an implicit-UTC time_zone is expected to be
// rare, so we don't mind paying a small synchronization cost.
return *time_zone::Impl::UTC().impl_;
}
return *impl_;
} }
bool load_time_zone(const std::string& name, time_zone* tz) { bool load_time_zone(const std::string& name, time_zone* tz) {
......
...@@ -651,6 +651,17 @@ time_zone LoadZone(const std::string& name) { ...@@ -651,6 +651,17 @@ time_zone LoadZone(const std::string& name) {
/* EXPECT_STREQ(zone, al.abbr); */ \ /* EXPECT_STREQ(zone, al.abbr); */ \
} while (0) } while (0)
// These tests sometimes run on platforms that have zoneinfo data so old
// that the transition we are attempting to check does not exist, most
// notably Android emulators. Fortunately, AndroidZoneInfoSource supports
// time_zone::version() so, in cases where we've learned that it matters,
// we can make the check conditionally.
int VersionCmp(time_zone tz, const std::string& target) {
std::string version = tz.version();
if (version.empty() && !target.empty()) return 1; // unknown > known
return version.compare(target);
}
} // namespace } // namespace
TEST(TimeZones, LoadZonesConcurrently) { TEST(TimeZones, LoadZonesConcurrently) {
...@@ -981,6 +992,69 @@ TEST(MakeTime, SysSecondsLimits) { ...@@ -981,6 +992,69 @@ TEST(MakeTime, SysSecondsLimits) {
EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
} }
TEST(NextTransition, UTC) {
const auto tz = utc_time_zone();
time_zone::civil_transition trans;
auto tp = time_point<absl::time_internal::cctz::seconds>::min();
EXPECT_FALSE(tz.next_transition(tp, &trans));
tp = time_point<absl::time_internal::cctz::seconds>::max();
EXPECT_FALSE(tz.next_transition(tp, &trans));
}
TEST(PrevTransition, UTC) {
const auto tz = utc_time_zone();
time_zone::civil_transition trans;
auto tp = time_point<absl::time_internal::cctz::seconds>::max();
EXPECT_FALSE(tz.prev_transition(tp, &trans));
tp = time_point<absl::time_internal::cctz::seconds>::min();
EXPECT_FALSE(tz.prev_transition(tp, &trans));
}
TEST(NextTransition, AmericaNewYork) {
const auto tz = LoadZone("America/New_York");
time_zone::civil_transition trans;
auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz);
EXPECT_TRUE(tz.next_transition(tp, &trans));
EXPECT_EQ(civil_second(2018, 11, 4, 2, 0, 0), trans.from);
EXPECT_EQ(civil_second(2018, 11, 4, 1, 0, 0), trans.to);
tp = time_point<absl::time_internal::cctz::seconds>::max();
EXPECT_FALSE(tz.next_transition(tp, &trans));
tp = time_point<absl::time_internal::cctz::seconds>::min();
EXPECT_TRUE(tz.next_transition(tp, &trans));
if (trans.from == civil_second(1918, 3, 31, 2, 0, 0)) {
// It looks like the tzdata is only 32 bit (probably macOS),
// which bottoms out at 1901-12-13T20:45:52+00:00.
EXPECT_EQ(civil_second(1918, 3, 31, 3, 0, 0), trans.to);
} else {
EXPECT_EQ(civil_second(1883, 11, 18, 12, 3, 58), trans.from);
EXPECT_EQ(civil_second(1883, 11, 18, 12, 0, 0), trans.to);
}
}
TEST(PrevTransition, AmericaNewYork) {
const auto tz = LoadZone("America/New_York");
time_zone::civil_transition trans;
auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz);
EXPECT_TRUE(tz.prev_transition(tp, &trans));
EXPECT_EQ(civil_second(2018, 3, 11, 2, 0, 0), trans.from);
EXPECT_EQ(civil_second(2018, 3, 11, 3, 0, 0), trans.to);
tp = time_point<absl::time_internal::cctz::seconds>::min();
EXPECT_FALSE(tz.prev_transition(tp, &trans));
tp = time_point<absl::time_internal::cctz::seconds>::max();
EXPECT_TRUE(tz.prev_transition(tp, &trans));
// We have a transition but we don't know which one.
}
TEST(TimeZoneEdgeCase, AmericaNewYork) { TEST(TimeZoneEdgeCase, AmericaNewYork) {
const time_zone tz = LoadZone("America/New_York"); const time_zone tz = LoadZone("America/New_York");
...@@ -1104,35 +1178,31 @@ TEST(TimeZoneEdgeCase, PacificApia) { ...@@ -1104,35 +1178,31 @@ TEST(TimeZoneEdgeCase, PacificApia) {
TEST(TimeZoneEdgeCase, AfricaCairo) { TEST(TimeZoneEdgeCase, AfricaCairo) {
const time_zone tz = LoadZone("Africa/Cairo"); const time_zone tz = LoadZone("Africa/Cairo");
#if defined(__ANDROID__) && __ANDROID_API__ < 21 if (VersionCmp(tz, "2014c") >= 0) {
// Only Android 'L' and beyond have this tz2014c transition. // An interesting case of midnight not existing.
#else //
// An interesting case of midnight not existing. // 1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET)
// // 1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST)
// 1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET) auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz);
// 1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST) ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET");
auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz); tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET"); ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST");
tp += absl::time_internal::cctz::seconds(1); }
ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST");
#endif
} }
TEST(TimeZoneEdgeCase, AfricaMonrovia) { TEST(TimeZoneEdgeCase, AfricaMonrovia) {
const time_zone tz = LoadZone("Africa/Monrovia"); const time_zone tz = LoadZone("Africa/Monrovia");
#if defined(__ANDROID__) && __ANDROID_API__ < 26 if (VersionCmp(tz, "2017b") >= 0) {
// Only Android 'O' and beyond have this tz2017b transition. // Strange offset change -00:44:30 -> +00:00:00 (non-DST)
#else //
// Strange offset change -00:44:30 -> +00:00:00 (non-DST) // 63593069 == Thu, 6 Jan 1972 23:59:59 -0044 (MMT)
// // 63593070 == Fri, 7 Jan 1972 00:44:30 +0000 (GMT)
// 63593069 == Thu, 6 Jan 1972 23:59:59 -0044 (MMT) auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz);
// 63593070 == Fri, 7 Jan 1972 00:44:30 +0000 (GMT) ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz); tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT"); ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
tp += absl::time_internal::cctz::seconds(1); }
ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
#endif
} }
TEST(TimeZoneEdgeCase, AmericaJamaica) { TEST(TimeZoneEdgeCase, AmericaJamaica) {
...@@ -1144,28 +1214,29 @@ TEST(TimeZoneEdgeCase, AmericaJamaica) { ...@@ -1144,28 +1214,29 @@ TEST(TimeZoneEdgeCase, AmericaJamaica) {
const time_zone tz = LoadZone("America/Jamaica"); const time_zone tz = LoadZone("America/Jamaica");
// Before the first transition. // Before the first transition.
auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz); if (!tz.version().empty() && VersionCmp(tz, "2018d") >= 0) {
#if AMERICA_JAMAICA_PRE_1913_OFFSET_FIX // We avoid the expectations on the -18430 offset below unless we are
// Commit 907241e: Fix off-by-1 error for Jamaica and T&C before 1913. // certain we have commit 907241e (Fix off-by-1 error for Jamaica and
// Until that commit has made its way into a full release we avoid the // T&C before 1913) from 2018d. TODO: Remove the "version() not empty"
// expectations on the -18430 offset below. TODO: Uncomment these. // part when 2018d is generally available from /usr/share/zoneinfo.
ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false, auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz);
tz.lookup(tp).abbr); ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false,
tz.lookup(tp).abbr);
// Over the first (abbreviation-change only) transition.
// -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT) // Over the first (abbreviation-change only) transition.
// -2524503169 == Wed, 1 Jan 1890 00:00:00 -0507 (KMT) // -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT)
tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz); // -2524503169 == Wed, 1 Jan 1890 00:00:00 -0507 (KMT)
ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false, tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz);
tz.lookup(tp).abbr); ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false,
tp += absl::time_internal::cctz::seconds(1); tz.lookup(tp).abbr);
ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT"); tp += absl::time_internal::cctz::seconds(1);
#endif ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT");
}
// Over the last (DST) transition. // Over the last (DST) transition.
// 436341599 == Sun, 30 Oct 1983 01:59:59 -0400 (EDT) // 436341599 == Sun, 30 Oct 1983 01:59:59 -0400 (EDT)
// 436341600 == Sun, 30 Oct 1983 01:00:00 -0500 (EST) // 436341600 == Sun, 30 Oct 1983 01:00:00 -0500 (EST)
tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz); auto tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz);
ExpectTime(tp, tz, 1983, 10, 30, 1, 59, 59, -4 * 3600, true, "EDT"); ExpectTime(tp, tz, 1983, 10, 30, 1, 59, 59, -4 * 3600, true, "EDT");
tp += absl::time_internal::cctz::seconds(1); tp += absl::time_internal::cctz::seconds(1);
ExpectTime(tp, tz, 1983, 10, 30, 1, 0, 0, -5 * 3600, false, "EST"); ExpectTime(tp, tz, 1983, 10, 30, 1, 0, 0, -5 * 3600, false, "EST");
......
...@@ -20,6 +20,7 @@ namespace cctz { ...@@ -20,6 +20,7 @@ namespace cctz {
// Defined out-of-line to avoid emitting a weak vtable in all TUs. // Defined out-of-line to avoid emitting a weak vtable in all TUs.
ZoneInfoSource::~ZoneInfoSource() {} ZoneInfoSource::~ZoneInfoSource() {}
std::string ZoneInfoSource::Version() const { return std::string(); }
} // namespace cctz } // namespace cctz
} // namespace time_internal } // namespace time_internal
......
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