diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2024-07-31 16:32:44 +0100 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2024-08-23 13:39:35 +0100 |
commit | 43b8153c26655a7a00f1584fcb4f511dc5e45fab (patch) | |
tree | 1b9ab24e6abd63b4361e5d4d6a98b35dba61b5d3 /gcc | |
parent | 591b71993f15ed95eb38f3314f3d9ac159b9d051 (diff) | |
download | gcc-43b8153c26655a7a00f1584fcb4f511dc5e45fab.zip gcc-43b8153c26655a7a00f1584fcb4f511dc5e45fab.tar.gz gcc-43b8153c26655a7a00f1584fcb4f511dc5e45fab.tar.bz2 |
libstdc++: Only use std::time_put in std::format for non-C locales
When testing on Solaris I noticed that std/time/year/io.cc was FAILing
because the year 1642 was being formatted as "+(" by %Ey. This turns out
to be because we defer to std::time_put for modified conversion specs,
and std::time_put uses std::strftime, and that's undefined for years
before 1970. In particular, years before 1900 mean that the tm_year
field is negative, which then causes incorrect results from strftime on
at least Solaris and AIX.
I've raised the general problem with LWG, but we can fix the FAILing
test case (and probably improve performance slightly) by ignoring the E
and O modifiers when the formatting locale is the "C" locale. The
modifiers have no effect for the C locale, so we can just treat %Ey as
%y and format it directly. This doesn't fix anything when the formatting
locale isn't the C locale, but that case is not adequately tested, so
doesn't cause any FAIL right now!
The naïve fix would be simply:
if (__mod)
if (auto __loc = _M_locale(__ctx); __loc != locale::classic())
// ...
However when the format string doesn't use the 'L' option, _M_locale
always returns locale::classic(). In that case, we make a copy of the
classic locale (which calls the non-inline copy constructor in
the library), then make another copy of the classic locale, then compare
the two. We can avoid all that by checking for the 'L' option first,
instead of letting _M_locale do that:
if (__mod && _M_spec._M_localized)
if (auto __loc = __ctx.locale(); __loc != locale::classic())
// ...
We could optimize this further if we had a __is_classic(__loc) function
that would do the __loc == locale::classic() check without making any
copies or non-inline calls. That would require examining the locale's
_M_impl member, and probably require checking its name, because the
locale::_S_classic singleton is not exported from the library.
For _M_S the change is slightly different from the other functions,
because if we skip using std::time_put for %OS then we fall through to
the code that potentially prints fractional seconds, but the %OS format
only prints whole seconds. So we need to format whole seconds directly
when not using std::time_put, instead of falling through to the code
below.
libstdc++-v3/ChangeLog:
* include/bits/chrono_io.h (__formatter_chrono::_M_C_y_Y):
Ignore modifiers unless the formatting locale is not the C
locale.
(__formatter_chrono::_M_d_e): Likewise.
(__formatter_chrono::_M_H_I): Likewise.
(__formatter_chrono::_M_m): Likewise.
(__formatter_chrono::_M_M): Likewise.
(__formatter_chrono::_M_S): Likewise.
(__formatter_chrono::_M_u_w): Likewise.
(__formatter_chrono::_M_U_V_W): Likewise.
Diffstat (limited to 'gcc')
0 files changed, 0 insertions, 0 deletions