On Tue, Jun 30, 2026 at 3:18 PM Tomasz Kaminski <[email protected]> wrote:

>
>
> On Tue, Jun 30, 2026 at 2:50 PM Anlai Lu <[email protected]> wrote:
>
>> Add __detail::__chrono_write which formats a chrono object into a
>> 128-byte stack buffer via std::format_to_n with _S_empty_fs(), then
>> writes through __ostream_insert.  All chrono operator<< overloads
>> now use this helper instead of hand-rolled format strings or
>> basic_stringstream construction.
>>
>> libstdc++-v3/ChangeLog:
>>
>>         * include/bits/chrono_io.h (__formatter_chrono::_S_empty_fs):
>>         Make public.
>>         (__detail::__chrono_write): New function template.
>>         (operator<<): Use __chrono_write consistently for all chrono
>>         types.
>>
>> Signed-off-by: Anlai Lu <[email protected]>
>> ---
>>  libstdc++-v3/include/bits/chrono_io.h | 227 ++++++--------------------
>>  1 file changed, 49 insertions(+), 178 deletions(-)
>>
> This looks good to me, I only have a small stylistic suggestion and think
> local info
> It can also be modified in same way.
>
I have convinced myself that changing local_info would not give us any
performance
benefits, so please keep it as is.

>
> Note, I do not have approval rights, so you will need one from Jonathan.
>
>
>>
>> diff --git a/libstdc++-v3/include/bits/chrono_io.h
>> b/libstdc++-v3/include/bits/chrono_io.h
>> index f8bb485d1..a07543bab 100644
>> --- a/libstdc++-v3/include/bits/chrono_io.h
>> +++ b/libstdc++-v3/include/bits/chrono_io.h
>> @@ -884,11 +884,13 @@ namespace __format
>>        static constexpr const _CharT* _S_minus_empty_spec = _S_chars + 17;
>>        static constexpr const _CharT* _S_empty_spec = _S_chars + 18;
>>
>> +    public:
>>        [[__gnu__::__always_inline__]]
>>        static _Dynamic_format_string<_CharT>
>>        _S_empty_fs()
>>        { return _Dynamic_format_string<_CharT>(_S_empty_spec); }
>>
>> +    protected:
>>        static constexpr const _CharT* _S_weekdays[]
>>        {
>>         _GLIBCXX_WIDEN("Sunday"),
>> @@ -3607,6 +3609,28 @@ namespace __detail
>>         }
>>      }
>>
>> +  // Format into a stack buffer via std::format_to_n with the empty
>> +  // chrono-spec (_S_empty_fs), then write through __ostream_insert.
>> +  // The empty spec lets each formatter use its __defSpec, producing
>> +  // output equivalent to the corresponding operator<<.
>> +  template<typename _CharT, typename _Traits, typename _Arg,
>> +          typename... _OptLocale>
>> +    inline basic_ostream<_CharT, _Traits>&
>> +    __chrono_write(basic_ostream<_CharT, _Traits>& __os,
>> +                  const _Arg& __arg, const _OptLocale&... __loc)
>> +    {
>> +      static_assert(sizeof...(_OptLocale) <= 1);
>> +      using _Fmt = __format::__formatter_chrono<_CharT>;
>>
> I would save __fs here, instad of alias, something like:
>             constexpr auto __fs   =
> __format::__formatter_chrono<_CharT>::_S_empty_fs();
>
> +      constexpr size_t __bufsize = 128;
>> +      _CharT __buf[__bufsize];
>> +      auto __res = std::format_to_n(__buf, __bufsize, __loc...,
>> +                                   _Fmt::_S_empty_fs(), __arg);
>> +      if (static_cast<size_t>(__res.size) <= __bufsize) [[likely]]
>> +       return std::__ostream_insert(__os, __buf, __res.size);
>> +      auto __s = std::format(__loc..., _Fmt::_S_empty_fs(), __arg);
>> +      return std::__ostream_insert(__os, __s.data(), __s.size());
>> +    }
>> +
>>  } // namespace __detail
>>  /// @endcond
>>
>> @@ -3629,14 +3653,7 @@ namespace __detail
>>      inline basic_ostream<_CharT, _Traits>&
>>      operator<<(basic_ostream<_CharT, _Traits>& __os, const day& __d)
>>      {
>> -      using _Ctx = __format::__format_context<_CharT>;
>> -      using _Str = basic_string_view<_CharT>;
>> -      _Str __s = _GLIBCXX_WIDEN("{:02d} is not a valid day");
>> -      if (__d.ok())
>> -       __s = __s.substr(0, 6);
>> -      auto __u = (unsigned)__d;
>> -      __os << std::vformat(__s, make_format_args<_Ctx>(__u));
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __d);
>>      }
>>
>>    template<typename _CharT, typename _Traits,
>> @@ -3657,18 +3674,7 @@ namespace __detail
>>      inline basic_ostream<_CharT, _Traits>&
>>      operator<<(basic_ostream<_CharT, _Traits>& __os, const month& __m)
>>      {
>> -      using _Ctx = __format::__format_context<_CharT>;
>> -      using _Str = basic_string_view<_CharT>;
>> -      _Str __s = _GLIBCXX_WIDEN("{:L%b}{} is not a valid month");
>> -      if (__m.ok())
>> -       __os << std::vformat(__os.getloc(), __s.substr(0, 6),
>> -                            make_format_args<_Ctx>(__m));
>> -      else
>> -       {
>> -         auto __u = (unsigned)__m;
>> -         __os << std::vformat(__s.substr(6),
>> make_format_args<_Ctx>(__u));
>> -       }
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __m, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits,
>> @@ -3689,18 +3695,7 @@ namespace __detail
>>      inline basic_ostream<_CharT, _Traits>&
>>      operator<<(basic_ostream<_CharT, _Traits>& __os, const year& __y)
>>      {
>> -      using _Ctx = __format::__format_context<_CharT>;
>> -      using _Str = basic_string_view<_CharT>;
>> -      _Str __s = _GLIBCXX_WIDEN("-{:04d} is not a valid year");
>> -      if (__y.ok())
>> -       __s = __s.substr(0, 7);
>> -      int __i = (int)__y;
>> -      if (__i >= 0) [[likely]]
>> -       __s.remove_prefix(1);
>> -      else
>> -       __i = -__i;
>> -      __os << std::vformat(__s, make_format_args<_Ctx>(__i));
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __y);
>>      }
>>
>>    template<typename _CharT, typename _Traits,
>> @@ -3721,18 +3716,7 @@ namespace __detail
>>      inline basic_ostream<_CharT, _Traits>&
>>      operator<<(basic_ostream<_CharT, _Traits>& __os, const weekday& __wd)
>>      {
>> -      using _Ctx = __format::__format_context<_CharT>;
>> -      using _Str = basic_string_view<_CharT>;
>> -      _Str __s = _GLIBCXX_WIDEN("{:L%a}{} is not a valid weekday");
>> -      if (__wd.ok())
>> -       __os << std::vformat(__os.getloc(), __s.substr(0, 6),
>> -                            make_format_args<_Ctx>(__wd));
>> -      else
>> -       {
>> -         auto __c = __wd.c_encoding();
>> -         __os << std::vformat(__s.substr(6),
>> make_format_args<_Ctx>(__c));
>> -       }
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __wd, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits,
>> @@ -3754,23 +3738,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const weekday_indexed& __wdi)
>>      {
>> -      // The standard says to format wdi.weekday() and wdi.index() using
>> -      // either "{:L}[{}]" or "{:L}[{} is not a valid index]". The {:L}
>> spec
>> -      // means to format the weekday using ostringstream, so just do
>> that.
>> -      basic_stringstream<_CharT> __os2;
>> -      __os2.imbue(__os.getloc());
>> -      __os2 << __wdi.weekday();
>> -      const auto __i = __wdi.index();
>> -      basic_string_view<_CharT> __s
>> -       = _GLIBCXX_WIDEN("[ is not a valid index]");
>> -      __os2 << __s[0];
>> -      __os2 << std::format(_GLIBCXX_WIDEN("{}"), __i);
>> -      if (__i >= 1 && __i <= 5)
>> -       __os2 << __s.back();
>> -      else
>> -       __os2 << __s.substr(1);
>> -      __os << __os2.view();
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __wdi, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits>
>> @@ -3778,29 +3746,14 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const weekday_last& __wdl)
>>      {
>> -      // As above, just write straight to a stringstream, as if by
>> "{:L}[last]"
>> -      basic_stringstream<_CharT> __os2;
>> -      __os2.imbue(__os.getloc());
>> -      __os2 << __wdl.weekday() << _GLIBCXX_WIDEN("[last]");
>> -      __os << __os2.view();
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __wdl, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits>
>>      inline basic_ostream<_CharT, _Traits>&
>>      operator<<(basic_ostream<_CharT, _Traits>& __os, const month_day&
>> __md)
>>      {
>> -      // As above, just write straight to a stringstream, as if by
>> "{:L}/{}"
>> -      basic_stringstream<_CharT> __os2;
>> -      __os2.imbue(__os.getloc());
>> -      __os2 << __md.month();
>> -      if constexpr (is_same_v<_CharT, char>)
>> -       __os2 << '/';
>> -      else
>> -       __os2 << L'/';
>> -      __os2 << __md.day();
>> -      __os << __os2.view();
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __md, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits,
>> @@ -3824,12 +3777,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const month_day_last& __mdl)
>>      {
>> -      // As above, just write straight to a stringstream, as if by
>> "{:L}/last"
>> -      basic_stringstream<_CharT> __os2;
>> -      __os2.imbue(__os.getloc());
>> -      __os2 << __mdl.month() << _GLIBCXX_WIDEN("/last");
>> -      __os << __os2.view();
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __mdl, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits>
>> @@ -3837,17 +3785,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const month_weekday& __mwd)
>>      {
>> -      // As above, just write straight to a stringstream, as if by
>> "{:L}/{:L}"
>> -      basic_stringstream<_CharT> __os2;
>> -      __os2.imbue(__os.getloc());
>> -      __os2 << __mwd.month();
>> -      if constexpr (is_same_v<_CharT, char>)
>> -       __os2 << '/';
>> -      else
>> -       __os2 << L'/';
>> -      __os2 << __mwd.weekday_indexed();
>> -      __os << __os2.view();
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __mwd, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits>
>> @@ -3855,34 +3793,14 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const month_weekday_last& __mwdl)
>>      {
>> -      // As above, just write straight to a stringstream, as if by
>> "{:L}/{:L}"
>> -      basic_stringstream<_CharT> __os2;
>> -      __os2.imbue(__os.getloc());
>> -      __os2 << __mwdl.month();
>> -      if constexpr (is_same_v<_CharT, char>)
>> -       __os2 << '/';
>> -      else
>> -       __os2 << L'/';
>> -      __os2 << __mwdl.weekday_last();
>> -      __os << __os2.view();
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __mwdl, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits>
>>      inline basic_ostream<_CharT, _Traits>&
>>      operator<<(basic_ostream<_CharT, _Traits>& __os, const year_month&
>> __ym)
>>      {
>> -      // As above, just write straight to a stringstream, as if by
>> "{}/{:L}"
>> -      basic_stringstream<_CharT> __os2;
>> -      __os2.imbue(__os.getloc());
>> -      __os2 << __ym.year();
>> -      if constexpr (is_same_v<_CharT, char>)
>> -       __os2 << '/';
>> -      else
>> -       __os2 << L'/';
>> -      __os2 << __ym.month();
>> -      __os << __os2.view();
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __ym, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits,
>> @@ -3906,12 +3824,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const year_month_day& __ymd)
>>      {
>> -      using _Ctx = __format::__format_context<_CharT>;
>> -      using _Str = basic_string_view<_CharT>;
>> -      _Str __s = _GLIBCXX_WIDEN("{:%F} is not a valid date");
>> -      __os << std::vformat(__ymd.ok() ? __s.substr(0, 5) : __s,
>> -                          make_format_args<_Ctx>(__ymd));
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __ymd);
>>      }
>>
>>    template<typename _CharT, typename _Traits,
>> @@ -3936,17 +3849,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const year_month_day_last& __ymdl)
>>      {
>> -      // As above, just write straight to a stringstream, as if by
>> "{}/{:L}"
>> -      basic_stringstream<_CharT> __os2;
>> -      __os2.imbue(__os.getloc());
>> -      __os2 << __ymdl.year();
>> -      if constexpr (is_same_v<_CharT, char>)
>> -       __os2 << '/';
>> -      else
>> -       __os2 << L'/';
>> -      __os2 << __ymdl.month_day_last();
>> -      __os << __os2.view();
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __ymdl, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits>
>> @@ -3954,19 +3857,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const year_month_weekday& __ymwd)
>>      {
>> -      // As above, just write straight to a stringstream, as if by
>> -      // "{}/{:L}/{:L}"
>> -      basic_stringstream<_CharT> __os2;
>> -      __os2.imbue(__os.getloc());
>> -      _CharT __slash;
>> -      if constexpr (is_same_v<_CharT, char>)
>> -       __slash = '/';
>> -      else
>> -       __slash = L'/';
>> -      __os2 << __ymwd.year() << __slash << __ymwd.month() << __slash
>> -           << __ymwd.weekday_indexed();
>> -      __os << __os2.view();
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __ymwd, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits>
>> @@ -3974,19 +3865,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const year_month_weekday_last& __ymwdl)
>>      {
>> -      // As above, just write straight to a stringstream, as if by
>> -      // "{}/{:L}/{:L}"
>> -      basic_stringstream<_CharT> __os2;
>> -      __os2.imbue(__os.getloc());
>> -      _CharT __slash;
>> -      if constexpr (is_same_v<_CharT, char>)
>> -       __slash = '/';
>> -      else
>> -       __slash = L'/';
>> -      __os2 << __ymwdl.year() << __slash << __ymwdl.month() << __slash
>> -           << __ymwdl.weekday_last();
>> -      __os << __os2.view();
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __ymwdl, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits, typename _Duration>
>> @@ -3994,7 +3873,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const hh_mm_ss<_Duration>& __hms)
>>      {
>> -      return __os << format(__os.getloc(), _GLIBCXX_WIDEN("{:L%T}"),
>> __hms);
>> +      return __detail::__chrono_write(__os, __hms, __os.getloc());
>>      }
>>
>>  #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
>> @@ -4003,7 +3882,7 @@ namespace __detail
>>      basic_ostream<_CharT, _Traits>&
>>      operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __i)
>>      {
>> -      return __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{}"),
>> __i);
>> +      return __detail::__chrono_write(__os, __i, __os.getloc());
>>      }
>>
>>    /// Writes a local_info object to an ostream in an unspecified format.
>> @@ -4033,8 +3912,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const zoned_time<_Duration, _TimeZonePtr>& __t)
>>      {
>> -      __os << format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T %Z}"), __t);
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __t, __os.getloc());
>>      }
>>  #endif
>>
>> @@ -4045,16 +3923,14 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const sys_time<_Duration>& __tp)
>>      {
>> -      __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"),
>> __tp);
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __tp, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits>
>>      inline basic_ostream<_CharT, _Traits>&
>>      operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_days&
>> __dp)
>>      {
>> -      __os << year_month_day{__dp};
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __dp);
>>      }
>>
>>    template<typename _CharT, typename _Traits, typename _Duration,
>> @@ -4090,8 +3966,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const utc_time<_Duration>& __t)
>>      {
>> -      __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"),
>> __t);
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __t, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits, typename _Duration,
>> @@ -4125,8 +4000,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const tai_time<_Duration>& __t)
>>      {
>> -      __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"),
>> __t);
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __t, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits, typename _Duration,
>> @@ -4164,8 +4038,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const gps_time<_Duration>& __t)
>>      {
>> -      __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"),
>> __t);
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __t, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits, typename _Duration,
>> @@ -4202,8 +4075,7 @@ namespace __detail
>>      operator<<(basic_ostream<_CharT, _Traits>& __os,
>>                const file_time<_Duration>& __t)
>>      {
>> -      __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"),
>> __t);
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __t, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits, typename _Duration,
>> @@ -4228,8 +4100,7 @@ namespace __detail
>>      // 4257. Stream insertion for chrono::local_time should be
>> constrained
>>      requires requires(const sys_time<_Duration>& __st) { __os << __st; }
>>      {
>> -      __os << sys_time<_Duration>{__lt.time_since_epoch()};
>> -      return __os;
>> +      return __detail::__chrono_write(__os, __lt, __os.getloc());
>>      }
>>
>>    template<typename _CharT, typename _Traits, typename _Duration,
>> --
>> 2.34.1
>>
>>
>>

Reply via email to