On Tue, 17 Mar 2026 at 14:00, Tomasz Kaminski <[email protected]> wrote:
>
>
>
> On Mon, Mar 16, 2026 at 11:52 PM Jonathan Wakely <[email protected]> wrote:
>>
>> This is a partial fix for PR 116110, so that zone changes that occur at
>> a time specified as standard time will be parsed correctly. Previously
>> all UNTIL times were assumed to be UTC, which is incorrect according to
>> the spec. Only times with a 'u' suffix (or the equivalent 'g' or 'z')
>> are UTC times. An 's' suffix means standard time (i.e. the zone's usual
>> offset from UTC, without any DST adjustment) and a 'w' suffix (or no
>> suffix) means wall time, so the offset and any DST adjustment. This
>> commit fixes the handling of 's' suffixes, so that the ZoneInfo::m_until
>> member is set correctly. It also fixes the handling of some UNTIL times
>> using wall time, specifically those where the DST adjustment is known
>> during parsing of the tzdata.zi file.
>>
>> This is not a complete fix, because as noted in PR 116110 a Zone line
>> that refers to a named Rule and uses wall time cannot be determined
>> while parsing tzdata.zi. We need to remember that the m_until member is
>> not correct, and then adjust it later when we find the rule that applies
>> at a given time point. That requires more work.
>>
>> Some existing tests need to be adjusted due to the fixes in this commit.
>> The std/time/time_zone/get_info_sys.cc and std/time/zoned_time/1.cc
>> tests are corrected to check that the changes happens at midnight local
>> time, not midnight UTC as previously assumed.
>>
>> libstdc++-v3/ChangeLog:
>>
>>         PR libstdc++/116110
>>         * src/c++20/tzdb.cc (operator>>(istream&, ZoneInfo&)): Adjust
>>         inf.m_until according to indicator suffix on AT time in UNTIL.
>>         * testsuite/std/time/time_zone/get_info_sys.cc: Adjust expected
>>         results to account for corrected logic.
>>         * testsuite/std/time/zoned_time/1.cc: Likewise.
>> ---
>>
>> Tested x86_64-linux.
>
> LGTM.
>>
>>
>>  libstdc++-v3/src/c++20/tzdb.cc                | 15 +++++++++----
>>  .../std/time/time_zone/get_info_sys.cc        | 22 +++++++++++--------
>>  .../testsuite/std/time/zoned_time/1.cc        | 20 ++++++++++-------
>>  3 files changed, 36 insertions(+), 21 deletions(-)
>>
>> diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc
>> index e26a9ee38072..82e4790b0d7f 100644
>> --- a/libstdc++-v3/src/c++20/tzdb.cc
>> +++ b/libstdc++-v3/src/c++20/tzdb.cc
>> @@ -2289,11 +2289,18 @@ namespace std::chrono
>>           at_time t{};
>>           // XXX DAY should support ON format, e.g. lastSun or Sun>=8
>>           in >> m >> d >> t;
>> -         // XXX UNTIL field should be interpreted
>> -         // "using the rules in effect just before the transition"
>> -         // so might need to store as year_month_day and hh_mm_ss and only
>> -         // convert to a sys_time once we know the offset in effect.
>>           inf.m_until = sys_days(year(y)/m.m/day(d)) + seconds(t.time);
>> +         if (t.indicator != at_time::Universal)
>> +           { // UNTIL uses "the rules in effect just before the transition"
>> +             // adjust by STDOFF
>
> Was initially confused, if that shouldn't be previous offset, but this is end 
> time
> (so the rule applies before transition) not start time.
>>
>> +             inf.m_until -= seconds(inf.m_offset);
>> +             if (t.indicator != at_time::Standard)
>> +               {
>> +                 if (inf.m_expanded) // Not a named Rule, SAVE is known now.
>> +                   inf.m_until -= inf.m_save;
>> +                 // else Named Rule, SAVE is unknown. FIXME: PR 116110
>
> Yes, we will need to extract `find_letters` into a separate function, so we 
> can determine
> save (DST offset) value for each transition.

Hmm, that could work ... I have another solution, but it's quite
complex. I will probably share the patch soon.


>>
>> +               }
>> +           }
>>         }
>>        else
>>         inf.m_until = sys_days(year::max()/December/31);
>> diff --git a/libstdc++-v3/testsuite/std/time/time_zone/get_info_sys.cc 
>> b/libstdc++-v3/testsuite/std/time/time_zone/get_info_sys.cc
>> index 769c7744017b..88f16b76ff48 100644
>> --- a/libstdc++-v3/testsuite/std/time/time_zone/get_info_sys.cc
>> +++ b/libstdc++-v3/testsuite/std/time/time_zone/get_info_sys.cc
>> @@ -12,33 +12,37 @@ test_zurich()
>>    const time_zone* const tz = locate_zone("Europe/Zurich");
>>
>>    {
>> -    sys_days d = 1853y/July/16;
>> +    sys_days d = 1853y/July/16; // On this date ...
>> +    auto offset = 34min + 8s;   // ... local time is this far ahead of UTC,
>> +    auto t = d - offset;        // so LMT to BMT transition is at this time.
>>
>> -    auto info = tz->get_info(d - 1s);
>> -    VERIFY( info.offset == (34min + 8s) );
>> +    auto info = tz->get_info(t - 1s);
>> +    VERIFY( info.offset == offset );
>>      VERIFY( info.abbrev == "LMT" );
>>
>> -    info = tz->get_info(d);
>> +    info = tz->get_info(t);
>>      VERIFY( info.offset == (29min + 46s) );
>>      VERIFY( info.abbrev == "BMT" );
>>
>> -    info = tz->get_info(d + 1s);
>> +    info = tz->get_info(t + 1s);
>>      VERIFY( info.offset == (29min + 46s) );
>>      VERIFY( info.abbrev == "BMT" );
>>
>> -    info = tz->get_info(d + 0.001s);
>> +    info = tz->get_info(t + 0.001s);
>>      VERIFY( info.offset == (29min + 46s) );
>>      VERIFY( info.abbrev == "BMT" );
>>    }
>>
>>    {
>>      sys_days d = 1894y/June/1;
>> +    auto offset = 29min + 46s;
>> +    auto t = d - offset;
>>
>> -    auto info = tz->get_info(d - 1s);
>> -    VERIFY( info.offset == (29min + 46s) );
>> +    auto info = tz->get_info(t - 1s);
>> +    VERIFY( info.offset == offset );
>>      VERIFY( info.abbrev == "BMT" );
>>
>> -    info = tz->get_info(d);
>> +    info = tz->get_info(t);
>>      VERIFY( info.offset == 1h );
>>      VERIFY( info.abbrev == "CET" );
>>    }
>> diff --git a/libstdc++-v3/testsuite/std/time/zoned_time/1.cc 
>> b/libstdc++-v3/testsuite/std/time/zoned_time/1.cc
>> index 1623aca1c7a8..a77cd99dd550 100644
>> --- a/libstdc++-v3/testsuite/std/time/zoned_time/1.cc
>> +++ b/libstdc++-v3/testsuite/std/time/zoned_time/1.cc
>> @@ -48,24 +48,26 @@ test_zurich()
>>    const time_zone* const zurich = locate_zone("Europe/Zurich");
>>
>>    {
>> -    sys_days d = 1853y/July/16;
>> +    sys_days d = 1853y/July/16; // On this date ...
>> +    auto offset = 34min + 8s;   // ... local time is this far ahead of UTC,
>> +    auto t = d - offset;        // so LMT to BMT transition is at this time.
>>
>> -    auto z = zoned_seconds(zurich, sys_seconds(d) - 1s);
>> +    auto z = zoned_seconds(zurich, t - 1s);
>>      auto info = z.get_info();
>> -    VERIFY( info.offset == (34min + 8s) );
>> +    VERIFY( info.offset == offset );
>>      VERIFY( info.abbrev == "LMT" );
>>
>> -    z = zoned_seconds(zurich, d);
>> +    z = zoned_seconds(zurich, t);
>>      info = z.get_info();
>>      VERIFY( info.offset == (29min + 46s) );
>>      VERIFY( info.abbrev == "BMT" );
>>
>> -    z = zoned_seconds(zurich, d + 1s);
>> +    z = zoned_seconds(zurich, t + 1s);
>>      info = z.get_info();
>>      VERIFY( info.offset == (29min + 46s) );
>>      VERIFY( info.abbrev == "BMT" );
>>
>> -    auto z2 = zoned_time(zurich, d + 0.001s);
>> +    auto z2 = zoned_time(zurich, t + 0.001s);
>>      info = z2.get_info();
>>      VERIFY( info.offset == (29min + 46s) );
>>      VERIFY( info.abbrev == "BMT" );
>> @@ -73,13 +75,15 @@ test_zurich()
>>
>>    {
>>      sys_days d = 1894y/June/1;
>> +    auto offset = 29min + 46s;
>> +    auto t = d - offset;
>>
>> -    auto z = zoned_seconds(zurich, sys_seconds(d) - 1s);
>> +    auto z = zoned_seconds(zurich, t - 1s);
>>      auto info = z.get_info();
>>      VERIFY( info.offset == (29min + 46s) );
>>      VERIFY( info.abbrev == "BMT" );
>>
>> -    z = zoned_seconds(zurich, d);
>> +    z = zoned_seconds(zurich, t);
>>      info = z.get_info();
>>      VERIFY( info.offset == 1h );
>>      VERIFY( info.abbrev == "CET" );
>> --
>> 2.53.0
>>

Reply via email to