* mktime result may depend on previous calls
@ 2023-01-18 15:39 Max Nikulin
2023-01-18 22:12 ` Paul Eggert
0 siblings, 1 reply; 6+ messages in thread
From: Max Nikulin @ 2023-01-18 15:39 UTC (permalink / raw)
To: libc-alpha
Hi,
I have noticed that the mktime function may return different values for
the same argument. More precisely, result may depend on previous calls.
I am aware that POSIX underspecifies behavior of mktime in the case of
backward jump of local time (and that is impossible to disambiguate if
the value before or after time transition should be used when DST is not
changed), but I am still surprised. Is it intended that hidden state
affects returned value?
The following output is obtained for glibc-2.31. I am sorry for the
noise if it has changed in the development version.
The following time transition keeps isdst=0:
zdump -v Africa/Juba | grep 2021
Africa/Juba Sun Jan 31 20:59:59 2021 UT = Sun Jan 31 23:59:59 2021 EAT
isdst=0 gmtoff=10800
Africa/Juba Sun Jan 31 21:00:00 2021 UT = Sun Jan 31 23:00:00 2021 CAT
isdst=0 gmtoff=7200
Notice that same input on lines 4 and 10 causes different conversion
result 5-6 and 11-12
1 input: +121!00-31 22:00:00 dst:-1 w:0 j:0
2 1612119600 +121!00-31 22:00:00 dst:0 w:0 j:30
3 strptime: 2021-01-31 22:00:00 +0300 EAT
4 input: +121!00-31 23:30:00 dst:-1 w:0 j:0
5 1612125000 +121!00-31 23:30:00 dst:0 w:0 j:30
6 strptime: 2021-01-31 23:30:00 +0300 EAT
7 input: +121!01-01 01:00:00 dst:-1 w:0 j:0
8 1612134000 +121!01-01 01:00:00 dst:0 w:1 j:31
9 strptime: 2021-02-01 01:00:00 +0200 CAT
10 input: +121!00-31 23:30:00 dst:-1 w:0 j:0
11 1612128600 +121!00-31 23:30:00 dst:0 w:0 j:30
12 strptime: 2021-01-31 23:30:00 +0200 CAT
I use the following script and C code to test mktime:
TZ=Africa/Juba ./mktime-state <<EOF | nl
2021 1 31 22 0 0 -1
2021 1 31 23 30 0 -1
2021 2 1 1 0 0 -1
2021 1 31 23 30 0 -1
EOF
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
void print_tm(const struct tm *bt) {
printf(
"%+04d!%02d-%02d %02d:%02d:%02d dst:%-2d w:%-2d j:%-3d",
bt->tm_year, bt->tm_mon, bt->tm_mday,
bt->tm_hour, bt->tm_min, bt->tm_sec,
bt->tm_isdst, bt->tm_wday, bt->tm_yday);
}
int main() {
struct tm bt;
char buf[64];
while (1) {
memset(&bt, 0, sizeof(bt));
bt.tm_isdst = -1;
if (7 != scanf(
"%d%d%d%d%d%d%d",
&bt.tm_year, &bt.tm_mon, &bt.tm_mday,
&bt.tm_hour, &bt.tm_min, &bt.tm_sec, &bt.tm_isdst)
) {
break;
}
bt.tm_year -= 1900;
bt.tm_mon -= 1;
printf("%-15s ", "input:");
print_tm(&bt);
putchar('\n');
errno = 0;
time_t ts = mktime(&bt);
if (ts == -1 && errno != 0) {
printf("error: %s", strerror(errno));
continue;
}
printf("%15zd ", ts);
print_tm(&bt);
putchar('\n');
strftime(buf, sizeof(buf) - 1, "%F %T %z %Z", &bt);
printf("%-15s %s\n\n", "strptime:", buf);
}
return 0;
}
At first I experimented with another cases, a usual DST transition and
Europe/Kyiv Sat Jun 30 21:59:59 1990 UT = Sun Jul 1 01:59:59 1990 MSD
isdst=1 gmtoff=14400
Europe/Kyiv Sat Jun 30 22:00:00 1990 UT = Sun Jul 1 01:00:00 1990 EEST
isdst=1 gmtoff=10800
At some moment I was sure that mktime return value depends on tm_isdst
of its argument. Later I have realized that I got different results due
to other calls.
A more funny result is hysteresis loop when time is increased and then
decreased in steps around time transition moment:
https://list.orgmode.org/tq6lg6$9m2$1@ciao.gmane.io/2-mktime-hyst.png
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: mktime result may depend on previous calls
2023-01-18 15:39 mktime result may depend on previous calls Max Nikulin
@ 2023-01-18 22:12 ` Paul Eggert
2023-01-19 3:00 ` Max Nikulin
0 siblings, 1 reply; 6+ messages in thread
From: Paul Eggert @ 2023-01-18 22:12 UTC (permalink / raw)
To: Max Nikulin; +Cc: libc-alpha
On 1/18/23 07:39, Max Nikulin via Libc-alpha wrote:
> Is it intended that hidden state affects returned value?
I intended for it, yes. The behavior is allowed by POSIX and by the GNU
spec, and it helps performance in the typical case.
If there's a faster way to implement mktime that doesn't involve a
cache, that might be a good thing to implement.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: mktime result may depend on previous calls
2023-01-18 22:12 ` Paul Eggert
@ 2023-01-19 3:00 ` Max Nikulin
2023-01-19 4:34 ` Paul Eggert
0 siblings, 1 reply; 6+ messages in thread
From: Max Nikulin @ 2023-01-19 3:00 UTC (permalink / raw)
To: libc-alpha
On 19/01/2023 05:12, Paul Eggert wrote:
> On 1/18/23 07:39, Max Nikulin via Libc-alpha wrote:
>> Is it intended that hidden state affects returned value?
>
> I intended for it, yes. The behavior is allowed by POSIX and by the GNU
> spec, and it helps performance in the typical case.
Paul, thank you for clarification. Should some note be added to the docs?
---- >8 ----
Notice that in the case of ambiguous local time (around a transition
with backward step) multiple mktime calls with the same argument may
return both values. Such kind of uncertainty appears if tm_isdst is set
to -1 or if local time changes keeping the same DST state.
---- 8< ----
It even might be used as a non-portable way of disambiguation: just add
an extra call of mktime well aside from the backward time step. Too
weird to use it in practice though. Unfortunately POSIX does not allow
anything like
https://www.python.org/dev/peps/pep-0495/
PEP 495 – Local Time Disambiguation (Python)
> If there's a faster way to implement mktime that doesn't involve a
> cache, that might be a good thing to implement.
I do not mind the cache. There is arbitrary choice to which time moment
it is valid. However I agree that any changes may cause negative
performance impact.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: mktime result may depend on previous calls
2023-01-19 3:00 ` Max Nikulin
@ 2023-01-19 4:34 ` Paul Eggert
2023-01-19 7:10 ` Max Nikulin
0 siblings, 1 reply; 6+ messages in thread
From: Paul Eggert @ 2023-01-19 4:34 UTC (permalink / raw)
To: Max Nikulin, libc-alpha
On 1/18/23 19:00, Max Nikulin via Libc-alpha wrote:
> Paul, thank you for clarification. Should some note be added to the docs?
The actual behavior is reasonably complicated. As a time nerd I'm
probably the wrong person to ask about whether it should be documented.
>
> ---- >8 ----
> Notice that in the case of ambiguous local time (around a transition
> with backward step) multiple mktime calls with the same argument may
> return both values. Such kind of uncertainty appears if tm_isdst is set
> to -1 or if local time changes keeping the same DST state.
> ---- 8< ----
The above wording isn't quite right, as it ignores the role of tm_gmtoff
in mktime. (Did I say it was complicated? :-)
> It even might be used as a non-portable way of disambiguation: just add
> an extra call of mktime well aside from the backward time step. Too
> weird to use it in practice though. Unfortunately POSIX does not allow
> anything like
>
> https://www.python.org/dev/peps/pep-0495/
> PEP 495 – Local Time Disambiguation (Python)
POSIX does allow that sort of thing as an extension, and glibc mktime
does so: it disambiguates via tm_gmtoff.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: mktime result may depend on previous calls
2023-01-19 4:34 ` Paul Eggert
@ 2023-01-19 7:10 ` Max Nikulin
2023-01-20 9:21 ` Paul Eggert
0 siblings, 1 reply; 6+ messages in thread
From: Max Nikulin @ 2023-01-19 7:10 UTC (permalink / raw)
To: libc-alpha
On 19/01/2023 11:34, Paul Eggert wrote:
> On 1/18/23 19:00, Max Nikulin wrote:
>
>> Notice that in the case of ambiguous local time (around a transition
>> with backward step) multiple mktime calls with the same argument may
>> return both values. Such kind of uncertainty appears if tm_isdst is
>> set to -1 or if local time changes keeping the same DST state.
>
> The above wording isn't quite right, as it ignores the role of tm_gmtoff
> in mktime. (Did I say it was complicated? :-)
...
> POSIX does allow that sort of thing as an extension, and glibc mktime
> does so: it disambiguates via tm_gmtoff.
Has it been fixed recently?
https://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html#index-mktime
> The mktime function *ignores* the specified contents of the tm_wday,
> tm_yday, *tm_gmtoff*, and tm_zone members of the broken-down time structure
From the example below I would expect 1612128600 +0200 CAT gmtoff=7200
on third line, but my glibc is dated
1612119600 2021-01-31 22:00:00 +0300 EAT gmtoff=10800
1612125000 2021-01-31 23:30:00 +0300 EAT gmtoff=10800
1612125000 2021-01-31 23:30:00 +0300 EAT gmtoff=10800
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
void bt_init(struct tm *bt) {
memset(bt, 0, sizeof(*bt));
bt->tm_isdst = -1;
bt->tm_year = 2021 - 1900;
bt->tm_mon = 1 - 1;
bt->tm_mday = 31;
bt->tm_hour = 23;
bt->tm_min = 30;
bt->tm_sec = 0;
}
void bt_print(struct tm *bt) {
char buf[64];
time_t ts = mktime(bt);
strftime(buf, sizeof(buf) - 1, "%F %T %z %Z", bt);
printf("%15zd %s gmtoff=%ld\n", ts, buf, bt->tm_gmtoff);
}
int main() {
struct tm bt;
putenv("TZ=Africa/Juba");
// Fix cached offset to the value before time jump
bt_init(&bt);
bt.tm_hour = 22;
bt.tm_min = 0;
// Default conversion
bt_print(&bt);
bt_init(&bt);
bt_print(&bt);
// Attempt to choose +0200 CAT instead of +0300 EAT
bt_init(&bt);
bt.tm_gmtoff = 2*3600;
bt_print(&bt);
return 0;
}
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: mktime result may depend on previous calls
2023-01-19 7:10 ` Max Nikulin
@ 2023-01-20 9:21 ` Paul Eggert
0 siblings, 0 replies; 6+ messages in thread
From: Paul Eggert @ 2023-01-20 9:21 UTC (permalink / raw)
To: Max Nikulin, libc-alpha
On 2023-01-18 23:10, Max Nikulin via Libc-alpha wrote:
> Has it been fixed recently?
>
> https://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html#index-mktime
Oops, looks like I was mistaken. I was thinking of the reference
implementation of mktime in TZDB, not glibc mktime. The latter should be
fixed to behave more like the former, and to use tm_gmtoff to
disambiguate in the rare dicey cases.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2023-01-20 9:21 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-18 15:39 mktime result may depend on previous calls Max Nikulin
2023-01-18 22:12 ` Paul Eggert
2023-01-19 3:00 ` Max Nikulin
2023-01-19 4:34 ` Paul Eggert
2023-01-19 7:10 ` Max Nikulin
2023-01-20 9:21 ` Paul Eggert
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).