Squashed 'third_party/abseil_cpp/' changes from 768eb2ca2..ccdbb5941

ccdbb5941 Export of internal Abseil changes
01f5f81f9 Export of internal Abseil changes
2c92bdc7c Export of internal Abseil changes
e7ebf9803 Export of internal Abseil changes
2eba343b5 Export of internal Abseil changes
a8b03d90e Export of internal Abseil changes
1d31b5c36 Export of internal Abseil changes
da3a87690 Export of internal Abseil changes
8faf20461 Exclude empty directories (#697)
2069dc796 Export of internal Abseil changes
4832bf6bf Added a BUILD file in root to expose license. (#695)
af8f994af Export of internal Abseil changes
33caf1097 Export of internal Abseil changes
cf1a02e2d Export of internal Abseil changes

git-subtree-dir: third_party/abseil_cpp
git-subtree-split: ccdbb5941f992fabda7eae3ce72f55efc17c826a
This commit is contained in:
Vincent Ambo 2020-06-17 14:53:10 +01:00
parent 768eb2ca28
commit 8f2828c4b4
97 changed files with 3546 additions and 2316 deletions

View file

@ -98,26 +98,26 @@ bool ParseLenient(string_view s, CivilT* c) {
} // namespace
std::string FormatCivilTime(CivilSecond c) {
return FormatYearAnd("-%m-%dT%H:%M:%S", c);
return FormatYearAnd("-%m-%d%ET%H:%M:%S", c);
}
std::string FormatCivilTime(CivilMinute c) {
return FormatYearAnd("-%m-%dT%H:%M", c);
return FormatYearAnd("-%m-%d%ET%H:%M", c);
}
std::string FormatCivilTime(CivilHour c) {
return FormatYearAnd("-%m-%dT%H", c);
return FormatYearAnd("-%m-%d%ET%H", c);
}
std::string FormatCivilTime(CivilDay c) { return FormatYearAnd("-%m-%d", c); }
std::string FormatCivilTime(CivilMonth c) { return FormatYearAnd("-%m", c); }
std::string FormatCivilTime(CivilYear c) { return FormatYearAnd("", c); }
bool ParseCivilTime(string_view s, CivilSecond* c) {
return ParseYearAnd("-%m-%dT%H:%M:%S", s, c);
return ParseYearAnd("-%m-%d%ET%H:%M:%S", s, c);
}
bool ParseCivilTime(string_view s, CivilMinute* c) {
return ParseYearAnd("-%m-%dT%H:%M", s, c);
return ParseYearAnd("-%m-%d%ET%H:%M", s, c);
}
bool ParseCivilTime(string_view s, CivilHour* c) {
return ParseYearAnd("-%m-%dT%H", s, c);
return ParseYearAnd("-%m-%d%ET%H", s, c);
}
bool ParseCivilTime(string_view s, CivilDay* c) {
return ParseYearAnd("-%m-%d", s, c);

View file

@ -27,14 +27,11 @@ namespace cctz = absl::time_internal::cctz;
namespace absl {
ABSL_NAMESPACE_BEGIN
ABSL_DLL extern const char RFC3339_full[] =
"%Y-%m-%dT%H:%M:%E*S%Ez";
ABSL_DLL extern const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
ABSL_DLL extern const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
ABSL_DLL extern const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
ABSL_DLL extern const char RFC1123_full[] =
"%a, %d %b %E4Y %H:%M:%S %z";
ABSL_DLL extern const char RFC1123_no_wday[] =
"%d %b %E4Y %H:%M:%S %z";
ABSL_DLL extern const char RFC1123_full[] = "%a, %d %b %E4Y %H:%M:%S %z";
ABSL_DLL extern const char RFC1123_no_wday[] = "%d %b %E4Y %H:%M:%S %z";
namespace {

View file

@ -26,7 +26,7 @@ const char* const kFormats[] = {
absl::RFC1123_no_wday, // 1
absl::RFC3339_full, // 2
absl::RFC3339_sec, // 3
"%Y-%m-%dT%H:%M:%S", // 4
"%Y-%m-%d%ET%H:%M:%S", // 4
"%Y-%m-%d", // 5
};
const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);

View file

@ -106,54 +106,64 @@ CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept {
CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh,
minute_t mm, second_t ss) noexcept {
y += (cd / 146097) * 400;
year_t ey = y % 400;
const year_t oey = ey;
ey += (cd / 146097) * 400;
cd %= 146097;
if (cd < 0) {
y -= 400;
ey -= 400;
cd += 146097;
}
y += (d / 146097) * 400;
ey += (d / 146097) * 400;
d = d % 146097 + cd;
if (d > 0) {
if (d > 146097) {
y += 400;
ey += 400;
d -= 146097;
}
} else {
if (d > -365) {
// We often hit the previous year when stepping a civil time backwards,
// so special case it to avoid counting up by 100/4/1-year chunks.
y -= 1;
d += days_per_year(y, m);
ey -= 1;
d += days_per_year(ey, m);
} else {
y -= 400;
ey -= 400;
d += 146097;
}
}
if (d > 365) {
for (int n = days_per_century(y, m); d > n; n = days_per_century(y, m)) {
for (;;) {
int n = days_per_century(ey, m);
if (d <= n) break;
d -= n;
y += 100;
ey += 100;
}
for (int n = days_per_4years(y, m); d > n; n = days_per_4years(y, m)) {
for (;;) {
int n = days_per_4years(ey, m);
if (d <= n) break;
d -= n;
y += 4;
ey += 4;
}
for (int n = days_per_year(y, m); d > n; n = days_per_year(y, m)) {
for (;;) {
int n = days_per_year(ey, m);
if (d <= n) break;
d -= n;
++y;
++ey;
}
}
if (d > 28) {
for (int n = days_per_month(y, m); d > n; n = days_per_month(y, m)) {
for (;;) {
int n = days_per_month(ey, m);
if (d <= n) break;
d -= n;
if (++m > 12) {
++y;
++ey;
m = 1;
}
}
}
return fields(y, m, static_cast<day_t>(d), hh, mm, ss);
return fields(y + (ey - oey), m, static_cast<day_t>(d), hh, mm, ss);
}
CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh,
minute_t mm, second_t ss) noexcept {

View file

@ -292,6 +292,7 @@ bool parse(const std::string&, const std::string&, const time_zone&,
// - %E#f - Fractional seconds with # digits of precision
// - %E*f - Fractional seconds with full precision (a literal '*')
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
// - %ET - The RFC3339 "date-time" separator "T"
//
// 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'.
@ -321,7 +322,8 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp,
// returns the corresponding time_point. Uses strftime()-like formatting
// 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
// and %E*z also accept the same inputs.
// and %E*z also accept the same inputs, which (along with %z) includes
// 'z' and 'Z' as synonyms for +00:00. %ET accepts either 'T' or 't'.
//
// %Y consumes as many numeric characters as it can, so the matching data
// should always be terminated with a non-numeric. %E4Y always consumes

View file

@ -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";
@ -991,12 +991,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]);

View file

@ -234,6 +234,16 @@ TEST(CivilTime, Difference) {
static_assert(diff == 365, "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) {
{

View file

@ -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
@ -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,23 @@ 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.
void FromWeek(int week_num, weekday week_start, year_t* year, std::tm* tm) {
const civil_year y(*year % 400);
civil_day cd = prev_weekday(y, week_start); // week 0
cd = next_weekday(cd - 1, FromTmWday(tm->tm_wday)) + (week_num * 7);
*year += cd.year() - y.year();
tm->tm_mon = cd.month() - 1;
tm->tm_mday = cd.day();
}
} // 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 +713,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 +753,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 +823,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;
@ -874,6 +964,9 @@ 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) FromWeek(week_num, week_start, &year, &tm);
const int month = tm.tm_mon + 1;
civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);

View file

@ -48,8 +48,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 +679,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()
//
@ -1379,10 +1407,80 @@ 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);
}
TEST(Parse, MaxRange) {

View file

@ -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;
}

View file

@ -933,7 +933,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));

View file

@ -83,7 +83,8 @@ ZoneInfoSourceFactory default_factory = DefaultFactory;
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
"@@ZA")
#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64)
#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM) || \
defined(_M_ARM64)
#pragma comment( \
linker, \
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \

View file

@ -1203,18 +1203,15 @@ struct tm ToTM(Time t, TimeZone tz);
// time with UTC offset. Also note the use of "%Y": RFC3339 mandates that
// years have exactly four digits, but we allow them to take their natural
// width.
ABSL_DLL extern const char
RFC3339_full[]; // %Y-%m-%dT%H:%M:%E*S%Ez
ABSL_DLL extern const char RFC3339_sec[]; // %Y-%m-%dT%H:%M:%S%Ez
ABSL_DLL extern const char RFC3339_full[]; // %Y-%m-%d%ET%H:%M:%E*S%Ez
ABSL_DLL extern const char RFC3339_sec[]; // %Y-%m-%d%ET%H:%M:%S%Ez
// RFC1123_full
// RFC1123_no_wday
//
// FormatTime()/ParseTime() format specifiers for RFC1123 date/time strings.
ABSL_DLL extern const char
RFC1123_full[]; // %a, %d %b %E4Y %H:%M:%S %z
ABSL_DLL extern const char
RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z
ABSL_DLL extern const char RFC1123_full[]; // %a, %d %b %E4Y %H:%M:%S %z
ABSL_DLL extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z
// FormatTime()
//
@ -1229,6 +1226,7 @@ ABSL_DLL extern const char
// - %E#f - Fractional seconds with # digits of precision
// - %E*f - Fractional seconds with full precision (a literal '*')
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
// - %ET - The RFC3339 "date-time" separator "T"
//
// 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'.
@ -1271,7 +1269,8 @@ inline std::ostream& operator<<(std::ostream& os, Time t) {
// returns the corresponding `absl::Time`. Uses strftime()-like formatting
// options, with the same extensions as FormatTime(), but with the
// 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, which (along with %z) includes
// 'z' and 'Z' as synonyms for +00:00. %ET accepts either 'T' or 't'.
//
// %Y consumes as many numeric characters as it can, so the matching data
// should always be terminated with a non-numeric. %E4Y always consumes