Skip to content

Commit fafda46

Browse files
Implement LWG-4274: The chrono::hh_mm_ss constructor is ill-formed for unsigned durations (#5893)
Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent e34ca44 commit fafda46

File tree

3 files changed

+46
-6
lines changed
  • stl/inc
  • tests/std/tests
    • P0355R7_calendars_and_time_zones_formatting
    • P0355R7_calendars_and_time_zones_hms

3 files changed

+46
-6
lines changed

stl/inc/chrono

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,18 +1634,18 @@ namespace chrono {
16341634
constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
16351635
constexpr explicit hh_mm_ss(_Duration _Dur)
16361636
: _Is_neg{_Dur < _Duration::zero()},
1637-
_Hours{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO hours>(_CHRONO abs(_Dur))},
1637+
_Hours{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO hours>(_Abs_if_needed(_Dur, _Is_neg))},
16381638
_Mins{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO minutes>(
1639-
_CHRONO _Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur)))},
1639+
_CHRONO _Remove_duration_part<_CHRONO hours>(_Abs_if_needed(_Dur, _Is_neg)))},
16401640
_Secs{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO seconds>(
16411641
_CHRONO _Remove_duration_part<_CHRONO minutes>(
1642-
_CHRONO _Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur))))} {
1642+
_CHRONO _Remove_duration_part<_CHRONO hours>(_Abs_if_needed(_Dur, _Is_neg))))} {
16431643
if constexpr (treat_as_floating_point_v<typename precision::rep>) {
16441644
// no need to deal with underflow here, because floating durations allow it
1645-
_Sub_secs = _CHRONO abs(_Dur) - hours() - minutes() - seconds();
1645+
_Sub_secs = _Abs_if_needed(_Dur, _Is_neg) - hours() - minutes() - seconds();
16461646
} else {
1647-
_Sub_secs =
1648-
_CHRONO duration_cast<precision>(_CHRONO _Remove_duration_part<_CHRONO seconds>(_CHRONO abs(_Dur)));
1647+
_Sub_secs = _CHRONO duration_cast<precision>(
1648+
_CHRONO _Remove_duration_part<_CHRONO seconds>(_Abs_if_needed(_Dur, _Is_neg)));
16491649
}
16501650
}
16511651

@@ -1674,6 +1674,14 @@ namespace chrono {
16741674
}
16751675

16761676
private:
1677+
_NODISCARD static constexpr _Duration _Abs_if_needed(_Duration _Dur, bool _Is_neg) {
1678+
if constexpr (is_unsigned_v<typename _Duration::rep>) {
1679+
return _Dur;
1680+
} else {
1681+
return _Is_neg ? -_Dur : _Dur;
1682+
}
1683+
}
1684+
16771685
bool _Is_neg;
16781686
_CHRONO hours _Hours;
16791687
_CHRONO minutes _Mins;

tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,15 @@ void test_locale() {
10611061
assert(stream(year_month_weekday_last{2021y / May / Tuesday[last]}) == STR("2021/Mai/Di[last]"));
10621062
}
10631063

1064+
void test_unsigned_sys_time_format_after_LWG_4274() {
1065+
const sys_time<duration<unsigned int>> tp{};
1066+
const string s = format("{:%Y-%m-%d %H:%M:%S}", tp);
1067+
assert(s == "1970-01-01 00:00:00");
1068+
}
1069+
10641070
void test() {
1071+
test_unsigned_sys_time_format_after_LWG_4274();
1072+
10651073
test_parse_conversion_spec<char>();
10661074
test_parse_conversion_spec<wchar_t>();
10671075

tests/std/tests/P0355R7_calendars_and_time_zones_hms/test.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <ratio>
88
#include <type_traits>
99

10+
// Extended to test LWG-4274 "The chrono::hh_mm_ss constructor is ill-formed for unsigned durations"
11+
1012
using namespace std;
1113
using namespace std::chrono;
1214

@@ -105,6 +107,27 @@ constexpr void constructor() {
105107
assert(f_hms_hours{}.subseconds() == f_hms_hours{hours::zero()}.subseconds());
106108
}
107109

110+
// Test LWG-4274 "The chrono::hh_mm_ss constructor is ill-formed for unsigned durations"
111+
constexpr void constructor_unsigned_durations() {
112+
{
113+
duration<unsigned long long, milli> unsigned_duration{37 + 1000 * (7 + 4 * 60 + 3 * 3600)};
114+
hh_mm_ss a{unsigned_duration};
115+
assert(!a.is_negative());
116+
assert(a.hours() == 3h);
117+
assert(a.minutes() == 4min);
118+
assert(a.seconds() == 7s);
119+
assert(a.subseconds() == 37ms);
120+
}
121+
122+
{
123+
// Reproducing example from GH-5569 "<chrono>: Cannot construct an hh_mm_ss object from an unsigned duration"
124+
duration<uint32_t> dur{1};
125+
hh_mm_ss hms{dur};
126+
assert(!hms.is_negative());
127+
assert(hms.seconds() == 1s);
128+
}
129+
}
130+
108131
constexpr void is_negative() {
109132
static_assert(noexcept(hms_hours{}.is_negative()));
110133
static_assert(noexcept(f_hms_hours{}.is_negative()));
@@ -210,6 +233,7 @@ constexpr bool test() {
210233
make12_24();
211234
fractional_width();
212235
constructor();
236+
constructor_unsigned_durations();
213237
is_negative();
214238
hour();
215239
mins();

0 commit comments

Comments
 (0)