Incorrect transition times in std::chrono::time_zone::get_info()
Jonathan Wakely
jwakely@redhat.com
Fri Jul 26 13:28:31 GMT 2024
On Fri, 26 Jul 2024 at 14:08, Edward Welbourne <edward.welbourne@qt.io> wrote:
>
> >> 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.
Specifically, I think the truncation happens because I convert the
sys_seconds to year_month_day for easy comparison to the dates in the
Rule records, and year_month_day cannot represent dates past
year::max().
>
> > 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).
I start calculating transitions from the date on which the Rule first
applies, which for Europe/Oslo is 1980, and then I find all
transitions from that date up to the requested time point (and then a
little further). If you ask for a date in the distant future, it will
generate A LOT of transitions. The transitions that get generated are
stored in the time_zone so that subsequent get_info requests for the
same time (or others that were previously generated) will be fast.
More information about the Libstdc++
mailing list