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