Skip to content

Commit

Permalink
Switch internal year calculations to long long
Browse files Browse the repository at this point in the history
  • Loading branch information
phprus committed Oct 22, 2021
1 parent 6bf96b5 commit 5cae10b
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 23 deletions.
45 changes: 22 additions & 23 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -1450,8 +1450,8 @@ template <typename OutputIt, typename Char> 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<long long>(tm_.tm_year);
}
auto tm_wday() const noexcept -> unsigned {
FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6,
Expand All @@ -1469,11 +1469,11 @@ template <typename OutputIt, typename Char> 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;
}
Expand All @@ -1482,21 +1482,20 @@ template <typename OutputIt, typename Char> 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<unsigned>(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<int>(
const unsigned curr_p = static_cast<unsigned>(
(curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
days_per_week);
const int prev_p = static_cast<int>(
const unsigned prev_p = static_cast<unsigned>(
(prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
days_per_week);
return 52u + ((curr_p == 4 || prev_p == 3) ? 1u : 0u);
Expand All @@ -1506,7 +1505,7 @@ template <typename OutputIt, typename Char> 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;
Expand All @@ -1528,24 +1527,23 @@ template <typename OutputIt, typename Char> 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) {
*out_++ = '-';
year = 0 - year;
--width;
}
uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(year, max_value<int>()));
uint32_or_64_or_128_t<long long> 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<Char>(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<unsigned>(year / 100));
write2(static_cast<unsigned>(year % 100));
} else
write_year_extended(year);
}
Expand All @@ -1569,13 +1567,13 @@ template <typename OutputIt, typename Char> class tm_writer {

void on_abbr_weekday() {
if (is_classic_)
out_ = write(out_, basic_string_view<Char>(tm_day_of_week(), 3));
out_ = write(out_, basic_string_view<Char>(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');
}
Expand All @@ -1591,13 +1589,13 @@ template <typename OutputIt, typename Char> class tm_writer {

void on_abbr_month() {
if (is_classic_)
out_ = write(out_, basic_string_view<Char>(tm_month(), 3));
out_ = write(out_, basic_string_view<Char>(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');
}
Expand Down Expand Up @@ -1651,7 +1649,8 @@ template <typename OutputIt, typename Char> 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<unsigned>(year % 100),
tm_mon() + 1, tm_mday(), '-');
out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
}

Expand All @@ -1677,7 +1676,7 @@ template <typename OutputIt, typename Char> class tm_writer {
*out_++ = '-';
*out_++ = '0';
} else if (upper >= 0 && upper < 100) {
write2(to_unsigned(upper));
write2(static_cast<unsigned>(upper));
} else {
out_ = write<Char>(out_, upper);
}
Expand Down
7 changes: 7 additions & 0 deletions test/chrono-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::tm> tm_list = {
Expand Down

0 comments on commit 5cae10b

Please sign in to comment.