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
prev parent 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).