aboutsummaryrefslogtreecommitdiff
path: root/third_party/abseil-cpp/absl/time/internal/cctz/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/abseil-cpp/absl/time/internal/cctz/src')
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/cctz_benchmark.cc18
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_test.cc10
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc2
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc165
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format_test.cc198
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h3
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc34
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h2
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc369
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h9
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc23
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc49
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc36
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h6
-rw-r--r--third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc2
15 files changed, 680 insertions, 246 deletions
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/cctz_benchmark.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/cctz_benchmark.cc
index d30a644e09..6770ad6b97 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/cctz_benchmark.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/cctz_benchmark.cc
@@ -97,8 +97,8 @@ void BM_PrevWeekday(benchmark::State& state) {
}
BENCHMARK(BM_PrevWeekday);
-const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez";
-const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
+const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
+const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z";
const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
@@ -280,6 +280,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/North_Dakota/Beulah",
"America/North_Dakota/Center",
"America/North_Dakota/New_Salem",
+ "America/Nuuk",
"America/Ojinaga",
"America/Panama",
"America/Pangnirtung",
@@ -647,6 +648,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Pacific/Guam",
"Pacific/Honolulu",
"Pacific/Johnston",
+ "Pacific/Kanton",
"Pacific/Kiritimati",
"Pacific/Kosrae",
"Pacific/Kwajalein",
@@ -990,12 +992,12 @@ void BM_Time_FromCivilDay0_Libc(benchmark::State& state) {
BENCHMARK(BM_Time_FromCivilDay0_Libc);
const char* const kFormats[] = {
- RFC1123_full, // 0
- RFC1123_no_wday, // 1
- RFC3339_full, // 2
- RFC3339_sec, // 3
- "%Y-%m-%dT%H:%M:%S", // 4
- "%Y-%m-%d", // 5
+ RFC1123_full, // 0
+ RFC1123_no_wday, // 1
+ RFC3339_full, // 2
+ RFC3339_sec, // 3
+ "%Y-%m-%d%ET%H:%M:%S", // 4
+ "%Y-%m-%d", // 5
};
const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_test.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_test.cc
index be894d7072..a5a7123041 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_test.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_test.cc
@@ -235,6 +235,16 @@ TEST(CivilTime, Difference) {
}
// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
+TEST(CivilTime, ConstructionWithHugeYear) {
+ constexpr civil_hour h(-9223372036854775807, 1, 1, -1);
+ static_assert(h.year() == -9223372036854775807 - 1,
+ "ConstructionWithHugeYear");
+ static_assert(h.month() == 12, "ConstructionWithHugeYear");
+ static_assert(h.day() == 31, "ConstructionWithHugeYear");
+ static_assert(h.hour() == 23, "ConstructionWithHugeYear");
+}
+
+// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
TEST(CivilTime, DifferenceWithHugeYear) {
{
constexpr civil_day d1(9223372036854775807, 1, 1);
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc
index 303c0244a8..f2b3294ef7 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc
@@ -53,7 +53,7 @@ int Parse02d(const char* p) {
} // namespace
bool FixedOffsetFromName(const std::string& name, seconds* offset) {
- if (name.compare(0, std::string::npos, "UTC", 3) == 0) {
+ if (name == "UTC" || name == "UTC0") {
*offset = seconds::zero();
return true;
}
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc
index 950b23a1d2..d8cb047425 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc
@@ -67,6 +67,48 @@ char* strptime(const char* s, const char* fmt, std::tm* tm) {
}
#endif
+// Convert a cctz::weekday to a tm_wday value (0-6, Sunday = 0).
+int ToTmWday(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*/
+}
+
+// Convert a tm_wday value (0-6, Sunday = 0) to a cctz::weekday.
+weekday FromTmWday(int tm_wday) {
+ switch (tm_wday) {
+ case 0:
+ return weekday::sunday;
+ case 1:
+ return weekday::monday;
+ case 2:
+ return weekday::tuesday;
+ case 3:
+ return weekday::wednesday;
+ case 4:
+ return weekday::thursday;
+ case 5:
+ return weekday::friday;
+ case 6:
+ return weekday::saturday;
+ }
+ return weekday::sunday; /*NOTREACHED*/
+}
+
std::tm ToTM(const time_zone::absolute_lookup& al) {
std::tm tm{};
tm.tm_sec = al.cs.second();
@@ -84,34 +126,19 @@ std::tm ToTM(const time_zone::absolute_lookup& al) {
tm.tm_year = static_cast<int>(al.cs.year() - 1900);
}
- switch (get_weekday(al.cs)) {
- case weekday::sunday:
- tm.tm_wday = 0;
- break;
- case weekday::monday:
- tm.tm_wday = 1;
- break;
- case weekday::tuesday:
- tm.tm_wday = 2;
- break;
- case weekday::wednesday:
- tm.tm_wday = 3;
- break;
- case weekday::thursday:
- tm.tm_wday = 4;
- break;
- case weekday::friday:
- tm.tm_wday = 5;
- break;
- case weekday::saturday:
- tm.tm_wday = 6;
- break;
- }
+ tm.tm_wday = ToTmWday(get_weekday(al.cs));
tm.tm_yday = get_yearday(al.cs) - 1;
tm.tm_isdst = al.is_dst ? 1 : 0;
return tm;
}
+// Returns the week of the year [0:53] given a civil day and the day on
+// which weeks are defined to start.
+int ToWeek(const civil_day& cd, weekday week_start) {
+ const civil_day d(cd.year() % 400, cd.month(), cd.day());
+ return static_cast<int>((d - prev_weekday(civil_year(d), week_start)) / 7);
+}
+
const char kDigits[] = "0123456789";
// Formats a 64-bit integer in the given field width. Note that it is up
@@ -189,7 +216,7 @@ void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) {
// strftime(3) returns the number of characters placed in the output
// array (which may be 0 characters). It also returns 0 to indicate
// an error, like the array wasn't large enough. To accommodate this,
- // the following code grows the buffer size from 2x the format std::string
+ // the following code grows the buffer size from 2x the format string
// length up to 32x.
for (std::size_t i = 2; i != 32; i *= 2) {
std::size_t buf_size = fmt.size() * i;
@@ -290,6 +317,7 @@ const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
// - %E#S - Seconds with # digits of fractional precision
// - %E*S - Seconds with full fractional precision (a literal '*')
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
+// - %ET - The RFC3339 "date-time" separator "T"
//
// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
// handled internally for performance reasons. strftime(3) is slow due to
@@ -354,7 +382,7 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
if (cur == end || (cur - percent) % 2 == 0) continue;
// Simple specifiers that we handle ourselves.
- if (strchr("YmdeHMSzZs%", *cur)) {
+ if (strchr("YmdeUuWwHMSzZs%", *cur)) {
if (cur - 1 != pending) {
FormatTM(&result, std::string(pending, cur - 1), tm);
}
@@ -375,6 +403,22 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
+ case 'U':
+ bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::sunday));
+ result.append(bp, static_cast<std::size_t>(ep - bp));
+ break;
+ case 'u':
+ bp = Format64(ep, 0, tm.tm_wday ? tm.tm_wday : 7);
+ result.append(bp, static_cast<std::size_t>(ep - bp));
+ break;
+ case 'W':
+ bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::monday));
+ result.append(bp, static_cast<std::size_t>(ep - bp));
+ break;
+ case 'w':
+ bp = Format64(ep, 0, tm.tm_wday);
+ result.append(bp, static_cast<std::size_t>(ep - bp));
+ break;
case 'H':
bp = Format02d(ep, al.cs.hour());
result.append(bp, static_cast<std::size_t>(ep - bp));
@@ -448,7 +492,14 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
if (*cur != 'E' || ++cur == end) continue;
// Format our extensions.
- if (*cur == 'z') {
+ if (*cur == 'T') {
+ // Formats %ET.
+ if (cur - 2 != pending) {
+ FormatTM(&result, std::string(pending, cur - 2), tm);
+ }
+ result.append("T");
+ pending = ++cur;
+ } else if (*cur == 'z') {
// Formats %Ez.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
@@ -551,7 +602,7 @@ const char* ParseOffset(const char* dp, const char* mode, int* offset) {
} else {
dp = nullptr;
}
- } else if (first == 'Z') { // Zulu
+ } else if (first == 'Z' || first == 'z') { // Zulu
*offset = 0;
} else {
dp = nullptr;
@@ -602,12 +653,32 @@ const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
return dp;
}
+// 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. 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);
+ 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
// Uses strptime(3) to parse the given input. Supports the same extended
// format specifiers as format(), although %E#S and %E*S are treated
// identically (and similarly for %E#f and %E*f). %Ez and %E*z also accept
-// the same inputs.
+// the same inputs. %ET accepts either 'T' or 't'.
//
// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
// handled internally so that we can normally avoid strptime() altogether
@@ -651,6 +722,8 @@ bool parse(const std::string& format, const std::string& input,
const char* fmt = format.c_str(); // NUL terminated
bool twelve_hour = false;
bool afternoon = false;
+ int week_num = -1;
+ weekday week_start = weekday::sunday;
bool saw_percent_s = false;
std::int_fast64_t percent_s = 0;
@@ -689,10 +762,27 @@ bool parse(const std::string& format, const std::string& input,
case 'm':
data = ParseInt(data, 2, 1, 12, &tm.tm_mon);
if (data != nullptr) tm.tm_mon -= 1;
+ week_num = -1;
continue;
case 'd':
case 'e':
data = ParseInt(data, 2, 1, 31, &tm.tm_mday);
+ week_num = -1;
+ continue;
+ case 'U':
+ data = ParseInt(data, 0, 0, 53, &week_num);
+ week_start = weekday::sunday;
+ continue;
+ case 'W':
+ data = ParseInt(data, 0, 0, 53, &week_num);
+ week_start = weekday::monday;
+ continue;
+ case 'u':
+ data = ParseInt(data, 0, 1, 7, &tm.tm_wday);
+ if (data != nullptr) tm.tm_wday %= 7;
+ continue;
+ case 'w':
+ data = ParseInt(data, 0, 0, 6, &tm.tm_wday);
continue;
case 'H':
data = ParseInt(data, 2, 0, 23, &tm.tm_hour);
@@ -742,6 +832,15 @@ bool parse(const std::string& format, const std::string& input,
data = (*data == '%' ? data + 1 : nullptr);
continue;
case 'E':
+ if (fmt[0] == 'T') {
+ if (*data == 'T' || *data == 't') {
+ ++data;
+ ++fmt;
+ } else {
+ data = nullptr;
+ }
+ continue;
+ }
if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) {
data = ParseOffset(data, ":", &offset);
if (data != nullptr) saw_offset = true;
@@ -839,7 +938,7 @@ bool parse(const std::string& format, const std::string& input,
// Skip any remaining whitespace.
while (std::isspace(*data)) ++data;
- // parse() must consume the entire input std::string.
+ // parse() must consume the entire input string.
if (*data != '\0') {
if (err != nullptr) *err = "Illegal trailing data in input string";
return false;
@@ -874,6 +973,14 @@ bool parse(const std::string& format, const std::string& input,
year += 1900;
}
+ // Compute year, tm.tm_mon and tm.tm_mday if we parsed a week number.
+ 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);
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format_test.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format_test.cc
index caebcc4d95..6487fa9373 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format_test.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format_test.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include <chrono>
+#include <cstdint>
#include <iomanip>
#include <sstream>
#include <string>
@@ -48,8 +49,8 @@ namespace {
EXPECT_STREQ(zone, al.abbr); \
} while (0)
-const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez";
-const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
+const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
+const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z";
const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
@@ -679,6 +680,34 @@ TEST(Format, RFC1123Format) { // locale specific
EXPECT_EQ("28 Jun 1977 09:08:07 -0700", format(RFC1123_no_wday, tp, tz));
}
+TEST(Format, Week) {
+ const time_zone utc = utc_time_zone();
+
+ auto tp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc);
+ EXPECT_EQ("2017-01-7", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2017-00-0", format("%Y-%W-%w", tp, utc));
+
+ tp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc);
+ EXPECT_EQ("2017-53-7", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2017-52-0", format("%Y-%W-%w", tp, utc));
+
+ tp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc);
+ EXPECT_EQ("2018-00-1", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2018-01-1", format("%Y-%W-%w", tp, utc));
+
+ tp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc);
+ EXPECT_EQ("2018-52-1", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2018-53-1", format("%Y-%W-%w", tp, utc));
+
+ tp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc);
+ EXPECT_EQ("2019-00-2", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2019-00-2", format("%Y-%W-%w", tp, utc));
+
+ tp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
+ EXPECT_EQ("2019-52-2", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2019-52-2", format("%Y-%W-%w", tp, utc));
+}
+
//
// Testing parse()
//
@@ -767,7 +796,7 @@ TEST(Parse, WithTimeZone) {
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
time_point<chrono::nanoseconds> tp;
- // We can parse a std::string without a UTC offset if we supply a timezone.
+ // We can parse a string without a UTC offset if we supply a timezone.
EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &tp));
ExpectTime(tp, tz, 2013, 6, 28, 19, 8, 9, -7 * 60 * 60, true, "PDT");
@@ -1107,7 +1136,7 @@ TEST(Parse, ExtendedSeconds) {
// All %E<prec>S cases are treated the same as %E*S on input.
auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "11", "12", "13", "14", "15"};
- for (const std::string& prec : precisions) {
+ for (const std::string prec : precisions) {
const std::string fmt = "%E" + prec + "S";
SCOPED_TRACE(fmt);
time_point<chrono::nanoseconds> tp = unix_epoch;
@@ -1189,7 +1218,7 @@ TEST(Parse, ExtendedSubeconds) {
// All %E<prec>f cases are treated the same as %E*f on input.
auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "11", "12", "13", "14", "15"};
- for (const std::string& prec : precisions) {
+ for (const std::string prec : precisions) {
const std::string fmt = "%E" + prec + "f";
SCOPED_TRACE(fmt);
time_point<chrono::nanoseconds> tp = unix_epoch - chrono::seconds(1);
@@ -1379,10 +1408,85 @@ TEST(Parse, RFC3339Format) {
EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00+00:00", tz, &tp));
ExpectTime(tp, tz, 2014, 2, 12, 20, 21, 0, 0, false, "UTC");
- // Check that %Ez also accepts "Z" as a synonym for "+00:00".
+ // Check that %ET also accepts "t".
time_point<chrono::nanoseconds> tp2;
- EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp2));
+ EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12t20:21:00+00:00", tz, &tp2));
EXPECT_EQ(tp, tp2);
+
+ // Check that %Ez also accepts "Z" as a synonym for "+00:00".
+ time_point<chrono::nanoseconds> tp3;
+ EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp3));
+ EXPECT_EQ(tp, tp3);
+
+ // Check that %Ez also accepts "z" as a synonym for "+00:00".
+ time_point<chrono::nanoseconds> tp4;
+ EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00z", tz, &tp4));
+ EXPECT_EQ(tp, tp4);
+}
+
+TEST(Parse, Week) {
+ const time_zone utc = utc_time_zone();
+ time_point<absl::time_internal::cctz::seconds> tp;
+
+ auto exp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2017-01-7", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2017-00-0", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2017-53-7", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2017-52-0", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2018-00-1", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2018-01-1", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2018-52-1", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2018-53-1", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2019-00-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2019-00-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2019-52-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2019-52-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+}
+
+TEST(Parse, WeekYearShift) {
+ // %U/%W conversions with week values in {0, 52, 53} can slip
+ // into the previous/following calendar years.
+ const time_zone utc = utc_time_zone();
+ time_point<absl::time_internal::cctz::seconds> tp;
+
+ auto exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2020-00-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2020-00-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2021, 1, 1, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2020-52-5", utc, &tp));
+ 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) {
@@ -1401,7 +1505,7 @@ TEST(Parse, MaxRange) {
parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp));
EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::max());
EXPECT_FALSE(
- parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp));
+ parse(RFC3339_sec, "292277026596-12-04T14:30:08-01:00", utc, &tp));
// tests the lower limit using +00:00 offset
EXPECT_TRUE(
@@ -1422,10 +1526,82 @@ TEST(Parse, MaxRange) {
parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", utc, &tp));
EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01",
utc, &tp));
+}
+
+TEST(Parse, TimePointOverflow) {
+ const time_zone utc = utc_time_zone();
- // TODO: Add tests that parsing times with fractional seconds overflow
- // appropriately. This can't be done until cctz::parse() properly detects
- // overflow when combining the chrono seconds and femto.
+ using D = chrono::duration<std::int64_t, std::nano>;
+ time_point<D> tp;
+
+ EXPECT_TRUE(
+ parse(RFC3339_full, "2262-04-11T23:47:16.8547758079+00:00", utc, &tp));
+ EXPECT_EQ(tp, time_point<D>::max());
+ EXPECT_EQ("2262-04-11T23:47:16.854775807+00:00",
+ format(RFC3339_full, tp, utc));
+#if 0
+ // TODO(#199): Will fail until cctz::parse() properly detects overflow.
+ EXPECT_FALSE(
+ parse(RFC3339_full, "2262-04-11T23:47:16.8547758080+00:00", utc, &tp));
+ EXPECT_TRUE(
+ parse(RFC3339_full, "1677-09-21T00:12:43.1452241920+00:00", utc, &tp));
+ EXPECT_EQ(tp, time_point<D>::min());
+ EXPECT_EQ("1677-09-21T00:12:43.145224192+00:00",
+ format(RFC3339_full, tp, utc));
+ EXPECT_FALSE(
+ parse(RFC3339_full, "1677-09-21T00:12:43.1452241919+00:00", utc, &tp));
+#endif
+
+ using DS = chrono::duration<std::int8_t, chrono::seconds::period>;
+ time_point<DS> stp;
+
+ EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T00:02:07.9+00:00", utc, &stp));
+ EXPECT_EQ(stp, time_point<DS>::max());
+ EXPECT_EQ("1970-01-01T00:02:07+00:00", format(RFC3339_full, stp, utc));
+ EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T00:02:08+00:00", utc, &stp));
+
+ EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T23:57:52+00:00", utc, &stp));
+ EXPECT_EQ(stp, time_point<DS>::min());
+ EXPECT_EQ("1969-12-31T23:57:52+00:00", format(RFC3339_full, stp, utc));
+ EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T23:57:51.9+00:00", utc, &stp));
+
+ using DM = chrono::duration<std::int8_t, chrono::minutes::period>;
+ time_point<DM> mtp;
+
+ EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T02:07:59+00:00", utc, &mtp));
+ EXPECT_EQ(mtp, time_point<DM>::max());
+ EXPECT_EQ("1970-01-01T02:07:00+00:00", format(RFC3339_full, mtp, utc));
+ EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T02:08:00+00:00", utc, &mtp));
+
+ EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T21:52:00+00:00", utc, &mtp));
+ EXPECT_EQ(mtp, time_point<DM>::min());
+ EXPECT_EQ("1969-12-31T21:52:00+00:00", format(RFC3339_full, mtp, utc));
+ EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T21:51:59+00:00", utc, &mtp));
+}
+
+TEST(Parse, TimePointOverflowFloor) {
+ const time_zone utc = utc_time_zone();
+
+ using D = chrono::duration<std::int64_t, std::micro>;
+ time_point<D> tp;
+
+ EXPECT_TRUE(
+ parse(RFC3339_full, "294247-01-10T04:00:54.7758079+00:00", utc, &tp));
+ EXPECT_EQ(tp, time_point<D>::max());
+ EXPECT_EQ("294247-01-10T04:00:54.775807+00:00",
+ format(RFC3339_full, tp, utc));
+#if 0
+ // TODO(#199): Will fail until cctz::parse() properly detects overflow.
+ EXPECT_FALSE(
+ parse(RFC3339_full, "294247-01-10T04:00:54.7758080+00:00", utc, &tp));
+ EXPECT_TRUE(
+ parse(RFC3339_full, "-290308-12-21T19:59:05.2241920+00:00", utc, &tp));
+ EXPECT_EQ(tp, time_point<D>::min());
+ EXPECT_EQ("-290308-12-21T19:59:05.224192+00:00",
+ format(RFC3339_full, tp, utc));
+ EXPECT_FALSE(
+ parse(RFC3339_full, "-290308-12-21T19:59:05.2241919+00:00", utc, &tp));
+#endif
}
//
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h
index 32c0891c1e..7d3e42d3cd 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h
@@ -56,7 +56,8 @@ class TimeZoneIf {
// Convert between time_point<seconds> and a count of seconds since the
// Unix epoch. We assume that the std::chrono::system_clock and the
-// Unix clock are second aligned, but not that they share an epoch.
+// Unix clock are second aligned, and that the results are representable.
+// (That is, that they share an epoch, which is required since C++20.)
inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
return (tp - std::chrono::time_point_cast<seconds>(
std::chrono::system_clock::from_time_t(0)))
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc
index 030ae0e19e..f34e3aec84 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc
@@ -15,6 +15,7 @@
#include "time_zone_impl.h"
#include <deque>
+#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
@@ -48,17 +49,16 @@ std::mutex& TimeZoneMutex() {
time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); }
bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
- const time_zone::Impl* const utc_impl = UTCImpl();
+ const Impl* const utc_impl = UTCImpl();
- // First check for UTC (which is never a key in time_zone_map).
+ // Check for UTC (which is never a key in time_zone_map).
auto offset = seconds::zero();
if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) {
*tz = time_zone(utc_impl);
return true;
}
- // Then check, under a shared lock, whether the time zone has already
- // been loaded. This is the common path. TODO: Move to shared_mutex.
+ // Check whether the time zone has already been loaded.
{
std::lock_guard<std::mutex> lock(TimeZoneMutex());
if (time_zone_map != nullptr) {
@@ -70,20 +70,15 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
}
}
- // Now check again, under an exclusive lock.
+ // Load the new time zone (outside the lock).
+ std::unique_ptr<const Impl> new_impl(new Impl(name));
+
+ // Add the new time zone to the map.
std::lock_guard<std::mutex> lock(TimeZoneMutex());
if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName;
const Impl*& impl = (*time_zone_map)[name];
- if (impl == nullptr) {
- // The first thread in loads the new time zone.
- Impl* new_impl = new Impl(name);
- new_impl->zone_ = TimeZoneIf::Load(new_impl->name_);
- if (new_impl->zone_ == nullptr) {
- delete new_impl; // free the nascent Impl
- impl = utc_impl; // and fallback to UTC
- } else {
- impl = new_impl; // install new time zone
- }
+ if (impl == nullptr) { // this thread won any load race
+ impl = new_impl->zone_ ? new_impl.release() : utc_impl;
}
*tz = time_zone(impl);
return impl != utc_impl;
@@ -104,14 +99,11 @@ void time_zone::Impl::ClearTimeZoneMapTestOnly() {
}
}
-time_zone::Impl::Impl(const std::string& name) : name_(name) {}
+time_zone::Impl::Impl(const std::string& name)
+ : name_(name), zone_(TimeZoneIf::Load(name_)) {}
const time_zone::Impl* time_zone::Impl::UTCImpl() {
- static Impl* utc_impl = [] {
- Impl* impl = new Impl("UTC");
- impl->zone_ = TimeZoneIf::Load(impl->name_); // never fails
- return impl;
- }();
+ static const Impl* utc_impl = new Impl("UTC"); // never fails
return utc_impl;
}
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h
index 69806c10d9..7d747ba966 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h
@@ -71,7 +71,7 @@ class time_zone::Impl {
return zone_->PrevTransition(tp, trans);
}
- // Returns an implementation-defined version std::string for this time zone.
+ // Returns an implementation-defined version string for this time zone.
std::string Version() const { return zone_->Version(); }
// Returns an implementation-defined description of this time zone.
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc
index f1697cdf04..4f175d95fc 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc
@@ -39,11 +39,12 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
+#include <fstream>
#include <functional>
-#include <iostream>
#include <memory>
#include <sstream>
#include <string>
+#include <utility>
#include "absl/base/config.h"
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
@@ -83,6 +84,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 +210,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 +226,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 +238,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 +280,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 +288,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 (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
+ 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);
}
+ // 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;
- }
- assert(tr == &transitions_[0] + transitions_.size());
+ leap_year = !leap_year && IsLeap(last_year_ + 1);
+ }
+
+ 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 +427,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 +446,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 +473,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;
@@ -506,7 +505,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
// 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.
+ // ZoneInfoSource for any out-of-bound version string it may be privy to.
if (version_.empty()) {
version_ = zip->Version();
}
@@ -525,19 +524,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().
@@ -569,14 +578,17 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
namespace {
+using FilePtr = std::unique_ptr<FILE, int (*)(FILE*)>;
+
// fopen(3) adaptor.
-inline FILE* FOpen(const char* path, const char* mode) {
+inline FilePtr FOpen(const char* path, const char* mode) {
#if defined(_MSC_VER)
FILE* fp;
if (fopen_s(&fp, path, mode) != 0) fp = nullptr;
- return fp;
+ return FilePtr(fp, fclose);
#else
- return fopen(path, mode); // TODO: Enable the close-on-exec flag.
+ // TODO: Enable the close-on-exec flag.
+ return FilePtr(fopen(path, mode), fclose);
#endif
}
@@ -604,11 +616,11 @@ class FileZoneInfoSource : public ZoneInfoSource {
protected:
explicit FileZoneInfoSource(
- FILE* fp, std::size_t len = std::numeric_limits<std::size_t>::max())
- : fp_(fp, fclose), len_(len) {}
+ FilePtr fp, std::size_t len = std::numeric_limits<std::size_t>::max())
+ : fp_(std::move(fp)), len_(len) {}
private:
- std::unique_ptr<FILE, int (*)(FILE*)> fp_;
+ FilePtr fp_;
std::size_t len_;
};
@@ -637,17 +649,9 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
path.append(name, pos, std::string::npos);
// Open the zoneinfo file.
- FILE* fp = FOpen(path.c_str(), "rb");
+ auto fp = FOpen(path.c_str(), "rb");
if (fp == nullptr) return nullptr;
- std::size_t length = 0;
- if (fseek(fp, 0, SEEK_END) == 0) {
- long offset = ftell(fp);
- if (offset >= 0) {
- length = static_cast<std::size_t>(offset);
- }
- rewind(fp);
- }
- return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length));
+ return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(std::move(fp)));
}
class AndroidZoneInfoSource : public FileZoneInfoSource {
@@ -656,8 +660,9 @@ class AndroidZoneInfoSource : public FileZoneInfoSource {
std::string Version() const override { return version_; }
private:
- explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers)
- : FileZoneInfoSource(fp, len), version_(vers) {}
+ explicit AndroidZoneInfoSource(FilePtr fp, std::size_t len,
+ std::string version)
+ : FileZoneInfoSource(std::move(fp), len), version_(std::move(version)) {}
std::string version_;
};
@@ -669,8 +674,8 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
// See Android's libc/tzcode/bionic.cpp for additional information.
for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
"/system/usr/share/zoneinfo/tzdata"}) {
- std::unique_ptr<FILE, int (*)(FILE*)> fp(FOpen(tzdata, "rb"), fclose);
- if (fp.get() == nullptr) continue;
+ auto fp = FOpen(tzdata, "rb");
+ if (fp == nullptr) continue;
char hbuf[24]; // covers header.zonetab_offset too
if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
@@ -696,7 +701,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
if (strcmp(name.c_str() + pos, ebuf) == 0) {
if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break;
return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource(
- fp.release(), static_cast<std::size_t>(length), vers));
+ std::move(fp), static_cast<std::size_t>(length), vers));
}
}
}
@@ -704,6 +709,69 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
return nullptr;
}
+// A zoneinfo source for use inside Fuchsia components. This attempts to
+// read zoneinfo files from one of several known paths in a component's
+// incoming namespace. [Config data][1] is preferred, but package-specific
+// resources are also supported.
+//
+// Fuchsia's implementation supports `FileZoneInfoSource::Version()`.
+//
+// [1]:
+// https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component
+class FuchsiaZoneInfoSource : public FileZoneInfoSource {
+ public:
+ static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
+ std::string Version() const override { return version_; }
+
+ private:
+ explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version)
+ : FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {}
+ std::string version_;
+};
+
+std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open(
+ const std::string& name) {
+ // Use of the "file:" prefix is intended for testing purposes only.
+ const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
+
+ // Prefixes where a Fuchsia component might find zoneinfo files,
+ // in descending order of preference.
+ const auto kTzdataPrefixes = {
+ "/config/data/tzdata/",
+ "/pkg/data/tzdata/",
+ "/data/tzdata/",
+ };
+ const auto kEmptyPrefix = {""};
+ const bool name_absolute = (pos != name.size() && name[pos] == '/');
+ const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes;
+
+ // Fuchsia builds place zoneinfo files at "<prefix><format><name>".
+ for (const std::string prefix : prefixes) {
+ std::string path = prefix;
+ if (!prefix.empty()) path += "zoneinfo/tzif2/"; // format
+ path.append(name, pos, std::string::npos);
+
+ auto fp = FOpen(path.c_str(), "rb");
+ if (fp == nullptr) continue;
+
+ std::string version;
+ if (!prefix.empty()) {
+ // Fuchsia builds place the version in "<prefix>revision.txt".
+ std::ifstream version_stream(prefix + "revision.txt");
+ if (version_stream.is_open()) {
+ // revision.txt should contain no newlines, but to be
+ // defensive we read just the first line.
+ std::getline(version_stream, version);
+ }
+ }
+
+ return std::unique_ptr<ZoneInfoSource>(
+ new FuchsiaZoneInfoSource(std::move(fp), std::move(version)));
+ }
+
+ return nullptr;
+}
+
} // namespace
bool TimeZoneInfo::Load(const std::string& name) {
@@ -718,12 +786,13 @@ 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;
+ if (auto z = FuchsiaZoneInfoSource::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 +966,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 +992,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);
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h
index 2a10c06c77..2467ff559d 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h
@@ -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,
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc
index 47cf84c663..887dd097c6 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc
@@ -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};
}
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc
index efdea64b4e..898d04c125 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc
@@ -28,6 +28,13 @@
#include <vector>
#endif
+#if defined(__Fuchsia__)
+#include <fuchsia/intl/cpp/fidl.h>
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/sys/cpp/component_context.h>
+#include <zircon/types.h>
+#endif
+
#include <cstdlib>
#include <cstring>
#include <string>
@@ -140,6 +147,48 @@ time_zone local_time_zone() {
}
CFRelease(tz_default);
#endif
+#if defined(__Fuchsia__)
+ std::string primary_tz;
+ [&]() {
+ // Note: We can't use the synchronous FIDL API here because it doesn't
+ // allow timeouts; if the FIDL call failed, local_time_zone() would never
+ // return.
+
+ const zx::duration kTimeout = zx::msec(500);
+
+ // Don't attach to the thread because otherwise the thread's dispatcher
+ // would be set to null when the loop is destroyed, causing any other FIDL
+ // code running on the same thread to crash.
+ async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
+ std::unique_ptr<sys::ComponentContext> context =
+ sys::ComponentContext::Create();
+
+ fuchsia::intl::PropertyProviderHandle handle;
+ zx_status_t status = context->svc()->Connect(handle.NewRequest());
+ if (status != ZX_OK) {
+ return;
+ }
+
+ fuchsia::intl::PropertyProviderPtr intl_provider;
+ status = intl_provider.Bind(std::move(handle), loop.dispatcher());
+ if (status != ZX_OK) {
+ return;
+ }
+
+ intl_provider->GetProfile(
+ [&loop, &primary_tz](fuchsia::intl::Profile profile) {
+ if (!profile.time_zones().empty()) {
+ primary_tz = profile.time_zones()[0].id;
+ }
+ loop.Quit();
+ });
+ loop.Run(zx::deadline_after(kTimeout));
+ }();
+
+ if (!primary_tz.empty()) {
+ zone = primary_tz.c_str();
+ }
+#endif
// Allow ${TZ} to override to default zone.
char* tz_env = nullptr;
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index 99137a082a..0226ab71ab 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -211,6 +211,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/North_Dakota/Beulah",
"America/North_Dakota/Center",
"America/North_Dakota/New_Salem",
+ "America/Nuuk",
"America/Ojinaga",
"America/Panama",
"America/Pangnirtung",
@@ -578,6 +579,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Pacific/Guam",
"Pacific/Honolulu",
"Pacific/Johnston",
+ "Pacific/Kanton",
"Pacific/Kiritimati",
"Pacific/Kosrae",
"Pacific/Kwajalein",
@@ -716,6 +718,18 @@ TEST(TimeZones, LoadZonesConcurrently) {
}
#endif
+TEST(TimeZone, UTC) {
+ const time_zone utc = utc_time_zone();
+
+ time_zone loaded_utc;
+ EXPECT_TRUE(load_time_zone("UTC", &loaded_utc));
+ EXPECT_EQ(loaded_utc, utc);
+
+ time_zone loaded_utc0;
+ EXPECT_TRUE(load_time_zone("UTC0", &loaded_utc0));
+ EXPECT_EQ(loaded_utc0, utc);
+}
+
TEST(TimeZone, NamedTimeZones) {
const time_zone utc = utc_time_zone();
EXPECT_EQ("UTC", utc.name());
@@ -749,7 +763,7 @@ TEST(TimeZone, Failures) {
EXPECT_EQ(chrono::system_clock::from_time_t(0),
convert(civil_second(1970, 1, 1, 0, 0, 0), tz)); // UTC
- // Loading an empty std::string timezone should fail.
+ // Loading an empty string timezone should fail.
tz = LoadZone("America/Los_Angeles");
EXPECT_FALSE(load_time_zone("", &tz));
EXPECT_EQ(chrono::system_clock::from_time_t(0),
@@ -932,7 +946,7 @@ TEST(MakeTime, Normalization) {
// NOTE: Run this with -ftrapv to detect overflow problems.
TEST(MakeTime, SysSecondsLimits) {
- const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez";
+ const char RFC3339[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
const time_zone utc = utc_time_zone();
const time_zone east = fixed_time_zone(chrono::hours(14));
const time_zone west = fixed_time_zone(-chrono::hours(14));
@@ -1003,13 +1017,21 @@ 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);
+#if defined(__Fuchsia__)
+ // Fuchsia's gmtime_r() fails on extreme negative values (fxbug.dev/78527).
+#else
+ EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut));
+#endif
#endif
}
}
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h b/third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h
index 1ed55e0f95..31e8598257 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h
@@ -43,7 +43,7 @@
struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */
- char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
+ char tzh_version[1]; /* '\0' or '2'-'4' as of 2021 */
char tzh_reserved[15]; /* reserved; must be zero */
char tzh_ttisutcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
@@ -83,13 +83,13 @@ struct tzhead {
** If tzh_version is '2' or greater, the above is followed by a second instance
** of tzhead and a second instance of the data in which each coded transition
** time uses 8 rather than 4 chars,
-** then a POSIX-TZ-environment-variable-style std::string for use in handling
+** then a POSIX-TZ-environment-variable-style string for use in handling
** instants after the last transition time stored in the file
** (with nothing between the newlines if there is no POSIX representation for
** such instants).
**
** If tz_version is '3' or greater, the above is extended as follows.
-** First, the POSIX TZ std::string's hour offset may range from -167
+** First, the POSIX TZ string's hour offset may range from -167
** through 167 as compared to the POSIX-required 0 through 24.
** Second, its DST start time may be January 1 at 00:00 and its stop
** time December 31 at 24:00 plus the difference between DST and
diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc
index 98ea161267..5ab5a59ecf 100644
--- a/third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc
+++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc
@@ -65,7 +65,7 @@ ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) =
extern ZoneInfoSourceFactory zone_info_source_factory;
extern ZoneInfoSourceFactory default_factory;
ZoneInfoSourceFactory default_factory = DefaultFactory;
-#if defined(_M_IX86)
+#if defined(_M_IX86) || defined(_M_ARM)
#pragma comment( \
linker, \
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \