Summary: | mktime: returns clock for UTC with isdst=1 | ||
---|---|---|---|
Product: | glibc | Reporter: | Aurelien Jarno <aurelien> |
Component: | time | Assignee: | Not yet assigned to anyone <unassigned> |
Status: | RESOLVED NOTABUG | ||
Severity: | normal | CC: | eggert, fweimer |
Priority: | P2 | ||
Version: | unspecified | ||
Target Milestone: | --- | ||
URL: | http://bugs.debian.org/1057856 | ||
Host: | Target: | ||
Build: | Last reconfirmed: | ||
Attachments: | Reproducer |
tm_isdst is just a hint, to resolve ambigous time stamps around the DST rollback. The C and POSIX standards do not specify what mktime should do with examples like this, so this is not a conformance issue. As Andreas mentioned, tm_isdst is intended as a hint for ambiguous timestamps; unfortunately this does not suffice in general for TZDB Zones, e.g., 1971-10-31 02:30 when TZ='Europe/London' because at 03:00 standard time moved back by an hour in London so there are two distinct matching timestamps both with tm_isdst==0, one with time_t==57720600 and one with time_t==57724200. So the whole area is a bit of an ill-thought-out mess. glibc currently supports the idea that if you want to find a time that's 1 year from now you can call localtime, add 1 to tm_year, and then call mktime on the result, and you'll get a timestamp that's 1 year later or thereabouts (the concept of "one year later" is not well-defined). This idea didn't work with older glibc (and I guess FreeBSD libc?) if DST is in use now but not 1 year from now (or vice versa) because in those cases older glibc mktime simply failed. In practice the new glibc behavior seems to be more useful than the old one. The old behavior was changed in response to a real-world use case <https://bugs.gnu.org/48085> whereas the Erlang/OTP issue is an artificial test case which is a weaker argument. With that in mind I'm going to resolve this as NOTABUG. The Erlang/OTP test case is wrong because it's making assumptions about mktime that are not present in the relative standards We already scan the entire tables while loading the time zone data, so we could make a note if we have seen any DST hints, and if there are none present, we could fail as before for tm_isdst > 0. We also don't know how the tz project will model the introduction of DST if a zone changes introduces DST for the first time, so the synthetic offset might well be wrong. (In reply to Florian Weimer from comment #3) > We already scan the entire tables while loading the time zone data, so we > could make a note if we have seen any DST hints, and if there are none > present, we could fail as before for tm_isdst > 0. We also don't know how > the tz project will model the introduction of DST if a zone changes > introduces DST for the first time, so the synthetic offset might well be > wrong. Yes, that could be done and shouldn't be hard to do. If so, we'd need to consult the trailing TZ string as well as the tm_isdst flags in the binary tables. However, failing would not be good, as we already learned when we put in code to have mktime fail. This is because a lot of code expects mktime to succeed unless the resulting time_t would be out of range. Better would be to ignore tm_isdst in timezones that have never observed tm_isdst. > We also don't know how the tz project will model the introduction of DST > if a zone changes introduces DST for the first time, > so the synthetic offset might well be wrong. There's no way in general to determine a synthetic offset. For example, America/St_Johns observed 1-hour DST in summer 1987 and 2-hour DST in summer 1988. If mktime is given a timestamp in January 1988 with tm_isdst=1 should it use a 1-hour offset or a 2-hour offset? That would depend on whether the timestamp was computed from in the previous summer but adding a few months, or from the next summer but subtracting a few months. Both scenarios are plausible. One possibility is that glibc mktime could use the incoming tm_gmtoff as a hint as to what synthetic offset to use. If the incoming tm_gmtoff is one of the DST offsets that appears in the TZif's binary data or TZ string, then mktime could use that offset. Although this would in theory rely on undefined behavior (tm_gmtoff might be uninitialized), in practice it would likely work better than what glibc does now. If this possibility were implemented lazily it shouldn't slow down the typical mktime case. |
Created attachment 15253 [details] Reproducer A Debian user reported a behaviour change of the mktime function affecting Erlang/OTP. When passing isdst=1 with an UTC timezone, a time is now returned (with isdst=0), while in the past it returned an error. The issue has been tracked to the following commit 83859e1115269cf56d21669361d4ddbe2687831c which went into 2.37, and got backported to the 2.34, 2.35 and 2.36 branches. The C standard is not fully clear about the behaviour in that case: "A positive or 0 value for tm_isdst shall cause mktime() to presume initially that Daylight Savings Time, respectively, is or is not in effect for the specified time. A negative value for tm_isdst shall cause mktime() to attempt to determine whether Daylight Savings Time is in effect for the specified time." However it appears that the original GNU libc behaviour before that commit matches the FreeBSD libc behaviour. The user provided a small test program to exercise the issue (see attachement): $ gcc -o check_dst check_dst.c $ TZ=UTC ./check_dst 0 tzname[0]: UTC, tzname[1]: UTC dst before: 0 dst after: 0 clock: 1217548800 $ TZ=UTC ./check_dst 1 tzname[0]: UTC, tzname[1]: UTC dst before: 1 dst after: 0 clock: 1217545200