public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-6410] libstdc++: Add %j, %U, %w, %W time_get support, fix %y, %Y, %C, %p [PR77760]
@ 2022-01-10 14:40 Jakub Jelinek
  0 siblings, 0 replies; only message in thread
From: Jakub Jelinek @ 2022-01-10 14:40 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:a8d3c98746098e2784be7144c1ccc9fcc34a0888

commit r12-6410-ga8d3c98746098e2784be7144c1ccc9fcc34a0888
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Mon Jan 10 15:38:47 2022 +0100

    libstdc++: Add %j, %U, %w, %W time_get support, fix %y, %Y, %C, %p [PR77760]
    
    glibc strptime passes around some state, what fields in struct tm have been
    set and what needs to be finalized through possibly recursive calls, and
    at the end performs various finalizations, like applying %p so that it
    works for both %I %p and %p %I orders, or applying century so that both
    %C %y and %y %C works, or computation of missing fields from others
    (e.g. from %Y and %j one can compute tm_mon, tm_mday and tm_wday,
    from %Y %U %w, %Y %W %w, %Y %U %a, or %Y %W %w one can compute
    tm_mon, tm_mday, tm_yday or e.g. from %Y %m %d one can compute tm_wday
    and tm_yday.
    
    As the finalization is quite large and doesn't need to be a template
    (doesn't depend on any iterators or char types), I've put it into libstdc++,
    and left some padding in the state struct, so that perhaps in the future we
    can track some more state without changing ABI.
    
    Unfortunately, there is an ugly problem that the standard mandates that
    get method calls the do_get virtual method and I don't see how we can
    cary on any state in between those calls (even if we did an ABI change
    for the facets, the methods are const, so that I think multiple threads
    could use the same time_get objects and we couldn't store state in there).
    
    There is a hack for that for GCC (seems to work with ICC too, doesn't work
    with clang++) if the do_get method isn't overriden we can pass the state
    around.
    
    For both do_get_year and per IRC discussions also for %y, the behavior is
    if 1-2 digits are parsed, the year is treated according to POSIX 2008 %y
    rules (0-68 is 2000-2068, 69-99 is 1969-1999), if 3-4 digits are parsed,
    it is treated as %Y.
    
    2022-01-10  Jakub Jelinek  <jakub@redhat.com>
    
            PR libstdc++/77760
            * include/bits/locale_facets_nonio.h (__time_get_state): New struct.
            (time_get::_M_extract_via_format): Declare new method with
            __time_get_state& as an extra argument.
            * include/bits/locale_facets_nonio.tcc (_M_extract_via_format): Add
            __state argument, set various fields in it while parsing.  Handle %j,
            %U, %w and %W, fix up handling of %y, %Y and %C, don't adjust tm_hour
            for %p immediately.  Add a wrapper around the method without the
            __state argument for backwards compatibility.
            (_M_extract_num): Remove all __len == 4 special cases.
            (time_get::do_get_time, time_get::do_get_date, time_get::do_get): Zero
            initialize __state, pass it to _M_extract_via_format and finalize it
            at the end.
            (do_get_year): For 1-2 digit parsed years, map 0-68 to 2000-2068,
            69-99 to 1969-1999.  For 3-4 digit parsed years use that as year.
            (get): If do_get isn't overloaded from the locale_facets_nonio.tcc
            version, don't call do_get but call _M_extract_via_format instead to
            pass around state.
            * config/abi/pre/gnu.ver (GLIBCXX_3.4.30): Export _M_extract_via_format
            with extra __time_get_state and __time_get_state::_M_finalize_state.
            * src/c++98/locale_facets.cc (is_leap, day_of_the_week,
            day_of_the_year): New functions in anon namespace.
            (mon_yday): New var in anon namespace.
            (__time_get_state::_M_finalize_state): Define.
            * testsuite/22_locale/time_get/get/char/4.cc: New test.
            * testsuite/22_locale/time_get/get/wchar_t/4.cc: New test.
            * testsuite/22_locale/time_get/get_year/char/1.cc (test01): Parse 197
            as year 197AD instead of error.
            * testsuite/22_locale/time_get/get_year/char/5.cc (test01): Parse 1 as
            year 2001 instead of error.
            * testsuite/22_locale/time_get/get_year/char/6.cc: New test.
            * testsuite/22_locale/time_get/get_year/wchar_t/1.cc (test01): Parse
            197 as year 197AD instead of error.
            * testsuite/22_locale/time_get/get_year/wchar_t/5.cc (test01): Parse
            1 as year 2001 instead of error.
            * testsuite/22_locale/time_get/get_year/wchar_t/6.cc: New test.

Diff:
---
 libstdc++-v3/config/abi/pre/gnu.ver                |  12 +-
 libstdc++-v3/include/bits/locale_facets_nonio.h    |  32 +++
 libstdc++-v3/include/bits/locale_facets_nonio.tcc  | 261 ++++++++++++++++++---
 libstdc++-v3/src/c++98/locale_facets.cc            | 110 +++++++++
 .../testsuite/22_locale/time_get/get/char/4.cc     | 243 +++++++++++++++++++
 .../testsuite/22_locale/time_get/get/wchar_t/4.cc  | 243 +++++++++++++++++++
 .../22_locale/time_get/get_year/char/1.cc          |   4 +-
 .../22_locale/time_get/get_year/char/5.cc          |   7 +-
 .../22_locale/time_get/get_year/char/6.cc          |  79 +++++++
 .../22_locale/time_get/get_year/wchar_t/1.cc       |   4 +-
 .../22_locale/time_get/get_year/wchar_t/5.cc       |   7 +-
 .../22_locale/time_get/get_year/wchar_t/6.cc       |  79 +++++++
 12 files changed, 1031 insertions(+), 50 deletions(-)

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index afd242b32aa..dedee8c1b0a 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -628,7 +628,7 @@ GLIBCXX_3.4 {
     _ZNSt8time_get*;
     _ZNKSt8time_getI[cw]St19istreambuf_iteratorI[cw]St11char_traitsI[cw]EEE1*;
     _ZNKSt8time_getI[cw]St19istreambuf_iteratorI[cw]St11char_traitsI[cw]EEE8*;
-    _ZNKSt8time_getI[cw]St19istreambuf_iteratorI[cw]St11char_traitsI[cw]EEE21*;
+    _ZNKSt8time_getI[cw]St19istreambuf_iteratorI[cw]St11char_traitsI[cw]EEE21*PK[cw];
 
     # std::time_get_byname
     _ZNSt15time_get_bynameI[cw]*EEEC[12]EPKc[jmy];
@@ -1887,7 +1887,9 @@ GLIBCXX_3.4.21 {
     _ZNKSt7__cxx119money_putI*;
     _ZNKSt7__cxx1110moneypunctI*;
     _ZNKSt7__cxx118numpunctI*;
-    _ZNKSt7__cxx118time_getI*;
+    _ZNKSt7__cxx118time_getI[cw]St19istreambuf_iteratorI[cw]St11char_traitsI[cw]EEE[13-9]*;
+    _ZNKSt7__cxx118time_getI[cw]St19istreambuf_iteratorI[cw]St11char_traitsI[cw]EEE21*PK[cw];
+    _ZNKSt7__cxx118time_getI[cw]St19istreambuf_iteratorI[cw]St11char_traitsI[cw]EEE24*;
 
     _ZSt9has_facetINSt7__cxx117collate*;
     _ZSt9has_facetINSt7__cxx118messages*;
@@ -2427,6 +2429,12 @@ GLIBCXX_3.4.30 {
     # Only defined #if ! __GXX_TYPEINFO_EQUALITY_INLINE
     _ZNKSt9type_info7__equalERKS_;
 
+    # std::time_get
+    _ZNKSt8time_getI[cw]St19istreambuf_iteratorI[cw]St11char_traitsI[cw]EEE21*__time_get_state;
+    _ZNKSt7__cxx118time_getI[cw]St19istreambuf_iteratorI[cw]St11char_traitsI[cw]EEE21*__time_get_state;
+
+    _ZNSt16__time_get_state17_M_finalize_stateEP2tm;
+
 } GLIBCXX_3.4.29;
 
 # Symbols in the support library (libsupc++) have their own tag.
diff --git a/libstdc++-v3/include/bits/locale_facets_nonio.h b/libstdc++-v3/include/bits/locale_facets_nonio.h
index 8f2aa863302..71a82af5ee3 100644
--- a/libstdc++-v3/include/bits/locale_facets_nonio.h
+++ b/libstdc++-v3/include/bits/locale_facets_nonio.h
@@ -355,6 +355,30 @@ namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
+  struct __time_get_state
+  {
+    // Finalize state.
+    void
+    _M_finalize_state(tm* __tm);
+
+    unsigned int _M_have_I : 1;
+    unsigned int _M_have_wday : 1;
+    unsigned int _M_have_yday : 1;
+    unsigned int _M_have_mon : 1;
+    unsigned int _M_have_mday : 1;
+    unsigned int _M_have_uweek : 1;
+    unsigned int _M_have_wweek : 1;
+    unsigned int _M_have_century : 1;
+    unsigned int _M_is_pm : 1;
+    unsigned int _M_want_century : 1;
+    unsigned int _M_want_xday : 1;
+    unsigned int _M_pad1 : 5;
+    unsigned int _M_week_no : 6;
+    unsigned int _M_pad2 : 10;
+    int _M_century;
+    int _M_pad3;
+  };
+
 _GLIBCXX_BEGIN_NAMESPACE_CXX11
 
   /**
@@ -756,6 +780,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       _M_extract_via_format(iter_type __beg, iter_type __end, ios_base& __io,
 			    ios_base::iostate& __err, tm* __tm,
 			    const _CharT* __format) const;
+
+      // Extract on a component-by-component basis, via __format argument, with
+      // state.
+      iter_type
+      _M_extract_via_format(iter_type __beg, iter_type __end, ios_base& __io,
+			    ios_base::iostate& __err, tm* __tm,
+			    const _CharT* __format,
+			    __time_get_state &__state) const;
     };
 
   template<typename _CharT, typename _InIter>
diff --git a/libstdc++-v3/include/bits/locale_facets_nonio.tcc b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
index d199bcc5ea9..64737823374 100644
--- a/libstdc++-v3/include/bits/locale_facets_nonio.tcc
+++ b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
@@ -691,7 +691,8 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
     time_get<_CharT, _InIter>::
     _M_extract_via_format(iter_type __beg, iter_type __end, ios_base& __io,
 			  ios_base::iostate& __err, tm* __tm,
-			  const _CharT* __format) const
+			  const _CharT* __format,
+			  __time_get_state &__state) const
     {
       const locale& __loc = __io._M_getloc();
       const __timepunct<_CharT>& __tp = use_facet<__timepunct<_CharT> >(__loc);
@@ -722,7 +723,10 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
 		  __beg = _M_extract_name(__beg, __end, __mem, __days,
 					  14, __io, __tmperr);
 		  if (!__tmperr)
-		    __tm->tm_wday = __mem % 7;
+		    {
+		      __tm->tm_wday = __mem % 7;
+		      __state._M_have_wday = 1;
+		    }
 		  break;
 		case 'h':
 		case 'b':
@@ -734,14 +738,31 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
 		  __beg = _M_extract_name(__beg, __end, __mem,
 					  __months, 24, __io, __tmperr);
 		  if (!__tmperr)
-		    __tm->tm_mon = __mem % 12;
+		    {
+		      __tm->tm_mon = __mem % 12;
+		      __state._M_have_mon = 1;
+		      __state._M_want_xday = 1;
+		    }
 		  break;
 		case 'c':
 		  // Default time and date representation.
 		  const char_type*  __dt[2];
 		  __tp._M_date_time_formats(__dt);
 		  __beg = _M_extract_via_format(__beg, __end, __io, __tmperr, 
-						__tm, __dt[0]);
+						__tm, __dt[0], __state);
+		  if (!__tmperr)
+		    __state._M_want_xday = 1;
+		  break;
+		case 'C':
+		  // Century.
+		  __beg = _M_extract_num(__beg, __end, __mem, 0, 99, 2,
+					 __io, __tmperr);
+		  if (!__tmperr)
+		    {
+		      __state._M_century = __mem;
+		      __state._M_have_century = 1;
+		      __state._M_want_xday = 1;
+		    }
 		  break;
 		case 'd':
 		case 'e':
@@ -751,35 +772,60 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
 		  __beg = _M_extract_num(__beg, __end, __mem, 1, 31, 2,
 					 __io, __tmperr);
 		  if (!__tmperr)
-		    __tm->tm_mday = __mem;
+		    {
+		      __tm->tm_mday = __mem;
+		      __state._M_have_mday = 1;
+		      __state._M_want_xday = 1;
+		    }
 		  break;
 		case 'D':
 		  // Equivalent to %m/%d/%y.[tm_mon, tm_mday, tm_year]
 		  __cs = "%m/%d/%y";
 		  __ctype.widen(__cs, __cs + 9, __wcs);
 		  __beg = _M_extract_via_format(__beg, __end, __io, __tmperr, 
-						__tm, __wcs);
+						__tm, __wcs, __state);
+		  if (!__tmperr)
+		    __state._M_want_xday = 1;
 		  break;
 		case 'H':
 		  // Hour [00, 23]. [tm_hour]
 		  __beg = _M_extract_num(__beg, __end, __mem, 0, 23, 2,
 					 __io, __tmperr);
 		  if (!__tmperr)
-		    __tm->tm_hour = __mem;
+		    {
+		      __tm->tm_hour = __mem;
+		      __state._M_have_I = 0;
+		    }
 		  break;
 		case 'I':
 		  // Hour [01, 12]. [tm_hour]
 		  __beg = _M_extract_num(__beg, __end, __mem, 1, 12, 2,
 					 __io, __tmperr);
 		  if (!__tmperr)
-		    __tm->tm_hour = __mem % 12;
+		    {
+		      __tm->tm_hour = __mem % 12;
+		      __state._M_have_I = 1;
+		    }
+		  break;
+		case 'j':
+		  // Day number of year.
+		  __beg = _M_extract_num(__beg, __end, __mem, 1, 366, 3,
+					 __io, __tmperr);
+		  if (!__tmperr)
+		    {
+		      __tm->tm_yday = __mem - 1;
+		      __state._M_have_yday = 1;
+		    }
 		  break;
 		case 'm':
 		  // Month [01, 12]. [tm_mon]
 		  __beg = _M_extract_num(__beg, __end, __mem, 1, 12, 2, 
 					 __io, __tmperr);
 		  if (!__tmperr)
-		    __tm->tm_mon = __mem - 1;
+		    {
+		      __tm->tm_mon = __mem - 1;
+		      __state._M_have_mon = 1;
+		    }
 		  break;
 		case 'M':
 		  // Minute [00, 59]. [tm_min]
@@ -802,23 +848,22 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
 		    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;
+		    __state._M_is_pm = 1;
 		  break;
 		case 'r':
 		  // Locale's 12-hour clock time format (in C %I:%M:%S %p).
 		  const char_type*  __ampm_format;
 		  __tp._M_am_pm_format(&__ampm_format);
 		  __beg = _M_extract_via_format(__beg, __end, __io, __tmperr,
-						__tm, __ampm_format);
+						__tm, __ampm_format, __state);
 		  break;
 		case 'R':
 		  // Equivalent to (%H:%M).
 		  __cs = "%H:%M";
 		  __ctype.widen(__cs, __cs + 6, __wcs);
 		  __beg = _M_extract_via_format(__beg, __end, __io, __tmperr, 
-						__tm, __wcs);
+						__tm, __wcs, __state);
 		  break;
 		case 'S':
 		  // Seconds. [tm_sec]
@@ -830,41 +875,105 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
 #endif
 					 __io, __tmperr);
 		  if (!__tmperr)
-		  __tm->tm_sec = __mem;
+		    __tm->tm_sec = __mem;
 		  break;
 		case 'T':
 		  // Equivalent to (%H:%M:%S).
 		  __cs = "%H:%M:%S";
 		  __ctype.widen(__cs, __cs + 9, __wcs);
 		  __beg = _M_extract_via_format(__beg, __end, __io, __tmperr, 
-						__tm, __wcs);
+						__tm, __wcs, __state);
+		  break;
+		case 'U':
+		  // Week number of the year (Sunday as first day of week).
+		  __beg = _M_extract_num(__beg, __end, __mem, 0, 53, 2,
+					 __io, __tmperr);
+		  if (!__tmperr)
+		    {
+		      __state._M_week_no = __mem;
+		      __state._M_have_uweek = 1;
+		    }
+		  break;
+		case 'w':
+		  // Weekday [tm_wday]
+		  __beg = _M_extract_num(__beg, __end, __mem, 0, 6, 1,
+					 __io, __tmperr);
+		  if (!__tmperr)
+		    {
+		      __tm->tm_wday = __mem;
+		      __state._M_have_wday = 1;
+		    }
+		  break;
+		case 'W':
+		  // Week number of the year (Monday as first day of week).
+		  __beg = _M_extract_num(__beg, __end, __mem, 0, 53, 2,
+					 __io, __tmperr);
+		  if (!__tmperr)
+		    {
+		      __state._M_week_no = __mem;
+		      __state._M_have_wweek = 1;
+		    }
 		  break;
 		case 'x':
 		  // Locale's date.
 		  const char_type*  __dates[2];
 		  __tp._M_date_formats(__dates);
 		  __beg = _M_extract_via_format(__beg, __end, __io, __tmperr, 
-						__tm, __dates[0]);
+						__tm, __dates[0], __state);
 		  break;
 		case 'X':
 		  // Locale's time.
 		  const char_type*  __times[2];
 		  __tp._M_time_formats(__times);
 		  __beg = _M_extract_via_format(__beg, __end, __io, __tmperr, 
-						__tm, __times[0]);
+						__tm, __times[0], __state);
 		  break;
 		case 'y':
-		case 'C': // C99
-		  // Two digit year.
+		  // The last 2 digits of year.
+		  __beg = _M_extract_num(__beg, __end, __mem, 0, 99, 2,
+					 __io, __tmperr);
+		  if (!__tmperr)
+		    {
+		      __state._M_want_century = 1;
+		      __state._M_want_xday = 1;
+		      // As an extension, if the 2 digits are followed by
+		      // 1-2 further digits, treat it like %Y.
+		      __c = 0;
+		      if (__beg != __end)
+			__c = __ctype.narrow(*__beg, '*');
+		      if (__c >= '0' && __c <= '9')
+			{
+			  ++__beg;
+			  __mem = __mem * 10 + (__c - '0');
+			  if (__beg != __end)
+			    {
+			      __c = __ctype.narrow(*__beg, '*');
+			      if (__c >= '0' && __c <= '9')
+				{
+				  ++__beg;
+				  __mem = __mem * 10 + (__c - '0');
+				}
+			    }
+			  __mem -= 1900;
+			  __state._M_want_century = 0;
+			}
+		      // Otherwise, as per POSIX 2008, 00-68 is 2000-2068,
+		      // while 69-99 is 1969-1999.
+		      else if (__mem < 69)
+			__mem += 100;
+		      __tm->tm_year = __mem;
+		    }
+		  break;
 		case 'Y':
-		  // Year [1900).
-		  // NB: We parse either two digits, implicitly years since
-		  // 1900, or 4 digits, full year.  In both cases we can 
-		  // reconstruct [tm_year].  See also libstdc++/26701.
+		  // Year.
 		  __beg = _M_extract_num(__beg, __end, __mem, 0, 9999, 4,
 					 __io, __tmperr);
 		  if (!__tmperr)
-		    __tm->tm_year = __mem < 0 ? __mem + 100 : __mem - 1900;
+		    {
+		      __tm->tm_year = __mem - 1900;
+		      __state._M_want_century = 0;
+		      __state._M_want_xday = 1;
+		    }
 		  break;
 		case 'Z':
 		  // Timezone info.
@@ -925,6 +1034,18 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
       return __beg;
     }
 
+  template<typename _CharT, typename _InIter>
+    _InIter
+    time_get<_CharT, _InIter>::
+    _M_extract_via_format(iter_type __beg, iter_type __end, ios_base& __io,
+			  ios_base::iostate& __err, tm* __tm,
+			  const _CharT* __format) const
+    {
+      __time_get_state __state = __time_get_state();
+      return _M_extract_via_format(__beg, __end, __io, __err, __tm,
+				   __format, __state);
+    }
+
   template<typename _CharT, typename _InIter>
     _InIter
     time_get<_CharT, _InIter>::
@@ -949,13 +1070,7 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
 	  else
 	    break;
 	}
-      // Special encoding for do_get_year, 'y', and 'Y' above.
-      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)
+      if (__i && __value >= __min && __value <= __max)
 	__member = __value;
       else
 	__err |= ios_base::failbit;
@@ -1212,8 +1327,10 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
       const __timepunct<_CharT>& __tp = use_facet<__timepunct<_CharT> >(__loc);
       const char_type*  __times[2];
       __tp._M_time_formats(__times);
+      __time_get_state __state = __time_get_state();
       __beg = _M_extract_via_format(__beg, __end, __io, __err, 
-				    __tm, __times[0]);
+				    __tm, __times[0], __state);
+      __state._M_finalize_state(__tm);
       if (__beg == __end)
 	__err |= ios_base::eofbit;
       return __beg;
@@ -1229,8 +1346,10 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
       const __timepunct<_CharT>& __tp = use_facet<__timepunct<_CharT> >(__loc);
       const char_type*  __dates[2];
       __tp._M_date_formats(__dates);
+      __time_get_state __state = __time_get_state();
       __beg = _M_extract_via_format(__beg, __end, __io, __err, 
-				    __tm, __dates[0]);
+				    __tm, __dates[0], __state);
+      __state._M_finalize_state(__tm);
       if (__beg == __end)
 	__err |= ios_base::eofbit;
       return __beg;
@@ -1296,11 +1415,38 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
     {
       int __tmpyear;
       ios_base::iostate __tmperr = ios_base::goodbit;
+      const locale& __loc = __io._M_getloc();
+      const ctype<_CharT>& __ctype = use_facet<ctype<_CharT> >(__loc);
 
-      __beg = _M_extract_num(__beg, __end, __tmpyear, 0, 9999, 4,
+      __beg = _M_extract_num(__beg, __end, __tmpyear, 0, 99, 2,
 			     __io, __tmperr);
       if (!__tmperr)
-	__tm->tm_year = __tmpyear < 0 ? __tmpyear + 100 : __tmpyear - 1900;
+	{
+	  char __c = 0;
+	  if (__beg != __end)
+	    __c = __ctype.narrow(*__beg, '*');
+	  // For 1-2 digit year, assume 69-99 is 1969-1999, 0-68 is 2000-2068.
+	  // For 3-4 digit year, use it as year.
+	  // __tm->tm_year needs year - 1900 though.
+	  if (__c >= '0' && __c <= '9')
+	    {
+	      ++__beg;
+	      __tmpyear = __tmpyear * 10 + (__c - '0');
+	      if (__beg != __end)
+		{
+		  __c = __ctype.narrow(*__beg, '*');
+		  if (__c >= '0' && __c <= '9')
+		    {
+		      ++__beg;
+		      __tmpyear = __tmpyear * 10 + (__c - '0');
+		    }
+		}
+	      __tmpyear -= 1900;
+	    }
+	  else if (__tmpyear < 69)
+	    __tmpyear += 100;
+	  __tm->tm_year = __tmpyear;
+	}
       else
 	__err |= ios_base::failbit;
 
@@ -1321,6 +1467,21 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
       const locale& __loc = __io._M_getloc();
       ctype<_CharT> const& __ctype = use_facet<ctype<_CharT> >(__loc);
       __err = ios_base::goodbit;
+      bool __use_state = false;
+#if __GNUC__ >= 5
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpmf-conversions"
+      // Nasty hack.  The C++ standard mandates that get invokes the do_get
+      // virtual method, but unfortunately at least without an ABI change
+      // for the facets we can't keep state across the different do_get
+      // calls.  So e.g. if __fmt is "%p %I:%M:%S", we can't handle it
+      // properly, because we first handle the %p am/pm specifier and only
+      // later the 12-hour format specifier.
+      if ((void*)(this->*(&time_get::do_get)) == (void*)(&time_get::do_get))
+	__use_state = true;
+#pragma GCC diagnostic pop
+#endif
+      __time_get_state __state = __time_get_state();
       while (__fmt != __fmtend &&
              __err == ios_base::goodbit)
         {
@@ -1331,6 +1492,7 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
             }
           else if (__ctype.narrow(*__fmt, 0) == '%')
             {
+	      const char_type* __fmt_start = __fmt;
               char __format;
               char __mod = 0;
               if (++__fmt == __fmtend)
@@ -1351,8 +1513,26 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
                   __err = ios_base::failbit;
                   break;
                 }
-              __s = this->do_get(__s, __end, __io, __err, __tm, __format,
-				 __mod);
+	      if (__use_state)
+		{
+		  char_type __new_fmt[4];
+		  __new_fmt[0] = __fmt_start[0];
+		  __new_fmt[1] = __fmt_start[1];
+		  if (__mod)
+		    {
+		      __new_fmt[2] = __fmt_start[2];
+		      __new_fmt[3] = char_type();
+		    }
+		  else
+		    __new_fmt[2] = char_type();
+		  __s = _M_extract_via_format(__s, __end, __io, __err, __tm,
+					      __new_fmt, __state);
+		  if (__s == __end)
+		    __err |= ios_base::eofbit;
+		}
+	      else
+		__s = this->do_get(__s, __end, __io, __err, __tm, __format,
+				   __mod);
               ++__fmt;
             }
           else if (__ctype.is(ctype_base::space, *__fmt))
@@ -1379,6 +1559,8 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
               break;
             }
         }
+      if (__use_state)
+	__state._M_finalize_state(__tm);
       return __s;
     }
 
@@ -1408,7 +1590,10 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
           __fmt[3] = char_type();
         }
 
-      __beg = _M_extract_via_format(__beg, __end, __io, __err, __tm, __fmt);
+      __time_get_state __state = __time_get_state();
+      __beg = _M_extract_via_format(__beg, __end, __io, __err, __tm, __fmt,
+				    __state);
+      __state._M_finalize_state(__tm);
       if (__beg == __end)
 	__err |= ios_base::eofbit;
       return __beg;
diff --git a/libstdc++-v3/src/c++98/locale_facets.cc b/libstdc++-v3/src/c++98/locale_facets.cc
index 9fcfaf99217..1293a37e642 100644
--- a/libstdc++-v3/src/c++98/locale_facets.cc
+++ b/libstdc++-v3/src/c++98/locale_facets.cc
@@ -134,5 +134,115 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                                   __grouping_tmp.size());
   }
 
+  namespace
+  {
+    bool
+    is_leap(int year)
+    {
+      return (year % 100 != 0 || year % 400 == 0) && year % 4 == 0;
+    }
+
+    const unsigned short int mon_yday[2][13] =
+    {
+      // Normal years.
+      { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+      // Leap years.
+      { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+    };
+
+    int
+    day_of_the_week (int year, int mon, int mday)
+    {
+      // We know that January 1st 1970 was a Thursday (= 4).  Compute the
+      // difference between this date and the one in arguments and so
+      // determine the weekday.
+      int corr_year = 1900 + year - (mon < 2);
+      int wday = (-473
+		  + (365 * (year - 70))
+		  + (corr_year / 4)
+		  - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
+		  + (((corr_year / 4) / 25) / 4)
+		  + mon_yday[0][mon]
+		  + mday - 1);
+      return ((wday % 7) + 7) % 7;
+    }
+
+    // Compute the day of the year.
+    int
+    day_of_the_year (tm *tm)
+    {
+      return (mon_yday[is_leap (1900 + tm->tm_year)][tm->tm_mon]
+	      + (tm->tm_mday - 1));
+    }
+  }
+
+  // Finalize time_get state.
+  void
+  __time_get_state::
+  _M_finalize_state(tm* tm)
+  {
+    if (_M_have_I && _M_is_pm)
+      tm->tm_hour += 12;
+    if (_M_have_century)
+      {
+	if (_M_want_century)
+	  tm->tm_year = tm->tm_year % 100;
+	else
+	  tm->tm_year = 0;
+	tm->tm_year += (_M_century - 19) * 100;
+      }
+    if (_M_want_xday && !_M_have_wday)
+      {
+	if (!(_M_have_mon && _M_have_mday) && _M_have_yday)
+	  {
+	    // We don't have tm_mon and/or tm_mday, compute them.
+	    int t_mon = 0;
+	    while (mon_yday[is_leap(1900 + tm->tm_year)][t_mon]
+		   <= tm->tm_yday)
+	      ++t_mon;
+	    if (!_M_have_mon)
+	      tm->tm_mon = t_mon - 1;
+	    if (!_M_have_mday)
+	      tm->tm_mday
+		= (tm->tm_yday
+		   - mon_yday[is_leap(1900 + tm->tm_year)][t_mon - 1] + 1);
+	    _M_have_mon = 1;
+	    _M_have_mday = 1;
+	  }
+	// Don't crash in day_of_the_week if tm_mon is uninitialized.
+	if (_M_have_mon || (unsigned) tm->tm_mon <= 11)
+	  tm->tm_wday
+	    = day_of_the_week (tm->tm_year, tm->tm_mon, tm->tm_mday);
+      }
+    if (_M_want_xday
+	&& !_M_have_yday
+	&& (_M_have_mon || (unsigned) tm->tm_mon <= 11))
+      tm->tm_yday = day_of_the_year (tm);
+    if ((_M_have_uweek || _M_have_wweek) && _M_have_wday)
+      {
+	int w_offset = _M_have_uweek ? 0 : 1;
+	int wday = day_of_the_week (tm->tm_year, 0, 1);
+
+	if (!_M_have_yday)
+	  tm->tm_yday = ((7 - (wday - w_offset)) % 7
+			 + (_M_week_no - 1) * 7
+			 + (tm->tm_wday - w_offset + 7) % 7);
+
+	if (!_M_have_mday || !_M_have_mon)
+	  {
+	    int t_mon = 0;
+	    while (mon_yday[is_leap(1900 + tm->tm_year)][t_mon]
+		   <= tm->tm_yday)
+	      ++t_mon;
+	    if (!_M_have_mon)
+	      tm->tm_mon = t_mon - 1;
+	    if (!_M_have_mday)
+	      tm->tm_mday
+		= (tm->tm_yday
+		   - mon_yday[is_leap(1900 + tm->tm_year)][t_mon - 1] + 1);
+	  }
+      }
+  }
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/char/4.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/char/4.cc
new file mode 100644
index 00000000000..d0619f9de1f
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get/char/4.cc
@@ -0,0 +1,243 @@
+// { dg-do run { target c++11 } }
+
+// Copyright (C) 2021-2022 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;
+
+  iss.str("PM01:38:12");
+  string format = "%p%I:%M:%S";
+  time = tm();
+  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_hour == 13 );
+  VERIFY( time.tm_min == 38 );
+  VERIFY( time.tm_sec == 12 );
+
+  iss.str("05 37");
+  format = "%C %y";
+  time = tm();
+  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_year == 537 - 1900 );
+
+  iss.str("68");
+  format = "%y";
+  time = tm();
+  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_year == 2068 - 1900 );
+
+  iss.str("69");
+  format = "%y";
+  time = tm();
+  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_year == 1969 - 1900 );
+
+  iss.str("03-Feb-2003");
+  format = "%d-%b-%Y";
+  time = tm();
+  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_year == 2003 - 1900 );
+  VERIFY( time.tm_mon == 1 );
+  VERIFY( time.tm_mday == 3 );
+  VERIFY( time.tm_wday == 1 );
+  VERIFY( time.tm_yday == 33 );
+
+  iss.str("16-Dec-2020");
+  format = "%d-%b-%Y";
+  time = tm();
+  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_year == 2020 - 1900 );
+  VERIFY( time.tm_mon == 11 );
+  VERIFY( time.tm_mday == 16 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 350 );
+
+  iss.str("16-Dec-2021");
+  format = "%d-%b-%Y";
+  time = tm();
+  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_year == 2021 - 1900 );
+  VERIFY( time.tm_mon == 11 );
+  VERIFY( time.tm_mday == 16 );
+  VERIFY( time.tm_wday == 4 );
+  VERIFY( time.tm_yday == 349 );
+
+  iss.str("253 2020");
+  format = "%j %Y";
+  time = tm();
+  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_year == 2020 - 1900 );
+  VERIFY( time.tm_mon == 8 );
+  VERIFY( time.tm_mday == 9 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 252 );
+
+  iss.str("233 2021");
+  format = "%j %Y";
+  time = tm();
+  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_year == 2021 - 1900 );
+  VERIFY( time.tm_mon == 7 );
+  VERIFY( time.tm_mday == 21 );
+  VERIFY( time.tm_wday == 6 );
+  VERIFY( time.tm_yday == 232 );
+
+  iss.str("2020 23 3");
+  format = "%Y %U %w";
+  time = tm();
+  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_year == 2020 - 1900 );
+  VERIFY( time.tm_mon == 5 );
+  VERIFY( time.tm_mday == 10 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 161 );
+
+  iss.str("2020 23 3");
+  format = "%Y %W %w";
+  time = tm();
+  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_year == 2020 - 1900 );
+  VERIFY( time.tm_mon == 5 );
+  VERIFY( time.tm_mday == 10 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 161 );
+
+  iss.str("2021 43 Fri");
+  format = "%Y %W %a";
+  time = tm();
+  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_year == 2021 - 1900 );
+  VERIFY( time.tm_mon == 9 );
+  VERIFY( time.tm_mday == 29 );
+  VERIFY( time.tm_wday == 5 );
+  VERIFY( time.tm_yday == 301 );
+
+  iss.str("2024 23 3");
+  format = "%Y %U %w";
+  time = tm();
+  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_year == 2024 - 1900 );
+  VERIFY( time.tm_mon == 5 );
+  VERIFY( time.tm_mday == 12 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 163 );
+
+  iss.str("2024 23 3");
+  format = "%Y %W %w";
+  time = tm();
+  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_year == 2024 - 1900 );
+  VERIFY( time.tm_mon == 5 );
+  VERIFY( time.tm_mday == 5 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 156 );
+
+  // As an extension, parse also 4 digit years.
+  iss.str("0068");
+  format = "%y";
+  time = tm();
+  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_year == 68 - 1900 );
+
+  iss.str("0069");
+  format = "%y";
+  time = tm();
+  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_year == 69 - 1900 );
+
+  iss.str("1492");
+  format = "%y";
+  time = tm();
+  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_year == 1492 - 1900 );
+}
+
+int
+main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/4.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/4.cc
new file mode 100644
index 00000000000..0518842692f
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/4.cc
@@ -0,0 +1,243 @@
+// { dg-do run { target c++11 } }
+
+// Copyright (C) 2021-2022 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;
+
+  iss.str(L"PM01:38:12");
+  wstring format = L"%p%I:%M:%S";
+  time = tm();
+  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_hour == 13 );
+  VERIFY( time.tm_min == 38 );
+  VERIFY( time.tm_sec == 12 );
+
+  iss.str(L"05 37");
+  format = L"%C %y";
+  time = tm();
+  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_year == 537 - 1900 );
+
+  iss.str(L"68");
+  format = L"%y";
+  time = tm();
+  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_year == 2068 - 1900 );
+
+  iss.str(L"69");
+  format = L"%y";
+  time = tm();
+  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_year == 1969 - 1900 );
+
+  iss.str(L"03-Feb-2003");
+  format = L"%d-%b-%Y";
+  time = tm();
+  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_year == 2003 - 1900 );
+  VERIFY( time.tm_mon == 1 );
+  VERIFY( time.tm_mday == 3 );
+  VERIFY( time.tm_wday == 1 );
+  VERIFY( time.tm_yday == 33 );
+
+  iss.str(L"16-Dec-2020");
+  format = L"%d-%b-%Y";
+  time = tm();
+  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_year == 2020 - 1900 );
+  VERIFY( time.tm_mon == 11 );
+  VERIFY( time.tm_mday == 16 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 350 );
+
+  iss.str(L"16-Dec-2021");
+  format = L"%d-%b-%Y";
+  time = tm();
+  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_year == 2021 - 1900 );
+  VERIFY( time.tm_mon == 11 );
+  VERIFY( time.tm_mday == 16 );
+  VERIFY( time.tm_wday == 4 );
+  VERIFY( time.tm_yday == 349 );
+
+  iss.str(L"253 2020");
+  format = L"%j %Y";
+  time = tm();
+  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_year == 2020 - 1900 );
+  VERIFY( time.tm_mon == 8 );
+  VERIFY( time.tm_mday == 9 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 252 );
+
+  iss.str(L"233 2021");
+  format = L"%j %Y";
+  time = tm();
+  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_year == 2021 - 1900 );
+  VERIFY( time.tm_mon == 7 );
+  VERIFY( time.tm_mday == 21 );
+  VERIFY( time.tm_wday == 6 );
+  VERIFY( time.tm_yday == 232 );
+
+  iss.str(L"2020 23 3");
+  format = L"%Y %U %w";
+  time = tm();
+  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_year == 2020 - 1900 );
+  VERIFY( time.tm_mon == 5 );
+  VERIFY( time.tm_mday == 10 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 161 );
+
+  iss.str(L"2020 23 3");
+  format = L"%Y %W %w";
+  time = tm();
+  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_year == 2020 - 1900 );
+  VERIFY( time.tm_mon == 5 );
+  VERIFY( time.tm_mday == 10 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 161 );
+
+  iss.str(L"2021 43 Fri");
+  format = L"%Y %W %a";
+  time = tm();
+  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_year == 2021 - 1900 );
+  VERIFY( time.tm_mon == 9 );
+  VERIFY( time.tm_mday == 29 );
+  VERIFY( time.tm_wday == 5 );
+  VERIFY( time.tm_yday == 301 );
+
+  iss.str(L"2024 23 3");
+  format = L"%Y %U %w";
+  time = tm();
+  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_year == 2024 - 1900 );
+  VERIFY( time.tm_mon == 5 );
+  VERIFY( time.tm_mday == 12 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 163 );
+
+  iss.str(L"2024 23 3");
+  format = L"%Y %W %w";
+  time = tm();
+  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_year == 2024 - 1900 );
+  VERIFY( time.tm_mon == 5 );
+  VERIFY( time.tm_mday == 5 );
+  VERIFY( time.tm_wday == 3 );
+  VERIFY( time.tm_yday == 156 );
+
+  // As an extension, parse also 4 digit years.
+  iss.str(L"0068");
+  format = L"%y";
+  time = tm();
+  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_year == 68 - 1900 );
+
+  iss.str(L"0069");
+  format = L"%y";
+  time = tm();
+  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_year == 69 - 1900 );
+
+  iss.str(L"1492");
+  format = L"%y";
+  time = tm();
+  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_year == 1492 - 1900 );
+}
+
+int
+main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get_year/char/1.cc b/libstdc++-v3/testsuite/22_locale/time_get/get_year/char/1.cc
index c9d9896a6c0..e6f53de4a55 100644
--- a/libstdc++-v3/testsuite/22_locale/time_get/get_year/char/1.cc
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get_year/char/1.cc
@@ -76,8 +76,8 @@ void test01()
   errorstate = good;
   iterator_type ret03 = tim_get.get_year(is_it03, end, iss, errorstate,
 					 &time03);
-  VERIFY( time03.tm_year == 3 );
-  VERIFY( errorstate == ios_base::failbit );
+  VERIFY( time03.tm_year == 197 - 1900 );
+  VERIFY( errorstate == good );
   VERIFY( *ret03 == 'd' );
 
   iss.str("71d71");
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get_year/char/5.cc b/libstdc++-v3/testsuite/22_locale/time_get/get_year/char/5.cc
index d518d96c45f..c4544479828 100644
--- a/libstdc++-v3/testsuite/22_locale/time_get/get_year/char/5.cc
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get_year/char/5.cc
@@ -49,12 +49,13 @@ void test01()
 
   const string str0 = "1";
   tg.get_year(str0.begin(), str0.end(), iss, err, &tm0);
-  VERIFY( err == (failbit | eofbit) );
-  VERIFY( tm0.tm_year == 0 );
+  VERIFY( err == eofbit );
+  VERIFY( tm0.tm_year == 2001 - 1900 );
 
   const string str1 = "1997 ";
+  err = goodbit;
   iter_type end1 = tg.get_year(str1.begin(), str1.end(), iss, err, &tm1);
-  VERIFY( err == (failbit | eofbit) );
+  VERIFY( err == goodbit );
   VERIFY( tm1.tm_year == time_sanity.tm_year );
   VERIFY( *end1 ==  ' ' );
 }
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get_year/char/6.cc b/libstdc++-v3/testsuite/22_locale/time_get/get_year/char/6.cc
new file mode 100644
index 00000000000..e50afa6aa73
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get_year/char/6.cc
@@ -0,0 +1,79 @@
+// Copyright (C) 2001-2022 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/>.
+
+// 22.2.5.1.1 time_get members
+
+#include <locale>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  using namespace std;
+
+  typedef istreambuf_iterator<char> iterator_type;
+
+  locale loc_c = locale::classic();
+
+  iterator_type end;
+
+  istringstream iss;
+  iss.imbue(loc_c);
+  const time_get<char>& tim_get = use_facet<time_get<char> >(iss.getloc());
+  ios_base::iostate errorstate = ios_base::goodbit;
+
+  iss.str("69");
+  iterator_type is_it01(iss);
+  tm time01;
+  tim_get.get_year(is_it01, end, iss, errorstate, &time01);
+  VERIFY( time01.tm_year == 1969 - 1900 );
+  VERIFY( errorstate == ios_base::eofbit );
+
+  iss.str("68 ");
+  iterator_type is_it02(iss);
+  tm time02;
+  errorstate = ios_base::goodbit;
+  iterator_type ret02 = tim_get.get_year(is_it02, end, iss, errorstate,
+					 &time02);
+  VERIFY( time02.tm_year == 2068 - 1900 );
+  VERIFY( errorstate == ios_base::goodbit );
+  VERIFY( *ret02 == ' ' );
+
+  iss.str("0069");
+  iterator_type is_it03(iss);
+  tm time03;
+  errorstate = ios_base::goodbit;
+  iterator_type ret03 = tim_get.get_year(is_it03, end, iss, errorstate,
+					 &time03);
+  VERIFY( time03.tm_year == 69 - 1900 );
+  VERIFY( errorstate == ios_base::eofbit );
+
+  iss.str("0068");
+  iterator_type is_it04(iss);
+  tm time04;
+  errorstate = ios_base::goodbit;
+  iterator_type ret04 = tim_get.get_year(is_it04, end, iss, errorstate,
+					 &time04);
+  VERIFY( time04.tm_year == 68 - 1900 );
+  VERIFY( errorstate == ios_base::eofbit );
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get_year/wchar_t/1.cc b/libstdc++-v3/testsuite/22_locale/time_get/get_year/wchar_t/1.cc
index e17712098f4..ba95fb91520 100644
--- a/libstdc++-v3/testsuite/22_locale/time_get/get_year/wchar_t/1.cc
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get_year/wchar_t/1.cc
@@ -76,8 +76,8 @@ void test01()
   errorstate = good;
   iterator_type ret03 = tim_get.get_year(is_it03, end, iss, errorstate,
 					 &time03);
-  VERIFY( time03.tm_year == 3 );
-  VERIFY( errorstate == ios_base::failbit );
+  VERIFY( time03.tm_year == 197 - 1900 );
+  VERIFY( errorstate == good );
   VERIFY( *ret03 == L'd' );
 
   iss.str(L"71d71");
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get_year/wchar_t/5.cc b/libstdc++-v3/testsuite/22_locale/time_get/get_year/wchar_t/5.cc
index 8ce1bdaac12..c1ee71015aa 100644
--- a/libstdc++-v3/testsuite/22_locale/time_get/get_year/wchar_t/5.cc
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get_year/wchar_t/5.cc
@@ -49,12 +49,13 @@ void test01()
 
   const wstring str0 = L"1";
   tg.get_year(str0.begin(), str0.end(), iss, err, &tm0);
-  VERIFY( err == (failbit | eofbit) );
-  VERIFY( tm0.tm_year == 0 );
+  VERIFY( err == eofbit );
+  VERIFY( tm0.tm_year == 2001 - 1900 );
 
   const wstring str1 = L"1997 ";
+  err = goodbit;
   iter_type end1 = tg.get_year(str1.begin(), str1.end(), iss, err, &tm1);
-  VERIFY( err == (failbit | eofbit) );
+  VERIFY( err == goodbit );
   VERIFY( tm1.tm_year == time_sanity.tm_year );
   VERIFY( *end1 ==  L' ' );
 }
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get_year/wchar_t/6.cc b/libstdc++-v3/testsuite/22_locale/time_get/get_year/wchar_t/6.cc
new file mode 100644
index 00000000000..f6e32b7f781
--- /dev/null
+++ b/libstdc++-v3/testsuite/22_locale/time_get/get_year/wchar_t/6.cc
@@ -0,0 +1,79 @@
+// Copyright (C) 2001-2022 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/>.
+
+// 22.2.5.1.1 time_get members
+
+#include <locale>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  using namespace std;
+
+  typedef istreambuf_iterator<wchar_t> iterator_type;
+
+  locale loc_c = locale::classic();
+
+  iterator_type end;
+
+  wistringstream iss;
+  iss.imbue(loc_c);
+  const time_get<wchar_t>& tim_get = use_facet<time_get<wchar_t> >(iss.getloc());
+  ios_base::iostate errorstate = ios_base::goodbit;
+
+  iss.str(L"69");
+  iterator_type is_it01(iss);
+  tm time01;
+  tim_get.get_year(is_it01, end, iss, errorstate, &time01);
+  VERIFY( time01.tm_year == 1969 - 1900 );
+  VERIFY( errorstate == ios_base::eofbit );
+
+  iss.str(L"68 ");
+  iterator_type is_it02(iss);
+  tm time02;
+  errorstate = ios_base::goodbit;
+  iterator_type ret02 = tim_get.get_year(is_it02, end, iss, errorstate,
+					 &time02);
+  VERIFY( time02.tm_year == 2068 - 1900 );
+  VERIFY( errorstate == ios_base::goodbit );
+  VERIFY( *ret02 == L' ' );
+
+  iss.str(L"0069");
+  iterator_type is_it03(iss);
+  tm time03;
+  errorstate = ios_base::goodbit;
+  iterator_type ret03 = tim_get.get_year(is_it03, end, iss, errorstate,
+					 &time03);
+  VERIFY( time03.tm_year == 69 - 1900 );
+  VERIFY( errorstate == ios_base::eofbit );
+
+  iss.str(L"0068");
+  iterator_type is_it04(iss);
+  tm time04;
+  errorstate = ios_base::goodbit;
+  iterator_type ret04 = tim_get.get_year(is_it04, end, iss, errorstate,
+					 &time04);
+  VERIFY( time04.tm_year == 68 - 1900 );
+  VERIFY( errorstate == ios_base::eofbit );
+}
+
+int main()
+{
+  test01();
+  return 0;
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-01-10 14:40 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-10 14:40 [gcc r12-6410] libstdc++: Add %j, %U, %w, %W time_get support, fix %y, %Y, %C, %p [PR77760] Jakub Jelinek

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