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? 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