Tested x86_64-linux. Pushed to trunk.

I'll backport it to gcc-13 some time after the 13.2 release.

-- >8 --

This fixes some TODOs in the C++20 <chrono> format support, where the
locale-specific output was incorrect or unimplemented. The approach
taken here is to either use the formatting locale's std::time_put facet
to do the formatting, or to remove subsecond precision from time points
so that locale-specific formats don't print fractional seconds. This
ensures that we are consistent with what the std::time_put facet would
print (which never includes fractional seconds) even if we actually
reimplement the formatting by hand instead of using the facet.

This also fixes a misplaced statement that allowed modifiers for %Z
which should have been on %z instead. There was also some ill-formed
code in an untested branch for formatting time zone names to wide
characters. A new test for zoned_time I/O has been added to exercise
that code properly.

libstdc++-v3/ChangeLog:

        PR libstdc++/110719
        * include/bits/chrono_io.h (__formatter_chrono::_M_parse): Fix
        allowed modifiers for %z and %Z. Fix -Wparentheses and
        -Wnarrowing warnings.
        (__formatter_chrono::_M_format): Call new functions for %d, %e,
        %H, %I, %m and %M.
        (__formatter_chrono::_M_c): Use _S_floor_seconds to remove
        subsecond precision.
        (__formatter_chrono::_M_C_y_Y): Use _M_locale_fmt to handle
        modifiers.
        (__formatter_chrono::_M_e): Replace with _M_d_e and use
        _M_locale_fmt.
        (__formatter_chrono::_M_I): Replace with _M_H_I and use
        _M_locale_fmt.
        (__formatter_chrono::_M_m): New function.
        (__formatter_chrono::_M_M): New function.
        (__formatter_chrono::_M_r): Use _M_locale_fmt.
        (__formatter_chrono::_M_S): Likewise.
        (__formatter_chrono::_M_u_w): Likewise.
        (__formatter_chrono::_M_U_V_W): Likewise.
        (__formatter_chrono::_M_X): Use _S_floor_seconds.
        (__formatter_chrono::_M_Z): Fix untested branch for wchar_t.
        (__formatter_chrono::_S_altnum): Remove function.
        (__formatter_chrono::_S_dd_zero_fill): Remove function.
        (__formatter_chrono::_S_floor_seconds): New function.
        (__formatter_chrono::_M_locale_fmt): New function.
        * testsuite/std/time/clock/system/io.cc: Adjust expected output
        for locale-specific formats and check modified formats.
        * testsuite/std/time/clock/utc/io.cc: Likewise.
        * testsuite/std/time/zoned_time/io.cc: New test.
---
 libstdc++-v3/include/bits/chrono_io.h         | 295 +++++++++++-------
 .../testsuite/std/time/clock/system/io.cc     |  20 +-
 .../testsuite/std/time/clock/utc/io.cc        |  12 +-
 .../testsuite/std/time/zoned_time/io.cc       |  64 ++++
 4 files changed, 272 insertions(+), 119 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/std/time/zoned_time/io.cc

diff --git a/libstdc++-v3/include/bits/chrono_io.h 
b/libstdc++-v3/include/bits/chrono_io.h
index 5f06a6d76b4..43eeab42869 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -414,11 +414,10 @@ namespace __format
                  break;
                case 'z':
                  __needed = _TimeZone;
-                 __allowed_mods = _Mod_E;
+                 __allowed_mods = _Mod_E_O;
                  break;
                case 'Z':
                  __needed = _TimeZone;
-                 __allowed_mods = _Mod_E_O;
                  break;
                case 'n':
                case 't':
@@ -439,7 +438,7 @@ namespace __format
                }
 
              if ((__mod == 'E' && !(__allowed_mods & _Mod_E))
-                   || __mod == 'O' && !(__allowed_mods & _Mod_O))
+                   || (__mod == 'O' && !(__allowed_mods & _Mod_O)))
                __throw_format_error("chrono format error: invalid "
                                     " modifier in chrono-specs");
              __mod = _CharT();
@@ -471,7 +470,8 @@ namespace __format
                                 "chrono-specs");
 
          _M_spec = __spec;
-         _M_spec._M_chrono_specs = {__chrono_specs, __first - __chrono_specs};
+         _M_spec._M_chrono_specs
+                = __string_view(__chrono_specs, __first - __chrono_specs);
 
          return __first;
        }
@@ -551,18 +551,12 @@ namespace __format
                  __out = _M_C_y_Y(__t, std::move(__out), __fc, __c, __mod);
                  break;
                case 'd':
-                 // %d  The day of month as a decimal number.
-                 // %Od Locale's alternative representation.
-                 __out = _S_dd_zero_fill((unsigned)_S_day(__t),
-                                         std::move(__out),
-                                         __fc, __mod == 'O');
+               case 'e':
+                 __out = _M_d_e(__t, std::move(__out), __fc, __c, __mod == 
'O');
                  break;
                case 'D':
                  __out = _M_D(__t, std::move(__out), __fc);
                  break;
-               case 'e':
-                 __out = _M_e(__t, std::move(__out), __fc, __mod == 'O');
-                 break;
                case 'F':
                  __out = _M_F(__t, std::move(__out), __fc);
                  break;
@@ -571,29 +565,17 @@ namespace __format
                  __out = _M_g_G(__t, std::move(__out), __fc, __c == 'G');
                  break;
                case 'H':
-                 // %H  The hour (24-hour clock) as a decimal number.
-                 // %OH Locale's alternative representation.
-                 __out = _S_dd_zero_fill(_S_hms(__t).hours().count(),
-                                         __print_sign(), __fc, __mod == 'O');
-                 break;
                case 'I':
-                 __out = _M_I(__t, __print_sign(), __fc, __mod == 'O');
+                 __out = _M_H_I(__t, __print_sign(), __fc, __c, __mod == 'O');
                  break;
                case 'j':
                  __out = _M_j(__t, __print_sign(), __fc);
                  break;
                case 'm':
-                 // %m  month as a decimal number.
-                 // %Om Locale's alternative representation.
-                 __out = _S_dd_zero_fill((unsigned)_S_month(__t),
-                                         std::move(__out), __fc,
-                                         __mod == 'O');
+                 __out = _M_m(__t, std::move(__out), __fc, __mod == 'O');
                  break;
                case 'M':
-                 // %M  The minute as a decimal number.
-                 // %OM Locale's alternative representation.
-                 __out = _S_dd_zero_fill(_S_hms(__t).minutes().count(),
-                                         __print_sign(), __fc, __mod == 'O');
+                 __out = _M_M(__t, __print_sign(), __fc, __mod == 'O');
                  break;
                case 'p':
                  __out = _M_p(__t, std::move(__out), __fc);
@@ -790,12 +772,13 @@ namespace __format
 
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
-       _M_c(const _Tp& __t, typename _FormatContext::iterator __out,
+       _M_c(const _Tp& __tt, typename _FormatContext::iterator __out,
             _FormatContext& __ctx, bool __mod = false) const
        {
          // %c  Locale's date and time representation.
          // %Ec Locale's alternate date and time representation.
 
+         auto __t = _S_floor_seconds(__tt);
          locale __loc = _M_locale(__ctx);
          const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
          const _CharT* __formats[2];
@@ -813,22 +796,24 @@ namespace __format
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
        _M_C_y_Y(const _Tp& __t, typename _FormatContext::iterator __out,
-              _FormatContext& __ctx, char __conv, char __mod = 0) const
+              _FormatContext& __ctx, _CharT __conv, _CharT __mod = 0) const
        {
          // %C  Year divided by 100 using floored division.
          // %EC Locale's alternative preresentation of the century (era name).
          // %y  Last two decimal digits of the year.
-         // %OY Locale's alternative represenation.
+         // %Oy Locale's alternative representation.
          // %Ey Locale's alternative representation of offset from %EC.
          // %Y  Year as a decimal number.
-         // %EY Locale's alternative full year represenation.
+         // %EY Locale's alternative full year representation.
 
          chrono::year __y = _S_year(__t);
 
-         if (__mod == 'E')
+         if (__mod) [[unlikely]]
            {
-             // TODO: %EC, %Ey or %EY
-             // return __out;
+             struct tm __tm{};
+             __tm.tm_year = (int)__y - 1900;
+             return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm,
+                                  __conv, __mod);
            }
 
          basic_string<_CharT> __s;
@@ -852,9 +837,6 @@ namespace __format
          if (__conv == 'Y' || __conv == 'y')
            __s += _S_two_digits(__yi % 100);
 
-         if (__mod == 'O') // %OY
-           _S_altnum(_M_locale(__ctx), __s, __is_neg);
-
          return __format::__write(std::move(__out), __string_view(__s));
        }
 
@@ -878,19 +860,33 @@ namespace __format
 
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
-       _M_e(const _Tp& __t, typename _FormatContext::iterator __out,
-            _FormatContext& __ctx, bool __mod = false) const
+       _M_d_e(const _Tp& __t, typename _FormatContext::iterator __out,
+              _FormatContext& __ctx, _CharT __conv, bool __mod = false) const
        {
+         // %d  The day of month as a decimal number.
+         // %Od Locale's alternative representation.
          // %e  Day of month as decimal number, padded with space.
          // %Oe Locale's alternative digits.
+
          chrono::day __d = _S_day(__t);
          unsigned __i = (unsigned)__d;
+
+         if (__mod) [[unlikely]]
+           {
+             struct tm __tm{};
+             __tm.tm_mday = __i;
+             return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm,
+                                  (char)__conv, 'O');
+           }
+
          auto __sv = _S_two_digits(__i);
-         basic_string<_CharT> __s;
-         if (__mod)
-           __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv));
-         if (__i < 10)
-           __sv = __s = {_S_space, __sv[1]};
+         _CharT __buf[2];
+         if (__conv == _CharT('e') && __i < 10)
+           {
+             __buf[0] = _S_space;
+             __buf[1] = __sv[1];
+             __sv = {__buf, 2};
+           }
          return __format::__write(std::move(__out), __sv);
        }
 
@@ -933,26 +929,39 @@ namespace __format
 
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
-       _M_I(const _Tp& __t, typename _FormatContext::iterator __out,
-            _FormatContext& __ctx, bool __mod = false) const
+       _M_H_I(const _Tp& __t, typename _FormatContext::iterator __out,
+              _FormatContext& __ctx, _CharT __conv, bool __mod = false) const
        {
-         auto __hms = _S_hms(__t);
+         // %H  The hour (24-hour clock) as a decimal number.
+         // %OH Locale's alternative representation.
+         // %I  The hour (12-hour clock) as a decimal number.
+         // %OI Locale's alternative representation.
+
+         const auto __hms = _S_hms(__t);
          int __i = __hms.hours().count();
-         if (__i == 0)
-           __i = 12;
-         else if (__i > 12)
-           __i -= 12;
-         auto __sv = _S_two_digits(__i);
-         basic_string<_CharT> __s;
-         if (__mod)
-           __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv));
-         return __format::__write(std::move(__out), __sv);
+
+         if (__mod) [[unlikely]]
+           {
+             struct tm __tm{};
+             __tm.tm_hour = __i;
+             return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm,
+                                  (char)__conv, 'O');
+           }
+
+         if (__conv == _CharT('I'))
+           {
+             if (__i == 0)
+               __i = 12;
+             else if (__i > 12)
+               __i -= 12;
+           }
+         return __format::__write(std::move(__out), _S_two_digits(__i));
        }
 
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
        _M_j(const _Tp& __t, typename _FormatContext::iterator __out,
-            _FormatContext& __ctx) const
+            _FormatContext&) const
        {
          if constexpr (chrono::__is_duration_v<_Tp>)
            {
@@ -978,6 +987,50 @@ namespace __format
            }
        }
 
+      template<typename _Tp, typename _FormatContext>
+       typename _FormatContext::iterator
+       _M_m(const _Tp& __t, typename _FormatContext::iterator __out,
+            _FormatContext& __ctx, bool __mod) const
+       {
+         // %m  month as a decimal number.
+         // %Om Locale's alternative representation.
+
+         auto __m = _S_month(__t);
+         auto __i = (unsigned)__m;
+
+         if (__mod) [[unlikely]] // %Om
+           {
+             struct tm __tm{};
+             __tm.tm_mon = __i - 1;
+             return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm,
+                                  'm', 'O');
+           }
+
+         return __format::__write(std::move(__out), _S_two_digits(__i));
+       }
+
+      template<typename _Tp, typename _FormatContext>
+       typename _FormatContext::iterator
+       _M_M(const _Tp& __t, typename _FormatContext::iterator __out,
+            _FormatContext& __ctx, bool __mod) const
+       {
+         // %M  The minute as a decimal number.
+         // %OM Locale's alternative representation.
+
+         auto __m = _S_hms(__t).minutes();
+         auto __i = __m.count();
+
+         if (__mod) [[unlikely]] // %OM
+           {
+             struct tm __tm{};
+             __tm.tm_min = __i;
+             return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm,
+                                  'M', 'O');
+           }
+
+         return __format::__write(std::move(__out), _S_two_digits(__i));
+       }
+
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
        _M_p(const _Tp& __t, typename _FormatContext::iterator __out,
@@ -995,7 +1048,7 @@ namespace __format
 
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
-       _M_q(const _Tp& __t, typename _FormatContext::iterator __out,
+       _M_q(const _Tp&, typename _FormatContext::iterator __out,
             _FormatContext& __ctx) const
        {
          // %q The duration's unit suffix
@@ -1025,12 +1078,15 @@ namespace __format
            }
        }
 
+      // %Q handled in _M_format
+
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
-       _M_r(const _Tp& __t, typename _FormatContext::iterator __out,
+       _M_r(const _Tp& __tt, typename _FormatContext::iterator __out,
             _FormatContext& __ctx) const
        {
          // %r locale's 12-hour clock time.
+         auto __t = _S_floor_seconds(__tt);
          locale __loc = _M_locale(__ctx);
          const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
          const _CharT* __ampm_fmt;
@@ -1075,10 +1131,19 @@ namespace __format
             _FormatContext& __ctx, bool __mod = false) const
        {
          // %S  Seconds as a decimal number.
-         // %OS (TODO) The locale's alternative representation.
+         // %OS The locale's alternative representation.
          auto __hms = _S_hms(__t);
-         __out = _S_dd_zero_fill(__hms.seconds().count(),
-                                 std::move(__out), __ctx, __mod);
+
+         if (__mod) [[unlikely]] // %OS
+           {
+             struct tm __tm{};
+             __tm.tm_sec = (int)__hms.seconds().count();
+             return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm,
+                                  'S', 'O');
+           }
+
+         __out = __format::__write(std::move(__out),
+                                   _S_two_digits(__hms.seconds().count()));
          using rep = typename decltype(__hms)::precision::rep;
          if constexpr (__hms.fractional_width != 0)
            {
@@ -1126,14 +1191,21 @@ namespace __format
          // %Ou Locale's alternative numeric rep.
          // %w  Weekday as a decimal number (0-6), where Sunday is 0.
          // %Ow Locale's alternative numeric rep.
+
          chrono::weekday __wd = _S_weekday(__t);
+
+         if (__mod) [[unlikely]]
+           {
+             struct tm __tm{};
+             __tm.tm_wday = __wd.c_encoding();
+             return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm,
+                                  (char)__conv, 'O');
+           }
+
          unsigned __wdi = __conv == 'u' ? __wd.iso_encoding()
                                         : __wd.c_encoding();
-         basic_string<_CharT> __s(1, _S_digit(__wdi));
-         if (__mod)
-           _S_altnum(_M_locale(__ctx), __s);
-         return __format::__write(std::move(__out), __string_view(__s));
-         return __out;
+         const _CharT __d = _S_digit(__wdi);
+         return __format::__write(std::move(__out), __string_view(&__d, 1));
        }
 
       template<typename _Tp, typename _FormatContext>
@@ -1151,6 +1223,18 @@ namespace __format
          auto __d = _S_days(__t);
          using _TDays = decltype(__d); // Either sys_days or local_days.
 
+         if (__mod) [[unlikely]]
+           {
+             const year_month_day __ymd(__d);
+             const year __y = __ymd.year();
+             struct tm __tm{};
+             __tm.tm_year = (int)__y - 1900;
+             __tm.tm_yday = (__d - _TDays(__y/January/1)).count();
+             __tm.tm_wday = weekday(__d).c_encoding();
+             return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm,
+                                  (char)__conv, 'O');
+           }
+
          _TDays __first; // First day of week 1.
          if (__conv == 'V') // W01 begins on Monday before first Thursday.
            {
@@ -1172,9 +1256,6 @@ namespace __format
            }
          auto __weeks = chrono::floor<weeks>(__d - __first);
          __string_view __sv = _S_two_digits(__weeks.count() + 1);
-         basic_string<_CharT> __s;
-         if (__mod)
-           __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv));
          return __format::__write(std::move(__out), __sv);
        }
 
@@ -1202,11 +1283,12 @@ namespace __format
 
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
-       _M_X(const _Tp& __t, typename _FormatContext::iterator __out,
+       _M_X(const _Tp& __tt, typename _FormatContext::iterator __out,
             _FormatContext& __ctx, bool __mod = false) const
        {
          // %X  Locale's time rep
          // %EX Locale's alternative time representation.
+         auto __t = _S_floor_seconds(__tt);
          locale __loc = _M_locale(__ctx);
          const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
          const _CharT* __time_reps[2];
@@ -1225,7 +1307,7 @@ namespace __format
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
        _M_z(const _Tp& __t, typename _FormatContext::iterator __out,
-            _FormatContext& __ctx, bool __mod = false) const
+            _FormatContext&, bool __mod = false) const
        {
          using ::std::chrono::__detail::__utc_leap_second;
          using ::std::chrono::__detail::__local_time_fmt;
@@ -1289,9 +1371,9 @@ namespace __format
                  else
                    {
                      string_view __sv = *__t._M_abbrev;
-                     basic_string<_CharT> __ws(__sv.size());
+                     basic_string<_CharT> __ws(__sv.size(), _CharT());
                      auto& __ct = use_facet<ctype<_CharT>>(_M_locale(__ctx));
-                     __ct.widen(__sv.data(), __sv.size(), __ws.data());
+                     __ct.widen(__sv.begin(), __sv.end(), __ws.data());
                      __wsv = __ws;
                    }
                  return __format::__write(std::move(__out), __wsv);
@@ -1329,39 +1411,6 @@ namespace __format
        };
       }
 
-      // Convert a numeric string to the locale's alternative numeric symbols.
-      static basic_string_view<_CharT>
-      _S_altnum(const locale& __loc, basic_string<_CharT>& __s,
-               bool __is_neg = false)
-      {
-       if (__loc == locale::classic())
-         return __s;
-
-#if 0 // TODO how can we access numpunct_cache?! Need to go via std::time_put?
-       auto& __np = use_facet<__numpunct_cache<_CharT>>(__loc);
-       auto __nums = __np._M_atoms_out; // alts for "-+xX01234..."
-       if (__is_neg)
-         __s[0] = __nums[0];
-       __nums += 4; // now points to alternate digits
-       for (int __i = __is_neg; __i < __s.size(); ++__i)
-         __s[__i] = __nums[__s[__i] - '0'];
-#endif
-       return __s;
-      }
-
-      // Write two digits, zero-filled.
-      template<typename _FormatContext>
-       typename _FormatContext::iterator
-       _S_dd_zero_fill(int __val, typename _FormatContext::iterator __out,
-                       _FormatContext& __ctx, bool __alt_num) const
-       {
-         auto __sv = _S_two_digits(__val);
-         basic_string<_CharT> __s;
-         if (__alt_num)
-           __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv));
-         return __format::__write(std::move(__out), __sv);
-       }
-
       // Accessors for the components of chrono types:
 
       // Returns a hh_mm_ss.
@@ -1490,6 +1539,38 @@ namespace __format
          else
            return weekday(_S_days(__t));
        }
+
+      // Remove subsecond precision from a time_point.
+      template<typename _Tp>
+       static auto
+       _S_floor_seconds(const _Tp& __t)
+       {
+         using chrono::__detail::__local_time_fmt;
+         if constexpr (chrono::__is_time_point_v<_Tp>)
+           if constexpr (_Tp::period::den != 1)
+             return chrono::floor<chrono::seconds>(__t);
+           else
+             return __t;
+         else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
+           return _S_floor_seconds(__t._M_time);
+         else
+           return __t;
+       }
+
+      // Use the formatting locale's std::time_put facet to produce
+      // a locale-specific representation.
+      template<typename _Iter>
+       _Iter
+       _M_locale_fmt(_Iter __out, const locale& __loc, const struct tm& __tm,
+                     char __fmt, char __mod) const
+       {
+         basic_ostringstream<_CharT> __os;
+         const auto& __tp = use_facet<time_put<_CharT>>(__loc);
+         __tp.put(__os, __os, _S_space, &__tm, __fmt, __mod);
+         if (__os)
+           __out = __format::__write(std::move(__out), __os.view());
+         return __out;
+       }
     };
 
 } // namespace __format
diff --git a/libstdc++-v3/testsuite/std/time/clock/system/io.cc 
b/libstdc++-v3/testsuite/std/time/clock/system/io.cc
index e7feebc9a3c..7bb6851c7de 100644
--- a/libstdc++-v3/testsuite/std/time/clock/system/io.cc
+++ b/libstdc++-v3/testsuite/std/time/clock/system/io.cc
@@ -47,22 +47,30 @@ test_format()
                              " | %H | %I | %j | %m | %M | %p | %r | %R"
                              " | %S | %T | %u | %U | %V | %w | %W | %x"
                              " | %X | %y | %Y | %z | %Z}", t);
-  VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022"
+  VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 17:26:25 2022"
               " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec"
-              " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26"
+              " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25 PM | 17:26"
               " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22"
-              " | 17:26:25.708 | 22 | 2022 | +0000 | UTC" );
+              " | 17:26:25 | 22 | 2022 | +0000 | UTC" );
 
   std::wstring ws = std::format(L"{:%a | %A | %b | %B | %c"
                                 " | %C | %d | %D | %e | %F | %g | %G | %h"
                                 " | %H | %I | %j | %m | %M | %p | %r | %R"
                                 " | %S | %T | %u | %U | %V | %w | %W | %x"
                                 " | %X | %y | %Y | %z | %Z}", t);
-  VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022"
+  VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 17:26:25 2022"
                 " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec"
-                " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26"
+                " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25 PM | 17:26"
                 " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22"
-                " | 17:26:25.708 | 22 | 2022 | +0000 | UTC" );
+                " | 17:26:25 | 22 | 2022 | +0000 | UTC" );
+
+  auto loc = std::locale::classic();
+  auto smod = std::format(loc, "{:%Ec %EC %Od %Oe %OH %OI %Om %OM %OS %Ou %OU"
+                              " %Ow %OW %Ex %EX %Oy %Ey %EY %Ez %Oz}", t);
+  s = std::format("{:%c %C %d %e %H %I %m %M %S %u %U"
+                 " %w %W %x %X %y %y %Y +00:00 +00:00}",
+                 std::chrono::time_point_cast<std::chrono::seconds>(t));
+  VERIFY( smod == s );
 }
 
 int main()
diff --git a/libstdc++-v3/testsuite/std/time/clock/utc/io.cc 
b/libstdc++-v3/testsuite/std/time/clock/utc/io.cc
index 933cba65f44..977643f1147 100644
--- a/libstdc++-v3/testsuite/std/time/clock/utc/io.cc
+++ b/libstdc++-v3/testsuite/std/time/clock/utc/io.cc
@@ -89,18 +89,18 @@ test_format()
   }
 
   std::string s = ss.str();
-  VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022"
+  VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 17:26:25 2022"
               " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec"
-              " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26"
+              " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25 PM | 17:26"
               " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22"
-              " | 17:26:25.708 | 22 | 2022 | +0000 | UTC | " );
+              " | 17:26:25 | 22 | 2022 | +0000 | UTC | " );
 
   std::wstring ws = wss.str();
-  VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022"
+  VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 17:26:25 2022"
                 " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec"
-                " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26"
+                " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25 PM | 17:26"
                 " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22"
-                " | 17:26:25.708 | 22 | 2022 | +0000 | UTC | " );
+                " | 17:26:25 | 22 | 2022 | +0000 | UTC | " );
 
   std::chrono::utc_seconds leap(1483228800s + 26s); // 1 Jan 2017
   s = std::format("{:%T}", leap - 1s);
diff --git a/libstdc++-v3/testsuite/std/time/zoned_time/io.cc 
b/libstdc++-v3/testsuite/std/time/zoned_time/io.cc
new file mode 100644
index 00000000000..ad85122e85d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/zoned_time/io.cc
@@ -0,0 +1,64 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+// { dg-require-effective-target cxx11_abi }
+
+#include <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test_ostream()
+{
+  using namespace std::chrono;
+  std::stringstream ss;
+  zoned_time<seconds> zt("America/New_York", sys_seconds{946'706'523s});
+  ss << zt;
+  VERIFY( ss.str() == "2000-01-01 01:02:03 EST" );
+}
+
+void
+test_format()
+{
+  using namespace std::chrono;
+  sys_time<milliseconds> t(1671470785708ms);
+  auto zone = "America/New_York";
+  zoned_time<milliseconds> zt(zone, t);
+
+  // Every conversion specifier is valid for a sys_time except %q and %Q.
+
+  std::string s = std::format("{:%a | %A | %b | %B | %c"
+                             " | %C | %d | %D | %e | %F | %g | %G | %h"
+                             " | %H | %I | %j | %m | %M | %p | %r | %R"
+                             " | %S | %T | %u | %U | %V | %w | %W | %x"
+                             " | %X | %y | %Y | %z | %Z}", zt);
+  VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 12:26:25 2022"
+              " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec"
+              " | 12 | 12 | 353 | 12 | 26 | PM | 12:26:25 PM | 12:26"
+              " | 25.708 | 12:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22"
+              " | 12:26:25 | 22 | 2022 | -0500 | EST" );
+
+  std::wstring ws = std::format(L"{:%a | %A | %b | %B | %c"
+                                " | %C | %d | %D | %e | %F | %g | %G | %h"
+                                " | %H | %I | %j | %m | %M | %p | %r | %R"
+                                " | %S | %T | %u | %U | %V | %w | %W | %x"
+                                " | %X | %y | %Y | %z | %Z}", zt);
+  VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 12:26:25 2022"
+                " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec"
+                " | 12 | 12 | 353 | 12 | 26 | PM | 12:26:25 PM | 12:26"
+                " | 25.708 | 12:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22"
+                " | 12:26:25 | 22 | 2022 | -0500 | EST" );
+
+  auto loc = std::locale::classic();
+  auto smod = std::format(loc, "{:%Ec %EC %Od %Oe %OH %OI %Om %OM %OS %Ou %OU"
+                              " %Ow %OW %Ex %EX %Oy %Ey %EY %Ez %Oz}", zt);
+  s = std::format("{:%c %C %d %e %H %I %m %M %S %u %U"
+                 " %w %W %x %X %y %y %Y -05:00 -05:00}",
+                 zoned_time<seconds>(zone, time_point_cast<seconds>(t)));
+  VERIFY( smod == s );
+}
+
+int main()
+{
+  test_ostream();
+  test_format();
+}
-- 
2.41.0

Reply via email to