bug in mktime() normalization?

Tom Peterson (USG) tomp at zk3.dec.com
Tue Aug 19 18:13:13 UTC 1997


Hi all,

I've been looking over the time zone package and testing various 
scenerios.  In mktime(), or rather time2(), I noticed that a change was 
made a while back which removed the following seconds normalization:

! 	if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0)
! 		normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN);

Instead, these seconds are now saved aside into saved_seconds and only 
added back in after an appropriate match is found using the remaining 
normalized data.  The question I have is whether this could lead to 
erroneous rejection of certain tm structs.

For example: If the values (other than tm_sec) of an incoming tm struct 
result in no match from time2()'s binary search, such as a time between 
a dst change, the struct is rejected and the function returns WRONG.  
However, the value of tm_sec may have been large enough to push the 
rest of my tm struct into an acceptible range where a match could have 
occurred.  As with other tm struct members, the value of tm_sec before 
normalization is not restricted to the range specified in <time.h>.  
Here's what UNIX98 has to say regarding this:

> The mktime() function converts the broken-down time, expressed as 
> local time, in the structure pointed to by timeptr, into a time since 
> the Epoch value with the same encoding as that of the values returned 
> by time(). The original values of the tm_wday and tm_yday components 
> of the structure are ignored, and the original values of the other 
> components are not restricted to the ranges described in the <time.h> 
> entry. 
>
> ...
>
> Upon successful completion, the values of the tm_wday and tm_yday 
> components of the structure are set appropriately, and the other 
> components are set to represent the specified time since the Epoch, 
> but with their values forced to the ranges indicated in the <time.h> 
> entry; the final value of tm_mday is not set until tm_mon and tm_year 
> are determined. 

Here's an example program:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

void display_tm(void);
void run_test();

struct tm tm;
time_t	mktime_returned;

#define TZ_value	"ABC0DEF,J81,J300"
#define TM_year		1988
#define TM_mon		3
#define TM_mday		22
#define TM_hour1	2
#define TM_hour2	4
#define TM_min		0
#define TM_sec1		7200
#define TM_sec2		0
#define TM_wday		-1
#define TM_yday		-1
#define TM_isdst	-1

main()
{
	putenv("TZ=ABC0DEF,J81,J300");
	printf("TZ = %s\n\n", getenv("TZ"));

/* same time as below, but tm_hour and tm_sec have not yet been
 * normalized */
        tm.tm_year = TM_year - 1900;
        tm.tm_mon  = TM_mon - 1;
        tm.tm_mday = TM_mday;
        tm.tm_hour = TM_hour1; /* un-normalized hour */
        tm.tm_min  = TM_min;
        tm.tm_sec  = TM_sec1; /* un-normalized seconds */
        tm.tm_wday = TM_wday;
        tm.tm_yday = TM_yday;
        tm.tm_isdst = TM_isdst;

	run_test();
	printf("\n");

/* same time as above, but tm_hour and tm_sec have already been
 * normalized */
        tm.tm_year = TM_year - 1900;
        tm.tm_mon  = TM_mon - 1;
        tm.tm_mday = TM_mday;
        tm.tm_hour = TM_hour2; /* pre-normalized hour */
        tm.tm_min  = TM_min;
        tm.tm_sec  = TM_sec2; /* pre-normalized seconds */
        tm.tm_wday = TM_wday;
        tm.tm_yday = TM_yday;
        tm.tm_isdst = TM_isdst;

        run_test();
}

void run_test(void)
{
	printf("tm struct set to:\n");
	display_tm();
	if ((mktime_returned = mktime(&tm)) == (time_t)-1) {
		(void)puts("mktime returned -1 (-unknown-)");
	}
	else {
		printf("mktime returned value = %d\n", (int) mktime_returned);
	}
	printf("mktime() final tm struct reset to:\n");
	display_tm();
}

void display_tm(void)
{
printf("tm_year = %d, tm_mon = %d, tm_mday = %d, tm_hour = %d, \
tm_min = %d, sec = %d,\nwday = %d, yday = %d, isdst = %d\n",
tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_wday, tm.tm_yday, tm.tm_isdst);
}



output
======
$ a.out
TZ = ABC0DEF,J81,J300

tm struct set to:
tm_year = 88, tm_mon = 2, tm_mday = 22, tm_hour = 2, tm_min = 0, sec = 7200,
wday = -1, yday = -1, isdst = -1
mktime returned -1 (-unknown-)
mktime() final tm struct reset to:
tm_year = 88, tm_mon = 2, tm_mday = 22, tm_hour = 2, tm_min = 0, sec = 7200,
wday = -1, yday = -1, isdst = -1

tm struct set to:
tm_year = 88, tm_mon = 2, tm_mday = 22, tm_hour = 4, tm_min = 0, sec = 0,
wday = -1, yday = -1, isdst = -1
mktime returned value = 575002800
mktime() final tm struct reset to:
tm_year = 88, tm_mon = 2, tm_mday = 22, tm_hour = 4, tm_min = 0, sec = 0,
wday = 2, yday = 81, isdst = 1
$


I would very much appreciate any thoughts concerning this matter and
any possible solutions.

thanks,
- Tom

=====================================================================
Tom Peterson                   | DIGITAL UNIX Development Environment
Digital Equipment Corporation  | Phone:(603)884-7550
110 Spit Brook Road ZKO3-2/W17 | FAX:(603)881-2257
Nashua, NH 03062-2698          | Email: mailto:tomp at zk3.dec.com





More information about the tz mailing list