From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 48) id 2DF9E3858C5F; Wed, 6 Mar 2024 23:51:23 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2DF9E3858C5F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1709769083; bh=hFf+SXN4bzr3hUySMH9gaRZ88FkI0OcdmdQDFQPw4Vs=; h=From:To:Subject:Date:From; b=guKwqSSuxzjgdbne3cP3Av6iVQ6nzz+Fc9R9qSUzSdIwLfFnBdWNfBLM4kVLUGcv7 HdHxijiDLDhs9FXoR0D+I9syHV+2rk1oAJGtXH7FVZpg7NCdz9SNe84/V/s29RO/UY j+bkyPZxnq7GxeTRmaqtQKDLBhZRY26mKBIxSUmU= From: "redi at gcc dot gnu.org" To: gcc-bugs@gcc.gnu.org Subject: [Bug libstdc++/114260] New: std::formatter> formats as the previous day Date: Wed, 06 Mar 2024 23:51:22 +0000 X-Bugzilla-Reason: CC X-Bugzilla-Type: new X-Bugzilla-Watch-Reason: None X-Bugzilla-Product: gcc X-Bugzilla-Component: libstdc++ X-Bugzilla-Version: 14.0 X-Bugzilla-Keywords: X-Bugzilla-Severity: normal X-Bugzilla-Who: redi at gcc dot gnu.org X-Bugzilla-Status: UNCONFIRMED X-Bugzilla-Resolution: X-Bugzilla-Priority: P3 X-Bugzilla-Assigned-To: unassigned at gcc dot gnu.org X-Bugzilla-Target-Milestone: --- X-Bugzilla-Flags: X-Bugzilla-Changed-Fields: bug_id short_desc product version bug_status bug_severity priority component assigned_to reporter target_milestone Message-ID: Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Bugzilla-URL: http://gcc.gnu.org/bugzilla/ Auto-Submitted: auto-generated MIME-Version: 1.0 List-Id: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D114260 Bug ID: 114260 Summary: std::formatter> formats as the previous day Product: gcc Version: 14.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: redi at gcc dot gnu.org Target Milestone: --- We give surprising output for std::formatter>: #include #include #include using namespace std::chrono; int main(){ auto sdays =3D std::chrono::sys_days(2024y/March/5); auto udays =3D std::chrono::utc_clock::from_sys(sdays); std::cout << udays << '\n'; std::cout << round(udays) << '\n'; } This prints: 2024-03-05 00:00:00 2024-03-04 23:59:33 This happens because formatter> subtracts leap secon= ds to get a sys_time (and checks to see whether we need to format the seconds = as "60") and then formats that result using formatter. The result has a higher precision than utc_time and is no longer the "correct" day. I think we want to use chrono::round after subtracting leap seconds, to = get back to the original resolution. Otherwise we're formatting a sys_time that differs from the supplied utc_time by less than that time's minimum tick. So: --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -2067,7 +2067,7 @@ namespace __format const auto __li =3D chrono::get_leap_second_info(__t); sys_time<_CDur> __s{__t.time_since_epoch() - __li.elapsed}; if (!__li.is_leap_second) [[likely]] - return _M_f._M_format(__s, __fc); + return _M_f._M_format(chrono::round<_Duration>(__s), __fc); else return _M_f._M_format(__utc_leap_second(__s), __fc); } Or maybe not even subtract leap seconds at all when the the sum of elapsed = leap seconds is less than Duration{1}? If the time being formatted can't represe= nt the number of elapsed leap seconds, is it meaningful to say the time falls within a leap second? For the first ever leap second, yes it is: clock_cast(sys_days{July/1/1972} - 500ms) + 500ms -> 1972-06-30 23:59:60.000 round(clock_cast(sys_days{July/1/1972} - 500ms) + 500ms) -> 1972-06-30 23:59:60 But for every leap second after that (and all future ones, unless the sum of positive and negative leap seconds becomes a multiple of 60 again) rounding= a sys_time to utc_time cannot fall within a leap second and so doesn= 't need to print "60" for the seconds: clock_cast(sys_days{January/1/1973} - 500ms) + 500ms -> 1972-12-31 23:59:60.000 round(clock_cast(sys_days{January/1/1973} - 500ms) + 50= 0ms) -> 1972-12-31 23:59:59 (with current GCC trunk, so not rounded to minutes) -> 1973-01-01 00:00:00 (with the patch above to round to minutes) The 23:59:59 result is not useful, it's neither a leap second like 23:59:60, nor a round number of minutes like 00:00:00. I think we should format it as 00:00:00, which we could do by not subtracting the leap seconds at all. Maybe we could do: if (auto li =3D get_leap_second_info(ut); !li.is_leap_second && li.elapsed < Duration{1}) _M_format(sys_time(ut.time_since_epoch()), fc); else if (!li.is_leap_second) _M_format(round(sys_time(ut.time_since_epoch()) - li.elapsed), fc); else // ... But I don't think that's necessary, just round should give the desired result. Avoiding the subtraction doesn't seem like a useful optimization (especially as we'd still have done the much slower get_leap_second_info lookup anyway). CC Howard to check I'm not talking nonsense.=