On Wed, 2 Jul 2025 at 10:30, Tomasz Kaminski <tkami...@redhat.com> wrote:
>
>
>
> On Wed, Jul 2, 2025 at 9:13 AM XU Kailiang <xu2k...@outlook.com> wrote:
>>
>>
>> C++ formatting locale could have a custom time_put that performs
>> differently from the C locale, so do not use __timepunct directly.
>>
>> 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 <xu2k...@outlook.com>
>> ---
>>  libstdc++-v3/include/bits/chrono_io.h         | 31 ++++++----------
>>  .../time/format/pr117214_custom_timeput.cc    | 36 +++++++++++++++++++
>>  2 files changed, 47 insertions(+), 20 deletions(-)
>>  create mode 100644 
>> libstdc++-v3/testsuite/std/time/format/pr117214_custom_timeput.cc
>>
>> diff --git a/libstdc++-v3/include/bits/chrono_io.h 
>> b/libstdc++-v3/include/bits/chrono_io.h
>> index abbf4efcc3b..8358105c26b 100644
>> --- a/libstdc++-v3/include/bits/chrono_io.h
>> +++ b/libstdc++-v3/include/bits/chrono_io.h
>> @@ -905,14 +905,10 @@ namespace __format
>>             }
>>
>>           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);
>
> I have recently removed all call to _M_locale_fmt from inside the specifiers,
> into the format loop, instead of calling it per each specifier individually.
> I think, we should follow same approach here, by updating _S_localized_spec to
> return true for all above specifiers.

Yes please.

> Then we can remove calls to _M_write and replace it with __format_writes, as
> this functions will be only used for C locale.
>>
>>         }
>>
>>        template<typename _Tp, typename _FormatContext>
>> @@ -936,14 +932,10 @@ namespace __format
>>             }
>>
>>           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>
>> @@ -1329,10 +1321,9 @@ namespace __format
>>             __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[__hi >= 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 00000000000..8c9f3d29bc6
>> --- /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");
>> +}
>> --
>> 2.50.0
>>

Reply via email to