diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 8ae57ae8..15cfa412 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1389,59 +1389,47 @@ inline std::chrono::duration get_milliseconds( #endif } -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(long long num, long long den, int n = 0) { - return num % den == 0 ? n : (n > 18 ? 6 : num_digits(num * 10, den, n + 1)); - } +// 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(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 long long pow10(std::uint32_t n) { - return n == 0 ? 1 : 10 * pow10(n - 1); - } +static constexpr long long pow10(std::uint32_t n) { + return n == 0 ? 1 : 10 * pow10(n - 1); +} - template ::is_signed)> - static constexpr std::chrono::duration abs( - std::chrono::duration 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 ::is_signed)> +static constexpr std::chrono::duration abs( + std::chrono::duration 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 ::is_signed)> - static constexpr std::chrono::duration abs( - std::chrono::duration d) { - return d; - } +template ::is_signed)> +static constexpr std::chrono::duration abs( + std::chrono::duration d) { + return d; +} - public: - static constexpr auto fractional_width = - num_digits(Duration::period::num, Duration::period::den); - - using precision = std::chrono::duration< - typename std::common_type::type, - std::ratio<1, pow10(fractional_width)>>; - - template - static constexpr typename precision::rep get_subseconds( - std::chrono::duration d) { - return std::chrono::treat_as_floating_point::value - ? (abs(d) - std::chrono::duration_cast(d)) - .count() - : std::chrono::duration_cast( - abs(d) - - std::chrono::duration_cast(d)) - .count(); - } -}; +template +static constexpr typename ToDuration::rep get_subseconds( + std::chrono::duration d) { + return std::chrono::treat_as_floating_point::value + ? (abs(d) - std::chrono::duration_cast(d)) + .count() + : std::chrono::duration_cast( + abs(d) - std::chrono::duration_cast(d)) + .count(); +} template ::value)> @@ -1593,16 +1581,47 @@ struct chrono_formatter { } } - template void write(RepType value, int width) { + void write(Rep 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; } + template void write_fractional_seconds(Duration d) { + static constexpr auto fractional_width = + detail::num_digits(Duration::period::num, Duration::period::den); + + using precision = std::chrono::duration< + typename std::common_type::type, + std::ratio<1, detail::pow10(fractional_width)>>; + // We could use c++ 17 if constexpr here. + if (std::ratio_less::value) { + *out++ = '.'; + const auto subseconds = + std::chrono::treat_as_floating_point::value + ? (detail::abs(d) - + std::chrono::duration_cast(d)) + .count() + : std::chrono::duration_cast( + detail::abs(d) - + std::chrono::duration_cast(d)) + .count(); + uint32_or_64_or_128_t n = + to_unsigned(to_nonnegative_int(subseconds, max_value())); + int num_digits = detail::count_digits(n); + if (fractional_width > num_digits) { + out = std::fill_n(out, fractional_width - num_digits, '0'); + } + out = format_decimal(out, n, num_digits).end; + } + } + void write_nan() { std::copy_n("nan", 3, out); } void write_pinf() { std::copy_n("inf", 3, out); } void write_ninf() { std::copy_n("-inf", 4, out); } @@ -1680,15 +1699,7 @@ struct chrono_formatter { if (ns == numeric_system::standard) { write(second(), 2); - using duration_rep = std::chrono::duration; - using subsec_helper = detail::subsecond_helper; - // Could use c++ 17 if constexpr - if (std::ratio_less::value) { - *out++ = '.'; - write(subsec_helper::get_subseconds(duration_rep{val}), - subsec_helper::fractional_width); - } + write_fractional_seconds(std::chrono::duration{val}); return; } auto time = tm();