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>
This commit is contained in:
XU Kailiang 2025-03-01 13:23:21 +08:00 committed by Jonathan Wakely
parent 3c7f2fd8c4
commit c24a1d58bc
No known key found for this signature in database
2 changed files with 53 additions and 16 deletions

View file

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

View file

@ -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();
}