Tested x86_64-linux. Pushed to trunk. -- >8 --
We fail to diagnose an error and extract an incorrect time for cases like "25:59" >> parse("%H:%M", mins). The bad "25" hour value gets ignored (on the basis that we might not care about it if trying to extract something like a weekday or a month name), but then when we get to the end of the function we think we have a valid time from "59" and so the result is 00:59. The problem is that the '__bad_h' value is used for "no hour value read yet" as well as "bad hour value read". If we just set __h = __bad_h and continue, we can't tell later that we read an invalid hour. The fix is to set failbit early when we're trying to extract a time-of-day (e.g. duration or time_point) and we encounter an invalid hour, minute, or second value. We can still delay other error checking to the end. libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (_Parser::operator()): Set failbit early if invalid values are read when _M_need & _TimeOfDay is non-zero. * testsuite/std/time/parse.cc: Check that "25:59" cannot be parsed for "%H:%M". --- libstdc++-v3/include/bits/chrono_io.h | 53 +++++++++++++----------- libstdc++-v3/testsuite/std/time/parse.cc | 7 ++++ 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index f359571b4db..7352df095ff 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -3327,7 +3327,16 @@ namespace __detail __h = __bad_h; } else if (__c == 'H' && __val >= 0 && __val <= 23) - __h = hours(__val); + { + __h = hours(__val); + __h12 = __bad_h; + } + else + { + if (_M_need & _ChronoParts::_TimeOfDay) + __err |= ios_base::failbit; + break; + } } __parts |= _ChronoParts::_TimeOfDay; break; @@ -3392,9 +3401,8 @@ namespace __detail __min = minutes(__val); else { - __h = __bad_h; - __min = __bad_min; - __s = __bad_sec; + if (_M_need & _ChronoParts::_TimeOfDay) + __err |= ios_base::failbit; break; } } @@ -3481,33 +3489,31 @@ namespace __detail else { auto __val = __read_unsigned(2); - if (__val == -1 || __val > 23) + if (__val == -1 || __val > 23) [[unlikely]] { - __h = __bad_h; - __min = __bad_min; - __s = __bad_sec; - break; - } - if (!__read_chr(':')) - { - __err |= ios_base::failbit; + if (_M_need & _ChronoParts::_TimeOfDay) + __err |= ios_base::failbit; break; } + if (!__read_chr(':')) [[unlikely]] + break; __h = hours(__val); __val = __read_unsigned(2); - if (__val == -1 || __val > 60) + if (__val == -1 || __val > 60) [[unlikely]] { - __h = __bad_h; - __min = __bad_min; - __s = __bad_sec; + if (_M_need & _ChronoParts::_TimeOfDay) + __err |= ios_base::failbit; break; } __min = minutes(__val); - __parts |= _ChronoParts::_TimeOfDay; - - if (__c != 'T' || !__read_chr(':')) + if (__c == 'R') + { + __parts |= _ChronoParts::_TimeOfDay; + break; + } + else if (!__read_chr(':')) [[unlikely]] break; } [[fallthrough]]; @@ -3527,13 +3533,12 @@ namespace __detail ratio<1>>) { auto __val = __read_unsigned(__num ? __num : 2); - if (0 <= __val && __val <= 59) + if (0 <= __val && __val <= 59) [[likely]] __s = seconds(__val); else { - __h = __bad_h; - __min = __bad_min; - __s = __bad_sec; + if (_M_need & _ChronoParts::_TimeOfDay) + __err |= ios_base::failbit; break; } } diff --git a/libstdc++-v3/testsuite/std/time/parse.cc b/libstdc++-v3/testsuite/std/time/parse.cc index 46eb7f28c85..86222d59596 100644 --- a/libstdc++-v3/testsuite/std/time/parse.cc +++ b/libstdc++-v3/testsuite/std/time/parse.cc @@ -252,6 +252,13 @@ test_errors() VERIFY( is.eof() && ! is.fail() ); VERIFY( y == 2010y ); + min = -1min; + is.clear(); + is.str("25:59"); + is >> parse("%H:%M", min); // 25h is out of range and needed + VERIFY( is.fail() ); + VERIFY( min == -1min ); + is.clear(); is.str("328 00"); is >> parse("%3C %y", y); // 328 is out of range for %C (PR libstdc++/111162) -- 2.41.0