On Tue, 6 May 2025 at 13:35, Tomasz Kamiński <tkami...@redhat.com> wrote: > > Use `__unicode::_field_width` to compute the field width of the output when > writting > the formatted output for std::chrono::types. This applies both to characters > copied > from format string, and one produced by localized formatting. > > We also use _Str_sink::view() instead of get(), which avoids copying the > content of > the buffer to std::string in case of small output. > > PR libstdc++/120114 > > libstdc++-v3/ChangeLog: > > * include/bits/chrono_io.h (__formatter_chrono::_M_format): Use > __field_width. > * testsuite/std/time/format/pr120114.cc: New test. > --- > Reduced the dg-options to just set add -fexec-charset=UTF-8. > OK for trunk?
OK thanks. I think this is OK for backport to gcc-15 too, but I think it's not needed on gcc-14 (because on that branch chrono formatting already has some flaws regarding non-ASCII text). > > libstdc++-v3/include/bits/chrono_io.h | 9 +- > .../testsuite/std/time/format/pr120114.cc | 125 ++++++++++++++++++ > 2 files changed, 132 insertions(+), 2 deletions(-) > create mode 100644 libstdc++-v3/testsuite/std/time/format/pr120114.cc > > diff --git a/libstdc++-v3/include/bits/chrono_io.h > b/libstdc++-v3/include/bits/chrono_io.h > index b7f6f5f49e5..620227a9f35 100644 > --- a/libstdc++-v3/include/bits/chrono_io.h > +++ b/libstdc++-v3/include/bits/chrono_io.h > @@ -705,8 +705,13 @@ namespace __format > if (__write_direct) > return __out; > > - auto __str = std::move(__sink).get(); > - return __format::__write_padded_as_spec(__str, __str.size(), > + auto __str = __sink.view(); > + size_t __width; > + if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>()) > + __width = __unicode::__field_width(__str); > + else > + __width = __str.size(); > + return __format::__write_padded_as_spec(__str, __width, > __fc, _M_spec); > } > > diff --git a/libstdc++-v3/testsuite/std/time/format/pr120114.cc > b/libstdc++-v3/testsuite/std/time/format/pr120114.cc > new file mode 100644 > index 00000000000..c630bb35a9d > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/time/format/pr120114.cc > @@ -0,0 +1,125 @@ > +// { dg-do run { target c++23 } } > +// { dg-options "-fexec-charset=UTF-8" } > +// { dg-timeout-factor 2 } > + > +#include <algorithm> > +#include <chrono> > +#include <testsuite_hooks.h> > + > +#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) > +#define WIDEN(S) WIDEN_(_CharT, S) > + > +template<typename _CharT> > +void > +test_from_format_string() > +{ > + std::basic_string<_CharT> res; > + using namespace std::chrono_literals; > + auto date = 2025y/std::chrono::May/05d; > + > + res = std::format(WIDEN("{:+<13%F\U0001f921}"), date); > + VERIFY( res == WIDEN("2025-05-05\U0001f921+") ); > + > + res = std::format(WIDEN("{:->15%F\U0001f921}"), date); > + VERIFY( res == WIDEN("---2025-05-05\U0001f921") ); > + > + res = std::format(WIDEN("{:=^20%F\U0001f921}"), date); > + VERIFY( res == WIDEN("====2025-05-05\U0001f921====") ); > +} > + > +template<typename _CharT> > +void > +test_formatted_value() > +{ > + // Custom time_put facet which returns Ideographic Telegraph Symbol > + // for given month for Om. > + struct TimePut : std::time_put<_CharT> > + { > + using iter_type = std::time_put<_CharT>::iter_type; > + using char_type = std::time_put<_CharT>::char_type; > + > + iter_type > + do_put(iter_type out, std::ios_base& io, char_type fill, const tm* t, > + char format, char modifier) const override > + { > + if (format != 'm' && modifier != 'm') > + return std::time_put<_CharT>::do_put(out, io, fill, t, format, > modifier); > + std::basic_string_view<_CharT> str; > + switch (t->tm_mon) > + { > + case 0: > + str = WIDEN("\u32C0"); > + break; > + case 1: > + str = WIDEN("\u32C1"); > + break; > + case 2: > + str = WIDEN("\u32C2"); > + break; > + case 3: > + str = WIDEN("\u32C3"); > + break; > + case 4: > + str = WIDEN("\u32C4"); > + break; > + case 5: > + str = WIDEN("\u32C5"); > + break; > + case 6: > + str = WIDEN("\u32C6"); > + break; > + case 7: > + str = WIDEN("\u32C7"); > + break; > + case 8: > + str = WIDEN("\u32C8"); > + break; > + case 9: > + str = WIDEN("\u32C9"); > + break; > + case 10: > + str = WIDEN("\u32CA"); > + break; > + case 11: > + str = WIDEN("\u32CB"); > + break; > + }; > + return std::copy(str.begin(), str.end(), out); > + } > + }; > + const std::locale loc(std::locale::classic(), new TimePut); > + > + std::basic_string<_CharT> res; > + > + res = std::format(loc, WIDEN("{:<1L%Om}"), std::chrono::January); > + VERIFY( res == WIDEN("\u32C0") ); > + > + res = std::format(loc, WIDEN("{:>2L%Om}"), std::chrono::February); > + VERIFY( res == WIDEN("\u32C1") ); > + > + res = std::format(loc, WIDEN("{:<3L%Om}"), std::chrono::March); > + VERIFY( res == WIDEN("\u32C2 ") ); > + > + res = std::format(loc, WIDEN("{:^4L%Om}"), std::chrono::April); > + VERIFY( res == WIDEN(" \u32C3 ") ); > + > + res = std::format(loc, WIDEN("{:>5L%Om}"), std::chrono::May); > + VERIFY( res == WIDEN(" \u32C4") ); > + > + res = std::format(loc, WIDEN("{:+<6L%Om}"), std::chrono::June); > + VERIFY( res == WIDEN("\u32C5++++") ); > + > + res = std::format(loc, WIDEN("{:=^7L%Om}"), std::chrono::July); > + VERIFY( res == WIDEN("==\u32C6===") ); > + > + res = std::format(loc, WIDEN("{:->8L%Om}"), std::chrono::August); > + VERIFY( res == WIDEN("------\u32C7") ); > +} > + > +int main() > +{ > + test_from_format_string<char>(); > + test_from_format_string<wchar_t>(); > + test_formatted_value<char>(); > + test_formatted_value<wchar_t>(); > +} > -- > 2.49.0 >