unofficial mirror of libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* 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).