On Mon, 22 Sep 2025 at 10:20 +0200, Tomasz KamiƄski wrote:
The handling of ISO week-calendar year specifiers (%G, %g) and ISO week
number (%V) was merged into a single _M_g_G_V function, as the latter
requires ISO year value, computed by the former.

The values for %U and %W, which are based on the number of days since the
first Sunday and Monday of the year respectively, are now expressed as an
offset from the existing _M_day_of_year field. This reduces redundant
computation. The required flags were also updated to only need _DayOfYear
and _Weekday.

The _M_g_G_V function uses _M_day_of_year to compute __idoy, the day of the
year for the nearest Thursday. This value is used to determine if the ISO
year is the previous year (__idoy <= 0), the current year (__idoy <= 365/366),
next year (__idoy <= 730), or later year. This avoids an expensive conversion
from local_days to year_month_day for all ok() values. If the ISO calendar year
is current year, the __idoy value is be reused for weekday index computation.

"is be reused" -> "is reused"


libstdc++-v3/ChangeLog:

        * include/bits/chrono_io.h(__formatter_chrono::_M_parse): Update
        needed flags for %g, %G, %V, %U, %W, and corrected formatting.
        (__formatter_chrono::_M_format_to): Change how %V is handled.
        (__formatter_chrono::_M_g_G): Merged into _M_g_G_V.
        (__formatter_chrono::_M_g_G_V): Reworked from _M_g_G.
        (__formatter_chrono::_M_U_V_W): Changed into _M_U_V.
        (__formatter_chrono::_M_U_W): Reworked implementation.
        * testsuite/std/time/year_month_day/io.cc: New tests.
---
v3:
* handles the leap year for same year case
* handles next year without conversion from local days
* add more test cases

OK for trunk?

libstdc++-v3/include/bits/chrono_io.h         | 98 +++++++++++--------
.../testsuite/std/time/year_month_day/io.cc   | 43 +++++++-
2 files changed, 100 insertions(+), 41 deletions(-)

diff --git a/libstdc++-v3/include/bits/chrono_io.h 
b/libstdc++-v3/include/bits/chrono_io.h
index 593a927811a..3e1391415cd 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -572,9 +572,9 @@ namespace __format

          auto __finalize = [this, &__spec, &__def] {
            using enum _ChronoParts;
-            _ChronoParts __checked
+           _ChronoParts __checked
              = __spec._M_debug ? _YearMonthDay|_IndexedWeekday
-                               : _Month|_Weekday;
+                               : _Month|_Weekday;
            // n.b. for calendar types __def._M_needed contains only parts
            // copied from the input, remaining ones are computed, and thus ok
            __spec._M_needs_ok_check
@@ -693,7 +693,8 @@ namespace __format
                  break;
                case 'g':
                case 'G':
-                 __needed = _LocalDays|_Weekday;
+               case 'V':
+                 __needed = _LocalDays|_Year|_DayOfYear|_Weekday;
                  break;
                case 'H':
                case 'I':
@@ -741,9 +742,8 @@ namespace __format
                  __allowed_mods = _Mod_O;
                  break;
                case 'U':
-               case 'V':
                case 'W':
-                 __needed = _LocalDays|_Year|_DayOfYear|_Weekday;
+                 __needed = _DayOfYear|_Weekday;
                  __allowed_mods = _Mod_O;
                  break;
                case 'x':
@@ -1147,7 +1147,8 @@ namespace __format
                  break;
                case 'g':
                case 'G':
-                 __out = _M_g_G(__t, std::move(__out), __c == 'G');
+               case 'V':
+                 __out = _M_g_G_V(__t, std::move(__out), __c);
                  break;
                case 'H':
                case 'I':
@@ -1189,9 +1190,8 @@ namespace __format
                  __out = _M_u_w(__t._M_weekday, std::move(__out), __c);
                  break;
                case 'U':
-               case 'V':
                case 'W':
-                 __out = _M_U_V_W(__t, std::move(__out), __c);
+                 __out = _M_U_W(__t, std::move(__out), __c);
                  break;
                case 'z':
                  __out = _M_z(__t._M_zone_offset, std::move(__out), 
(bool)__mod);
@@ -1441,18 +1441,52 @@ namespace __format

      template<typename _OutIter>
        _OutIter
-       _M_g_G(const _ChronoData<_CharT>& __t, _OutIter __out,
-              bool __full) const
+       _M_g_G_V(const _ChronoData<_CharT>& __t, _OutIter __out,
+               _CharT __conv) const
        {
-         // %g last two decimal digits of the ISO week-based year.
-         // %G ISO week-based year.
-         using namespace chrono;
-         auto __d = __t._M_ldays;
-         // Move to nearest Thursday:
-         __d -= (__t._M_weekday - Monday) - days(3);
+         // %g  last two decimal digits of the ISO week-based year.
+         // %G  ISO week-based year.
+         // %V  ISO week-based week number as a decimal number.
+         // %OV Locale's alternative numeric rep.
+       
          // ISO week-based year is the year that contains that Thursday:

This comment seems orphaned now, as "that Thursday" referred to the
"nearest Thursday" calculation above, which is gone now.

And ending the line with a colon seems strange when the comment
continues.

-         year __y = year_month_day(__d).year();
-         return _M_C_y_Y(__y, std::move(__out), "yY"[__full]);
+         // ISO week of __t is number of weeks since January 1 of the ISO year.

Maybe adjust the two comment lines slightly, like so:

    // ISO week-based year of __t is the year that contains the nearest
    // Thursday. The ISO week of __t is the number of weeks since
    // January 1 of that year.

+       
+         using namespace chrono;
+         const _CharT __yconv = "yY"[__conv == 'G'];
+         // Offset of the nearest Thursday:
+         const days __offset = (__t._M_weekday - Monday) - days(3);
+
+         // Year of nearest Thursday:
+         year __iyear = __t._M_year;
+         // Day of year of nearest Thursday:
+         days __idoy = __t._M_day_of_year - __offset;
+         if ((__idoy > days(0) && __idoy <= days(365))
+              || (__idoy == days(366) && __iyear.is_leap()))
+           {
+             // Nearest Thrusday is in the same year as __t._M_year

"Thursday"

+             if (__conv != 'V')
+               return _M_C_y_Y(__iyear, std::move(__out), __yconv);
+
+             const auto __wi = chrono::floor<weeks>(__idoy - days(1)).count() 
+ 1;
+             return __format::__write(std::move(__out), _S_two_digits(__wi));

These two return statements are identical to the ones below. Would it
make more sense to structure this as:

    local_days __ild;
    if (nearest thursday is NOT in the same year as __t._M_year)
      {
        // calculate __ild
        // adjust __iyear
        // adjust __idoy
      }

    // do return statements

+           }
+
+         // Nearest Thursday as local days:
+         local_days __ild = __t._M_ldays - __offset;
+         if (__idoy <= days(0))
+           __iyear -= years(1);
+         else if (__idoy <= days(730))
+           __iyear += years(1);
+         else [[unlikely]]
+           __iyear = year_month_day(__ild).year();
+
+         if (__conv != 'V')
+           return _M_C_y_Y(__iyear, std::move(__out), __yconv);
+
+         __idoy = __ild - local_days(__iyear/January/0);
+         const auto __wi = chrono::floor<weeks>(__idoy - days(1)).count() + 1;
+         return __format::__write(std::move(__out), _S_two_digits(__wi));
        }

      template<typename _OutIter>
@@ -1709,35 +1743,19 @@ namespace __format

      template<typename _OutIter>
        _OutIter
-       _M_U_V_W(const _ChronoData<_CharT>& __t, _OutIter __out,
+       _M_U_W(const _ChronoData<_CharT>& __t, _OutIter __out,
                 _CharT __conv) const
        {
          // %U  Week number of the year as a decimal number, from first Sunday.
          // %OU Locale's alternative numeric rep.
-         // %V  ISO week-based week number as a decimal number.
-         // %OV Locale's alternative numeric rep.
          // %W  Week number of the year as a decimal number, from first Monday.
          // %OW Locale's alternative numeric rep.
+       
          using namespace chrono;
-
-         auto __d = __t._M_ldays;
-         local_days __first; // First day of week 1.
-         if (__conv == 'V') // W01 begins on Monday before first Thursday.
-           {
-             // Move to nearest Thursday:
-             __d -= (__t._M_weekday - Monday) - days(3);
-             // ISO week of __t is number of weeks since January 1 of the
-             // same year as that nearest Thursday.
-             __first = local_days(year_month_day(__d).year()/January/1);
-           }
-         else
-           {
-             const weekday __weekstart = __conv == 'U' ? Sunday : Monday;
-             __first = local_days(__t._M_year/January/__weekstart[1]);
-           }
-         auto __weeks = chrono::floor<weeks>(__d - __first);
-         __string_view __sv = _S_two_digits(__weeks.count() + 1);
-         return __format::__write(std::move(__out), __sv);
+         const weekday __weekstart = __conv == 'U' ? Sunday : Monday;
+         const days __offset = __t._M_weekday - __weekstart;
+         auto __weeks = chrono::floor<weeks>(__t._M_day_of_year - __offset - 
days(1));
+         return __format::__write(std::move(__out), 
_S_two_digits(__weeks.count() + 1));

Nice, this function is much simpler without handling %V here.

        }

      template<typename _OutIter>
diff --git a/libstdc++-v3/testsuite/std/time/year_month_day/io.cc 
b/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
index 7b09ff4b95a..5ce2794347a 100644
--- a/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
+++ b/libstdc++-v3/testsuite/std/time/year_month_day/io.cc
@@ -49,6 +49,12 @@ test_format()
  VERIFY( s == "Day 6 (Sat) of Week 00 of 2022" );
  s = std::format("Day {:%w (%a) of Week %U of %Y}", 2022y/January/2);
  VERIFY( s == "Day 0 (Sun) of Week 01 of 2022" );
+  s = std::format("Day {:%w (%a) of Week %U of %Y}", 2024y/January/1);
+  VERIFY( s == "Day 1 (Mon) of Week 00 of 2024" );
+  s = std::format("Day {:%w (%a) of Week %U of %Y}", 2024y/January/7);
+  VERIFY( s == "Day 0 (Sun) of Week 01 of 2024" );
+  s = std::format("Day {:%w (%a) of Week %U of %Y}", 2024y/January/8);
+  VERIFY( s == "Day 1 (Mon) of Week 01 of 2024" );
  s = std::format("Day {:%w (%a) of Week %U of %Y}", 2022y/Quindecember/20);
  VERIFY( s == "Day 1 (Mon) of Week 73 of 2022" );
  // %W: Week number for weeks starting on Monday
@@ -56,7 +62,13 @@ test_format()
  VERIFY( s == "Day 7 (Sun) of Week 00 of 2022" );
  s = std::format("Day {:%u (%a) of Week %W of %Y}", 2022y/January/3);
  VERIFY( s == "Day 1 (Mon) of Week 01 of 2022" );
-  s = std::format("Day {:%w (%a) of Week %U of %Y}", 2022y/Quindecember/20);
+  s = std::format("Day {:%w (%a) of Week %W of %Y}", 2019y/January/1);
+  VERIFY( s == "Day 2 (Tue) of Week 00 of 2019" );
+  s = std::format("Day {:%w (%a) of Week %W of %Y}", 2019y/January/7);
+  VERIFY( s == "Day 1 (Mon) of Week 01 of 2019" );
+  s = std::format("Day {:%w (%a) of Week %W of %Y}", 2019y/January/8);
+  VERIFY( s == "Day 2 (Tue) of Week 01 of 2019" );
+  s = std::format("Day {:%w (%a) of Week %W of %Y}", 2022y/Quindecember/20);
  VERIFY( s == "Day 1 (Mon) of Week 73 of 2022" );

  // %G: ISO week-calendar year (ISO 8601)
@@ -65,6 +77,8 @@ test_format()
  VERIFY( s == "1976-W53" );
  s = std::format("{:%G-W%V}", 1977y/1/2);
  VERIFY( s == "1976-W53" );
+  s = std::format("{:%G-W%V}", 1977y/1/3);
+  VERIFY( s == "1977-W01" );
  s = std::format("{:%G-W%V}", 1977y/12/31);
  VERIFY( s == "1977-W52" );
  s = std::format("{:%G-W%V}", 1978y/1/1);
@@ -84,6 +98,33 @@ test_format()
  s = std::format("{:%G-W%V}", 1980y/18/20);
  VERIFY( s == "1981-W26" );

+  // "Leap weak" on year starting on Thursday
+  s = std::format("{:%G-W%V}", 2009y/12/31);
+  VERIFY( s == "2009-W53" );
+  s = std::format("{:%G-W%V}", 2010y/1/1);
+  VERIFY( s == "2009-W53" );
+  s = std::format("{:%G-W%V}", 2010y/1/3);
+  VERIFY( s == "2009-W53" );
+  s = std::format("{:%G-W%V}", 2010y/1/4);
+  VERIFY( s == "2010-W01" );
+
+  // "Leap weak" on leap year stating on Wednesday
+  // 2020/Dec/31 is Thurday, thus 366 day of year
+  s = std::format("{:%G-W%V}", 2020y/12/30);
+  VERIFY( s == "2020-W53" );
+  s = std::format("{:%G-W%V}", 2020y/12/31);
+  VERIFY( s == "2020-W53" );
+  s = std::format("{:%G-W%V}", 2021y/1/1);
+  VERIFY( s == "2020-W53" );
+  s = std::format("{:%G-W%V}", 2021y/1/3);
+  VERIFY( s == "2020-W53" );
+  s = std::format("{:%G-W%V}", 2021y/1/4);
+  VERIFY( s == "2021-W01" );
+  s = std::format("{:%G-W%V}", 2021y/1/7);
+  VERIFY( s == "2021-W01" );
+  s = std::format("{:%G-W%V}", 2021y/1/8);
+  VERIFY( s == "2021-W01" );
+
  s = std::format("{:%x}", 2022y/December/19);
  VERIFY( s == "12/19/22" );
  s = std::format("{:L%x}", 2022y/December/19);
--
2.51.0



Reply via email to