On Tue, 6 May 2025 at 13:35, Tomasz Kamiński <[email protected]> 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
>