Rename rep to abs_rep in duration_formatter and simplify the tests

The old name differed from Rep only in case, which made the narrowing
fixed in #5003 easy to miss.
This commit is contained in:
Victor Zverovich 2026-11-07 15:42:47 -08:00
parent 3a52eb4f1d
commit a9e41a6df2
2 changed files with 25 additions and 28 deletions

View File

@ -1665,30 +1665,33 @@ template <typename Char, typename Rep, typename Period>
struct duration_formatter {
using iterator = basic_appender<Char>;
iterator out;
// rep is unsigned to avoid overflow.
using rep =
// Integral reps are made unsigned so that negating the minimum value can't
// overflow, and at least as wide as int to leave headroom for the conversion
// to seconds.
using abs_rep =
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
rep val;
abs_rep val;
int precision;
locale_ref locale;
bool localized = false;
using seconds = std::chrono::duration<rep>;
using seconds = std::chrono::duration<abs_rep>;
seconds s;
using milliseconds = std::chrono::duration<rep, std::milli>;
bool negative;
using milliseconds = std::chrono::duration<abs_rep, std::milli>;
bool negative = false;
using tm_writer_type = tm_writer<iterator, Char>;
duration_formatter(iterator o, std::chrono::duration<Rep, Period> d,
locale_ref loc)
: out(o), val(static_cast<rep>(d.count())), locale(loc), negative(false) {
: out(o), val(static_cast<abs_rep>(d.count())), locale(loc) {
if (d.count() < 0) {
val = 0 - val;
negative = true;
}
s = detail::duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
s = detail::duration_cast<seconds>(
std::chrono::duration<abs_rep, Period>(val));
}
// If val is NaN or infinite, writes it to out and returns true.
@ -1703,16 +1706,16 @@ struct duration_formatter {
return true;
}
auto days() const -> rep { return s.count() / 86400; }
auto hour() const -> rep { return mod((s.count() / 3600), 24); }
auto days() const -> abs_rep { return s.count() / 86400; }
auto hour() const -> abs_rep { return mod(s.count() / 3600, 24); }
auto hour12() const -> rep {
auto hour = mod((s.count() / 3600), 12);
auto hour12() const -> abs_rep {
auto hour = mod(s.count() / 3600, 12);
return hour < 1 ? 12 : hour;
}
auto minute() const -> rep { return mod((s.count() / 60), 60); }
auto second() const -> rep { return mod(s.count(), 60); }
auto minute() const -> abs_rep { return mod(s.count() / 60, 60); }
auto second() const -> abs_rep { return mod(s.count(), 60); }
auto time() const -> std::tm {
auto time = std::tm();
@ -1728,7 +1731,7 @@ struct duration_formatter {
negative = false;
}
void write(rep value, int width, pad_type pad = pad_type::zero) {
void write(abs_rep value, int width, pad_type pad = pad_type::zero) {
write_sign();
if (isnan(value)) return write_nan();
uint32_or_64_or_128_t<int> n =
@ -1816,9 +1819,9 @@ struct duration_formatter {
if (write_if_nonfinite()) return;
if (ns == numeric_system::standard) {
if (std::is_floating_point<rep>::value) {
if (std::is_floating_point<abs_rep>::value) {
auto buf = memory_buffer();
write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
write_floating_seconds(buf, std::chrono::duration<abs_rep, Period>(val),
precision);
write_sign();
if (buf.size() < 2 || buf[1] == '.')
@ -1827,7 +1830,7 @@ struct duration_formatter {
} else {
write(second(), 2, pad);
write_fractional_seconds<Char>(
out, std::chrono::duration<rep, Period>(val), precision);
out, std::chrono::duration<abs_rep, Period>(val), precision);
}
return;
}

View File

@ -538,6 +538,10 @@ TEST(chrono_test, format_specs) {
EXPECT_EQ(fmt::format("{:%j}", days(12)), "12");
EXPECT_EQ(fmt::format("{:%j}", days(12345)), "12345");
EXPECT_EQ(fmt::format("{:%j}", std::chrono::hours(12345 * 24 + 12)), "12345");
using signed_days = std::chrono::duration<signed char, std::ratio<86400>>;
EXPECT_EQ(fmt::format("{:%j}", signed_days(-128)), "-128");
using two_days = std::chrono::duration<unsigned char, std::ratio<172800>>;
EXPECT_EQ(fmt::format("{:%j}", two_days(200)), "400");
EXPECT_EQ(fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)),
"03:25:45");
EXPECT_EQ(fmt::format("{:%R}", std::chrono::seconds(12345)), "03:25");
@ -546,16 +550,6 @@ TEST(chrono_test, format_specs) {
EXPECT_EQ(fmt::format("{:%q}", std::chrono::seconds(12345)), "s");
}
TEST(chrono_test, format_days_with_signed_char_rep) {
using signed_days = std::chrono::duration<signed char, std::ratio<86400>>;
EXPECT_EQ(fmt::format("{:%j}", signed_days(-128)), "-128");
}
TEST(chrono_test, format_days_with_unsigned_char_rep) {
using two_days = std::chrono::duration<unsigned char, std::ratio<172800>>;
EXPECT_EQ(fmt::format("{:%j}", two_days(200)), "400");
}
TEST(chrono_test, invalid_specs) {
auto sec = std::chrono::seconds(0);
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%a}"), sec), fmt::format_error,