Incorrect transition times in std::chrono::time_zone::get_info()

Edward Welbourne edward.welbourne@qt.io
Fri Jul 26 13:07:32 GMT 2024


>> Jonathan Wakely (26 July 2024 12:41) replied:
>> > What exactly is the problem you see? Do you have a testcase for this?

On Fri, 26 Jul 2024 at 11:56, Edward Welbourne <edward.welbourne@qt.io> wrote:
>> static void lateInfo(void)
>> {
>>     using namespace std::chrono;
>>     constexpr long long huge = 0x7fffffffffffffff;
>>     constexpr auto late = sys_time<milliseconds>(milliseconds(huge));
>>     const time_zone *zone = idToZone("Europe/Oslo");
>>     const sys_info info = zone->get_info(late); // std::runtime_error
>>     std::cout << '\n' << info.abbrev << '\n';
>> }
>>
>> Slap a call to this into the start of my previous main(), for example,
>> to see the exception thrown.  It's using the same idToZone() as before.

Jonathan Wakely (26 July 2024 13:40) replied:
> I replaced idToZone with chrono::locate_zone just because.

Fair enough, I think we had the binary chop to avoid having to
deal with exceptions in builds without them.

> Your sys_time is greater than sys_days(year::max()/January/1), so I
> think libstdc++ gives up calculating transitions past that point and
> can't give you the info.
>
> I find it hard to be motivated to support get_info past the 33rd
> millennium, I don't expect this code to still be in use by the time of
> the Horus Heresy.

and, as I noted before, we can live with that - even year::max() beats
the socks off a certain other platform which shall remain unnamed, so I
have kludges in place to take care of out-of-bounds.

>> > time_zone::get_info converts its argument to sys_seconds and calls
>> > _M_get_sys_info. That should have exactly the same range as time_t
>> > (64-bit signed number of seconds, with 1970-01-01 as the epoch).
>>
>> I'm on a 64-bit system, where the time_t functions cope with times such
>> as the one above, and this is a 64-bit number of milliseconds out, so
>> should be well within a 64-bit second range.

> But not within the chrono::year::max() upper limit.

That's the sort of bound I'd begun to suspect, nice to have it pinned down.

> I could add an explicit check for times past that limit and give a
> better what() in the exception (and not bother calculating 64 thousand
> transitions before realising the requested time point is still too
> high).

I'm surprised you compute all intervening transitions, rather than just
jumping straight to the final year (and, in this case, recognising
you're out of bounds).

>> > There's a known bug (well, known to me) where libstdc++ should
>> > inerpret the transition time relative to the current wall clock time,
>> > not UTC.
[snip]
>> > So if a transition should happen at (for example) 2am local time,
>> > libstdc++ might be applying it at 2am UTC in some cases.
>>
>> That fits some of the cases in the list I gave before.
>> Others seem to be using UTC 00:00.

> For example, this one:
> Asia/Bishkek        : 2005-08-12 06:00:00 +06

> The transition in that case is:

> 5 KG +05/+06 2005 Au 12
> 6 - +06

> Since the UNTIL field is just Au 12 without a TIME, it occurred at
> midnight, but it should be midnight standard time, not midnight UTC.
> So it's the same bug.

Sounds like there's no point me fighting my way past the "User account
creation has been restricted" that I hit when trying to create an
account, then, since you already have it in your sights.

I note that in tz/asia the entry is (with apologies for a certain mailer I
did not chose probably messing up the formatting):

# Zone	NAME		STDOFF	RULES	FORMAT	[UNTIL]
Zone	Asia/Bishkek	4:58:24 -	LMT	1924 May  2
			5:00	-	+05	1930 Jun 21
			6:00 RussiaAsia +06/+07	1991 Mar 31  2:00s
			5:00 RussiaAsia	+05/+06	1991 Aug 31  2:00
			5:00	Kyrgyz	+05/+06	2005 Aug 12
			6:00	-	+06

so I guess you're working with a pre-digested form of it.  The Kyrgyz
rule it references appears just before this,

# Rule	NAME	FROM	TO	-	IN	ON	AT	SAVE	LETTER/S
Rule	Kyrgyz	1992	1996	-	Apr	Sun>=7	0:00s	1:00	-
Rule	Kyrgyz	1992	1996	-	Sep	lastSun	0:00	0	-
Rule	Kyrgyz	1997	2005	-	Mar	lastSun	2:30	1:00	-
Rule	Kyrgyz	1997	2004	-	Oct	lastSun	2:30	0	-

and gives no AT for 2005 Aug, although its 2:30 transition times in
March and October, each ending before it, are suggestive.  The lack of a
specified transition time may not be as simple as midnight - there are
some entries that specify 0:00 overtly as transition time
(e.g. Europe/Brussels in 1916), which they probably wouldn't if it were
the default - but I don't know what the actual rule is.  The only
documentation I can find is the tz-how-to.html in the tz repo [0] and
it's a bit incomplete.  Consulting others who maintain parsers for this
format, or their parser source-code, is probably the only option.

[0] https://github.com/eggert/tz

	Eddy.


More information about the Libstdc++ mailing list