https://gcc.gnu.org/g:e52f71b6dd888f0d5548d9f5bd139601dbafb3be

commit r15-9631-ge52f71b6dd888f0d5548d9f5bd139601dbafb3be
Author: Tomasz Kamiński <tkami...@redhat.com>
Date:   Mon May 5 16:32:58 2025 +0200

    libstdc++: Fix width computation for the chrono formatting [PR120114]
    
    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.
    
    Reviewed-by: Jonathan Wakely <jwak...@redhat.com>
    Signed-off-by: Tomasz Kamiński <tkami...@redhat.com>
    (cherry picked from commit 52f6ab55051ff43fd1b40ff06d9501043f8ba844)

Diff:
---
 libstdc++-v3/include/bits/chrono_io.h              |  10 +-
 libstdc++-v3/testsuite/std/time/format/pr120114.cc | 125 +++++++++++++++++++++
 2 files changed, 133 insertions(+), 2 deletions(-)

diff --git a/libstdc++-v3/include/bits/chrono_io.h 
b/libstdc++-v3/include/bits/chrono_io.h
index b7f6f5f49e5e..f5aa8a3e18af 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -705,8 +705,14 @@ namespace __format
            if (__write_direct)
              return __out;
 
-         auto __str = std::move(__sink).get();
-         return __format::__write_padded_as_spec(__str, __str.size(),
+         auto __span = __sink.view();
+         __string_view __str(__span.data(), __span.size());
+         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 000000000000..c630bb35a9d0
--- /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>();
+}

Reply via email to