Skip to content

Commit 99d36f5

Browse files
Fix hh_mm_ss formatting for values >= 1 day (#3727)
Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent 47679bb commit 99d36f5

File tree

6 files changed

+78
-29
lines changed

6 files changed

+78
-29
lines changed

stl/inc/__msvc_chrono.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ namespace chrono {
501501
is_arithmetic_v<_Rep>) /* strengthened */ {
502502
// create a duration whose count() is the absolute value of _Dur.count()
503503
if (_Dur < duration<_Rep, _Period>::zero()) {
504-
return duration<_Rep, _Period>::zero() - _Dur;
504+
return -_Dur;
505505
} else {
506506
return _Dur;
507507
}

stl/inc/chrono

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,12 +1601,12 @@ namespace chrono {
16011601

16021602
constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
16031603
constexpr explicit hh_mm_ss(_Duration _Dur)
1604-
: _Is_neg{_Dur < _Duration::zero()}, //
1604+
: _Is_neg{_Dur < _Duration::zero()},
16051605
_Hours{_Duration_cast_underflow_to_zero<_CHRONO hours>(_CHRONO abs(_Dur))},
16061606
_Mins{_Duration_cast_underflow_to_zero<_CHRONO minutes>(
16071607
_Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur)))},
16081608
_Secs{_Duration_cast_underflow_to_zero<_CHRONO seconds>(
1609-
_Remove_duration_part<_CHRONO minutes>(_CHRONO abs(_Dur)))} {
1609+
_Remove_duration_part<_CHRONO minutes>(_Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur))))} {
16101610
if constexpr (treat_as_floating_point_v<typename precision::rep>) {
16111611
// no need to deal with underflow here, because floating durations allow it
16121612
_Sub_secs = _CHRONO abs(_Dur) - hours() - minutes() - seconds();
@@ -4942,11 +4942,6 @@ namespace chrono {
49424942
_STL_INTERNAL_CHECK(false);
49434943
}
49444944

4945-
template <class _Duration>
4946-
_NODISCARD constexpr auto _Hh_mm_ss_part_underflow_to_zero(const _Duration& _Val) {
4947-
return hh_mm_ss<common_type_t<days, _Duration>>{_Remove_duration_part<days>(_Val)};
4948-
}
4949-
49504945
template <unsigned int _Fractional_width, class _CharT, class _Traits, class _Precision>
49514946
void _Write_fractional_seconds(
49524947
basic_ostream<_CharT, _Traits>& _Os, const seconds& _Seconds, const _Precision& _Subseconds) {
@@ -4993,7 +4988,7 @@ namespace chrono {
49934988

49944989
template <class _CharT, class _Traits, class _Rep, class _Period>
49954990
void _Write_seconds(basic_ostream<_CharT, _Traits>& _Os, const duration<_Rep, _Period>& _Val) {
4996-
_Write_seconds(_Os, _Hh_mm_ss_part_underflow_to_zero(_Val));
4991+
_Write_seconds(_Os, hh_mm_ss{_Val});
49974992
}
49984993

49994994
template <class _Ty>
@@ -5008,7 +5003,7 @@ namespace chrono {
50085003
int _Seconds = 0;
50095004

50105005
if constexpr (_Is_specialization_v<_Ty, duration>) {
5011-
return _Fill_tm(_Hh_mm_ss_part_underflow_to_zero(_Val));
5006+
return _Fill_tm(hh_mm_ss{_Val});
50125007
} else if constexpr (_Is_specialization_v<_Ty, _Local_time_format_t>) {
50135008
return _Fill_tm(_Val._Time);
50145009
} else if constexpr (is_same_v<_Ty, day>) {
@@ -5616,10 +5611,13 @@ namespace chrono {
56165611
_Os << _CharT{'/'};
56175612
_Custom_write(_Os, {._Type = 'y'}, _Time, _Val);
56185613
return true;
5614+
case 'H':
5615+
// Print hour even if >= 24.
5616+
_Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:02}"), _Time.tm_hour);
5617+
return true;
56195618
case 'T':
5620-
// Alias for %H:%M:%S but we need to rewrite %S to display fractions of a second.
5621-
_Validate_specifiers({._Type = 'H'}, _Val);
5622-
_Os << _STD put_time(&_Time, _STATICALLY_WIDEN(_CharT, "%H:%M:"));
5619+
// Alias for %H:%M:%S but we allow hours > 23 and rewrite %S to display fractions of a second.
5620+
_Os << _STD format(_STATICALLY_WIDEN(_CharT, "{:02}:{:02}:"), _Time.tm_hour, _Time.tm_min);
56235621
[[fallthrough]];
56245622
case 'S':
56255623
if (_Has_modifier) {
@@ -5674,14 +5672,8 @@ namespace chrono {
56745672
template <class _Ty>
56755673
static void _Validate_specifiers(const _Chrono_spec<_CharT>& _Spec, const _Ty& _Val) {
56765674
if constexpr (_Is_specialization_v<_Ty, duration> || is_same_v<_Ty, sys_info>
5677-
|| _Is_specialization_v<_Ty, time_point> || _Is_specialization_v<_Ty, _Local_time_format_t>) {
5678-
return;
5679-
}
5680-
5681-
if constexpr (_Is_specialization_v<_Ty, hh_mm_ss>) {
5682-
if (_Spec._Type == 'H' && _Val.hours() >= hours{24}) {
5683-
_Throw_format_error("Cannot localize hh_mm_ss with an absolute value of 24 hours or more.");
5684-
}
5675+
|| _Is_specialization_v<_Ty, time_point> || _Is_specialization_v<_Ty, _Local_time_format_t>
5676+
|| _Is_specialization_v<_Ty, hh_mm_ss>) {
56855677
return;
56865678
}
56875679

tests/std/test.lst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ tests\GH_003105_piecewise_densities
223223
tests\GH_003119_error_category_ctor
224224
tests\GH_003246_cmath_narrowing
225225
tests\GH_003617_vectorized_meow_element
226+
tests\GH_003676_format_large_hh_mm_ss_values
226227
tests\LWG2381_num_get_floating_point
227228
tests\LWG2597_complex_branch_cut
228229
tests\LWG3018_shared_ptr_function
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
RUNALL_INCLUDE ..\concepts_20_matrix.lst
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
#include <cassert>
5+
#include <chrono>
6+
#include <format>
7+
#include <iostream>
8+
#include <sstream>
9+
#include <string>
10+
11+
using namespace std;
12+
using namespace chrono;
13+
14+
void assert_equal(const auto& expected, const auto& value) {
15+
if (expected == value) {
16+
return;
17+
}
18+
19+
cout << "Expected: " << expected << endl;
20+
cout << "Got: " << value << endl;
21+
assert(false);
22+
}
23+
24+
template <typename R, typename P>
25+
void assert_duration_format_equal(const duration<R, P>& dur, const string& str) {
26+
ostringstream oss;
27+
oss << hh_mm_ss{dur};
28+
assert_equal(str, oss.str());
29+
assert_equal(str, format("{:%T}", dur));
30+
}
31+
32+
template <typename R, typename P>
33+
void assert_duration_format_equal_positive(const duration<R, P>& dur, const string& str) {
34+
assert_duration_format_equal(dur, str);
35+
assert_duration_format_equal(-dur, '-' + str);
36+
}
37+
38+
int main() {
39+
assert_duration_format_equal(0ns, "00:00:00.000000000");
40+
assert_duration_format_equal(-0h, "00:00:00");
41+
assert_duration_format_equal(years{0}, "00:00:00");
42+
43+
assert_duration_format_equal_positive(3h, "03:00:00");
44+
assert_duration_format_equal_positive(10h + 20min + 30s, "10:20:30");
45+
assert_duration_format_equal_positive(days{3}, "72:00:00");
46+
assert_duration_format_equal_positive(years{1}, "8765:49:12");
47+
assert_duration_format_equal_positive(duration<float, days::period>{1.55f}, "37:11:59");
48+
assert_duration_format_equal_positive(2ms, "00:00:00.002");
49+
assert_duration_format_equal_positive(60min, "01:00:00");
50+
}

tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -276,12 +276,12 @@ void test_duration_formatter() {
276276
assert(format(STR("{:%T}"), 4083007ms) == STR("01:08:03.007"));
277277
assert(format(STR("{:%T}"), -4083007ms) == STR("-01:08:03.007"));
278278

279-
assert(format(STR("{:%T %j %q %Q}"), days{4} + 30min) == STR("00:30:00 4 min 5790"));
280-
assert(format(STR("{:%T %j %q %Q}"), -days{4} - 30min) == STR("-00:30:00 4 min 5790"));
281-
assert(format(STR("{:%T %j}"), days{4} + 23h + 30min) == STR("23:30:00 4"));
282-
assert(format(STR("{:%T %j}"), -days{4} - 23h - 30min) == STR("-23:30:00 4"));
283-
assert(format(STR("{:%T %j}"), duration<float, days::period>{1.55f}) == STR("13:11:59 1"));
284-
assert(format(STR("{:%T %j}"), duration<float, days::period>{-1.55f}) == STR("-13:11:59 1"));
279+
assert(format(STR("{:%T %j %q %Q}"), days{4} + 30min) == STR("96:30:00 4 min 5790"));
280+
assert(format(STR("{:%T %j %q %Q}"), -days{4} - 30min) == STR("-96:30:00 4 min 5790"));
281+
assert(format(STR("{:%T %j}"), days{4} + 23h + 30min) == STR("119:30:00 4"));
282+
assert(format(STR("{:%T %j}"), -days{4} - 23h - 30min) == STR("-119:30:00 4"));
283+
assert(format(STR("{:%T %j}"), duration<float, days::period>{1.55f}) == STR("37:11:59 1"));
284+
assert(format(STR("{:%T %j}"), duration<float, days::period>{-1.55f}) == STR("-37:11:59 1"));
285285
}
286286

287287
template <typename CharT>
@@ -775,8 +775,10 @@ void test_hh_mm_ss_formatter() {
775775
assert(format(STR("{:%H %I %M %S %r %R %T %p}"), hh_mm_ss{-13h - 14min - 15351ms})
776776
== STR("-13 01 14 15.351 01:14:15 PM 13:14 13:14:15.351 PM"));
777777

778-
throw_helper(STR("{}"), hh_mm_ss{24h});
779-
throw_helper(STR("{}"), hh_mm_ss{-24h});
778+
assert(format(STR("{}"), hh_mm_ss{24h}) == STR("24:00:00"));
779+
assert(format(STR("{}"), hh_mm_ss{-24h}) == STR("-24:00:00"));
780+
assert(format(STR("{:%H}"), hh_mm_ss{24h}) == STR("24"));
781+
assert(format(STR("{:%H}"), hh_mm_ss{-24h}) == STR("-24"));
780782
assert(format(STR("{:%M %S}"), hh_mm_ss{27h + 12min + 30s}) == STR("12 30"));
781783
}
782784

0 commit comments

Comments
 (0)