On Mon, 22 Sept 2025 at 11:40, Tomasz Kamiński <[email protected]> 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 in most cases. If the ISO calendar year
> is current year, the __idoy value is reused for weekday index computation.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/chrono_io.h(__formatter_chrono::_M_parse): Update
>         needed flags for %g, %G, %V, %U, %W.
>         (__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.
> ---
> v4 reworks _M_g_G_V function to avoid repeated returns.

This reworked structure seems clearer to me, thanks.

OK for trunk


>
>  libstdc++-v3/include/bits/chrono_io.h         | 95 +++++++++++--------
>  .../testsuite/std/time/year_month_day/io.cc   | 43 ++++++++-
>  2 files changed, 96 insertions(+), 42 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/chrono_io.h 
> b/libstdc++-v3/include/bits/chrono_io.h
> index 593a927811a..617899a7a71 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,47 @@ 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.
> +         // %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 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;
> -         auto __d = __t._M_ldays;
> -         // Move to nearest Thursday:
> -         __d -= (__t._M_weekday - Monday) - days(3);
> -         // ISO week-based year is the year that contains that Thursday:
> -         year __y = year_month_day(__d).year();
> -         return _M_C_y_Y(__y, std::move(__out), "yY"[__full]);
> +         // Offset of the nearest Thursday:
> +         const days __offset = (__t._M_weekday - Monday) - days(3);
> +         // Nearest Thursday as local days:
> +         const local_days __ild = __t._M_ldays - __offset;
> +         // Day of year of nearest Thursday:
> +         days __idoy = __t._M_day_of_year - __offset;
> +
> +         // Year of nearest Thursday:
> +         year __iyear;
> +         if (__idoy <= days(0))
> +           __iyear = __t._M_year - years(1);
> +         else if (__idoy <= days(365))
> +           __iyear = __t._M_year;
> +         else if (__idoy == days(366) && __t._M_year.is_leap())
> +           __iyear = __t._M_year;
> +         else if (__idoy <= days(730))
> +           __iyear = __t._M_year + years(1);
> +         else [[unlikely]]
> +           __iyear = year_month_day(__ild).year();
> +
> +         if (__conv != 'V')
> +           return _M_C_y_Y(__iyear, std::move(__out), "yY"[__conv == 'G']);
> +
> +         if (__iyear != __t._M_year)
> +           __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 +1738,19 @@ namespace __format
>
>        template<typename _OutIter>
>         _OutIter
> -       _M_U_V_W(const _ChronoData<_CharT>& __t, _OutIter __out,
> -                _CharT __conv) const
> +       _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));
>         }
>
>        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