diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 840e11f1..571081b2 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1026,8 +1026,7 @@ struct count_fractional_digits { // Format subseconds which are given as an integer type with an appropriate // number of digits. template -void write_fractional_seconds(OutputIt& out, Duration d) { - FMT_ASSERT(!std::is_floating_point::value, ""); +void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { constexpr auto num_fractional_digits = count_fractional_digits::value; @@ -1036,59 +1035,37 @@ void write_fractional_seconds(OutputIt& out, Duration d) { typename std::common_type::type, std::ratio<1, detail::pow10(num_fractional_digits)>>; - if (std::ratio_less::value) { - *out++ = '.'; - auto fractional = - detail::abs(d) - std::chrono::duration_cast(d); - auto subseconds = - std::chrono::treat_as_floating_point< - typename subsecond_precision::rep>::value - ? fractional.count() - : std::chrono::duration_cast(fractional) - .count(); - uint32_or_64_or_128_t n = - to_unsigned(to_nonnegative_int(subseconds, max_value())); - int num_digits = detail::count_digits(n); - if (num_fractional_digits > num_digits) - out = std::fill_n(out, num_fractional_digits - num_digits, '0'); - out = format_decimal(out, n, num_digits).end; - } -} -template -void write_fractional_seconds(OutputIt& out, Duration d, int precision) { - constexpr auto num_fractional_digits = - count_fractional_digits::value; + const auto fractional = + detail::abs(d) - std::chrono::duration_cast(d); + uint32_or_64_or_128_t subseconds = + std::chrono::treat_as_floating_point< + typename subsecond_precision::rep>::value + ? fractional.count() + : std::chrono::duration_cast(fractional).count(); + const int num_digits = detail::count_digits(subseconds); - using subsecond_precision = std::chrono::duration< - typename std::common_type::type, - std::ratio<1, detail::pow10(num_fractional_digits)>>; - if (precision > 0) { + int leading_zeroes = std::max(0, num_fractional_digits - num_digits); + if (precision < 0) { + FMT_ASSERT(!std::is_floating_point::value, ""); + if (std::ratio_less::value) { + *out++ = '.'; + out = std::fill_n(out, leading_zeroes, '0'); + out = format_decimal(out, subseconds, num_digits).end; + } + } else { *out++ = '.'; - auto fractional = - detail::abs(d) - std::chrono::duration_cast(d); - auto subseconds = - std::chrono::treat_as_floating_point< - typename subsecond_precision::rep>::value - ? fractional.count() - : std::chrono::duration_cast(fractional) - .count(); - uint32_or_64_or_128_t n = - to_unsigned(to_nonnegative_int(subseconds, max_value())); - int num_digits = detail::count_digits(n); - int zeroes = - std::min(std::max(0, num_fractional_digits - num_digits), precision); - if (num_fractional_digits > num_digits) out = std::fill_n(out, zeroes, '0'); - int remaining = precision - zeroes; + leading_zeroes = std::min(leading_zeroes, precision); + out = std::fill_n(out, leading_zeroes, '0'); + int remaining = precision - leading_zeroes; if (remaining < num_digits) { - n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining))); - out = format_decimal(out, n, remaining).end; + subseconds /= + to_unsigned(detail::pow10(to_unsigned(num_digits - remaining))); + out = format_decimal(out, subseconds, remaining).end; return; } - out = format_decimal(out, n, num_digits).end; + out = format_decimal(out, subseconds, num_digits).end; remaining -= num_digits; out = std::fill_n(out, remaining, '0'); } @@ -1097,48 +1074,27 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision) { // Format subseconds which are given as a floating point type with an appropiate // number of digits. We cannot pass the Duration here, as we explicitly need to // pass the Rep value in the chrono_formatter. -template -void write_floating_seconds(memory_buffer& buf, Duration duration) { - using Rep = typename Duration::rep; - FMT_ASSERT(std::is_floating_point::value, ""); - auto num_fractional_digits = - count_fractional_digits::value; - // For non-integer values, we ensure at least 6 digits to get microsecond - // precision. - auto val = duration.count(); - if (num_fractional_digits < 6 && - static_cast(std::round(val)) != val) - num_fractional_digits = 6; - - format_to( - std::back_inserter(buf), runtime("{:.{}f}"), - std::fmod(val * - static_cast(Duration::period::num) / - static_cast(Duration::period::den), - static_cast(60)), - num_fractional_digits); -} - template void write_floating_seconds(memory_buffer& buf, Duration duration, - int precision) { - using Rep = typename Duration::rep; - if (precision < 0) { - write_floating_seconds(buf, duration); - return; + int num_fractional_digits = -1) { + using rep = typename Duration::rep; + FMT_ASSERT(std::is_floating_point::value, ""); + + auto val = duration.count(); + + if (num_fractional_digits < 0) { + num_fractional_digits = + count_fractional_digits::value; + if (num_fractional_digits < 6 && static_cast(std::round(val)) != val) + num_fractional_digits = 6; } - FMT_ASSERT(std::is_floating_point::value, ""); - auto val = duration.count(); - - format_to( - std::back_inserter(buf), runtime("{:.{}f}"), - std::fmod(val * - static_cast(Duration::period::num) / - static_cast(Duration::period::den), - static_cast(60)), - precision); + format_to(std::back_inserter(buf), runtime("{:.{}f}"), + std::fmod(val * static_cast(Duration::period::num) / + static_cast(Duration::period::den), + static_cast(60)), + num_fractional_digits); } template { - bool is_floating_point = false; - bool has_precision = false; + bool has_precision_integral = false; FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } @@ -1594,7 +1549,7 @@ struct chrono_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} FMT_CONSTEXPR void on_duration_value() const { - if (has_precision && !is_floating_point) { + if (has_precision_integral) { FMT_THROW(format_error("precision not allowed for this argument type")); } } @@ -2073,9 +2028,8 @@ struct formatter, Char> { begin = detail::parse_width(begin, end, handler); if (begin == end) return {begin, begin}; auto checker = detail::chrono_format_checker(); - checker.is_floating_point = std::is_floating_point::value; if (*begin == '.') { - checker.has_precision = true; + checker.has_precision_integral = !std::is_floating_point::value; begin = detail::parse_precision(begin, end, handler); } if (begin != end && *begin == 'L') {