On Mon, Sep 22, 2025 at 12:09 PM Jonathan Wakely <[email protected]> wrote:

> 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
>
Adjusting __idoy is not needed if the __conv is not 'V', and I wanted to
avoid
doing that. I can add if __conv == 'V'  if here, or guard computation
(__t._M_year != __iyear).
The repeated ifs, allow me to not do repeated checks.

       }
>
>      // 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