[PATCH 2/4] libstdc++: Format empty chrono-spec for the time points and hh_mm_ss directly.

Tomasz Kamiński tkaminsk@redhat.com
Fri Jun 6 10:41:40 GMT 2025


This patch change implementation of the formatters for time points and hh_mm_ss,
so they no longer delegate to operator<< for ostream in case of empty chrono-spec.
As in case of calendar types, the formatters for specific type now provide
__formatter_chrono with default _ChronoSpec that are used in case if empty
chron-spec.

The configuration of __defSpec is straight forward, expect for the sys_time,
and local_time that print time, if the duration is convertible to days,
which is equivalent to setting _M_chrono_specs "%F" instead of "%F %T".
Furthermore, certain sys_time<Dur> do not support ostream operator, and
should not be formattable with empty spec - in such case default _M_chron_spec,
allowing the issue to still be detected in _M_parse.

Finally, _ChronoFormats are extended to cover required format strings.

libstdc++-v3/ChangeLog:

	* include/bits/chrono_io.h (_ChronoFormats::_S_ftz)
	(_ChronoFormats::_S_ft, _ChronoFormats::_S_t): Define.
	(__formatter_chrono::_M_format_to_ostream): Remove handling for
	time_points.
	(std::formatter<chrono::hh_mm_ss<_Dur>, _CharT>)
	(std::formatter<chrono::sys_time<_Dur>, _CharT>)
	(std::formatter<chrono::utc_time<_Dur>, _CharT>)
	(std::formatter<chrono::tai_time<_Dur>, _CharT>)
	(std::formatter<chrono::gps_time<_Dur>, _CharT>)
	(std::formatter<chrono::file_time<_Dur>, _CharT>)
	(std::formatter<chrono::local_time<_Dur>, _CharT>)
	(std::formatter<chrono::__detail::__local_time_fmt<_Dur>, _CharT>)
	(std::formatter<chrono::zoned_time<_Dur>, _CharT>):
	Define __defSpec, and pass it as argument to _M_prase and
	constructor of __formatter_chrono.
---
 libstdc++-v3/include/bits/chrono_io.h | 220 ++++++++++++++++----------
 1 file changed, 135 insertions(+), 85 deletions(-)

diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
index e36cf1ffd31..24c7dfb91c9 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -243,10 +243,25 @@ namespace __format
   {
     using _String_view = basic_string_view<_CharT>;
 
+    static consteval
+    _String_view
+    _S_ftz() noexcept
+    { return _GLIBCXX_WIDEN("%F %T %Z"); }
+
+    static consteval
+    _String_view
+    _S_ft() noexcept
+    { return _S_ftz().substr(0, 5); }
+
     static consteval
     _String_view
     _S_f() noexcept
-    { return _GLIBCXX_WIDEN("%F"); }
+    { return _S_ftz().substr(0, 2); }
+
+    static consteval
+    _String_view
+    _S_t() noexcept
+    { return _S_ftz().substr(3, 2); }
 
     static consteval
     _String_view
@@ -836,58 +851,17 @@ namespace __format
 	  __os.imbue(_M_locale(__fc));
 
 	  if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
-	    {
-	      // Format as "{:L%F %T}"
-	      auto __days = chrono::floor<chrono::days>(__t._M_time);
-	      __os << chrono::year_month_day(__days) << ' '
-		   << chrono::hh_mm_ss(__t._M_time - __days);
-
-	      // For __local_time_fmt the __is_neg flags says whether to
-	      // append " %Z" to the result.
-	      if (__is_neg)
-		{
-		  if (!__t._M_abbrev) [[unlikely]]
-		    __format::__no_timezone_available();
-		  else if constexpr (is_same_v<_CharT, char>)
-		    __os << ' ' << *__t._M_abbrev;
-		  else
-		    {
-		      __os << L' ';
-		      for (char __c : *__t._M_abbrev)
-			__os << __c;
-		    }
-		}
-	    }
+	    __builtin_trap();
+	  else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
+	    __builtin_trap();
+	  else if constexpr (chrono::__is_time_point_v<_Tp>)
+	     __builtin_trap();
 	  else
 	    {
-	      if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
-		__os << __t._M_date << ' ' << __t._M_time;
-	      else if constexpr (chrono::__is_time_point_v<_Tp>)
-		{
-		  // Need to be careful here because not all specializations
-		  // of chrono::sys_time can be written to an ostream.
-		  // For the specializations of time_point that can be
-		  // formatted with an empty chrono-specs, either it's a
-		  // sys_time with period greater or equal to days:
-		  if constexpr (is_convertible_v<_Tp, chrono::sys_days>)
-		    __os << _S_date(__t);
-		  // Or a local_time with period greater or equal to days:
-		  else if constexpr (is_convertible_v<_Tp, chrono::local_days>)
-		    __os << _S_date(__t);
-		  else // Or it's formatted as "{:L%F %T}":
-		    {
-		      auto __days = chrono::floor<chrono::days>(__t);
-		      __os << chrono::year_month_day(__days) << ' '
-			 << chrono::hh_mm_ss(__t - __days);
-		    }
-		}
-	      else
-		{
-		  if constexpr (chrono::__is_duration_v<_Tp>)
-		    if (__is_neg) [[unlikely]]
-		      __os << _S_plus_minus[1];
-		  __os << __t;
-		}
+	      if constexpr (chrono::__is_duration_v<_Tp>)
+		if (__is_neg) [[unlikely]]
+		  __os << _S_plus_minus[1];
+	      __os << __t;
 	    }
 
 	  auto __str = std::move(__os).str();
@@ -2438,7 +2412,7 @@ namespace __format
     {
       constexpr typename basic_format_parse_context<_CharT>::iterator
       parse(basic_format_parse_context<_CharT>& __pc)
-      { return _M_f._M_parse(__pc, __format::_TimeOfDay); }
+      { return _M_f._M_parse(__pc, __format::_TimeOfDay, __defSpec); }
 
       template<typename _Out>
 	typename basic_format_context<_Out, _CharT>::iterator
@@ -2447,7 +2421,15 @@ namespace __format
 	{ return _M_f._M_format(__t, __fc); }
 
     private:
-      __format::__formatter_chrono<_CharT> _M_f;
+      static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
+	{
+	  __format::_ChronoSpec<_CharT> __res{};
+	  __res._M_localized = true;
+	  __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_t();
+	  return __res;
+	}();
+
+      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
 #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
@@ -2492,7 +2474,7 @@ namespace __format
       constexpr typename basic_format_parse_context<_CharT>::iterator
       parse(basic_format_parse_context<_CharT>& __pc)
       {
-	auto __next = _M_f._M_parse(__pc, __format::_ZonedDateTime);
+	auto __next = _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec);
 	if constexpr (!__stream_insertable)
 	  if (_M_f._M_spec._M_chrono_specs.empty())
 	    __format::__invalid_chrono_spec(); // chrono-specs can't be empty
@@ -2510,7 +2492,22 @@ namespace __format
 	= requires (basic_ostream<_CharT>& __os,
 		    chrono::sys_time<_Duration> __t) { __os << __t; };
 
-      __format::__formatter_chrono<_CharT> _M_f;
+      static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
+	{
+	  __format::_ChronoSpec<_CharT> __res{};
+	  if constexpr (!__stream_insertable)
+	    return __res;
+	  else if constexpr (is_convertible_v<_Duration, chrono::days>)
+	    __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_f();
+	  else
+	    {
+	      __res._M_localized = true;
+	      __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
+	    }
+	  return __res;
+	}();
+
+      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
   template<typename _Duration, __format::__char _CharT>
@@ -2519,11 +2516,11 @@ namespace __format
     {
       constexpr typename basic_format_parse_context<_CharT>::iterator
       parse(basic_format_parse_context<_CharT>& __pc)
-      { return _M_f._M_parse(__pc, __format::_ZonedDateTime); }
+      { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); }
 
       template<typename _Out>
-        typename basic_format_context<_Out, _CharT>::iterator
-        format(const chrono::utc_time<_Duration>& __t,
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(const chrono::utc_time<_Duration>& __t,
 	       basic_format_context<_Out, _CharT>& __fc) const
 	{
 	  // Adjust by removing leap seconds to get equivalent sys_time.
@@ -2544,7 +2541,15 @@ namespace __format
     private:
       friend formatter<chrono::__detail::__utc_leap_second<_Duration>, _CharT>;
 
-      __format::__formatter_chrono<_CharT> _M_f;
+      static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
+	{
+	  __format::_ChronoSpec<_CharT> __res{};
+	  __res._M_localized = true;
+	  __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
+	  return __res;
+	}();
+
+      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
   template<typename _Duration, __format::__char _CharT>
@@ -2553,11 +2558,11 @@ namespace __format
     {
       constexpr typename basic_format_parse_context<_CharT>::iterator
       parse(basic_format_parse_context<_CharT>& __pc)
-      { return _M_f._M_parse(__pc, __format::_ZonedDateTime); }
+      { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); }
 
       template<typename _Out>
-        typename basic_format_context<_Out, _CharT>::iterator
-        format(const chrono::tai_time<_Duration>& __t,
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(const chrono::tai_time<_Duration>& __t,
 	       basic_format_context<_Out, _CharT>& __fc) const
 	{
 	  // Convert to __local_time_fmt with abbrev "TAI" and offset 0s.
@@ -2575,7 +2580,15 @@ namespace __format
 	}
 
     private:
-      __format::__formatter_chrono<_CharT> _M_f;
+      static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
+	{
+	  __format::_ChronoSpec<_CharT> __res{};
+	  __res._M_localized = true;
+	  __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
+	  return __res;
+	}();
+
+      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
   template<typename _Duration, __format::__char _CharT>
@@ -2584,11 +2597,11 @@ namespace __format
     {
       constexpr typename basic_format_parse_context<_CharT>::iterator
       parse(basic_format_parse_context<_CharT>& __pc)
-      { return _M_f._M_parse(__pc, __format::_ZonedDateTime); }
+      { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); }
 
       template<typename _Out>
-        typename basic_format_context<_Out, _CharT>::iterator
-        format(const chrono::gps_time<_Duration>& __t,
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(const chrono::gps_time<_Duration>& __t,
 	       basic_format_context<_Out, _CharT>& __fc) const
 	{
 	  // Convert to __local_time_fmt with abbrev "GPS" and offset 0s.
@@ -2606,7 +2619,15 @@ namespace __format
 	}
 
     private:
-      __format::__formatter_chrono<_CharT> _M_f;
+      static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
+	{
+	  __format::_ChronoSpec<_CharT> __res{};
+	  __res._M_localized = true;
+	  __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
+	  return __res;
+	}();
+
+      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
   template<typename _Duration, __format::__char _CharT>
@@ -2614,11 +2635,11 @@ namespace __format
     {
       constexpr typename basic_format_parse_context<_CharT>::iterator
       parse(basic_format_parse_context<_CharT>& __pc)
-      { return _M_f._M_parse(__pc, __format::_ZonedDateTime); }
+      { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); }
 
       template<typename _Out>
-        typename basic_format_context<_Out, _CharT>::iterator
-        format(const chrono::file_time<_Duration>& __t,
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(const chrono::file_time<_Duration>& __t,
 	       basic_format_context<_Out, _CharT>& __fc) const
 	{
 	  using namespace chrono;
@@ -2626,24 +2647,45 @@ namespace __format
 	}
 
     private:
-      __format::__formatter_chrono<_CharT> _M_f;
-    };
+       static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
+	{
+	  __format::_ChronoSpec<_CharT> __res{};
+	  __res._M_localized = true;
+	  __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
+	  return __res;
+	}();
+
+      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
+     };
 
   template<typename _Duration, __format::__char _CharT>
     struct formatter<chrono::local_time<_Duration>, _CharT>
     {
       constexpr typename basic_format_parse_context<_CharT>::iterator
       parse(basic_format_parse_context<_CharT>& __pc)
-      {  return _M_f._M_parse(__pc, __format::_DateTime); }
+      {  return _M_f._M_parse(__pc, __format::_DateTime, __defSpec); }
 
       template<typename _Out>
-        typename basic_format_context<_Out, _CharT>::iterator
-        format(const chrono::local_time<_Duration>& __t,
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(const chrono::local_time<_Duration>& __t,
 	       basic_format_context<_Out, _CharT>& __fc) const
 	{ return _M_f._M_format(__t, __fc); }
 
     private:
-      __format::__formatter_chrono<_CharT> _M_f;
+      static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
+	{
+	  __format::_ChronoSpec<_CharT> __res{};
+	  if constexpr (is_convertible_v<_Duration, chrono::days>)
+	    __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_f();
+	  else
+	    {
+	      __res._M_localized = true;
+	      __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
+	    }
+	  return __res;
+	}();
+
+      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
   template<typename _Duration, __format::__char _CharT>
@@ -2651,16 +2693,24 @@ namespace __format
     {
       constexpr typename basic_format_parse_context<_CharT>::iterator
       parse(basic_format_parse_context<_CharT>& __pc)
-      { return _M_f._M_parse(__pc, __format::_ZonedDateTime); }
+      { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); }
 
       template<typename _Out>
-        typename basic_format_context<_Out, _CharT>::iterator
-        format(const chrono::__detail::__local_time_fmt<_Duration>& __t,
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(const chrono::__detail::__local_time_fmt<_Duration>& __t,
 	       basic_format_context<_Out, _CharT>& __fc) const
-	{ return _M_f._M_format(__t, __fc, /* use %Z for {} */ true); }
+	{ return _M_f._M_format(__t, __fc); }
 
     private:
-      __format::__formatter_chrono<_CharT> _M_f;
+       static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
+	{
+	  __format::_ChronoSpec<_CharT> __res{};
+	  __res._M_localized = true;
+	  __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ftz();
+	  return __res;
+	}();
+
+      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
 #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
@@ -2669,8 +2719,8 @@ namespace __format
     : formatter<chrono::__detail::__local_time_fmt_for<_Duration>, _CharT>
     {
       template<typename _Out>
-        typename basic_format_context<_Out, _CharT>::iterator
-        format(const chrono::zoned_time<_Duration, _TimeZonePtr>& __tp,
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(const chrono::zoned_time<_Duration, _TimeZonePtr>& __tp,
 	       basic_format_context<_Out, _CharT>& __fc) const
 	{
 	  using _Ltf = chrono::__detail::__local_time_fmt_for<_Duration>;
@@ -2690,8 +2740,8 @@ namespace __format
     : formatter<chrono::utc_time<_Duration>, _CharT>
     {
       template<typename _Out>
-        typename basic_format_context<_Out, _CharT>::iterator
-        format(const chrono::__detail::__utc_leap_second<_Duration>& __t,
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(const chrono::__detail::__utc_leap_second<_Duration>& __t,
 	       basic_format_context<_Out, _CharT>& __fc) const
 	{ return this->_M_f._M_format(__t, __fc); }
     };
-- 
2.49.0



More information about the Libstdc++ mailing list