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 <ja...@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