* mktime(3), asctime(3): possible bug in tm_isdst?
@ 2021-10-09 21:43 Alejandro Colomar (man-pages) via Libc-alpha
2021-10-09 22:12 ` Paul Eggert
0 siblings, 1 reply; 5+ messages in thread
From: Alejandro Colomar (man-pages) via Libc-alpha @ 2021-10-09 21:43 UTC (permalink / raw
To: Paul Eggert, Albert Aribaud, Libc-alpha
Hello,
I was adding some tests to an application that makes use of mktime(3),
and I got some weird results.
I wrote the following test program to see what was going on,
and the results are even more puzzling.
I don't know what's going on,
but it doesn't make any sense to me.
I read mktime(3p) and mktime(3),
and neither seem to explain this behavior.
Could you please confirm if it is a bug and I should open a bugzilla report?
Otherwise, could you explain the behavior so I can better document it?
Thanks,
Alex
---
$ cat mktime.c
#include <time.h>
#include <stdio.h>
int main(void)
{
struct tm tm = {0};
time_t tim;
tm.tm_sec = 0xE;
tm.tm_min = 0x32;
tm.tm_hour = 0x02;
tm.tm_mday = 0x09;
tm.tm_mon = 0x04 - 1;
tm.tm_year = 0x15 + 100;
puts("tm_isdst = 1");
tm.tm_isdst = 1;
tim = mktime(&tm);
printf("%s", ctime(&tim));
printf("%s", asctime(&tm));
putchar('\n');
puts("tm_isdst = -1");
tm.tm_isdst = -1;
tim = mktime(&tm);
printf("%s", ctime(&tim));
printf("%s", asctime(&tm));
putchar('\n');
puts("tm_isdst = 0");
tm.tm_isdst = 0;
tim = mktime(&tm);
printf("%s", ctime(&tim));
printf("%s", asctime(&tm));
putchar('\n');
puts("tm_isdst = 1");
tm.tm_isdst = 1;
tim = mktime(&tm);
printf("%s", ctime(&tim));
printf("%s", asctime(&tm));
putchar('\n');
puts("tm_isdst = -1");
tm.tm_isdst = -1;
tim = mktime(&tm);
printf("%s", ctime(&tim));
printf("%s", asctime(&tm));
return 0;
}
$ ./a.out
tm_isdst = 1
Fri Apr 9 02:50:14 2021
Fri Apr 9 02:50:14 2021
tm_isdst = -1
Fri Apr 9 02:50:14 2021
Fri Apr 9 02:50:14 2021
tm_isdst = 0
Fri Apr 9 03:50:14 2021
Fri Apr 9 03:50:14 2021
tm_isdst = 1
Fri Apr 9 03:50:14 2021
Fri Apr 9 03:50:14 2021
tm_isdst = -1
Fri Apr 9 03:50:14 2021
Fri Apr 9 03:50:14 2021
Reproduced both in:
Ubuntu
Ubuntu GLIBC 2.31-0ubuntu9.2
Debian Sid
Debian GLIBC 2.32-4
--
Alejandro Colomar
Linux man-pages comaintainer; https://www.kernel.org/doc/man-pages/
http://www.alejandro-colomar.es/
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: mktime(3), asctime(3): possible bug in tm_isdst?
2021-10-09 21:43 mktime(3), asctime(3): possible bug in tm_isdst? Alejandro Colomar (man-pages) via Libc-alpha
@ 2021-10-09 22:12 ` Paul Eggert
2021-10-10 0:55 ` Alejandro Colomar (man-pages) via Libc-alpha
0 siblings, 1 reply; 5+ messages in thread
From: Paul Eggert @ 2021-10-09 22:12 UTC (permalink / raw
To: Alejandro Colomar (man-pages), Albert Aribaud, Libc-alpha
I don't see a bug in the behavior you describe. POSIX says the following:
"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."
and that's what mktime is doing for you (I am assuming you're in a
location where the specified time is during a spring-forward transition).
Your program uses the output of each mktime call as the input to the
next one, which is a bit confusing - perhaps that's what's biting you?
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: mktime(3), asctime(3): possible bug in tm_isdst?
2021-10-09 22:12 ` Paul Eggert
@ 2021-10-10 0:55 ` Alejandro Colomar (man-pages) via Libc-alpha
2021-10-10 1:07 ` Alejandro Colomar (man-pages) via Libc-alpha
2021-10-10 1:18 ` Alejandro Colomar (man-pages) via Libc-alpha
0 siblings, 2 replies; 5+ messages in thread
From: Alejandro Colomar (man-pages) via Libc-alpha @ 2021-10-10 0:55 UTC (permalink / raw
To: Paul Eggert, Albert Aribaud, Libc-alpha
Hi Paul,
Thanks for the fast reply!
On 10/10/21 12:12 AM, Paul Eggert wrote:
> I don't see a bug in the behavior you describe. POSIX says the following:
>
> "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."
>
> and that's what mktime is doing for you (I am assuming you're in a
> location where the specified time is during a spring-forward transition).
>
> Your program uses the output of each mktime call as the input to the
> next one, which is a bit confusing - perhaps that's what's biting you?
Hmm, yes, that was biting me. But I didn't expect mktime() to modify
tm_hour. Is it due to the "normalization"? See the updated test
program below.
[
The mktime() function modifies the fields of the tm struc‐
ture as follows: tm_wday and tm_yday are set to values de‐
termined from the contents of the other fields; if struc‐
ture members are outside their valid interval, they will be
normalized (so that, for example, 40 October is changed
into 9 November); tm_isdst is set (regardless of its ini‐
tial value) to a positive value or to 0, respectively, to
indicate whether DST is or is not in effect at the speci‐
fied time. Calling mktime() also sets the external vari‐
able tzname with information about the current timezone.
]
I think it's not obvious from there that mktime() will correct the
structure hour field with the DST.
---
alx@sqli:~/src/test$ cat mktime.c
#include <time.h>
#include <stdio.h>
int main(void)
{
struct tm tm0 = {0};
struct tm tm1, tm2;
time_t tim;
tm0.tm_sec = 0xE;
tm0.tm_min = 0x32;
tm0.tm_hour = 0x02;
tm0.tm_mday = 0x09;
tm0.tm_mon = 0x04 - 1;
tm0.tm_year = 0x15 + 100;
puts("tm_isdst = 1");
tm1 = tm0;
tm1.tm_isdst = 1;
tim = mktime(&tm1);
printf("%s", ctime(&tim));
printf("%is %im %ih %imday %imon %iyr %iwday %iyday %idst\n",
tm1.tm_sec, tm1.tm_min, tm1.tm_hour, tm1.tm_mday, tm1.tm_mon,
tm1.tm_year, tm1.tm_wday, tm1.tm_yday, tm1.tm_isdst);
tm2 = tm0;
tm2.tm_isdst = 1;
printf("%s", asctime(&tm2));
putchar('\n');
puts("tm_isdst = -1");
tm1 = tm0;
tm1.tm_isdst = -1;
tim = mktime(&tm1);
printf("%s", ctime(&tim));
printf("%is %im %ih %imday %imon %iyr %iwday %iyday %idst\n",
tm1.tm_sec, tm1.tm_min, tm1.tm_hour, tm1.tm_mday, tm1.tm_mon,
tm1.tm_year, tm1.tm_wday, tm1.tm_yday, tm1.tm_isdst);
tm2 = tm0;
tm2.tm_isdst = -1;
printf("%s", asctime(&tm2));
putchar('\n');
puts("tm_isdst = 0");
tm1 = tm0;
tm1.tm_isdst = 0;
tim = mktime(&tm1);
printf("%s", ctime(&tim));
printf("%is %im %ih %imday %imon %iyr %iwday %iyday %idst\n",
tm1.tm_sec, tm1.tm_min, tm1.tm_hour, tm1.tm_mday, tm1.tm_mon,
tm1.tm_year, tm1.tm_wday, tm1.tm_yday, tm1.tm_isdst);
tm2 = tm0;
tm2.tm_isdst = 0;
printf("%s", asctime(&tm2));
putchar('\n');
puts("tm_isdst = 1");
tm1 = tm0;
tm1.tm_isdst = 1;
tim = mktime(&tm1);
printf("%s", ctime(&tim));
printf("%is %im %ih %imday %imon %iyr %iwday %iyday %idst\n",
tm1.tm_sec, tm1.tm_min, tm1.tm_hour, tm1.tm_mday, tm1.tm_mon,
tm1.tm_year, tm1.tm_wday, tm1.tm_yday, tm1.tm_isdst);
tm2 = tm0;
tm2.tm_isdst = 1;
printf("%s", asctime(&tm2));
putchar('\n');
puts("tm_isdst = -1");
tm1 = tm0;
tm1.tm_isdst = -1;
tim = mktime(&tm1);
printf("%s", ctime(&tim));
printf("%is %im %ih %imday %imon %iyr %iwday %iyday %idst\n",
tm1.tm_sec, tm1.tm_min, tm1.tm_hour, tm1.tm_mday, tm1.tm_mon,
tm1.tm_year, tm1.tm_wday, tm1.tm_yday, tm1.tm_isdst);
tm2 = tm0;
tm2.tm_isdst = -1;
printf("%s", asctime(&tm2));
return 0;
}
alx@sqli:~/src/test$ ./a.out
tm_isdst = 1
Fri Apr 9 02:50:14 2021
14s 50m 2h 9mday 3mon 121yr 5wday 98yday 1dst
Sun Apr 9 02:50:14 2021
tm_isdst = -1
Fri Apr 9 02:50:14 2021
14s 50m 2h 9mday 3mon 121yr 5wday 98yday 1dst
Sun Apr 9 02:50:14 2021
tm_isdst = 0
Fri Apr 9 03:50:14 2021
14s 50m 3h 9mday 3mon 121yr 5wday 98yday 1dst
Sun Apr 9 02:50:14 2021
tm_isdst = 1
Fri Apr 9 02:50:14 2021
14s 50m 2h 9mday 3mon 121yr 5wday 98yday 1dst
Sun Apr 9 02:50:14 2021
tm_isdst = -1
Fri Apr 9 02:50:14 2021
14s 50m 2h 9mday 3mon 121yr 5wday 98yday 1dst
Sun Apr 9 02:50:14 2021
---
So now I can try to debug my original problem.
If I have some time (struct tm), and I don't want the system to apply
any modifications due to DST (I carry that information separately, so I
want the system to assume this is UTC), I would expect that I need to
set tm_isdst to 0.
[
The mktime() function converts a broken‐down time struc‐
ture, expressed as local time, to calendar time representa‐
tion. The function ignores the values supplied by the
caller in the tm_wday and tm_yday fields. The value speci‐
fied in the tm_isdst field informs mktime() whether or not
daylight saving time (DST) is in effect for the time sup‐
plied in the tm structure: a positive value means DST is in
effect; zero means that DST is not in effect; and a nega‐
tive value means that mktime() should (use timezone infor‐
mation and system databases to) attempt to determine
whether DST is in effect at the specified time.
]
If I understand correctly the above,
if I use -1,
I'm asking mktime() to determine from the system if DST is in effect;
if I use 1 (or positive),
I'm saying that DST is in effect,
so mktime() should correct the time to get the time since Epoch (which
is of course number of seconds UTC);
and if I use 0,
I'm saying that DST is *not* in effect,
so I expect mktime() to not modify the time, and output it as is.
However, neither of them produces the Epoch time I would expect, which
is 1617936614. Instead, they produce two different values, which I
don't really understand.
Then, if I set TZ=0 through setenv(3), I get what I wanted in the
beginning, but it fails for tm_isdst = 1 (maybe you can't ask it to use
DST for a UTC timezone [TZ="" defaults to UTC per the tzset(3) man page]?).
See below:
---
$ cat mktime.c
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
struct tm tm0 = {0};
struct tm tm1 = {0};
struct tm tm2 = {0};
time_t tim;
// if (setenv("TZ", "", true) == -1)
// exit(1);
tm0.tm_sec = 0xE;
tm0.tm_min = 0x32;
tm0.tm_hour = 0x02;
tm0.tm_mday = 0x09;
tm0.tm_mon = 0x04 - 1;
tm0.tm_year = 0x15 + 100;
puts("tm_isdst = 1");
tm1 = tm0;
tm1.tm_isdst = 1;
tim = mktime(&tm1);
printf("%ji\n", (intmax_t)tim);
printf("%s", ctime(&tim));
printf("%is %im %ih %imday %imon %iyr %iwday %iyday %idst\n",
tm1.tm_sec, tm1.tm_min, tm1.tm_hour, tm1.tm_mday, tm1.tm_mon,
tm1.tm_year, tm1.tm_wday, tm1.tm_yday, tm1.tm_isdst);
tm2 = tm0;
tm2.tm_isdst = 1;
printf("%s", asctime(&tm2));
putchar('\n');
puts("tm_isdst = -1");
tm1 = tm0;
tm1.tm_isdst = -1;
tim = mktime(&tm1);
printf("%ji\n", (intmax_t)tim);
printf("%s", ctime(&tim));
printf("%is %im %ih %imday %imon %iyr %iwday %iyday %idst\n",
tm1.tm_sec, tm1.tm_min, tm1.tm_hour, tm1.tm_mday, tm1.tm_mon,
tm1.tm_year, tm1.tm_wday, tm1.tm_yday, tm1.tm_isdst);
tm2 = tm0;
tm2.tm_isdst = -1;
printf("%s", asctime(&tm2));
putchar('\n');
puts("tm_isdst = 0");
tm1 = tm0;
tm1.tm_isdst = 0;
tim = mktime(&tm1);
printf("%ji\n", (intmax_t)tim);
printf("%s", ctime(&tim));
printf("%is %im %ih %imday %imon %iyr %iwday %iyday %idst\n",
tm1.tm_sec, tm1.tm_min, tm1.tm_hour, tm1.tm_mday, tm1.tm_mon,
tm1.tm_year, tm1.tm_wday, tm1.tm_yday, tm1.tm_isdst);
tm2 = tm0;
tm2.tm_isdst = 0;
printf("%s", asctime(&tm2));
putchar('\n');
return 0;
}
$ ./a.out
tm_isdst = 1
1617929414
Fri Apr 9 02:50:14 2021
14s 50m 2h 9mday 3mon 121yr 5wday 98yday 1dst
Sun Apr 9 02:50:14 2021
tm_isdst = -1
1617929414
Fri Apr 9 02:50:14 2021
14s 50m 2h 9mday 3mon 121yr 5wday 98yday 1dst
Sun Apr 9 02:50:14 2021
tm_isdst = 0
1617933014
Fri Apr 9 03:50:14 2021
14s 50m 3h 9mday 3mon 121yr 5wday 98yday 1dst
Sun Apr 9 02:50:14 2021
And if I uncomment setenv(), I get the following:
$ ./a.out
tm_isdst = 1
-1
Wed Dec 31 23:59:59 1969
14s 50m 2h 9mday 3mon 121yr 0wday 0yday 1dst
Sun Apr 9 02:50:14 2021
tm_isdst = -1
1617936614
Fri Apr 9 02:50:14 2021
14s 50m 2h 9mday 3mon 121yr 5wday 98yday 0dst
Sun Apr 9 02:50:14 2021
tm_isdst = 0
1617936614
Fri Apr 9 02:50:14 2021
14s 50m 2h 9mday 3mon 121yr 5wday 98yday 0dst
Sun Apr 9 02:50:14 2021
I think the solution for my problem would be to setenv("TZ", "", 1); and
then use tm_isdst = -1; but don't understand why.
Thanks,
Alex
--
Alejandro Colomar
Linux man-pages comaintainer; https://www.kernel.org/doc/man-pages/
http://www.alejandro-colomar.es/
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: mktime(3), asctime(3): possible bug in tm_isdst?
2021-10-10 0:55 ` Alejandro Colomar (man-pages) via Libc-alpha
@ 2021-10-10 1:07 ` Alejandro Colomar (man-pages) via Libc-alpha
2021-10-10 1:18 ` Alejandro Colomar (man-pages) via Libc-alpha
1 sibling, 0 replies; 5+ messages in thread
From: Alejandro Colomar (man-pages) via Libc-alpha @ 2021-10-10 1:07 UTC (permalink / raw
To: Paul Eggert, Albert Aribaud, Libc-alpha
On 10/10/21 2:55 AM, Alejandro Colomar (man-pages) wrote:
> Then, if I set TZ=0 through setenv(3), I get what I wanted in the
Oops, I meant TZ=""; to get the behavior specified in tzset(3) (since
mktime(3) calls tzset(3), or acts as if it did):
[
If the TZ variable does appear in the environment, but
its value is empty, or its value cannot be interpreted
using any of the formats specified below, then Coordi‐
nated Universal Time (UTC) is used.
]
> beginning, but it fails for tm_isdst = 1 (maybe you can't ask it to use
> DST for a UTC timezone [TZ="" defaults to UTC per the tzset(3) man page]?).
>
--
Alejandro Colomar
Linux man-pages comaintainer; https://www.kernel.org/doc/man-pages/
http://www.alejandro-colomar.es/
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: mktime(3), asctime(3): possible bug in tm_isdst?
2021-10-10 0:55 ` Alejandro Colomar (man-pages) via Libc-alpha
2021-10-10 1:07 ` Alejandro Colomar (man-pages) via Libc-alpha
@ 2021-10-10 1:18 ` Alejandro Colomar (man-pages) via Libc-alpha
1 sibling, 0 replies; 5+ messages in thread
From: Alejandro Colomar (man-pages) via Libc-alpha @ 2021-10-10 1:18 UTC (permalink / raw
To: Paul Eggert, Albert Aribaud, Libc-alpha
Hi Paul,
TL;DR: Solved :)
On 10/10/21 2:55 AM, Alejandro Colomar (man-pages) wrote:
> If I understand correctly the above,
> if I use -1,
> I'm asking mktime() to determine from the system if DST is in effect;
> if I use 1 (or positive),
> I'm saying that DST is in effect,
> so mktime() should correct the time to get the time since Epoch (which
> is of course number of seconds UTC);
> and if I use 0,
> I'm saying that DST is *not* in effect,
> so I expect mktime() to not modify the time, and output it as is.
>
> However, neither of them produces the Epoch time I would expect, which
> is 1617936614. Instead, they produce two different values, which I
> don't really understand.
Ahhh, they also include the timezone, not only the DST, and ctime(3)
prints local time, so I wasn't realizing that!
> $ ./a.out
> tm_isdst = 1
> 1617929414
> Fri Apr 9 02:50:14 2021
> 14s 50m 2h 9mday 3mon 121yr 5wday 98yday 1dst
> Sun Apr 9 02:50:14 2021
>
> tm_isdst = -1
> 1617929414
> Fri Apr 9 02:50:14 2021
> 14s 50m 2h 9mday 3mon 121yr 5wday 98yday 1dst
> Sun Apr 9 02:50:14 2021
>
> tm_isdst = 0
> 1617933014
> Fri Apr 9 03:50:14 2021
> 14s 50m 3h 9mday 3mon 121yr 5wday 98yday 1dst
> Sun Apr 9 02:50:14 2021
And I guess here, as ctime(3) is printing in local time (and my system
has DST enabled (tz is +1, and DST adds another +1), I'm observing the
opposite behavior of what I expected, simply because my locale is
fooling me, and if I printed the same Epoch times in a time when I don't
have DST enabled, I'd observe that 0 is actually getting me the correct
02:50:14 time!!
Nice.
So, I definitely need setenv("TZ", "", true);
>
>
> And if I uncomment setenv(), I get the following:
>
>
> $ ./a.out
> tm_isdst = 1
> -1
> Wed Dec 31 23:59:59 1969
> 14s 50m 2h 9mday 3mon 121yr 0wday 0yday 1dst
> Sun Apr 9 02:50:14 2021
I'm curious as to why this results in an error, but I guess it's simply
because UTC time doesn't support DST.
>
> tm_isdst = -1
> 1617936614
> Fri Apr 9 02:50:14 2021
> 14s 50m 2h 9mday 3mon 121yr 5wday 98yday 0dst
> Sun Apr 9 02:50:14 2021
>
> tm_isdst = 0
> 1617936614
> Fri Apr 9 02:50:14 2021
> 14s 50m 2h 9mday 3mon 121yr 5wday 98yday 0dst
> Sun Apr 9 02:50:14 2021
>
>
>
> I think the solution for my problem would be to setenv("TZ", "", 1); and
> then use tm_isdst = -1; but don't understand why.
>
>
> Thanks,
>
> Alex
>
>
Thanks!!
Alex
--
Alejandro Colomar
Linux man-pages comaintainer; https://www.kernel.org/doc/man-pages/
http://www.alejandro-colomar.es/
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2021-10-10 1:18 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-10-09 21:43 mktime(3), asctime(3): possible bug in tm_isdst? Alejandro Colomar (man-pages) via Libc-alpha
2021-10-09 22:12 ` Paul Eggert
2021-10-10 0:55 ` Alejandro Colomar (man-pages) via Libc-alpha
2021-10-10 1:07 ` Alejandro Colomar (man-pages) via Libc-alpha
2021-10-10 1:18 ` Alejandro Colomar (man-pages) via Libc-alpha
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).