From 6a89657c3fdd5aaca84a92a780775331a619083f Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Fri, 22 Oct 2021 19:39:41 +0500 Subject: [PATCH] Switch internal year calculations to long long --- include/fmt/chrono.h | 45 ++++++++++++++++++++++---------------------- test/chrono-test.cc | 7 +++++++ 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 97fbad7893767..cfda5f2218ef6 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1450,8 +1450,8 @@ template class tm_writer { "tm_mon not in range [0, 11]"); return to_unsigned(tm_.tm_mon); } - auto tm_year() const noexcept -> int { - return 1900 + tm_.tm_year; + auto tm_year() const noexcept -> long long { + return 1900ll + static_cast(tm_.tm_year); } auto tm_wday() const noexcept -> unsigned { FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, @@ -1469,11 +1469,11 @@ template class tm_writer { return hour == 0 ? 12 : hour; } - FMT_CONSTEXPR const Char* tm_day_of_week() const noexcept { + FMT_CONSTEXPR const Char* tm_day_of_week_name() const noexcept { auto wday = tm_wday(); return (wday >= 0 && wday <= 6) ? day_of_week[wday] : invalid; } - FMT_CONSTEXPR const Char* tm_month() const noexcept { + FMT_CONSTEXPR const Char* tm_mon_name() const noexcept { auto mon = tm_mon(); return (mon >= 0 && mon <= 11) ? month[mon] : invalid; } @@ -1482,21 +1482,20 @@ template class tm_writer { // do if the year is negative or exceeds 9999. Use the convention that %C // concatenated with %y yields the same output as %Y, and that %Y contains at // least 4 characters, with more only if necessary. - auto split_year_lower(int year) const noexcept -> unsigned { + auto split_year_lower(const long long year) const noexcept -> unsigned { auto l = year % 100; if (l < 0) l = -l; // l in [0, 99] - return to_unsigned(l); + return static_cast(l); } // Algorithm: // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date - auto iso_year_weeks(const int year) const noexcept -> unsigned { - const long long curr_year = year; + auto iso_year_weeks(const long long curr_year) const noexcept -> unsigned { const long long prev_year = curr_year - 1; - const int curr_p = static_cast( + const unsigned curr_p = static_cast( (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % days_per_week); - const int prev_p = static_cast( + const unsigned prev_p = static_cast( (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % days_per_week); return 52u + ((curr_p == 4 || prev_p == 3) ? 1u : 0u); @@ -1506,7 +1505,7 @@ template class tm_writer { return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / days_per_week; } - auto tm_iso_week_year() const noexcept -> int { + auto tm_iso_week_year() const noexcept -> long long { const auto year = tm_year(); const unsigned w = iso_week_num(tm_yday(), tm_wday()); if (w < 1) return year - 1; @@ -1528,7 +1527,7 @@ template class tm_writer { *out_++ = *d; } - void write_year_extended(int year) { + void write_year_extended(long long year) { // At least 4 characters. int width = 4; if (year < 0) { @@ -1536,16 +1535,15 @@ template class tm_writer { year = 0 - year; --width; } - uint32_or_64_or_128_t n = - to_unsigned(to_nonnegative_int(year, max_value())); + uint32_or_64_or_128_t n = to_unsigned(year); const int num_digits = count_digits(n); if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); out_ = format_decimal(out_, n, num_digits).end; } - void write_year(int year) { + void write_year(long long year) { if (year >= 0 && year < 10000) { - write2(to_unsigned(year / 100)); - write2(to_unsigned(year % 100)); + write2(static_cast(year / 100)); + write2(static_cast(year % 100)); } else write_year_extended(year); } @@ -1569,13 +1567,13 @@ template class tm_writer { void on_abbr_weekday() { if (is_classic_) - out_ = write(out_, basic_string_view(tm_day_of_week(), 3)); + out_ = write(out_, basic_string_view(tm_day_of_week_name(), 3)); else format_localized('a'); } void on_full_weekday() { if (is_classic_) - out_ = write(out_, tm_day_of_week()); + out_ = write(out_, tm_day_of_week_name()); else format_localized('A'); } @@ -1591,13 +1589,13 @@ template class tm_writer { void on_abbr_month() { if (is_classic_) - out_ = write(out_, basic_string_view(tm_month(), 3)); + out_ = write(out_, basic_string_view(tm_mon_name(), 3)); else format_localized('b'); } void on_full_month() { if (is_classic_) - out_ = write(out_, tm_month()); + out_ = write(out_, tm_mon_name()); else format_localized('B'); } @@ -1651,7 +1649,8 @@ template class tm_writer { write_year_extended(year); year = 0; } - write_digit2_separated(buf + 2, year % 100, tm_mon() + 1, tm_mday(), '-'); + write_digit2_separated(buf + 2, static_cast(year % 100), + tm_mon() + 1, tm_mday(), '-'); out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); } @@ -1677,7 +1676,7 @@ template class tm_writer { *out_++ = '-'; *out_++ = '0'; } else if (upper >= 0 && upper < 100) { - write2(to_unsigned(upper)); + write2(static_cast(upper)); } else { out_ = write(out_, upper); } diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 7dfcdf10d422b..6eaf1cfb5d1dd 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -96,6 +96,13 @@ TEST(chrono_test, format_tm) { EXPECT_EQ(fmt::format("{:%Y}", tm), "0027"); EXPECT_EQ(fmt::format("{:%C%y}", tm), "0027"); + // Overflow year + tm.tm_year = 2147483647; + EXPECT_EQ(fmt::format("{:%Y}", tm), "2147485547"); + + tm.tm_year = -2147483648; + EXPECT_EQ(fmt::format("{:%Y}", tm), "-2147481748"); + // for week on the year // https://www.cl.cam.ac.uk/~mgk25/iso-time.html std::vector tm_list = {