diff --git a/libcxx/include/__chrono/convert_to_tm.h b/libcxx/include/__chrono/convert_to_tm.h --- a/libcxx/include/__chrono/convert_to_tm.h +++ b/libcxx/include/__chrono/convert_to_tm.h @@ -31,6 +31,7 @@ #include <__config> #include <__format/format_error.h> #include <__memory/addressof.h> +#include <__type_traits/is_convertible.h> #include #include #include @@ -116,12 +117,23 @@ // ... However, if a flag refers to a "time of day" (e.g. %H, %I, %p, // etc.), then a specialization of duration is interpreted as the time of // day elapsed since midnight. - uint64_t __sec = chrono::duration_cast(__value).count(); - __sec %= 24 * 3600; - __result.tm_hour = __sec / 3600; - __sec %= 3600; - __result.tm_min = __sec / 60; - __result.tm_sec = __sec % 60; + + // Not all values can be converted to hours, it may run into ratio + // conversion errors. In that case the conversion to seconds works. + if constexpr (is_convertible_v<_ChronoT, chrono::hours>) { + auto __hour = chrono::floor(__value); + auto __sec = chrono::duration_cast(__value - __hour); + __result.tm_hour = __hour.count() % 24; + __result.tm_min = __sec.count() / 60; + __result.tm_sec = __sec.count() % 60; + } else { + uint64_t __sec = chrono::duration_cast(__value).count(); + __sec %= 24 * 3600; + __result.tm_hour = __sec / 3600; + __sec %= 3600; + __result.tm_min = __sec / 60; + __result.tm_sec = __sec % 60; + } } else if constexpr (same_as<_ChronoT, chrono::day>) __result.tm_mday = static_cast(__value); else if constexpr (same_as<_ChronoT, chrono::month>) diff --git a/libcxx/test/std/time/time.syn/formatter.duration.pass.cpp b/libcxx/test/std/time/time.syn/formatter.duration.pass.cpp --- a/libcxx/test/std/time/time.syn/formatter.duration.pass.cpp +++ b/libcxx/test/std/time/time.syn/formatter.duration.pass.cpp @@ -1152,6 +1152,22 @@ check_exception("End of input while parsing the modifier E", SV("{:%E"), 0ms); check_exception("End of input while parsing the modifier O", SV("{:%O"), 0ms); + // Make sure the the required values work, based on their minimum number of required bits per [time.syn]. + check(SV("23:47:16.854775807"), + SV("{:%T}"), + std::chrono::nanoseconds{0x7fff'ffff'ffff'ffffll}); // 64 bit signed value max + check(SV("23:35:09.481983"), + SV("{:%T}"), + std::chrono::microseconds{0x003f'ffff'ffff'ffffll}); // 55 bit signed value max + check(SV("06:20:44.415"), SV("{:%T}"), std::chrono::milliseconds{0x0000'fff'ffff'ffffll}); // 45 bit signed value max + check(SV("01:53:03"), SV("{:%T}"), std::chrono::seconds{0x0000'0003'ffff'ffffll}); // 35 bit signed value max + check(SV("12:15:00"), SV("{:%T}"), std::chrono::minutes{0x0fff'ffff}); // 29 bit signed value max + check(SV("15:00:00"), SV("{:%T}"), std::chrono::hours{0x003f'ffff}); // 23 bit signed value max + check(SV("00:00:00"), SV("{:%T}"), std::chrono::days{0x0ff'ffff}); // 25 bit signed value max + check(SV("00:00:00"), SV("{:%T}"), std::chrono::weeks{0x003f'ffff}); // 22 bit signed value max + check(SV("21:11:42"), SV("{:%T}"), std::chrono::months{0x0007'ffff}); // 20 bit signed value max + check(SV("05:42:00"), SV("{:%T}"), std::chrono::years{0xffff}); // 17 bit signed value max + // Precision not allowed check_exception("Expected '%' or '}' in the chrono format-string", SV("{:.3}"), 0ms); }