[gcc r15-8490] libstdc++: Fix localized D_T_FMT %c formatting for <chrono> [PR117214]

Jonathan Wakely redi@gcc.gnu.org
Fri Mar 21 10:28:58 GMT 2025


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

commit r15-8490-gc24a1d58bc02daabafd5c94fca0984b70db811c8
Author: XU Kailiang <xu2k3l4@outlook.com>
Date:   Sat Mar 1 13:23:21 2025 +0800

    libstdc++: Fix localized D_T_FMT %c formatting for <chrono> [PR117214]
    
    Formatting a time point with %c was implemented by calling
    std::vprint_to with format string constructed from locale's D_T_FMT
    string, but in some locales this string contains strftime specifiers
    which are not valid for chrono-specs, e.g. %l. So just use _M_locale_fmt
    to avoid this problem.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/117214
            * include/bits/chrono_io.h (__formatter_chrono::_M_c): Use
            _M_locale_fmt to format %c time point.
            * testsuite/std/time/format/pr117214.cc: New test.
    
    Signed-off-by: XU Kailiang <xu2k3l4@outlook.com>
    
    Co-authored-by: Jonathan Wakely <jwakely@redhat.com>

Diff:
---
 libstdc++-v3/include/bits/chrono_io.h              | 35 ++++++++++++----------
 libstdc++-v3/testsuite/std/time/format/pr117214.cc | 34 +++++++++++++++++++++
 2 files changed, 53 insertions(+), 16 deletions(-)

diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
index 55ebd4ee0610..86338d48d183 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -887,27 +887,30 @@ namespace __format
 
       template<typename _Tp, typename _FormatContext>
 	typename _FormatContext::iterator
-	_M_c(const _Tp& __tt, typename _FormatContext::iterator __out,
+	_M_c(const _Tp& __t, typename _FormatContext::iterator __out,
 	     _FormatContext& __ctx, bool __mod = false) const
 	{
 	  // %c  Locale's date and time representation.
 	  // %Ec Locale's alternate date and time representation.
 
-	  basic_string<_CharT> __fmt;
-	  auto __t = _S_floor_seconds(__tt);
-	  locale __loc = _M_locale(__ctx);
-	  const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
-	  const _CharT* __formats[2];
-	  __tp._M_date_time_formats(__formats);
-	  if (*__formats[__mod]) [[likely]]
-	    {
-	      __fmt = _GLIBCXX_WIDEN("{:L}");
-	      __fmt.insert(3u, __formats[__mod]);
-	    }
-	  else
-	    __fmt = _GLIBCXX_WIDEN("{:L%a %b %e %T %Y}");
-	  return std::vformat_to(std::move(__out), __loc, __fmt,
-				 std::make_format_args<_FormatContext>(__t));
+	  using namespace chrono;
+	  auto __d = _S_days(__t); // Either sys_days or local_days.
+	  using _TDays = decltype(__d);
+	  const year_month_day __ymd(__d);
+	  const auto __y = __ymd.year();
+	  const auto __hms = _S_hms(__t);
+
+	  struct tm __tm{};
+	  __tm.tm_year = (int)__y - 1900;
+	  __tm.tm_yday = (__d - _TDays(__y/January/1)).count();
+	  __tm.tm_mon = (unsigned)__ymd.month() - 1;
+	  __tm.tm_mday = (unsigned)__ymd.day();
+	  __tm.tm_wday = weekday(__d).c_encoding();
+	  __tm.tm_hour = __hms.hours().count();
+	  __tm.tm_min = __hms.minutes().count();
+	  __tm.tm_sec = __hms.seconds().count();
+	  return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, 'c',
+			       __mod ? 'E' : '\0');
 	}
 
       template<typename _Tp, typename _FormatContext>
diff --git a/libstdc++-v3/testsuite/std/time/format/pr117214.cc b/libstdc++-v3/testsuite/std/time/format/pr117214.cc
new file mode 100644
index 000000000000..e78318322065
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/format/pr117214.cc
@@ -0,0 +1,34 @@
+// { dg-do run { target c++20 } }
+// { dg-require-namedlocale "aa_DJ.UTF-8" }
+// { dg-require-namedlocale "ar_SA.UTF-8" }
+// { dg-require-namedlocale "ca_AD.UTF-8" }
+// { dg-require-namedlocale "az_IR.UTF-8" }
+// { dg-require-namedlocale "my_MM.UTF-8" }
+
+#include <chrono>
+#include <locale>
+#include <testsuite_hooks.h>
+
+void
+test_c()
+{
+  const char *test_locales[] = {
+    "aa_DJ.UTF-8",
+    "ar_SA.UTF-8",
+    "ca_AD.UTF-8",
+    "az_IR.UTF-8",
+    "my_MM.UTF-8",
+  };
+  std::chrono::sys_seconds t{std::chrono::seconds{1}};
+
+  for (auto locale_name : test_locales)
+  {
+    auto s = std::format(std::locale(locale_name), "{:L%c}", t);
+    VERIFY( !s.empty() );
+  }
+}
+
+int main()
+{
+  test_c();
+}


More information about the Gcc-cvs mailing list