From patchwork Thu Sep 11 09:59:24 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Tomasz_Kami=C5=84ski?= X-Patchwork-Id: 120061 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D56F33858D33 for ; Thu, 11 Sep 2025 10:03:44 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D56F33858D33 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=GHvJV69H X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id F1CB93858406 for ; Thu, 11 Sep 2025 10:00:33 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org F1CB93858406 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org F1CB93858406 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1757584839; cv=none; b=E+Am1wxy6V9PK7JtRnhatXo5J8i4zdF0P1k0zoDA8m5QIj5QJqEpAFafSzkszbW5dvZaeCyFhWoXkRKgMC+HE4rml3dUc/0jMdutDVMkjuhgOVds/bT1/OH68eQZnyoLBCMCPk1XUEcTkWu7IIH0BLGgbncZWeUzJD5iMf1WRmI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1757584839; c=relaxed/simple; bh=QQmZaLLHcznlvJt4+8D2rkgZmZNQZJcRuJGMZbsTP/s=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=Em+HMU6mNtuowXcdhPZ7Wo3srPDHP18PLjw7DpewGlACSQ6XxvWICXK1UZvglkur9zCbtAB6llap54nGEoLdNnOFwJOEV6cCziJdZb7suzPx/f3BNpDQGKvIyDIvL+dWLdTDT/lkhoY8oQPHStylYbPAcF5VoE1CRNkjvhtuq9E= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org F1CB93858406 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1757584833; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=gDB0H/uGKlmUi3Npq8KIU2nHG7oKAeDAxfwrGQUUZ9M=; b=GHvJV69HfJV+3UCTnNoxoR5GgruU+95LI834nQmjoqGOglX1OJZScQRaLoehtT5Zj9jyOf xg3s15UgM3Cy7eyiB6Wo0IyD+SoByFQr6ouOKdHdD4R3NXfLpeWTeEcrDmUnEmXbpMDGZ8 U7TO9Vs34J5bgazuLWoCJvW+etvDYbw= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-122-c5StdsKANDSdKgVTPDYhDA-1; Thu, 11 Sep 2025 06:00:32 -0400 X-MC-Unique: c5StdsKANDSdKgVTPDYhDA-1 X-Mimecast-MFC-AGG-ID: c5StdsKANDSdKgVTPDYhDA_1757584831 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 1CF461800293; Thu, 11 Sep 2025 10:00:31 +0000 (UTC) Received: from localhost (unknown [10.45.225.180]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 445BF19560B1; Thu, 11 Sep 2025 10:00:29 +0000 (UTC) From: =?utf-8?q?Tomasz_Kami=C5=84ski?= To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH] libstdc++: Rework handling of ISO week calendar and week index formatting. Date: Thu, 11 Sep 2025 11:59:24 +0200 Message-ID: <20250911100028.508638-1-tkaminsk@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: JegTAZdu8y2f-widZDC6nE6ZfZuljKyEqaHAFKoQkRI_1757584831 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org 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, or a later year (__idoy > 366/365). This avoids an expensive conversion from local_days to year_month_day if __idoy <= 365. If the ISO calendar year is unchanged, the __idoy value is be 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. --- Tested on x86_64-linux locally. OK for trunk? libstdc++-v3/include/bits/chrono_io.h | 93 +++++++++++++++------------ 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index 809d795cbf2..c939b73c4c3 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -573,9 +573,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 @@ -694,7 +694,8 @@ namespace __format break; case 'g': case 'G': - __needed = _LocalDays|_Weekday; + case 'V': + __needed = _LocalDays|_Year|_DayOfYear|_Weekday; break; case 'H': case 'I': @@ -742,9 +743,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': @@ -1148,7 +1148,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': @@ -1190,9 +1191,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); @@ -1442,18 +1442,47 @@ namespace __format template _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: - 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. + + using namespace chrono; + const _CharT __yconv = "yY"[__conv == 'G']; + // Offset of the nearest Thursday: + const days __offset = (__t._M_weekday - Monday) - days(3); + // Day of year of nearest Thursday: + days __idoy = __t._M_day_of_year - __offset; + if (__idoy > days(0) && __idoy <= days(365)) [[likely]] + { + // Nearest Thrusday is in the same year as __t._M_year + if (__conv != 'V') + return _M_C_y_Y(__t._M_year, std::move(__out), __yconv); + + const unsigned __wi = chrono::floor(__idoy).count() + 1; + return __format::__write(std::move(__out), _S_two_digits(__wi)); + } + + // Nearest Thursday as local days + const local_days __ild = __t._M_ldays - __offset; + // Nearest Thursday in previous year (__idoy <= 0), on a leap day + // of same year, or later year (__idoy >= 366) + const year __iyear = (__idoy <= days(0)) + ? __t._M_year - years(1) + : year_month_day(__ild).year(); + if (__conv != 'V') + return _M_C_y_Y(__iyear, std::move(__out), __yconv); + + // Recompute day of year from possibly updated year + __idoy = __ild - local_days(__iyear/January/1); + const unsigned __wi = chrono::floor(__idoy).count() + 1; + return __format::__write(std::move(__out), _S_two_digits(__wi)); } template @@ -1710,35 +1739,19 @@ namespace __format template _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(__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 __weekoffset = __t._M_weekday - __weekstart; + auto __weeks = chrono::floor(__t._M_day_of_year - __weekoffset); + return __format::__write(std::move(__out), _S_two_digits(__weeks.count() + 1)); } template