Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<chrono>: Fix formatting for unusual durations #3649

Merged
merged 11 commits into from
Apr 28, 2023
23 changes: 15 additions & 8 deletions stl/inc/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -1578,16 +1578,23 @@ namespace chrono {
"N4885 [time.hms.overview]/2 mandates Duration to be a specialization of chrono::duration.");

static constexpr unsigned int fractional_width = [] {
auto _Num = _Duration::period::num;
constexpr auto _Den = _Duration::period::den;
// Returns the number of fractional digits of _Num / _Den in the range [0, 18].
// If it can't be represented, 6 is returned.
// Example: _Fractional_width(1, 8) would return 3 for 0.125.
_STL_ASSERT(_Num > 0 && _Den > 0, "Numerator and denominator can't be less than 1.");
unsigned int _Result = 0;
for (; _Num % _Den != 0 && _Result < 19; _Num = _Num % _Den * 10, ++_Result) {
auto _Den = _Duration::period::den;
_STL_ASSERT(_Duration::period::num > 0 && _Den > 0, "Numerator and denominator can't be less than 1.");
unsigned int _Power_of_2_in_den = 0u;
unsigned int _Power_of_5_in_den = 0u;
for (; _Den % 2 == 0; _Den /= 2, ++_Power_of_2_in_den) {
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
}
return _Result == 19 ? 6 : _Result;
for (; _Den % 5 == 0; _Den /= 5, ++_Power_of_5_in_den) {
}

if (_Den != 1) {
return 6u;
}
const auto _Result = _STD max(_Power_of_2_in_den, _Power_of_5_in_den);
return _Result > 18u ? 6u : _Result;
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
}();
using precision =
duration<common_type_t<typename _Duration::rep, seconds::rep>, ratio<1, _Pow10(fractional_width)>>;
Expand Down Expand Up @@ -4945,8 +4952,8 @@ namespace chrono {
}

template <class _Duration>
_NODISCARD constexpr hh_mm_ss<_Duration> _Hh_mm_ss_part_underflow_to_zero(const _Duration& _Val) {
return hh_mm_ss<_Duration>{_Remove_duration_part<days>(_Val)};
_NODISCARD constexpr auto _Hh_mm_ss_part_underflow_to_zero(const _Duration& _Val) {
return hh_mm_ss<common_type_t<days, _Duration>>{_Remove_duration_part<days>(_Val)};
}

template <unsigned int _Fractional_width, class _CharT, class _Traits, class _Precision>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <cstdio>
#include <format>
#include <iostream>
#include <limits>
#include <locale>
#include <sstream>
#include <string>
Expand All @@ -20,6 +21,8 @@
using namespace std;
using namespace chrono;

constexpr auto intmax_max = numeric_limits<intmax_t>::max();
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

template <typename CharT>
[[nodiscard]] constexpr const CharT* choose_literal(const char* const str, const wchar_t* const wstr) noexcept {
if constexpr (is_same_v<CharT, char>) {
Expand Down Expand Up @@ -247,13 +250,22 @@ void empty_braces_helper(

template <typename CharT>
void test_duration_formatter() {
using LongRatio = ratio<intmax_max - 1, intmax_max>;
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

empty_braces_helper(seconds{5}, STR("5s"));
empty_braces_helper(minutes{7}, STR("7min"));
empty_braces_helper(hours{9}, STR("9h"));
empty_braces_helper(days{2}, STR("2d"));
empty_braces_helper(-seconds{5}, STR("-5s"));
empty_braces_helper(duration<int, ratio<2>>{40}, STR("40[2]s"));
empty_braces_helper(duration<int, ratio<3, 1>>{40}, STR("40[3]s"));
empty_braces_helper(duration<int, ratio<3, 7>>{40}, STR("40[3/7]s"));
empty_braces_helper(duration<int, ratio<1, 2>>{40}, STR("40[1/2]s"));
empty_braces_helper(duration<int, ratio<22, 7>>{40}, STR("40[22/7]s"));
empty_braces_helper(duration<int, ratio<53, 101>>{40}, STR("40[53/101]s"));
empty_braces_helper(duration<int, ratio<201, 2147483647>>{40}, STR("40[201/2147483647]s"));
// TRANSITION, LWG-3921: duration_cast used in formatting may raise UB
empty_braces_helper(duration<int, LongRatio>{1}, STR("1[9223372036854775806/9223372036854775807]s"));

// formatting small types needs to work as iostreams << VSO-1521926
empty_braces_helper(duration<long long, atto>{123}, STR("123as"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,12 @@ void test_duration_output() {
assert(test_duration_basic_out(duration<int, ratio<60 * 60 * 24>>{42}, "42d"));

assert(test_duration_basic_out(duration<int, ratio<2>>{24}, "24[2]s"));
assert(test_duration_basic_out(duration<int, ratio<3, 1>>{24}, "24[3]s"));
assert(test_duration_basic_out(duration<int, ratio<3, 7>>{24}, "24[3/7]s"));
assert(test_duration_basic_out(duration<int, ratio<1, 2>>{24}, "24[1/2]s"));
assert(test_duration_basic_out(duration<int, ratio<22, 7>>{24}, "24[22/7]s"));
assert(test_duration_basic_out(duration<int, ratio<53, 101>>{24}, "24[53/101]s"));
assert(test_duration_basic_out(duration<int, ratio<201, 2147483647>>{24}, "24[201/2147483647]s"));
assert(test_duration_basic_out(duration<int, LongRatio>{24}, "24[9223372036854775806/9223372036854775807]s"));

assert(test_duration_basic_out(duration<double>{0.140625}, "0.140625s"));
Expand Down Expand Up @@ -119,8 +123,12 @@ void test_duration_output() {
assert(test_duration_basic_out(duration<int, ratio<60 * 60 * 24>>{42}, L"42d"));

assert(test_duration_basic_out(duration<int, ratio<2>>{24}, L"24[2]s"));
assert(test_duration_basic_out(duration<int, ratio<3, 1>>{24}, L"24[3]s"));
assert(test_duration_basic_out(duration<int, ratio<3, 7>>{24}, L"24[3/7]s"));
assert(test_duration_basic_out(duration<int, ratio<1, 2>>{24}, L"24[1/2]s"));
assert(test_duration_basic_out(duration<int, ratio<22, 7>>{24}, L"24[22/7]s"));
assert(test_duration_basic_out(duration<int, ratio<53, 101>>{24}, L"24[53/101]s"));
assert(test_duration_basic_out(duration<int, ratio<201, 2147483647>>{24}, L"24[201/2147483647]s"));
assert(test_duration_basic_out(duration<int, LongRatio>{24}, L"24[9223372036854775806/9223372036854775807]s"));

assert(test_duration_basic_out(duration<double>{0.140625}, L"0.140625s"));
Expand Down