[gcc r15-11263] libstdc++: Format chrono %a/%A/%b/%B/%p using locale's time_put [PR117214]
Jonathan Wakely
redi@gcc.gnu.org
Fri Jun 5 10:19:48 GMT 2026
https://gcc.gnu.org/g:bf0465b3e2c3142957444f46b9a3cd3719c8299f
commit r15-11263-gbf0465b3e2c3142957444f46b9a3cd3719c8299f
Author: XU Kailiang <xu2k3l4@outlook.com>
Date: Wed Jul 2 15:10:29 2025 +0800
libstdc++: Format chrono %a/%A/%b/%B/%p using locale's time_put [PR117214]
C++ formatting locale could have a custom time_put that performs
differently from the C locale, so do not use __timepunct directly.
This patch for gcc-15 was originally submitted for trunk, but then got
rebased and committed as r16-2063-g8ad5968a8dcb47. For the gcc-15
backport we need to go back to the original v1 patch.
libstdc++-v3/ChangeLog:
PR libstdc++/117214
* include/bits/chrono_io.h (__formatter_chrono::_M_a_A)
(__formatter_chrono::_M_b_B, __formatter_chrono::_M_p): Use
_M_locale_fmt to format %a/%A/%b/%B/%p.
* testsuite/std/time/format/pr117214_custom_timeput.cc: New
test.
Signed-off-by: XU Kailiang <xu2k3l4@outlook.com>
Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Diff:
---
libstdc++-v3/include/bits/chrono_io.h | 37 +++++++++-------------
.../std/time/format/pr117214_custom_timeput.cc | 36 +++++++++++++++++++++
2 files changed, 51 insertions(+), 22 deletions(-)
diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
index 243d3ff8026d..d84c6690f01c 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -843,14 +843,10 @@ namespace __format
__throw_format_error("format error: invalid weekday");
locale __loc = _M_locale(__ctx);
- const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
- const _CharT* __days[7];
- if (__full)
- __tp._M_days(__days);
- else
- __tp._M_days_abbreviated(__days);
- __string_view __str(__days[__wd.c_encoding()]);
- return _M_write(std::move(__out), __loc, __str);
+ struct tm __tm{};
+ __tm.tm_wday = __wd.c_encoding();
+ return _M_locale_fmt(std::move(__out), __loc, __tm,
+ __full ? 'A' : 'a', 0);
}
template<typename _Tp, typename _FormatContext>
@@ -864,14 +860,10 @@ namespace __format
if (!__m.ok())
__throw_format_error("format error: invalid month");
locale __loc = _M_locale(__ctx);
- const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
- const _CharT* __months[12];
- if (__full)
- __tp._M_months(__months);
- else
- __tp._M_months_abbreviated(__months);
- __string_view __str(__months[(unsigned)__m - 1]);
- return _M_write(std::move(__out), __loc, __str);
+ struct tm __tm{};
+ __tm.tm_mon = (unsigned)__m - 1;
+ return _M_locale_fmt(std::move(__out), __loc, __tm,
+ __full ? 'B' : 'b', 0);
}
template<typename _Tp, typename _FormatContext>
@@ -1196,13 +1188,14 @@ namespace __format
_FormatContext& __ctx) const
{
// %p The locale's equivalent of the AM/PM designations.
- auto __hms = _S_hms(__t);
+ auto __hi = _S_hms(__t).hours().count();
+ if (__hi >= 24) [[unlikely]]
+ __hi %= 24;
+
locale __loc = _M_locale(__ctx);
- const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
- const _CharT* __ampm[2];
- __tp._M_am_pm(__ampm);
- return _M_write(std::move(__out), __loc,
- __ampm[__hms.hours().count() >= 12]);
+ struct tm __tm{};
+ __tm.tm_hour = __hi;
+ return _M_locale_fmt(std::move(__out), __loc, __tm, 'p', 0);
}
template<typename _Tp, typename _FormatContext>
diff --git a/libstdc++-v3/testsuite/std/time/format/pr117214_custom_timeput.cc b/libstdc++-v3/testsuite/std/time/format/pr117214_custom_timeput.cc
new file mode 100644
index 000000000000..8c9f3d29bc6a
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/format/pr117214_custom_timeput.cc
@@ -0,0 +1,36 @@
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <format>
+#include <locale>
+#include <testsuite_hooks.h>
+
+struct custom_time_put : std::time_put<char>
+{
+ iter_type
+ do_put(iter_type out, std::ios_base& io, char_type fill, const tm* t,
+ char format, char modifier) const override
+ {
+ using Base = std::time_put<char>;
+
+ switch (format) {
+ case 'a': case 'A': case 'b': case 'B': case 'p':
+ *out++ = '[';
+ *out++ = format;
+ *out++ = ']';
+ }
+ return Base::do_put(out, io, fill, t, format, modifier);
+ }
+};
+
+int main()
+{
+ using namespace std::chrono;
+ std::locale loc(std::locale::classic(), new custom_time_put);
+#define test(t, fmt, exp) VERIFY( std::format(loc, fmt, t) == exp )
+ test(Monday, "{:L%a}", "[a]Mon");
+ test(Monday, "{:L%A}", "[A]Monday");
+ test(January, "{:L%b}", "[b]Jan");
+ test(January, "{:L%B}", "[B]January");
+ test(1h, "{:L%p}", "[p]AM");
+}
More information about the Libstdc++-cvs
mailing list