]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Improve handling of !ok() weekday index in formatting [PR121929]
authorTomasz Kamiński <tkaminsk@redhat.com>
Thu, 9 Oct 2025 12:38:54 +0000 (14:38 +0200)
committerTomasz Kamiński <tkaminsk@redhat.com>
Fri, 10 Oct 2025 05:03:49 +0000 (07:03 +0200)
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.

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.

Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
libstdc++-v3/include/bits/chrono_io.h
libstdc++-v3/testsuite/std/time/year_month_weekday/io.cc

index 690c10d79ce5c441f01baf379e54a13364a2ebac..1e2f45b0bf8109b629ca3586bceaabf3230753b7 100644 (file)
@@ -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);
        }
 
index 92fd67022a243998b4a6903609bdb0d201f9b2c7..253d4f6a552e620c427cdd2b2a5d94e9e7d3c659 100644 (file)
@@ -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]);