public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r14-2640] libstdc++: Implement correct locale-specific chrono formatting [PR110719]
@ 2023-07-19 11:37 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2023-07-19 11:37 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

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

commit r14-2640-gf4bce119f617dc4663fb43f55784908daf16b4b6
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Jul 18 12:46:07 2023 +0100

    libstdc++: Implement correct locale-specific chrono formatting [PR110719]
    
    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.

Diff:
---
 libstdc++-v3/include/bits/chrono_io.h              | 295 +++++++++++++--------
 libstdc++-v3/testsuite/std/time/clock/system/io.cc |  20 +-
 libstdc++-v3/testsuite/std/time/clock/utc/io.cc    |  12 +-
 libstdc++-v3/testsuite/std/time/zoned_time/io.cc   |  64 +++++
 4 files changed, 272 insertions(+), 119 deletions(-)

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();
+}

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

only message in thread, other threads:[~2023-07-19 11:37 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-19 11:37 [gcc r14-2640] libstdc++: Implement correct locale-specific chrono formatting [PR110719] Jonathan Wakely

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