merge(3p/abseil_cpp): Merge upstream at 'ccdbb5941'

Change-Id: I6e85fc7b5f76bba1f1eef15e600a8acb64e97ef5
This commit is contained in:
Vincent Ambo 2020-06-17 14:53:11 +01:00
commit 543379ce45
97 changed files with 3546 additions and 2316 deletions

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 \