[PATCH] libstdc++: Improve handling of !ok() weekday index in formatting [PR121929]
Jonathan Wakely
jwakely@redhat.com
Thu Oct 9 14:20:19 GMT 2025
On Thu, 9 Oct 2025 at 14:24, Tomasz Kamiński <tkaminsk@redhat.com> wrote:
>
> Previously, formatting a year_month_weekday with the weekday index equal to
> 0, 6, or 7 (which are !ok() values in the supported range) produced a
> seemingly correct day output. For example %Y-%m-%d produced:
> * 2024-09-06 for 2024y/September/Sunday[6] (2024-10-06)
> * 2024-09-25 for 2024y/September/Sunday[0] (2023-08-25)
>
> This patch changes how the internal _M_day value is computed for
> year_month_weekday. Instead of converting to local_days then to year_month_day,
> _M_day is now set as the number of days since ymd.year()/ymd.month()/0. If this
> difference is negative (which occurs when index() is 0), _M_day is set to 0 to
> avoid handling negative days of the month.
>
> This change yields identical results for all ok() values. However, for !ok() dates,
> it now consistently produces invalid dates, ensuring the formatted output clearly
> reflects the !ok input state:
> * 2024-09-36 for 2024y/September/Sunday[6]
> * 2024-09-00 for 2024y/September/Sunday[0]
>
> For consistency, _M_day is computed in the same manner for year_month_weekday_last.
>
> Finally, for year_month_day_last, we fill _M_day directly with ymd.day().
> This provides a more efficient implementation and avoids the need to compute
> local_days for %Y-%m-%d, %F and similar specifiers.
OK
>
> PR libstdc++/121929
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/chrono_io.h (_ChronoData::_M_fill_aux)
> (_ChronoData::_M_fill_aux): Add comment documenting precondition.
> (formatter<chrono::year_month_day, _CharT>::format): Compute
> local_days inline.
> (formatter<chrono::year_month_day_last, _CharT>::format)
> (formatter<chrono::year_month_weekday, _CharT>::format)
> (formatter<chrono::year_month_weekday_last, _CharT>::format):
> Change how the _M_day field is computed.
> * testsuite/std/time/year_month_weekday/io.cc: Adjust tests.
>
> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
> ---
> libstdc++-v3/include/bits/chrono_io.h | 29 ++++++++++---------
> .../std/time/year_month_weekday/io.cc | 9 +++---
> 2 files changed, 20 insertions(+), 18 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
> index 690c10d79ce..1e2f45b0bf8 100644
> --- a/libstdc++-v3/include/bits/chrono_io.h
> +++ b/libstdc++-v3/include/bits/chrono_io.h
> @@ -479,6 +479,7 @@ namespace __format
> return __parts;
> }
>
> + // pre: _M_year is set
> [[__gnu__::__always_inline__]]
> _ChronoParts
> _M_fill_aux(chrono::local_days __ld, _ChronoParts __parts)
> @@ -495,6 +496,7 @@ namespace __format
> return __parts;
> }
>
> + // pre: _M_year is set
> [[__gnu__::__always_inline__]]
> _ChronoParts
> _M_fill_ldays(chrono::local_days __ld, _ChronoParts __parts)
> @@ -2671,8 +2673,7 @@ namespace __format
> if (__parts == 0)
> return _M_f._M_format(__cd, __fc);
>
> - chrono::local_days __ld(__t);
> - __cd._M_fill_ldays(__ld, __parts);
> + __cd._M_fill_ldays(chrono::local_days(__t), __parts);
> return _M_f._M_format(__cd, __fc);
> }
>
> @@ -2707,19 +2708,17 @@ namespace __format
> format(const chrono::year_month_day_last& __t,
> basic_format_context<_Out, _CharT>& __fc) const
> {
> + using enum __format::_ChronoParts;
> +
> __format::_ChronoData<_CharT> __cd{};
> auto __parts = _M_f._M_spec._M_needed;
> __parts = __cd._M_fill_year_month(__t, __parts);
> + if (_M_f._M_spec._M_needs(_Day|_WeekdayIndex))
> + __parts = __cd._M_fill_day(__t.day(), __parts);
> if (__parts == 0)
> return _M_f._M_format(__cd, __fc);
>
> - chrono::local_days __ld(__t);
> - __parts = __cd._M_fill_ldays(__ld, __parts);
> - if (__parts == 0)
> - return _M_f._M_format(__cd, __fc);
> -
> - chrono::year_month_day __ymd(__ld);
> - __cd._M_fill_day(__ymd.day(), __parts);
> + __cd._M_fill_ldays(chrono::local_days(__t), __parts);
> return _M_f._M_format(__cd, __fc);
> }
>
> @@ -2760,6 +2759,10 @@ namespace __format
> auto __parts = _M_f._M_spec._M_needed;
> __parts = __cd._M_fill_year_month(__t, __parts);
> __parts = __cd._M_fill_weekday(__t.weekday_indexed(), __parts);
> + if (__t.index() == 0) [[unlikely]]
> + // n.b. day cannot be negative, so any 0th weekday uses
> + // value-initialized (0) day of month
> + __parts -= __format::_ChronoParts::_Day;
> if (__parts == 0)
> return _M_f._M_format(__cd, __fc);
>
> @@ -2768,9 +2771,9 @@ namespace __format
> if (__parts == 0)
> return _M_f._M_format(__cd, __fc);
>
> - chrono::year_month_day __ymd(__ld);
> + auto __dom = __ld - chrono::local_days(__t.year()/__t.month()/0);
> // n.b. weekday index is supplied by input, do not override it
> - __cd._M_day = __ymd.day();
> + __cd._M_day = chrono::day(__dom.count());
> return _M_f._M_format(__cd, __fc);
> }
>
> @@ -2820,8 +2823,8 @@ namespace __format
> if (__parts == 0)
> return _M_f._M_format(__cd, __fc);
>
> - chrono::year_month_day __ymd(__ld);
> - __cd._M_fill_day(__ymd.day(), __parts);
> + auto __dom = __ld - chrono::local_days(__t.year()/__t.month()/0);
> + __cd._M_fill_day(chrono::day(__dom.count()), __parts);
> return _M_f._M_format(__cd, __fc);
> }
>
> diff --git a/libstdc++-v3/testsuite/std/time/year_month_weekday/io.cc b/libstdc++-v3/testsuite/std/time/year_month_weekday/io.cc
> index 92fd67022a2..253d4f6a552 100644
> --- a/libstdc++-v3/testsuite/std/time/year_month_weekday/io.cc
> +++ b/libstdc++-v3/testsuite/std/time/year_month_weekday/io.cc
> @@ -69,17 +69,16 @@ test_format()
> VERIFY( s == "2024-09-01 245" );
> s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[5]);
> VERIFY( s == "2024-09-29 273" );
> - // see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121929
> // first weeks of next month
> s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[6]);
> - VERIFY( s == "2024-09-06 280" );
> + VERIFY( s == "2024-09-36 280" );
> s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[7]);
> - VERIFY( s == "2024-09-13 287" );
> + VERIFY( s == "2024-09-43 287" );
> // last week on previous month
> s = std::format("{:%Y-%m-%d %j}", 2024y/September/Saturday[0]);
> - VERIFY( s == "2024-09-31 244" );
> + VERIFY( s == "2024-09-00 244" );
> s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[0]);
> - VERIFY( s == "2024-09-25 238" );
> + VERIFY( s == "2024-09-00 238" ); // day is de-facto -6
>
> // %U: Week number for weeks starting on Sunday
> s = std::format("{:%Y-U%U}", 2023y/January/Sunday[0]);
> --
> 2.51.0
>
More information about the Libstdc++
mailing list