Skip to content

Commit b9a2dfb

Browse files
author
Tomasz Kamiński
committed
libstdc++: Improve handling of !ok() weekday index in formatting [PR121929]
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>
1 parent ea7fa6b commit b9a2dfb

File tree

2 files changed

+20
-18
lines changed

2 files changed

+20
-18
lines changed

libstdc++-v3/include/bits/chrono_io.h

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ namespace __format
479479
return __parts;
480480
}
481481

482+
// pre: _M_year is set
482483
[[__gnu__::__always_inline__]]
483484
_ChronoParts
484485
_M_fill_aux(chrono::local_days __ld, _ChronoParts __parts)
@@ -495,6 +496,7 @@ namespace __format
495496
return __parts;
496497
}
497498

499+
// pre: _M_year is set
498500
[[__gnu__::__always_inline__]]
499501
_ChronoParts
500502
_M_fill_ldays(chrono::local_days __ld, _ChronoParts __parts)
@@ -2671,8 +2673,7 @@ namespace __format
26712673
if (__parts == 0)
26722674
return _M_f._M_format(__cd, __fc);
26732675

2674-
chrono::local_days __ld(__t);
2675-
__cd._M_fill_ldays(__ld, __parts);
2676+
__cd._M_fill_ldays(chrono::local_days(__t), __parts);
26762677
return _M_f._M_format(__cd, __fc);
26772678
}
26782679

@@ -2707,19 +2708,17 @@ namespace __format
27072708
format(const chrono::year_month_day_last& __t,
27082709
basic_format_context<_Out, _CharT>& __fc) const
27092710
{
2711+
using enum __format::_ChronoParts;
2712+
27102713
__format::_ChronoData<_CharT> __cd{};
27112714
auto __parts = _M_f._M_spec._M_needed;
27122715
__parts = __cd._M_fill_year_month(__t, __parts);
2716+
if (_M_f._M_spec._M_needs(_Day|_WeekdayIndex))
2717+
__parts = __cd._M_fill_day(__t.day(), __parts);
27132718
if (__parts == 0)
27142719
return _M_f._M_format(__cd, __fc);
27152720

2716-
chrono::local_days __ld(__t);
2717-
__parts = __cd._M_fill_ldays(__ld, __parts);
2718-
if (__parts == 0)
2719-
return _M_f._M_format(__cd, __fc);
2720-
2721-
chrono::year_month_day __ymd(__ld);
2722-
__cd._M_fill_day(__ymd.day(), __parts);
2721+
__cd._M_fill_ldays(chrono::local_days(__t), __parts);
27232722
return _M_f._M_format(__cd, __fc);
27242723
}
27252724

@@ -2760,6 +2759,10 @@ namespace __format
27602759
auto __parts = _M_f._M_spec._M_needed;
27612760
__parts = __cd._M_fill_year_month(__t, __parts);
27622761
__parts = __cd._M_fill_weekday(__t.weekday_indexed(), __parts);
2762+
if (__t.index() == 0) [[unlikely]]
2763+
// n.b. day cannot be negative, so any 0th weekday uses
2764+
// value-initialized (0) day of month
2765+
__parts -= __format::_ChronoParts::_Day;
27632766
if (__parts == 0)
27642767
return _M_f._M_format(__cd, __fc);
27652768

@@ -2768,9 +2771,9 @@ namespace __format
27682771
if (__parts == 0)
27692772
return _M_f._M_format(__cd, __fc);
27702773

2771-
chrono::year_month_day __ymd(__ld);
2774+
auto __dom = __ld - chrono::local_days(__t.year()/__t.month()/0);
27722775
// n.b. weekday index is supplied by input, do not override it
2773-
__cd._M_day = __ymd.day();
2776+
__cd._M_day = chrono::day(__dom.count());
27742777
return _M_f._M_format(__cd, __fc);
27752778
}
27762779

@@ -2820,8 +2823,8 @@ namespace __format
28202823
if (__parts == 0)
28212824
return _M_f._M_format(__cd, __fc);
28222825

2823-
chrono::year_month_day __ymd(__ld);
2824-
__cd._M_fill_day(__ymd.day(), __parts);
2826+
auto __dom = __ld - chrono::local_days(__t.year()/__t.month()/0);
2827+
__cd._M_fill_day(chrono::day(__dom.count()), __parts);
28252828
return _M_f._M_format(__cd, __fc);
28262829
}
28272830

libstdc++-v3/testsuite/std/time/year_month_weekday/io.cc

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,16 @@ test_format()
6969
VERIFY( s == "2024-09-01 245" );
7070
s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[5]);
7171
VERIFY( s == "2024-09-29 273" );
72-
// see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121929
7372
// first weeks of next month
7473
s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[6]);
75-
VERIFY( s == "2024-09-06 280" );
74+
VERIFY( s == "2024-09-36 280" );
7675
s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[7]);
77-
VERIFY( s == "2024-09-13 287" );
76+
VERIFY( s == "2024-09-43 287" );
7877
// last week on previous month
7978
s = std::format("{:%Y-%m-%d %j}", 2024y/September/Saturday[0]);
80-
VERIFY( s == "2024-09-31 244" );
79+
VERIFY( s == "2024-09-00 244" );
8180
s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[0]);
82-
VERIFY( s == "2024-09-25 238" );
81+
VERIFY( s == "2024-09-00 238" ); // day is de-facto -6
8382

8483
// %U: Week number for weeks starting on Sunday
8584
s = std::format("{:%Y-U%U}", 2023y/January/Sunday[0]);

0 commit comments

Comments
 (0)