On Thu, Jul 30, 2026 at 1:41 PM Jonathan Wakely <[email protected]> wrote:

> When parsing a time with %R or %T we should ignore out of range hours
> and minutes if the type being parsed doesn't need them, e.g. when
> parsing a chrono::year_month_day from "2026-07-29 99:99:99" we do not
> set failbit, and should continue parsing after the invalid hours and
> minutes.
>
> Because we were short circuiting as soon as we saw "99" (in either
> field) we didn't parse to the end of the %R or %T field, and then could
> set failbit if there were any subsequent characters or flags to parse.
>
> The fix is to only short-circuit when setting failbit, and continue
> parsing otherwise.
>
> libstdc++-v3/ChangeLog:
>
>         PR libstdc++/126364
>         * include/bits/chrono_io.h (_Parser::operator()) <R>: Only break
>         early when setting failbit.
>         * testsuite/std/time/parse/126364.cc: New test.
> ---
>
> Tested x86_64-linux.
>
LGTM with only one small change.

>
> This should be backported to 14, 15 and 16 as well.
>
Agreed; this is OK to backport as the patch is trivial.
However, this is C++20 feature, so we do not strictly need to if I
understand
correctly.

>
>  libstdc++-v3/include/bits/chrono_io.h         | 12 ++++---
>  .../testsuite/std/time/parse/126364.cc        | 34 +++++++++++++++++++
>  2 files changed, 42 insertions(+), 4 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/std/time/parse/126364.cc
>
> diff --git a/libstdc++-v3/include/bits/chrono_io.h
> b/libstdc++-v3/include/bits/chrono_io.h
> index c5170368f82f..51e07ff55593 100644
> --- a/libstdc++-v3/include/bits/chrono_io.h
> +++ b/libstdc++-v3/include/bits/chrono_io.h
> @@ -4737,8 +4737,10 @@ namespace __detail
>                       if (__val == -1 || __val > 23) [[unlikely]]
>                         {
>
You do not need these braces anymore, and can put it directly under the
above if
(We want to merge them into one, as the condition mentioned above is not
unlikely)

>                           if ((_M_need & _ChronoParts::_TimeOfDay) != 0)
> -                           __err |= ios_base::failbit;
> -                         break;
> +                           {
> +                             __err |= ios_base::failbit;
> +                             break;
> +                           }
>                         }
>                       if (!__read_chr(':')) [[unlikely]]
>                         break;
> @@ -4748,8 +4750,10 @@ namespace __detail
>                       if (__val == -1 || __val > 60) [[unlikely]]
>                         {
>
Same here.

>                           if ((_M_need & _ChronoParts::_TimeOfDay) != 0)
> -                           __err |= ios_base::failbit;
> -                         break;
> +                           {
> +                             __err |= ios_base::failbit;
> +                             break;
> +                           }
>                         }
>                       __min = minutes(__val);
>
> diff --git a/libstdc++-v3/testsuite/std/time/parse/126364.cc
> b/libstdc++-v3/testsuite/std/time/parse/126364.cc
> new file mode 100644
> index 000000000000..74a94a6b84b8
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/time/parse/126364.cc
> @@ -0,0 +1,34 @@
> +// { dg-do run { target c++20 } }
> +
> +// Bug 126364 - chrono::from_stream %T and %R short circuit on out of
> range
> +// values even when it doesn't fail the parse
> +
> +#include <chrono>
> +#include <sstream>
> +#include <testsuite_hooks.h>
> +
> +using namespace std::chrono;
> +
> +void
> +test_pr126364()
> +{
> +  auto check = [](const char* input, char const* fmt) {
> +      std::istringstream is(input);
> +      year_month_day ymd{};
> +      return from_stream(is, fmt, ymd).good() && ymd.ok();
> +  };
> +
> +  VERIFY( check("2019-09-05T25:36:57Z", "%FT%TZ") );  // hour 25 only
> +  VERIFY( check("2019-09-05T20:99:57Z", "%FT%TZ") );  // minute 99 only
> +  VERIFY( check("2019-09-05T20:36:99Z", "%FT%TZ") );  // second 99 only
> +  VERIFY( check("2019-09-05T25:36:57Z", "%FT%T") );   // hour 25 only, no
> Z
> +  VERIFY( check("2019-09-05T20:99:57Z", "%FT%T") );   // minute 99 only,
> no Z
> +  VERIFY( check("2019-09-05T20:36:99Z", "%FT%T") );   // second 99 only,
> no Z
> +  VERIFY( check("2019-09-05 25", "%F %H") );          // %H out of range
> alone
> +  VERIFY( check("2019-09-05 99", "%F %M") );          // %M out of range
> alone
> +}
> +
> +int main()
> +{
> +  test_pr126364();
> +}
> --
> 2.55.0
>
>

Reply via email to