Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions absl/time/civil_time.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,20 @@ bool ParseYearAnd(string_view fmt, string_view s, CivilT* c) {
const civil_year_t y =
std::strtoll(np, &endp, 10); // NOLINT(runtime/deprecated_fn)
if (endp == np || errno == ERANGE) return false;
const std::string norm = StrCat(NormalizeYear(y), endp);
const civil_year_t normalized_year = NormalizeYear(y);
const std::string norm = StrCat(normalized_year, endp);

const TimeZone utc = UTCTimeZone();
Time t;
if (ParseTime(StrCat("%Y", fmt), norm, utc, &t, nullptr)) {
const auto cs = ToCivilSecond(t, utc);
*c = CivilT(y, cs.month(), cs.day(), cs.hour(), cs.minute(), cs.second());
// Field normalization while parsing (e.g. a ":60" leap second or an
// end-of-year rollover) can carry into the year. The other fields are taken
// from `cs`, so the same carry must be applied to the original year;
// otherwise the reconstructed value would use the wrong (un-carried) year.
const civil_year_t year = y + (cs.year() - normalized_year);
*c =
CivilT(year, cs.month(), cs.day(), cs.hour(), cs.minute(), cs.second());
return true;
}

Expand Down
17 changes: 17 additions & 0 deletions absl/time/civil_time_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,23 @@ TEST(CivilTime, ParseEdgeCases) {
EXPECT_EQ(absl::CivilMonth(-1, 1), m);
}

TEST(CivilTime, ParseFieldNormalizationCarriesYear) {
// When a field normalizes past the end of the year (e.g. a ":60" leap
// second on the last second of December), the carry must be reflected in
// the parsed year, so parsing agrees with direct field construction.
absl::CivilSecond ss;
EXPECT_TRUE(absl::ParseCivilTime("2020-12-31T23:59:60", &ss)) << ss;
EXPECT_EQ(absl::CivilSecond(2020, 12, 31, 23, 59, 60), ss);
EXPECT_EQ(absl::CivilSecond(2021, 1, 1, 0, 0, 0), ss);

EXPECT_TRUE(absl::ParseLenientCivilTime("2020-12-31T23:59:60", &ss)) << ss;
EXPECT_EQ(absl::CivilSecond(2021, 1, 1, 0, 0, 0), ss);

// The carry also works for negative years crossing zero.
EXPECT_TRUE(absl::ParseCivilTime("-1-12-31T23:59:60", &ss)) << ss;
EXPECT_EQ(absl::CivilSecond(0, 1, 1, 0, 0, 0), ss);
}

TEST(CivilTime, AbslStringify) {
EXPECT_EQ("2015-01-02T03:04:05",
absl::StrFormat("%v", absl::CivilSecond(2015, 1, 2, 3, 4, 5)));
Expand Down