diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 39369add..b2768e7b 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -376,9 +376,20 @@ struct chrono_format_checker { FMT_NORETURN void on_tz_name() { report_no_date(); } }; +template ::value, int>::type = 0> +inline bool isnan(T) { + return false; +} +template ::value, + int>::type = 0> +inline bool isnan(T value) { + return std::isnan(value); +} + template inline int to_int(T value) { - FMT_ASSERT(value >= (std::numeric_limits::min)() && - value <= (std::numeric_limits::max)(), + FMT_ASSERT(isnan(value) || (value >= (std::numeric_limits::min)() && + value <= (std::numeric_limits::max)()), "invalid value"); return static_cast(value); } @@ -439,33 +450,37 @@ struct chrono_formatter { ms = std::chrono::duration_cast(d - s); } - int hour() const { return to_int(mod((s.count() / 3600), 24)); } + Rep hour() const { return mod((s.count() / 3600), 24); } - int hour12() const { - auto hour = to_int(mod((s.count() / 3600), 12)); - return hour > 0 ? hour : 12; + Rep hour12() const { + Rep hour = mod((s.count() / 3600), 12); + return hour <= 0 ? 12 : hour; } - int minute() const { return to_int(mod((s.count() / 60), 60)); } - int second() const { return to_int(mod(s.count(), 60)); } + Rep minute() const { return mod((s.count() / 60), 60); } + Rep second() const { return mod(s.count(), 60); } std::tm time() const { auto time = std::tm(); - time.tm_hour = hour(); - time.tm_min = minute(); - time.tm_sec = second(); + time.tm_hour = to_int(hour()); + time.tm_min = to_int(minute()); + time.tm_sec = to_int(second()); return time; } - void write(int value, int width) { + void write(Rep value, int width) { + if (isnan(value)) return write_nan(); typedef typename int_traits::main_type main_type; - main_type n = to_unsigned(value); + main_type n = to_unsigned(to_int(value)); int num_digits = internal::count_digits(n); if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); out = format_decimal(out, n, num_digits); } + void write_nan() { std::copy_n("nan", 3, out); } + void format_localized(const tm& time, const char* format) { + if (isnan(val)) return write_nan(); auto locale = context.locale().template get(); auto& facet = std::use_facet>(locale); std::basic_ostringstream os; @@ -497,14 +512,14 @@ struct chrono_formatter { void on_24_hour(numeric_system ns) { if (ns == numeric_system::standard) return write(hour(), 2); auto time = tm(); - time.tm_hour = hour(); + time.tm_hour = to_int(hour()); format_localized(time, "%OH"); } void on_12_hour(numeric_system ns) { if (ns == numeric_system::standard) return write(hour12(), 2); auto time = tm(); - time.tm_hour = hour(); + time.tm_hour = hour12(); format_localized(time, "%OI"); } @@ -520,7 +535,7 @@ struct chrono_formatter { write(second(), 2); if (ms != std::chrono::milliseconds(0)) { *out++ = '.'; - write(to_int(ms.count()), 3); + write(ms.count(), 3); } return; } diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 573606d8..738686c9 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -306,12 +306,14 @@ TEST(ChronoTest, InvalidColons) { fmt::format_error); } -TEST(ChronoTest, LargeDuration) { +TEST(ChronoTest, SpecialDurations) { EXPECT_EQ("40", fmt::format("{:%S}", std::chrono::duration(1e20))); -} - -TEST(ChronoTest, NegativeDuration) { - EXPECT_EQ("-00:01", fmt::format("{:%M:%S}", std::chrono::duration(-1))); + EXPECT_EQ("-00:01", + fmt::format("{:%M:%S}", std::chrono::duration(-1))); + auto nan = std::numeric_limits::quiet_NaN(); + EXPECT_EQ( + "nan nan nan nan.nan nan:nan nan", + fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration(nan))); } #endif // FMT_STATIC_THOUSANDS_SEPARATOR