diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index b5ee0c9a..8ae57ae8 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1393,12 +1393,11 @@ template class subsecond_helper { // Returns the amount of digits according to the c++ 20 spec // In the range [0, 18], if more than 18 fractional digits are required, // then we return 6 for microseconds precision - static constexpr int num_digits(std::intmax_t num, std::intmax_t den, - int n = 0) { + static constexpr int num_digits(long long num, long long den, int n = 0) { return num % den == 0 ? n : (n > 18 ? 6 : num_digits(num * 10, den, n + 1)); } - static constexpr std::intmax_t pow10(std::uint32_t n) { + static constexpr long long pow10(std::uint32_t n) { return n == 0 ? 1 : 10 * pow10(n - 1); } @@ -1406,7 +1405,13 @@ template class subsecond_helper { FMT_ENABLE_IF(std::numeric_limits::is_signed)> static constexpr std::chrono::duration abs( std::chrono::duration d) { - return d >= d.zero() ? d : -d; + // We need to compare the duration using the count() method directly + // due to a compiler bug in clang-11 regarding the spaceship operator, + // when -Wzero-as-null-pointer-constant is enabled. + // In clang-12 the bug has been fixed. See + // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example: + // https://www.godbolt.org/z/Knbb5joYx + return d.count() >= d.zero().count() ? d : -d; } template void write(RepType value, int width) { write_sign(); if (isnan(value)) return write_nan(); - uint32_or_64_or_128_t n = - to_unsigned(to_nonnegative_int(value, max_value())); + uint32_or_64_or_128_t n = + to_unsigned(to_nonnegative_int(value, max_value())); int num_digits = detail::count_digits(n); if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); out = format_decimal(out, n, num_digits).end; diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 23e45da9..f98daac3 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -543,9 +543,10 @@ TEST(chrono_test, negative_durations) { } TEST(chrono_test, special_durations) { - EXPECT_EQ( - "40", - fmt::format("{:%S}", std::chrono::duration(1e20)).substr(0, 2)); + auto value = fmt::format("{:%S}", std::chrono::duration(1e20)); + // No decimal point is printed so size() is 2. + EXPECT_EQ(value.size(), 2); + EXPECT_EQ("40", value.substr(0, 2)); auto nan = std::numeric_limits::quiet_NaN(); EXPECT_EQ( "nan nan nan nan nan:nan nan", @@ -584,8 +585,8 @@ TEST(chrono_test, weekday) { } TEST(chrono_test, cpp20_duration_subsecond_support) { - using attoseconds = std::chrono::duration; - // Check that 18 digits of subsecond precision are supported + using attoseconds = std::chrono::duration; + // Check that 18 digits of subsecond precision are supported. EXPECT_EQ(fmt::format("{:%S}", attoseconds{999999999999999999}), "00.999999999999999999"); EXPECT_EQ(fmt::format("{:%S}", attoseconds{673231113420148734}), @@ -598,29 +599,27 @@ TEST(chrono_test, cpp20_duration_subsecond_support) { "-13.420148734"); EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds{1234}), "01.234"); { - // Check that {:%H:%M:%S} is equivalent to {:%T} + // Check that {:%H:%M:%S} is equivalent to {:%T}. auto dur = std::chrono::milliseconds{3601234}; auto formatted_dur = fmt::format("{:%T}", dur); EXPECT_EQ(formatted_dur, "01:00:01.234"); EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur); } using nanoseconds_dbl = std::chrono::duration; - EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{-123456789.123456789}), - "-00.123456789"); - EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{9123456789.123456789}), - "09.123456789"); - // Only seconds part is printed + EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{-123456789}), "-00.123456789"); + EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{9123456789}), "09.123456789"); + // Verify that only the seconds part is extracted and printed. EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123456789}), "39.123456789"); EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123000000}), "39.123000000"); { - // Now the hour is printed, and we also test if negative doubles work + // Now the hour is printed, and we also test if negative doubles work. auto dur = nanoseconds_dbl{-99123456789}; auto formatted_dur = fmt::format("{:%T}", dur); EXPECT_EQ(formatted_dur, "-00:01:39.123456789"); EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur); } // Check that durations with precision greater than std::chrono::seconds have - // fixed precision and empty zeros + // fixed precision, and print zeros even if there is no fractional part. EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}), "07.000000"); }