[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