From 30bce6c14cfaefaf799cbb9d3110ee7f12090345 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 29 May 2019 18:02:26 -0700 Subject: [PATCH] Fix a few chrono formatting corner cases (#1178) --- include/fmt/chrono.h | 34 ++++++++++++++++++++-------------- test/chrono-test.cc | 18 ++++++++++++++++-- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 06ced173..926600e2 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -419,13 +419,10 @@ inline std::chrono::duration get_milliseconds( } template -OutputIt static format_chrono_duration_value(OutputIt out, Rep val, - int precision) { - if (precision < 0) { - return format_to(out, std::is_floating_point::value ? "{:g}" : "{}", - val); - } - return format_to(out, "{:.{}f}", val, precision); +OutputIt format_chrono_duration_value(OutputIt out, Rep val, int precision) { + if (precision >= 0) return format_to(out, "{:.{}f}", val, precision); + return format_to(out, std::is_floating_point::value ? "{:g}" : "{}", + val); } template @@ -441,20 +438,20 @@ struct chrono_formatter { FormatContext& context; OutputIt out; int precision; - Rep val; - typedef std::chrono::duration seconds; + typedef typename std::conditional::value && + sizeof(Rep) < sizeof(int), + int, Rep>::type rep; + rep val; + typedef std::chrono::duration seconds; seconds s; - typedef std::chrono::duration milliseconds; + typedef std::chrono::duration milliseconds; typedef typename FormatContext::char_type char_type; explicit chrono_formatter(FormatContext& ctx, OutputIt o, std::chrono::duration d) : context(ctx), out(o), val(d.count()) { - if (d.count() < 0) { - d = -d; - *out++ = '-'; - } + if (d.count() < 0) d = -d; s = std::chrono::duration_cast(d); } @@ -476,7 +473,15 @@ struct chrono_formatter { return time; } + void write_sign() { + if (val < 0) { + *out++ = '-'; + val = -val; + } + } + void write(Rep value, int width) { + write_sign(); if (isnan(value)) return write_nan(); typedef typename int_traits::main_type main_type; main_type n = to_unsigned(to_int(value)); @@ -570,6 +575,7 @@ struct chrono_formatter { void on_am_pm() { format_localized(time(), "%p"); } void on_duration_value() { + write_sign(); out = format_chrono_duration_value(out, val, precision); } diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 2179e0e7..daeca5f4 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -306,12 +306,22 @@ TEST(ChronoTest, InvalidColons) { fmt::format_error); } +TEST(ChronoTest, NegativeDuration) { + EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345))); + EXPECT_EQ("-03:25:45", + fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345))); + EXPECT_EQ("-00:01", + fmt::format("{:%M:%S}", std::chrono::duration(-1))); + EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(-12345))); + EXPECT_EQ("-00.127", + fmt::format("{:%S}", + std::chrono::duration{-127})); +} + TEST(ChronoTest, SpecialDurations) { EXPECT_EQ( "40.", fmt::format("{:%S}", std::chrono::duration(1e20)).substr(0, 3)); - 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", @@ -322,6 +332,10 @@ TEST(ChronoTest, SpecialDurations) { "1Es"); EXPECT_EQ(fmt::format("{}", std::chrono::duration(1)), "1as"); + EXPECT_EQ(fmt::format("{:%R}", std::chrono::duration{2}), + "03:33"); + EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration{2}), + "03:33:20"); } #endif // FMT_STATIC_THOUSANDS_SEPARATOR