On 01/07/2026 13:33, Tomasz Kaminski wrote:
> 
> 
> On Wed, Jul 1, 2026 at 2:15 PM Anlai Lu <[email protected] <mailto:[email protected]>> 
> wrote:
> 
>     Add __detail::__chrono_write which formats a chrono object into a
>     stack buffer via std::format_to_n with _S_empty_fs(), then writes
>     through __ostream_insert.  A non-type template parameter _BufSize
>     (default 128) allows per-type buffer tuning.  Each _BufSize is the
>     smallest power of two that accommodates the longest possible formatted
>     output for that type, including error cases.  All chrono operator<<
>     overloads that previously used std::format, std::vformat, or
>     basic_stringstream now use this helper.
> 
>     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] <mailto:[email protected]>>
>     ---
>      libstdc++-v3/include/bits/chrono_io.h | 244 ++++++--------------------
>      1 file changed, 52 insertions(+), 192 deletions(-)
> 
> LGTM. Thank you for working on this. 
> 

I think this is causing:

/home/rearnsha/gnusrc/gcc-cross/misc-fixes/libstdc++-v3/src/c++20/tzdb.cc: In st
atic member function ‘static const std::chrono::tzdb& std::chrono::tzdb_list::_N
ode::_S_replace_head(std::shared_ptr<std::chrono::tzdb_list::_Node>, std::shared
_ptr<std::chrono::tzdb_list::_Node>)’:
/home/rearnsha/gnusrc/gcc-cross/misc-fixes/libstdc++-v3/src/c++20/tzdb.cc:1617:22:
 error: ‘struct std::chrono::tzdb_list::_Node::NumLeapSeconds’ has no member 
named ‘set_locked’
 1617 |     num_leap_seconds.set_locked(new_head_ptr->db.leap_seconds.size(), 
lock);
      |                      ^~~~~~~~~~
make[9]: *** [Makefile:575: tzdb.lo] Error 1

when building an arm-eabi cross with newlib.

> 
>     diff --git a/libstdc++-v3/include/bits/chrono_io.h 
> b/libstdc++-v3/include/bits/chrono_io.h
>     index f8bb485d1..d3740a0e1 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,30 @@ 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<<.
>     +  // _BufSize allows per-type tuning of the stack buffer size.
>     +  template<size_t _BufSize = 128, typename _CharT, typename _Traits,
> 
> I am wondering about making buffer size explicit on each call, and
> removing the default. But I will handle that before committing, if Jonathan
> also like that direction.
> 
>     +          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);
>     +      constexpr auto __fs
>     +       = __format::__formatter_chrono<_CharT>::_S_empty_fs;
> 
> Nice trick for storing a function pointer. I really like it.
>  
> 
>     +      constexpr size_t __bufsize = _BufSize;
>     +      _CharT __buf[__bufsize];
>     +      auto __res = std::format_to_n(__buf, __bufsize, __loc...,
>     +                                   __fs(), __arg);
>     +      if (static_cast<size_t>(__res.size) <= __bufsize) [[likely]]
>     +       return std::__ostream_insert(__os, __buf, __res.size);
>     +      auto __s = std::format(__loc..., __fs(), __arg);
>     +      return std::__ostream_insert(__os, __s.data(), __s.size());
>     +    }
>     +
>      } // namespace __detail
>      /// @endcond
> 
>     @@ -3629,14 +3655,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<32>(__os, __d);
>          }
> 
>        template<typename _CharT, typename _Traits,
>     @@ -3657,18 +3676,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 +3697,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<32>(__os, __y);
>          }
> 
>        template<typename _CharT, typename _Traits,
>     @@ -3721,18 +3718,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 +3740,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 +3748,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 +3779,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 +3787,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 +3795,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 +3826,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<64>(__os, __ymd);
>          }
> 
>        template<typename _CharT, typename _Traits,
>     @@ -3936,17 +3851,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 +3859,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 +3867,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 +3875,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<64>(__os, __hms, __os.getloc());
>          }
> 
>      #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
>     @@ -4003,7 +3884,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.
>     @@ -4011,20 +3892,7 @@ namespace __detail
>          basic_ostream<_CharT, _Traits>&
>          operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& 
> __li)
>          {
>     -      __os << __format::_Separators<_CharT>::_S_squares()[0];
>     -      if (__li.result == local_info::unique)
>     -       __os << __li.first;
>     -      else
>     -       {
>     -         if (__li.result == local_info::nonexistent)
>     -           __os << _GLIBCXX_WIDEN("nonexistent");
>     -         else
>     -           __os << _GLIBCXX_WIDEN("ambiguous");
>     -         __os << _GLIBCXX_WIDEN(" local time between ") << __li.first;
>     -         __os << _GLIBCXX_WIDEN(" and ") << __li.second;
>     -       }
>     -      __os << __format::_Separators<_CharT>::_S_squares()[1];
>     -      return __os;
>     +      return __detail::__chrono_write<256>(__os, __li);
>          }
> 
>        template<typename _CharT, typename _Traits, typename _Duration,
>     @@ -4033,8 +3901,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 +3912,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<64>(__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<32>(__os, __dp);
>          }
> 
>        template<typename _CharT, typename _Traits, typename _Duration,
>     @@ -4090,8 +3955,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<64>(__os, __t, __os.getloc());
>          }
> 
>        template<typename _CharT, typename _Traits, typename _Duration,
>     @@ -4125,8 +3989,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<64>(__os, __t, __os.getloc());
>          }
> 
>        template<typename _CharT, typename _Traits, typename _Duration,
>     @@ -4164,8 +4027,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<64>(__os, __t, __os.getloc());
>          }
> 
>        template<typename _CharT, typename _Traits, typename _Duration,
>     @@ -4202,8 +4064,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<64>(__os, __t, __os.getloc());
>          }
> 
>        template<typename _CharT, typename _Traits, typename _Duration,
>     @@ -4228,8 +4089,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<64>(__os, __lt, __os.getloc());
>          }
> 
>        template<typename _CharT, typename _Traits, typename _Duration,
>     -- 
>     2.34.1
> 

Reply via email to