From dc10427a58d5c99cb895c7b138e9edcee185ae58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Fri, 21 Nov 2025 10:43:07 +0100 Subject: [PATCH 01/11] Add tests --- .../P0355R7_calendars_and_time_zones_hms/test.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp index 05d0320e311..5360aff1874 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp @@ -7,6 +7,8 @@ #include #include +// Extended to test LWG-4274 "The chrono::hh_mm_ss constructor is ill-formed for unsigned durations" + using namespace std; using namespace std::chrono; @@ -103,6 +105,18 @@ constexpr void constructor() { assert(f_hms_hours{}.minutes() == f_hms_hours{hours::zero()}.minutes()); assert(f_hms_hours{}.seconds() == f_hms_hours{hours::zero()}.seconds()); assert(f_hms_hours{}.subseconds() == f_hms_hours{hours::zero()}.subseconds()); + +} + +// Test LWG-4274 "The chrono::hh_mm_ss constructor is ill-formed for unsigned durations" +constexpr void constructor_unsigned_durations() { + duration unsigned_duration{37 + 1000 * (7 + 4 * 60 + 3 * 3600)}; + hh_mm_ss a{unsigned_duration}; + assert(!a.is_negative()); + assert(a.hours() == 3h); + assert(a.minutes() == 4min); + assert(a.seconds() == 7s); + assert(a.subseconds() == 37ms); } constexpr void is_negative() { @@ -210,6 +224,7 @@ constexpr bool test() { make12_24(); fractional_width(); constructor(); + constructor_unsigned_durations(); is_negative(); hour(); mins(); From 1c812b5be12474c03cddff0ae8e6b61e2feb67ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Fri, 21 Nov 2025 10:44:57 +0100 Subject: [PATCH 02/11] Enable construction of chrono::hh_mm_ss from chrono::duration with unsigned rep. --- stl/inc/chrono | 20 +++++++++++++------ .../test.cpp | 1 - 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index deed0e8b51f..1a29c773e7e 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -1634,18 +1634,18 @@ namespace chrono { constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {} constexpr explicit hh_mm_ss(_Duration _Dur) : _Is_neg{_Dur < _Duration::zero()}, - _Hours{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO hours>(_CHRONO abs(_Dur))}, + _Hours{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO hours>(_ABS_D(_Dur, _Is_neg))}, _Mins{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO minutes>( - _CHRONO _Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur)))}, + _CHRONO _Remove_duration_part<_CHRONO hours>(_ABS_D(_Dur, _Is_neg)))}, _Secs{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO seconds>( _CHRONO _Remove_duration_part<_CHRONO minutes>( - _CHRONO _Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur))))} { + _CHRONO _Remove_duration_part<_CHRONO hours>(_ABS_D(_Dur, _Is_neg))))} { if constexpr (treat_as_floating_point_v) { // no need to deal with underflow here, because floating durations allow it - _Sub_secs = _CHRONO abs(_Dur) - hours() - minutes() - seconds(); + _Sub_secs = _ABS_D(_Dur, _Is_neg) - hours() - minutes() - seconds(); } else { - _Sub_secs = - _CHRONO duration_cast(_CHRONO _Remove_duration_part<_CHRONO seconds>(_CHRONO abs(_Dur))); + _Sub_secs = _CHRONO duration_cast( + _CHRONO _Remove_duration_part<_CHRONO seconds>(_ABS_D(_Dur, _Is_neg))); } } @@ -1674,6 +1674,14 @@ namespace chrono { } private: + _NODISCARD constexpr static _Duration _ABS_D(_Duration _Dur, bool _Is_neg) { + if constexpr (is_unsigned_v) { + return _Dur; + } else { + return _Is_neg ? -_Dur : _Dur; + } + } + bool _Is_neg; _CHRONO hours _Hours; _CHRONO minutes _Mins; diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp index 5360aff1874..debeff6e52b 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp @@ -105,7 +105,6 @@ constexpr void constructor() { assert(f_hms_hours{}.minutes() == f_hms_hours{hours::zero()}.minutes()); assert(f_hms_hours{}.seconds() == f_hms_hours{hours::zero()}.seconds()); assert(f_hms_hours{}.subseconds() == f_hms_hours{hours::zero()}.subseconds()); - } // Test LWG-4274 "The chrono::hh_mm_ss constructor is ill-formed for unsigned durations" From 354c677ecdcf23ef6b086a165faf2dc0e3423115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Fri, 21 Nov 2025 12:35:00 +0100 Subject: [PATCH 03/11] Rename helper function _ABS_D to _Abs_if_needed. --- stl/inc/chrono | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 1a29c773e7e..5eaaa86b6bf 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -1634,18 +1634,18 @@ namespace chrono { constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {} constexpr explicit hh_mm_ss(_Duration _Dur) : _Is_neg{_Dur < _Duration::zero()}, - _Hours{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO hours>(_ABS_D(_Dur, _Is_neg))}, + _Hours{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO hours>(_Abs_if_needed(_Dur, _Is_neg))}, _Mins{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO minutes>( - _CHRONO _Remove_duration_part<_CHRONO hours>(_ABS_D(_Dur, _Is_neg)))}, + _CHRONO _Remove_duration_part<_CHRONO hours>(_Abs_if_needed(_Dur, _Is_neg)))}, _Secs{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO seconds>( _CHRONO _Remove_duration_part<_CHRONO minutes>( - _CHRONO _Remove_duration_part<_CHRONO hours>(_ABS_D(_Dur, _Is_neg))))} { + _CHRONO _Remove_duration_part<_CHRONO hours>(_Abs_if_needed(_Dur, _Is_neg))))} { if constexpr (treat_as_floating_point_v) { // no need to deal with underflow here, because floating durations allow it - _Sub_secs = _ABS_D(_Dur, _Is_neg) - hours() - minutes() - seconds(); + _Sub_secs = _Abs_if_needed(_Dur, _Is_neg) - hours() - minutes() - seconds(); } else { _Sub_secs = _CHRONO duration_cast( - _CHRONO _Remove_duration_part<_CHRONO seconds>(_ABS_D(_Dur, _Is_neg))); + _CHRONO _Remove_duration_part<_CHRONO seconds>(_Abs_if_needed(_Dur, _Is_neg))); } } @@ -1674,7 +1674,7 @@ namespace chrono { } private: - _NODISCARD constexpr static _Duration _ABS_D(_Duration _Dur, bool _Is_neg) { + _NODISCARD constexpr static _Duration _Abs_if_needed(_Duration _Dur, bool _Is_neg) { if constexpr (is_unsigned_v) { return _Dur; } else { From ab7870047b1c180efc7c4473d91099b51281f1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Michal?= Date: Sun, 23 Nov 2025 13:30:54 +0100 Subject: [PATCH 04/11] Add tests from other issues related to ill-formed construction of hh_mm_ss from unsigned duration. --- .../P0355R7_calendars_and_time_zones_formatting/test.cpp | 9 +++++++++ .../tests/P0355R7_calendars_and_time_zones_hms/test.cpp | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp index ad371a4d257..24b2cf6a754 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp @@ -1061,7 +1061,16 @@ void test_locale() { assert(stream(year_month_weekday_last{2021y / May / Tuesday[last]}) == STR("2021/Mai/Di[last]")); } +void test_unsigned_sys_time_format_after_LWG_4274() { + const sys_time> tp{}; + const string s = format("{:%Y-%m-%d %H:%M:%S}\n", tp); + cout << s; +} + void test() { + + test_unsigned_sys_time_format_after_LWG_4274(); + test_parse_conversion_spec(); test_parse_conversion_spec(); diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp index debeff6e52b..b047002b21f 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp @@ -116,6 +116,10 @@ constexpr void constructor_unsigned_durations() { assert(a.minutes() == 4min); assert(a.seconds() == 7s); assert(a.subseconds() == 37ms); + + // Reproducing example from issue #5569 + std::chrono::duration dur{1}; + [[maybe_unused]] auto hms = std::chrono::hh_mm_ss(dur); } constexpr void is_negative() { From b9a516e609dfc67c6a03ef306bdcb851400ac70a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 24 Nov 2025 10:10:30 -0800 Subject: [PATCH 05/11] `constexpr static` => `static constexpr` --- stl/inc/chrono | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 5eaaa86b6bf..85ef562b804 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -1674,7 +1674,7 @@ namespace chrono { } private: - _NODISCARD constexpr static _Duration _Abs_if_needed(_Duration _Dur, bool _Is_neg) { + _NODISCARD static constexpr _Duration _Abs_if_needed(_Duration _Dur, bool _Is_neg) { if constexpr (is_unsigned_v) { return _Dur; } else { From 4a66a67fd65ce2e1db64515f309ead461f1f6a02 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 24 Nov 2025 10:12:51 -0800 Subject: [PATCH 06/11] Drop unnecessary qualification. --- tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp index b047002b21f..2b00e26c349 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp @@ -118,8 +118,8 @@ constexpr void constructor_unsigned_durations() { assert(a.subseconds() == 37ms); // Reproducing example from issue #5569 - std::chrono::duration dur{1}; - [[maybe_unused]] auto hms = std::chrono::hh_mm_ss(dur); + duration dur{1}; + [[maybe_unused]] auto hms = hh_mm_ss(dur); } constexpr void is_negative() { From 9613cf91c70f1f935dd9ae77cadb1f5e76dcf663 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 24 Nov 2025 10:16:15 -0800 Subject: [PATCH 07/11] Use direct-init. --- tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp index 2b00e26c349..cdf72fa5ff9 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp @@ -119,7 +119,7 @@ constexpr void constructor_unsigned_durations() { // Reproducing example from issue #5569 duration dur{1}; - [[maybe_unused]] auto hms = hh_mm_ss(dur); + hh_mm_ss hms{dur}; } constexpr void is_negative() { From 25bfcc9e9b6f8611dd06af5a25645afbb03deca3 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 24 Nov 2025 10:18:33 -0800 Subject: [PATCH 08/11] Cite GitHub issue properly. --- tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp index cdf72fa5ff9..243c9e85134 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp @@ -117,7 +117,7 @@ constexpr void constructor_unsigned_durations() { assert(a.seconds() == 7s); assert(a.subseconds() == 37ms); - // Reproducing example from issue #5569 + // Reproducing example from GH-5569 ": Cannot construct an hh_mm_ss object from an unsigned duration" duration dur{1}; hh_mm_ss hms{dur}; } From f37a1a86b328d03d4ce87d9024563d95d92a11fc Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 24 Nov 2025 10:24:14 -0800 Subject: [PATCH 09/11] Assert instead of cout, drop newline. --- .../P0355R7_calendars_and_time_zones_formatting/test.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp index 24b2cf6a754..abb71a2505b 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp @@ -1063,12 +1063,11 @@ void test_locale() { void test_unsigned_sys_time_format_after_LWG_4274() { const sys_time> tp{}; - const string s = format("{:%Y-%m-%d %H:%M:%S}\n", tp); - cout << s; + const string s = format("{:%Y-%m-%d %H:%M:%S}", tp); + assert(s == "1970-01-01 00:00:00"); } void test() { - test_unsigned_sys_time_format_after_LWG_4274(); test_parse_conversion_spec(); From c7450b67dbcd879b7d181dfc711151dfc56649d9 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 24 Nov 2025 10:32:41 -0800 Subject: [PATCH 10/11] Inspect hms. --- tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp index 243c9e85134..6e10d498e1f 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp @@ -120,6 +120,8 @@ constexpr void constructor_unsigned_durations() { // Reproducing example from GH-5569 ": Cannot construct an hh_mm_ss object from an unsigned duration" duration dur{1}; hh_mm_ss hms{dur}; + assert(!hms.is_negative()); + assert(hms.seconds() == 1s); } constexpr void is_negative() { From 0ca1c0bf2d390fda1d1cb56d4f2f006c298c4640 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 24 Nov 2025 10:34:11 -0800 Subject: [PATCH 11/11] Add scopes to prevent mistakes. --- .../test.cpp | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp index 6e10d498e1f..a23a6845824 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp @@ -109,19 +109,23 @@ constexpr void constructor() { // Test LWG-4274 "The chrono::hh_mm_ss constructor is ill-formed for unsigned durations" constexpr void constructor_unsigned_durations() { - duration unsigned_duration{37 + 1000 * (7 + 4 * 60 + 3 * 3600)}; - hh_mm_ss a{unsigned_duration}; - assert(!a.is_negative()); - assert(a.hours() == 3h); - assert(a.minutes() == 4min); - assert(a.seconds() == 7s); - assert(a.subseconds() == 37ms); - - // Reproducing example from GH-5569 ": Cannot construct an hh_mm_ss object from an unsigned duration" - duration dur{1}; - hh_mm_ss hms{dur}; - assert(!hms.is_negative()); - assert(hms.seconds() == 1s); + { + duration unsigned_duration{37 + 1000 * (7 + 4 * 60 + 3 * 3600)}; + hh_mm_ss a{unsigned_duration}; + assert(!a.is_negative()); + assert(a.hours() == 3h); + assert(a.minutes() == 4min); + assert(a.seconds() == 7s); + assert(a.subseconds() == 37ms); + } + + { + // Reproducing example from GH-5569 ": Cannot construct an hh_mm_ss object from an unsigned duration" + duration dur{1}; + hh_mm_ss hms{dur}; + assert(!hms.is_negative()); + assert(hms.seconds() == 1s); + } } constexpr void is_negative() {