public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jonathan Wakely <jwakely@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>
Cc: "libstdc++" <libstdc++@gcc.gnu.org>,
	gcc Patches <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH] libstdc++: Some time_get fixes [PR78714]
Date: Fri, 10 Dec 2021 15:48:31 +0000	[thread overview]
Message-ID: <CACb0b4=nCaS9cDNijY0TA2Vnq-u0Qd7_89_7qLftCBg_hZPT6w@mail.gmail.com> (raw)
In-Reply-To: <20211209141953.GH2646553@tucnak>

On Thu, 9 Dec 2021 at 14:20, Jakub Jelinek wrote:
>
> Hi!
>
> The following patch is an attempt to fix various time_get related issues.
> Sorry, it is long...
>
> One of them is PR78714.  It seems _M_extract_via_format has been written
> with how strftime behaves in mind rather than how strptime behaves.
> There is a significant difference between the two, for strftime %a and %A
> behave differently etc., one emits an abbreviated name, the other full name.
> For strptime both should behave the same and accept both the full or
> abbreviated names.  This needed large changes in _M_extract_name, which
> was assuming the names are unique and names aren't prefixes of other names.
> The _M_extract_name changes allow to deal with those cases.  As can be
> seen in the new testcase, e.g. for %b and english locales we need to
> accept both Apr and April.  If we see Apr in the input, the code looks
> at whether there is end right after those 3 chars or if the next
> character doesn't match characters in the longer names; in that case
> it accepts the abbreviated name.  Otherwise, if the input has Apri, it
> commits to a longer name and fails if it isn't April.  This behavior is
> different from strptime, which for %bix and Aprix accepts it, but for
> an input iterator I'm afraid we can't do better, we can't go back (peek
> more than the current character).

Yes, I think that's the best we can do.

> Another case is that %d and %e in strptime should work the same, while
> previously the code was hardcoding that %d would be 01 to 31 and %e
>  1 to 31 (with leading 0 replaced by space).
> strptime POSIX 2009 documentation seems to suggest for numbers it should
> accept up to the specified number of digits rather than exactly that number
> of digits:
> The pattern "[x,y]" indicates that the value shall fall within the range
> given (both bounds being inclusive), and the maximum number of characters scanned
> shall be the maximum required to represent any value in the range without leading
> zeros.
> so by my reading "1:" is valid for "%H:".

I agree.

> The glibc strptime implementation actually skips any amount of whitespace
> in all the cases where a number is read, my current patch skips a single
> space at the start of %d/%e but not the others, but doesn't subtract the
> space length from the len characters.
> One option would be to do the leading whitespace skipping in _M_extract_num
> but take it into account how many digits can be read.
> This matters for " 12:" and "%H:", but not for " 12:" and " %H:"
> as in the latter case the space in the format string results in all the
> whitespace at the start to be consumed.
> Note, the allowing of a single digit rather than 2 changes a behavior in
> other ways, e.g. when seeing 40 in a number for range [1, 31] we reject
> it as before, but previously we'd keep *ret == '4' because it was assuming
> it has to be 2 digits and 40 isn't valid, so we know error already on the
> 4, but now we accept the 4 as value and fail iff the next format string
> doesn't match the 0.

Again, I think that's the best we can do. And I think that's fine. The
primary purpose of these interfaces is to read valid dates, not check
whether input forms a valid date or not.

If you want to be able to tentatively parse a date (or number, or
monetary quantity etc) and then try recovering and parsing it
differently (e.g. "if it doesn't parse as a date in format X, try
format Y") then you can copy the input to a new buffer and re-parse
it. We don't need to make that work by rewinding the input iterator or
trying to peek ahead until we know if the parse will succeed.

> Also, previously it wasn't really checking the number was in the right
> range, it would accept 00 for [1, 31] numbers, or would accept 39.

Doh.

>
> Another thing is that %I was parsing 12 as tm_hour 12 rather than as tm_hour 0
> like e.g. glibc does.
>
> Another thing is that %t was matching a single tab and %n a single newline,
> while strptime docs say it skips over whitespace (again, zero or more).
>
> Another thing is that %p wasn't handled at all, I think this was the main
> cause of

Yes, and that's PR 71367

> FAIL: 22_locale/time_get/get_time/char/2.cc execution test
> FAIL: 22_locale/time_get/get_time/char/wrapped_env.cc execution test
> FAIL: 22_locale/time_get/get_time/char/wrapped_locale.cc execution test
> FAIL: 22_locale/time_get/get_time/wchar_t/2.cc execution test
> FAIL: 22_locale/time_get/get_time/wchar_t/wrapped_env.cc execution test
> FAIL: 22_locale/time_get/get_time/wchar_t/wrapped_locale.cc execution test
> before this patch, because en_HK* locales do use %I and %p in it.
> The patch handles %p only if it follows %I (i.e. when the hour is parsed
> first), which is the more usual case (in glibc):
> grep '%I' localedata/locales/* | grep '%I.*%p' | wc -l
> 282
> grep '%I' localedata/locales/* | grep -v '%I.*%p' | wc -l
> 44
> grep '%I' localedata/locales/* | grep -v '%p' | wc -l
> 17
> The last case use %P instead of %p in t_fmt_ampm, not sure if that one
> is never used by strptime because %P isn't handled by strptime.
> Anyway, the right thing to handle even %p%I would be to pass some state
> around through all the _M_extract_via_format calls like glibc passes
>   struct __strptime_state
>   {
>     unsigned int have_I : 1;
>     unsigned int have_wday : 1;
>     unsigned int have_yday : 1;
>     unsigned int have_mon : 1;
>     unsigned int have_mday : 1;
>     unsigned int have_uweek : 1;
>     unsigned int have_wweek : 1;
>     unsigned int is_pm : 1;
>     unsigned int want_century : 1;
>     unsigned int want_era : 1;
>     unsigned int want_xday : 1;
>     enum ptime_locale_status decided : 2;
>     signed char week_no;
>     signed char century;
>     int era_cnt;
>   } s;
> around.  That is for the %p case used like:
>   if (s.have_I && s.is_pm)
>     tm->tm_hour += 12;
> during finalization, but handles tons of other cases which it is unclear
> if libstdc++ needs or doesn't need to handle, e.g. strptime if one
> specifies year and yday computes wday/mon/day from it, etc. basically for
> the redundant fields computes them from other fields if those have been
> parsed and are sufficient to determine it.
> To do this we'd need to change ABI for the _M_extract_via_format,
> though sure, we could add a wrapper around the new one with the old
> arguments that would just use a dummy state.  And we'd need a new
> _M_whatever finalizer that would do those post parsing tweaks.
>
> Also, %% wasn't handled.
>
> For a whitespace in the strings there was inconsistent behavior,
> _M_extract_via_format would require exactly that whitespace char (say
> matching space, or matching tab), while the caller follows what
> https://eel.is/c++draft/locale.time.get#members-8.5 says, that
> when encountering whitespace it skips whitespace in the format and
> then whitespace in the input if any.  I've changed _M_extract_via_format
> to skip whitespace in the input (looping over format isn't IMHO necessary,
> because next iteration of the loop will handle that too).
>
> Tested on x86_64-linux by make check-target-libstdc++-v3, ok for trunk
> if it passes full bootstrap/regtest?

Yes, thanks very much.

>
> For the new 3.cc testcases, I have included hopefully correctly
> corresponding C testcase using strptime in an attachment, and to the
> extent where it can be compared (e.g. strptime on failure just
> returns NULL, doesn't tell where it exactly stopped) I think the
> only difference is that
>   str = "Novembur";
>   format = "%bembur";
>   ret = strptime (str, format, &time);
> case where strptime accepts it but there is no way to do it with input
> operator.
>
> I admit I don't have libc++ or other STL libraries around to be able to
> check how much the new 3.cc matches or disagrees with other implementations.

You can just "dnf install clang libcxx" and then "clang++
-stdlib=libc++ ..." to use it.

But I can check that myself (probably not until next week though).
I'll also see what difference this makes for running the libc++ tests
against libstdc++.

Either way, it's a big improvement on what we have now, thanks!


>
> Now, the things not handled by this patch but which should be fixed (I
> probably need to go back to compiler work) or at least looked at:
>
> 1) seems %j, %r, %U, %w and %W aren't handled (not sure if all of them
>    are already in POSIX 2009 or some are later)

I think that's all in POSIX 2009.

> 2) I haven't touched the %y/%Y/%C and year handling stuff, that is
>    definitely not matching what POSIX 2009 says:
>        C       All  but the last two digits of the year {2}; leading zeros shall be permitted but shall not be required. A leading '+' or '−' character shall be permitted before
>                any leading zeros but shall not be required.
>        y       The  last  two  digits of the year. When format contains neither a C conversion specifier nor a Y conversion specifier, values in the range [69,99] shall refer to
>                years 1969 to 1999 inclusive and values in the range [00,68] shall refer to years 2000 to 2068 inclusive; leading zeros shall be permitted but shall  not  be  re‐
>                quired. A leading '+' or '−' character shall be permitted before any leading zeros but shall not be required.
>
>                Note:     It is expected that in a future version of this standard the default century inferred from a 2-digit year will change. (This would apply to all commands
>                          accepting a 2-digit year as input.)
>        Y       The full year {4}; leading zeros shall be permitted but shall not be required. A leading '+' or '−' character shall be permitted  before  any  leading  zeros  but
>                shall not be required.
>    I've tried to avoid making changes to _M_extract_num for these as well
>    to keep current status quo (the __len == 4 cases).  One thing is what
>    to do for things with %C %y and/or %Y in the formats, another thing
>    is what to do in the methods that directly perform _M_extract_num
>    for year
> 3) the above question what to do for leading whitespace of any numbers
>    being parsed
> 4) the %p%I issue mentioned above and generally what to do if we
>    pass state and have finalizers at the end of parsing
> 5) _M_extract_via_format is also inconsistent with its callers on handling
>    the non-whitespace characters in between format specifiers, the caller
>    follows https://eel.is/c++draft/locale.time.get#members-8.6 and does
>    case insensitive comparison:
>           // TODO real case-insensitive comparison
>           else if (__ctype.tolower(*__s) == __ctype.tolower(*__fmt) ||
>                    __ctype.toupper(*__s) == __ctype.toupper(*__fmt))
>    while _M_extract_via_format only compares exact characters:
>               // Verify format and input match, extract and discard.
>               if (__format[__i] == *__beg)
>                 ++__beg;
>    (another question is if there is a better way how to do real
>    case-insensitive comparison of 2 characters and whether we e.g. need
>    to handle the Turkish i/İ and ı/I which have different number of bytes
>    in UTF-8)
> 6) _M_extract_name does something weird for case-sensitivity,
>       // NB: Some of the locale data is in the form of all lowercase
>       // names, and some is in the form of initially-capitalized
>       // names. Look for both.
>       if (__beg != __end)
>    and
>             if (__c == __names[__i1][0]
>                 || __c == __ctype.toupper(__names[__i1][0]))
>    for the first letter while just
>         __name[__pos] == *__beg
>    on all the following letters.  strptime says:
>    In case a text string (such as the name of a day of the week or a month
>    name) is to be matched, the comparison is case insensitive.
>    so supposedly all the _M_extract_name comparisons should be case
>    insensitive.
>
> 2021-12-09  Jakub Jelinek  <jakub@redhat.com>
>
>         PR libstdc++/78714
>         * include/bits/locale_facets_nonio.tcc (_M_extract_via_format):
>         Mention in function comment it interprets strptime format string
>         rather than strftime.  Handle %a and %A the same by accepting both
>         full and abbreviated names.  Similarly handle %h, %b and %B the same.
>         Handle %d and %e the same by accepting possibly optional single space
>         and 1 or 2 digits.  For %I store tm_hour 0 instead of tm_hour 12.  For
>         %t and %n skip any whitespace.  Handle %p and %%.  For whitespace in
>         the string skip any whitespace.
>         (_M_extract_num): For __len == 2 accept 1 or 2 digits rather than
>         always 2.  Don't punt early if __value * __mult is larget than __max
>         or smaller than __min - __mult, instead punt if __value > __max.
>         At the end verify __value is in between __min and __max and punt
>         otherwise.
>         (_M_extract_name): Allow non-unique names or names which are prefixes
>         of other names.  Don't recompute lengths of names for every character.
>         * testsuite/22_locale/time_get/get/char/3.cc: New test.
>         * testsuite/22_locale/time_get/get/wchar_t/3.cc: New test.
>         * testsuite/22_locale/time_get/get_date/char/12791.cc (test01): Use
>         62 instead 60 and expect 6 to be accepted and thus *ret01 == '2'.
>         * testsuite/22_locale/time_get/get_date/wchar_t/12791.cc (test01):
>         Similarly.
>         * testsuite/22_locale/time_get/get_time/char/2.cc (test02): Add " PM"
>         to the string.
>         * testsuite/22_locale/time_get/get_time/char/5.cc (test01): Expect
>         tm_hour 1 rather than 0.
>         * testsuite/22_locale/time_get/get_time/wchar_t/2.cc (test02): Add
>         " PM" to the string.
>         * testsuite/22_locale/time_get/get_time/wchar_t/5.cc (test01): Expect
>         tm_hour 1 rather than 0.
>
> --- libstdc++-v3/include/bits/locale_facets_nonio.tcc.jj        2021-01-05 00:13:58.292297226 +0100
> +++ libstdc++-v3/include/bits/locale_facets_nonio.tcc   2021-12-09 12:41:53.117583447 +0100
> @@ -684,7 +684,7 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>      time_get<_CharT, _InIter>::do_date_order() const
>      { return time_base::no_order; }
>
> -  // Expand a strftime format string and parse it.  E.g., do_get_date() may
> +  // Expand a strptime format string and parse it.  E.g., do_get_date() may
>    // pass %m/%d/%Y => extracted characters.
>    template<typename _CharT, typename _InIter>
>      _InIter
> @@ -714,41 +714,27 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>                   const char* __cs;
>                   _CharT __wcs[10];
>                 case 'a':
> -                 // Abbreviated weekday name [tm_wday]
> -                 const char_type*  __days1[7];
> -                 __tp._M_days_abbreviated(__days1);
> -                 __beg = _M_extract_name(__beg, __end, __mem, __days1,
> -                                         7, __io, __tmperr);
> -                 if (!__tmperr)
> -                   __tm->tm_wday = __mem;
> -                 break;
>                 case 'A':
> -                 // Weekday name [tm_wday].
> -                 const char_type*  __days2[7];
> -                 __tp._M_days(__days2);
> -                 __beg = _M_extract_name(__beg, __end, __mem, __days2,
> -                                         7, __io, __tmperr);
> +                 // Weekday name (possibly abbreviated) [tm_wday]
> +                 const char_type*  __days[14];
> +                 __tp._M_days(&__days[0]);
> +                 __tp._M_days_abbreviated(&__days[7]);
> +                 __beg = _M_extract_name(__beg, __end, __mem, __days,
> +                                         14, __io, __tmperr);
>                   if (!__tmperr)
> -                   __tm->tm_wday = __mem;
> +                   __tm->tm_wday = __mem % 7;
>                   break;
>                 case 'h':
>                 case 'b':
> -                 // Abbreviated month name [tm_mon]
> -                 const char_type*  __months1[12];
> -                 __tp._M_months_abbreviated(__months1);
> -                 __beg = _M_extract_name(__beg, __end, __mem,
> -                                         __months1, 12, __io, __tmperr);
> -                 if (!__tmperr)
> -                   __tm->tm_mon = __mem;
> -                 break;
>                 case 'B':
> -                 // Month name [tm_mon].
> -                 const char_type*  __months2[12];
> -                 __tp._M_months(__months2);
> +                 // Month name (possibly abbreviated) [tm_mon]
> +                 const char_type*  __months[24];
> +                 __tp._M_months(&__months[0]);
> +                 __tp._M_months_abbreviated(&__months[12]);
>                   __beg = _M_extract_name(__beg, __end, __mem,
> -                                         __months2, 12, __io, __tmperr);
> +                                         __months, 24, __io, __tmperr);
>                   if (!__tmperr)
> -                   __tm->tm_mon = __mem;
> +                   __tm->tm_mon = __mem % 12;
>                   break;
>                 case 'c':
>                   // Default time and date representation.
> @@ -758,21 +744,12 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>                                                 __tm, __dt[0]);
>                   break;
>                 case 'd':
> -                 // Day [01, 31]. [tm_mday]
> -                 __beg = _M_extract_num(__beg, __end, __mem, 1, 31, 2,
> -                                        __io, __tmperr);
> -                 if (!__tmperr)
> -                   __tm->tm_mday = __mem;
> -                 break;
>                 case 'e':
> -                 // Day [1, 31], with single digits preceded by
> -                 // space. [tm_mday]
> +                 // Day [1, 31]. [tm_mday]
>                   if (__ctype.is(ctype_base::space, *__beg))
> -                   __beg = _M_extract_num(++__beg, __end, __mem, 1, 9,
> -                                          1, __io, __tmperr);
> -                 else
> -                   __beg = _M_extract_num(__beg, __end, __mem, 10, 31,
> -                                          2, __io, __tmperr);
> +                   ++__beg;
> +                 __beg = _M_extract_num(__beg, __end, __mem, 1, 31, 2,
> +                                        __io, __tmperr);
>                   if (!__tmperr)
>                     __tm->tm_mday = __mem;
>                   break;
> @@ -795,7 +772,7 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>                   __beg = _M_extract_num(__beg, __end, __mem, 1, 12, 2,
>                                          __io, __tmperr);
>                   if (!__tmperr)
> -                   __tm->tm_hour = __mem;
> +                   __tm->tm_hour = __mem % 12;
>                   break;
>                 case 'm':
>                   // Month [01, 12]. [tm_mon]
> @@ -812,10 +789,22 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>                     __tm->tm_min = __mem;
>                   break;
>                 case 'n':
> -                 if (__ctype.narrow(*__beg, 0) == '\n')
> +               case 't':
> +                 while (__beg != __end
> +                        && __ctype.is(ctype_base::space, *__beg))
>                     ++__beg;
> -                 else
> -                   __tmperr |= ios_base::failbit;
> +                 break;
> +               case 'p':
> +                 // Locale's a.m. or p.m.
> +                 const char_type*  __ampm[2];
> +                 __tp._M_am_pm(&__ampm[0]);
> +                 if (!__ampm[0][0] || !__ampm[1][0])
> +                   break;
> +                 __beg = _M_extract_name(__beg, __end, __mem, __ampm,
> +                                         2, __io, __tmperr);
> +                 // FIXME: This only works if %I comes before %p.
> +                 if (!__tmperr && __mem)
> +                   __tm->tm_hour += 12;
>                   break;
>                 case 'R':
>                   // Equivalent to (%H:%M).
> @@ -836,12 +825,6 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>                   if (!__tmperr)
>                   __tm->tm_sec = __mem;
>                   break;
> -               case 't':
> -                 if (__ctype.narrow(*__beg, 0) == '\t')
> -                   ++__beg;
> -                 else
> -                   __tmperr |= ios_base::failbit;
> -                 break;
>                 case 'T':
>                   // Equivalent to (%H:%M:%S).
>                   __cs = "%H:%M:%S";
> @@ -899,11 +882,24 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>                   else
>                     __tmperr |= ios_base::failbit;
>                   break;
> +               case '%':
> +                 if (*__beg == __ctype.widen('%'))
> +                   ++__beg;
> +                 else
> +                   __tmperr |= ios_base::failbit;
> +                 break;
>                 default:
>                   // Not recognized.
>                   __tmperr |= ios_base::failbit;
>                 }
>             }
> +         else if (__ctype.is(ctype_base::space, __format[__i]))
> +           {
> +             // Skip any whitespace.
> +             while (__beg != __end
> +                    && __ctype.is(ctype_base::space, *__beg))
> +               ++__beg;
> +           }
>           else
>             {
>               // Verify format and input match, extract and discard.
> @@ -930,10 +926,6 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>        const locale& __loc = __io._M_getloc();
>        const ctype<_CharT>& __ctype = use_facet<ctype<_CharT> >(__loc);
>
> -      // As-is works for __len = 1, 2, 4, the values actually used.
> -      int __mult = __len == 2 ? 10 : (__len == 4 ? 1000 : 1);
> -
> -      ++__min;
>        size_t __i = 0;
>        int __value = 0;
>        for (; __beg != __end && __i < __len; ++__beg, (void)++__i)
> @@ -942,19 +934,20 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>           if (__c >= '0' && __c <= '9')
>             {
>               __value = __value * 10 + (__c - '0');
> -             const int __valuec = __value * __mult;
> -             if (__valuec > __max || __valuec + __mult < __min)
> +             if (__value > __max)
>                 break;
> -             __mult /= 10;
>             }
>           else
>             break;
>         }
> -      if (__i == __len)
> -       __member = __value;
>        // Special encoding for do_get_year, 'y', and 'Y' above.
> -      else if (__len == 4 && __i == 2)
> +      if (__len == 4 && __i == 2)
>         __member = __value - 100;
> +      else if (__len == 4 && __i == 4)
> +       __member = __value;
> +      else if (__len == 2 && __i && __i <= 2
> +              && __value >= __min && __value <= __max)
> +       __member = __value;
>        else
>         __err |= ios_base::failbit;
>
> @@ -962,7 +955,10 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>      }
>
>    // Assumptions:
> -  // All elements in __names are unique.
> +  // All elements in __names are unique, except if __indexlen is
> +  // even __names in the first half could be the same as corresponding
> +  // __names in the second half (May is abbreviated as May).  Some __names
> +  // elements could be prefixes of other __names elements.
>    template<typename _CharT, typename _InIter>
>      _InIter
>      time_get<_CharT, _InIter>::
> @@ -974,12 +970,15 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>        const locale& __loc = __io._M_getloc();
>        const ctype<_CharT>& __ctype = use_facet<ctype<_CharT> >(__loc);
>
> -      int* __matches = static_cast<int*>(__builtin_alloca(sizeof(int)
> -                                                         * __indexlen));
> +      size_t* __matches
> +       = static_cast<size_t*>(__builtin_alloca(2 * sizeof(size_t)
> +                                               * __indexlen));
> +      size_t* __lengths = __matches + __indexlen;
>        size_t __nmatches = 0;
>        size_t __pos = 0;
>        bool __testvalid = true;
>        const char_type* __name;
> +      bool __begupdated = false;
>
>        // Look for initial matches.
>        // NB: Some of the locale data is in the form of all lowercase
> @@ -991,26 +990,88 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>           for (size_t __i1 = 0; __i1 < __indexlen; ++__i1)
>             if (__c == __names[__i1][0]
>                 || __c == __ctype.toupper(__names[__i1][0]))
> -             __matches[__nmatches++] = __i1;
> +             {
> +               __lengths[__nmatches]
> +                 = __traits_type::length(__names[__i1]);
> +               __matches[__nmatches++] = __i1;
> +             }
>         }
>
>        while (__nmatches > 1)
>         {
>           // Find smallest matching string.
> -         size_t __minlen = __traits_type::length(__names[__matches[0]]);
> +         size_t __minlen = __lengths[0];
>           for (size_t __i2 = 1; __i2 < __nmatches; ++__i2)
> -           __minlen = std::min(__minlen,
> -                             __traits_type::length(__names[__matches[__i2]]));
> -         ++__beg;
> +           __minlen = std::min(__minlen, __lengths[__i2]);
>           ++__pos;
> +         ++__beg;
> +         if (__pos == __minlen)
> +           {
> +             // If some match has remaining length of 0,
> +             // need to decide if any match with remaining
> +             // length non-zero matches the next character.
> +             // If so, remove all matches with remaining length
> +             // 0 from consideration, otherwise keep only matches
> +             // with remaining length 0.
> +             bool __match_longer = false;
> +
> +             if (__beg != __end)
> +               for (size_t __i3 = 0; __i3 < __nmatches; ++__i3)
> +                 {
> +                   __name = __names[__matches[__i3]];
> +                   if (__lengths[__i3] > __pos && (__name[__pos] == *__beg))
> +                     {
> +                       __match_longer = true;
> +                       break;
> +                     }
> +                 }
> +             for (size_t __i4 = 0; __i4 < __nmatches;)
> +               if (__match_longer == (__lengths[__i4] == __pos))
> +                 {
> +                   __matches[__i4] = __matches[--__nmatches];
> +                   __lengths[__i4] = __lengths[__nmatches];
> +                 }
> +               else
> +                 ++__i4;
> +             if (__match_longer)
> +               {
> +                 __minlen = __lengths[0];
> +                 for (size_t __i5 = 1; __i5 < __nmatches; ++__i5)
> +                   __minlen = std::min(__minlen, __lengths[__i5]);
> +               }
> +             else
> +               {
> +                 // Deal with May being full as well as abbreviated month
> +                 // name.  Pick the smaller index.
> +                 if (__nmatches == 2 && (__indexlen & 1) == 0)
> +                   {
> +                     if (__matches[0] < __indexlen / 2)
> +                       {
> +                         if (__matches[1] == __matches[0] + __indexlen / 2)
> +                           __nmatches = 1;
> +                       }
> +                     else if (__matches[1] == __matches[0] - __indexlen / 2)
> +                       {
> +                         __matches[0] = __matches[1];
> +                         __lengths[0] = __lengths[1];
> +                         __nmatches = 1;
> +                       }
> +                   }
> +                 __begupdated = true;
> +                 break;
> +               }
> +           }
>           if (__pos < __minlen && __beg != __end)
> -           for (size_t __i3 = 0; __i3 < __nmatches;)
> +           for (size_t __i6 = 0; __i6 < __nmatches;)
>               {
> -               __name = __names[__matches[__i3]];
> +               __name = __names[__matches[__i6]];
>                 if (!(__name[__pos] == *__beg))
> -                 __matches[__i3] = __matches[--__nmatches];
> +                 {
> +                   __matches[__i6] = __matches[--__nmatches];
> +                   __lengths[__i6] = __lengths[__nmatches];
> +                 }
>                 else
> -                 ++__i3;
> +                 ++__i6;
>               }
>           else
>             break;
> @@ -1019,10 +1080,13 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
>        if (__nmatches == 1)
>         {
>           // Make sure found name is completely extracted.
> -         ++__beg;
> -         ++__pos;
> +         if (!__begupdated)
> +           {
> +             ++__beg;
> +             ++__pos;
> +           }
>           __name = __names[__matches[0]];
> -         const size_t __len = __traits_type::length(__name);
> +         const size_t __len = __lengths[0];
>           while (__pos < __len && __beg != __end && __name[__pos] == *__beg)
>             ++__beg, (void)++__pos;
>
> --- libstdc++-v3/testsuite/22_locale/time_get/get/char/3.cc.jj  2021-12-09 11:10:32.472129017 +0100
> +++ libstdc++-v3/testsuite/22_locale/time_get/get/char/3.cc     2021-12-09 13:11:04.103251278 +0100
> @@ -0,0 +1,356 @@
> +// { dg-do run { target c++11 } }
> +
> +// Copyright (C) 2021 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library.  This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +// GNU General Public License for more details.
> +
> +// You should have received a copy of the GNU General Public License along
> +// with this library; see the file COPYING3.  If not see
> +// <http://www.gnu.org/licenses/>.
> +
> +#include <locale>
> +#include <sstream>
> +#include <iterator>
> +#include <testsuite_hooks.h>
> +
> +void
> +test01()
> +{
> +  using namespace std;
> +
> +  locale loc_c = locale::classic();
> +
> +  istringstream iss;
> +  iss.imbue(loc_c);
> +  const time_get<char>& tget = use_facet<time_get<char>>(iss.getloc());
> +  typedef istreambuf_iterator<char> iter;
> +  const iter end;
> +
> +  tm time;
> +  ios_base::iostate err = ios_base::badbit;
> +
> +  // PR78714 tests
> +  iss.str("Mon");
> +  string format = "%a";
> +  auto ret = tget.get(iter(iss), end, iss, err, &time,
> +                     format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_wday == 1 );
> +
> +  iss.str("Tue ");
> +  format = "%a";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret != end && *ret == ' ' );
> +  VERIFY( time.tm_wday == 2 );
> +
> +  iss.str("Wednesday");
> +  format = "%a";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_wday == 3 );
> +
> +  iss.str("Thu");
> +  format = "%A";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_wday == 4 );
> +
> +  iss.str("Fri ");
> +  format = "%A";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret != end && *ret == ' ' );
> +  VERIFY( time.tm_wday == 5 );
> +
> +  iss.str("Saturday");
> +  format = "%A";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_wday == 6 );
> +
> +  iss.str("Feb");
> +  format = "%b";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 1 );
> +
> +  iss.str("Mar ");
> +  format = "%b";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret != end && *ret == ' ' );
> +  VERIFY( time.tm_mon == 2 );
> +
> +  iss.str("April");
> +  format = "%b";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 3 );
> +
> +  iss.str("May");
> +  format = "%B";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 4 );
> +
> +  iss.str("Jun ");
> +  format = "%B";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret != end && *ret == ' ' );
> +  VERIFY( time.tm_mon == 5 );
> +
> +  iss.str("July");
> +  format = "%B";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 6 );
> +
> +  iss.str("Aug");
> +  format = "%h";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 7 );
> +
> +  iss.str("May ");
> +  format = "%h";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret != end && *ret == ' ' );
> +  VERIFY( time.tm_mon == 4 );
> +
> +  iss.str("October");
> +  format = "%h";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 9 );
> +
> +  // Other tests.
> +  iss.str(" 1.");
> +  format = "%d.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 1 );
> +
> +  iss.str("2.");
> +  format = "%d.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 2 );
> +
> +  iss.str("03.");
> +  format = "%d.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 3 );
> +
> +  iss.str("0.");
> +  format = "%d.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit );
> +  VERIFY( ret != end && *ret == '.' );
> +
> +  iss.str("32.");
> +  format = "%d.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit );
> +  VERIFY( ret != end && *ret == '2' );
> +
> +  iss.str(" 4.");
> +  format = "%e.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 4 );
> +
> +  iss.str("5.");
> +  format = "%e.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 5 );
> +
> +  iss.str("06.");
> +  format = "%e.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 6 );
> +
> +  iss.str("0");
> +  format = "%e";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit|ios_base::eofbit );
> +  VERIFY( ret == end );
> +
> +  iss.str("35");
> +  format = "%e";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit );
> +  VERIFY( ret != end && *ret == '5' );
> +
> +  iss.str(" \t\t 02");
> +  format = "%t%m";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 1 );
> +
> +  iss.str(" \t \t 03");
> +  format = "%n%m";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 2 );
> +
> +  iss.str(" \t \t 4");
> +  format = " %m";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 3 );
> +
> +  iss.str(" \t \t 5");
> +  format = "\t%m";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 4 );
> +
> +  iss.str("12:00AM");
> +  format = "%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 0 );
> +  VERIFY( time.tm_min == 0 );
> +
> +  iss.str("12:37AM");
> +  format = "%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 0 );
> +  VERIFY( time.tm_min == 37 );
> +
> +  iss.str("01:25AM");
> +  format = "%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 1 );
> +  VERIFY( time.tm_min == 25 );
> +
> +  iss.str("12:00PM");
> +  format = "%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 12 );
> +  VERIFY( time.tm_min == 0 );
> +
> +  iss.str("12:42PM");
> +  format = "%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 12 );
> +  VERIFY( time.tm_min == 42 );
> +
> +  iss.str("07:23PM");
> +  format = "%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 19 );
> +  VERIFY( time.tm_min == 23 );
> +
> +  iss.str("17%20");
> +  format = "%H%%%M";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 17 );
> +  VERIFY( time.tm_min == 20 );
> +
> +  iss.str("24:30");
> +  format = "%H:%M";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit );
> +  VERIFY( ret != end && *ret == '4' );
> +
> +  // This one behaves differently from strptime, in a single
> +  // pass scaning we can't go back.
> +  iss.str("Novembur");
> +  format = "%bembur";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit );
> +  VERIFY( ret != end && *ret == 'u' );
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +  return 0;
> +}
> --- libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/3.cc.jj       2021-12-09 13:08:34.785398279 +0100
> +++ libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/3.cc  2021-12-09 13:14:01.012746470 +0100
> @@ -0,0 +1,356 @@
> +// { dg-do run { target c++11 } }
> +
> +// Copyright (C) 2021 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library.  This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +// GNU General Public License for more details.
> +
> +// You should have received a copy of the GNU General Public License along
> +// with this library; see the file COPYING3.  If not see
> +// <http://www.gnu.org/licenses/>.
> +
> +#include <locale>
> +#include <sstream>
> +#include <iterator>
> +#include <testsuite_hooks.h>
> +
> +void
> +test01()
> +{
> +  using namespace std;
> +
> +  locale loc_c = locale::classic();
> +
> +  wistringstream iss;
> +  iss.imbue(loc_c);
> +  const time_get<wchar_t>& tget = use_facet<time_get<wchar_t>>(iss.getloc());
> +  typedef istreambuf_iterator<wchar_t> iter;
> +  const iter end;
> +
> +  tm time;
> +  ios_base::iostate err = ios_base::badbit;
> +
> +  // PR78714 tests
> +  iss.str(L"Mon");
> +  wstring format = L"%a";
> +  auto ret = tget.get(iter(iss), end, iss, err, &time,
> +                     format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_wday == 1 );
> +
> +  iss.str(L"Tue L");
> +  format = L"%a";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret != end && *ret == L' ' );
> +  VERIFY( time.tm_wday == 2 );
> +
> +  iss.str(L"Wednesday");
> +  format = L"%a";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_wday == 3 );
> +
> +  iss.str(L"Thu");
> +  format = L"%A";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_wday == 4 );
> +
> +  iss.str(L"Fri L");
> +  format = L"%A";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret != end && *ret == L' ' );
> +  VERIFY( time.tm_wday == 5 );
> +
> +  iss.str(L"Saturday");
> +  format = L"%A";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_wday == 6 );
> +
> +  iss.str(L"Feb");
> +  format = L"%b";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 1 );
> +
> +  iss.str(L"Mar L");
> +  format = L"%b";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret != end && *ret == L' ' );
> +  VERIFY( time.tm_mon == 2 );
> +
> +  iss.str(L"April");
> +  format = L"%b";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 3 );
> +
> +  iss.str(L"May");
> +  format = L"%B";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 4 );
> +
> +  iss.str(L"Jun L");
> +  format = L"%B";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret != end && *ret == L' ' );
> +  VERIFY( time.tm_mon == 5 );
> +
> +  iss.str(L"July");
> +  format = L"%B";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 6 );
> +
> +  iss.str(L"Aug");
> +  format = L"%h";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 7 );
> +
> +  iss.str(L"May L");
> +  format = L"%h";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret != end && *ret == L' ' );
> +  VERIFY( time.tm_mon == 4 );
> +
> +  iss.str(L"October");
> +  format = L"%h";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 9 );
> +
> +  // Other tests.
> +  iss.str(L" 1.");
> +  format = L"%d.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 1 );
> +
> +  iss.str(L"2.");
> +  format = L"%d.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 2 );
> +
> +  iss.str(L"03.");
> +  format = L"%d.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 3 );
> +
> +  iss.str(L"0.");
> +  format = L"%d.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit );
> +  VERIFY( ret != end && *ret == L'.' );
> +
> +  iss.str(L"32.");
> +  format = L"%d.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit );
> +  VERIFY( ret != end && *ret == L'2' );
> +
> +  iss.str(L" 4.");
> +  format = L"%e.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 4 );
> +
> +  iss.str(L"5.");
> +  format = L"%e.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 5 );
> +
> +  iss.str(L"06.");
> +  format = L"%e.";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::goodbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mday == 6 );
> +
> +  iss.str(L"0");
> +  format = L"%e";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit|ios_base::eofbit );
> +  VERIFY( ret == end );
> +
> +  iss.str(L"35");
> +  format = L"%e";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit );
> +  VERIFY( ret != end && *ret == L'5' );
> +
> +  iss.str(L" \t\t 02");
> +  format = L"%t%m";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 1 );
> +
> +  iss.str(L" \t \t 03");
> +  format = L"%n%m";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 2 );
> +
> +  iss.str(L" \t \t 4");
> +  format = L" %m";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 3 );
> +
> +  iss.str(L" \t \t 5");
> +  format = L"\t%m";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_mon == 4 );
> +
> +  iss.str(L"12:00AM");
> +  format = L"%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 0 );
> +  VERIFY( time.tm_min == 0 );
> +
> +  iss.str(L"12:37AM");
> +  format = L"%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 0 );
> +  VERIFY( time.tm_min == 37 );
> +
> +  iss.str(L"01:25AM");
> +  format = L"%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 1 );
> +  VERIFY( time.tm_min == 25 );
> +
> +  iss.str(L"12:00PM");
> +  format = L"%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 12 );
> +  VERIFY( time.tm_min == 0 );
> +
> +  iss.str(L"12:42PM");
> +  format = L"%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 12 );
> +  VERIFY( time.tm_min == 42 );
> +
> +  iss.str(L"07:23PM");
> +  format = L"%I:%M%p";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 19 );
> +  VERIFY( time.tm_min == 23 );
> +
> +  iss.str(L"17%20");
> +  format = L"%H%%%M";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::eofbit );
> +  VERIFY( ret == end );
> +  VERIFY( time.tm_hour == 17 );
> +  VERIFY( time.tm_min == 20 );
> +
> +  iss.str(L"24:30");
> +  format = L"%H:%M";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit );
> +  VERIFY( ret != end && *ret == L'4' );
> +
> +  // This one behaves differently from strptime, in a single
> +  // pass scaning we can't go back.
> +  iss.str(L"Novembur");
> +  format = L"%bembur";
> +  ret = tget.get(iter(iss), end, iss, err, &time,
> +                format.data(), format.data()+format.size());
> +  VERIFY( err == ios_base::failbit );
> +  VERIFY( ret != end && *ret == L'u' );
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +  return 0;
> +}
> --- libstdc++-v3/testsuite/22_locale/time_get/get_date/char/12791.cc.jj 2021-01-05 00:13:58.426295719 +0100
> +++ libstdc++-v3/testsuite/22_locale/time_get/get_date/char/12791.cc    2021-12-09 13:02:02.498075474 +0100
> @@ -39,14 +39,14 @@ void test01()
>    const ios_base::iostate good = ios_base::goodbit;
>    ios_base::iostate errorstate = good;
>
> -  iss.str("60/04/71");
> +  iss.str("62/04/71");
>    iterator_type is_it01(iss);
>    tm time01;
>    errorstate = good;
>    iterator_type ret01 = tim_get.get_date(is_it01, end, iss, errorstate,
>                                          &time01);
>    VERIFY( errorstate == ios_base::failbit );
> -  VERIFY( *ret01 == '6' );
> +  VERIFY( *ret01 == '2' );
>
>    iss.str("04/38/71");
>    iterator_type is_it02(iss);
> --- libstdc++-v3/testsuite/22_locale/time_get/get_date/wchar_t/12791.cc.jj      2021-01-05 00:13:58.426295719 +0100
> +++ libstdc++-v3/testsuite/22_locale/time_get/get_date/wchar_t/12791.cc 2021-12-09 13:02:46.579437526 +0100
> @@ -40,14 +40,14 @@ void test01()
>    const ios_base::iostate good = ios_base::goodbit;
>    ios_base::iostate errorstate = good;
>
> -  iss.str(L"60/04/71");
> +  iss.str(L"62/04/71");
>    iterator_type is_it01(iss);
>    tm time01;
>    errorstate = good;
>    iterator_type ret01 = tim_get.get_date(is_it01, end, iss, errorstate,
>                                          &time01);
>    VERIFY( errorstate == ios_base::failbit );
> -  VERIFY( *ret01 == L'6' );
> +  VERIFY( *ret01 == L'2' );
>
>    iss.str(L"04/38/71");
>    iterator_type is_it02(iss);
> --- libstdc++-v3/testsuite/22_locale/time_get/get_time/char/2.cc.jj     2021-01-05 00:13:58.000000000 +0100
> +++ libstdc++-v3/testsuite/22_locale/time_get/get_time/char/2.cc        2021-12-08 15:18:42.586859294 +0100
> @@ -48,7 +48,7 @@ void test02()
>
>    // inspection of named locales, en_HK
>    iss.imbue(loc_hk);
> -  iss.str("12:00:00 PST");
> +  iss.str("12:00:00 PM PST");
>    // Hong Kong in California! Well, they have Paris in Vegas... this
>    // is all a little disney-esque anyway. Besides, you can get decent
>    // Dim Sum in San Francisco.
> --- libstdc++-v3/testsuite/22_locale/time_get/get_time/char/5.cc.jj     2021-01-05 00:13:58.000000000 +0100
> +++ libstdc++-v3/testsuite/22_locale/time_get/get_time/char/5.cc        2021-12-08 19:12:54.519198801 +0100
> @@ -52,7 +52,7 @@ void test01()
>    VERIFY( err == (failbit | eofbit) );
>    VERIFY( tm0.tm_sec == 0 );
>    VERIFY( tm0.tm_min == 0 );
> -  VERIFY( tm0.tm_hour == 0 );
> +  VERIFY( tm0.tm_hour == 1 );
>
>    const string str1 = "12:00:00 ";
>    iter_type end1 = tg.get_time(str1.begin(), str1.end(), iss, err, &tm1);
> --- libstdc++-v3/testsuite/22_locale/time_get/get_time/wchar_t/2.cc.jj  2021-01-05 00:13:58.427295707 +0100
> +++ libstdc++-v3/testsuite/22_locale/time_get/get_time/wchar_t/2.cc     2021-12-08 15:24:10.378202522 +0100
> @@ -48,7 +48,7 @@ void test02()
>
>    // inspection of named locales, en_HK
>    iss.imbue(loc_hk);
> -  iss.str(L"12:00:00 PST");
> +  iss.str(L"12:00:00 PM PST");
>    // Hong Kong in California! Well, they have Paris in Vegas... this
>    // is all a little disney-esque anyway. Besides, you can get decent
>    // Dim Sum in San Francisco.
> --- libstdc++-v3/testsuite/22_locale/time_get/get_time/wchar_t/5.cc.jj  2021-01-05 00:13:58.428295696 +0100
> +++ libstdc++-v3/testsuite/22_locale/time_get/get_time/wchar_t/5.cc     2021-12-08 19:14:32.826801803 +0100
> @@ -52,7 +52,7 @@ void test01()
>    VERIFY( err == (failbit | eofbit) );
>    VERIFY( tm0.tm_sec == 0 );
>    VERIFY( tm0.tm_min == 0 );
> -  VERIFY( tm0.tm_hour == 0 );
> +  VERIFY( tm0.tm_hour == 1 );
>
>    const wstring str1 = L"12:00:00 ";
>    iter_type end1 = tg.get_time(str1.begin(), str1.end(), iss, err, &tm1);
>
>         Jakub


      reply	other threads:[~2021-12-10 15:48 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-09 14:19 Jakub Jelinek
2021-12-10 15:48 ` Jonathan Wakely [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CACb0b4=nCaS9cDNijY0TA2Vnq-u0Qd7_89_7qLftCBg_hZPT6w@mail.gmail.com' \
    --to=jwakely@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jakub@redhat.com \
    --cc=libstdc++@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).