On Thu, Mar 20, 2025 at 10:31 AM Jonathan Wakely <jwak...@redhat.com> wrote:
> From: XU Kailiang <xu2k...@outlook.com> > > 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 <xu2k...@outlook.com> > > Co-authored-by: Jonathan Wakely <jwak...@redhat.com> > --- > > Tested x86_64-linux. > LGTM > > libstdc++-v3/include/bits/chrono_io.h | 35 ++++++++++--------- > .../testsuite/std/time/format/pr117214.cc | 34 ++++++++++++++++++ > 2 files changed, 53 insertions(+), 16 deletions(-) > create mode 100644 libstdc++-v3/testsuite/std/time/format/pr117214.cc > > diff --git a/libstdc++-v3/include/bits/chrono_io.h > b/libstdc++-v3/include/bits/chrono_io.h > index 55ebd4ee061..86338d48d18 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 00000000000..e7831832206 > --- /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(); > +} > -- > 2.48.1 > >