Mktime on systems with exotic time_t types
Below find proposed changes to localtime.c to get mktime working correctly on systems with exotic time_t types (such as unsigned or double). Also included are changes to zdump.c and Makefile to arrange things that doing a... make typecheck ...exercises mktime for a good range of values. --ado ------- localtime.c ------- *** /tmp/geta3175 Tue Jan 11 11:57:49 2005 --- /tmp/getb3175 Tue Jan 11 11:57:49 2005 *************** *** 1,9 **** /* - ** XXX--have mktime et al. do the right thing when time_t is exotic - ** (for example, double). - */ - - /* ** This file is in the public domain, so clarified as of ** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). */ --- 1,4 ---- *************** *** 10,16 **** #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)localtime.c 7.86"; #endif /* !defined NOID */ #endif /* !defined lint */ --- 5,11 ---- #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)localtime.c 7.87"; #endif /* !defined NOID */ #endif /* !defined lint */ *************** *** 1445,1450 **** --- 1440,1447 ---- register int i, j; register int saved_seconds; register long li; + register time_t lo; + register time_t hi; long y; time_t newt; time_t t; *************** *** 1518,1545 **** yourtm.tm_sec = 0; } /* ! ** Divide the search space in half ! ** (this works whether time_t is signed or unsigned). */ ! bits = TYPE_BIT(time_t) - 1; ! /* ! ** If time_t is signed, then 0 is just above the median, ! ** assuming two's complement arithmetic. ! ** If time_t is unsigned, then (1 << bits) is just above the median. ! */ ! t = TYPE_SIGNED(time_t) ? 0 : (((unsigned long) 1) << bits); for ( ; ; ) { ! if ((*funcp)(&t, offset, &mytm) == NULL) ! return WRONG; /* XXX probably wrong */ ! dir = tmcomp(&mytm, &yourtm); if (dir != 0) { ! if (bits-- < 0) return WRONG; ! if (bits < 0) ! --t; /* may be needed if new t is minimal */ ! else if (dir > 0) ! t -= ((long) 1) << bits; ! else t += ((long) 1) << bits; continue; } if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) --- 1515,1565 ---- yourtm.tm_sec = 0; } /* ! ** Do a binary search (this works whatever time_t's type is). */ ! if (!TYPE_SIGNED(time_t)) { ! lo = 0; ! hi = lo - 1; ! } else if (!TYPE_INTEGRAL(time_t)) { ! if (sizeof(time_t) > sizeof(float)) ! hi = (time_t) FLT_MAX; ! else hi = (time_t) DBL_MAX; ! lo = -hi; ! } else { ! register int i; ! ! lo = 1; ! for (i = 0; i < TYPE_BIT(time_t) - 1; ++i) ! lo *= 2; ! hi = -(lo + 1); ! } for ( ; ; ) { ! t = lo / 2 + hi / 2; ! if (t < lo) ! t = lo; ! else if (t > hi) ! t = hi; ! if ((*funcp)(&t, offset, &mytm) == NULL) { ! /* ! ** Assume that t is too extreme to be represented in ! ** a struct tm; arrange things so that it is less ! ** extreme on the next pass. ! */ ! dir = (t > 0) ? 1 : -1; ! } else dir = tmcomp(&mytm, &yourtm); if (dir != 0) { ! if (t == lo) { ! ++t; ! ++lo; ! } else if (t == hi) { ! --t; ! --hi; ! } ! if (lo > hi) return WRONG; ! if (dir > 0) ! hi = t; ! else lo = t; continue; } if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) *************** *** 1569,1575 **** newt = t + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; if ((*funcp)(&newt, offset, &mytm) == NULL) ! return WRONG; /* XXX probably wrong */ if (tmcomp(&mytm, &yourtm) != 0) continue; if (mytm.tm_isdst != yourtm.tm_isdst) --- 1589,1595 ---- newt = t + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; if ((*funcp)(&newt, offset, &mytm) == NULL) ! continue; if (tmcomp(&mytm, &yourtm) != 0) continue; if (mytm.tm_isdst != yourtm.tm_isdst) ------- zdump.c ------- *** /tmp/geta3196 Tue Jan 11 11:57:58 2005 --- /tmp/getb3196 Tue Jan 11 11:57:58 2005 *************** *** 1,4 **** ! static char elsieid[] = "@(#)zdump.c 7.59"; /* ** This code has been made independent of the rest of the time --- 1,4 ---- ! static char elsieid[] = "@(#)zdump.c 7.60"; /* ** This code has been made independent of the rest of the time *************** *** 154,159 **** --- 154,195 ---- static const char * tformat P((void)); static time_t yeartot P((long y)); + #ifndef TYPECHECK + #define my_localtime localtime + #else /* !defined TYPECHECK */ + static struct tm * + my_localtime(tp) + time_t * tp; + { + register struct tm * tmp; + + tmp = localtime(tp); + if (tp != NULL && tmp != NULL) { + struct tm tm; + register time_t t; + + tm = *tmp; + t = mktime(&tm); + if (t - *tp >= 1 || *tp - t >= 1) { + (void) fflush(stdout); + (void) fprintf(stderr, "\n%s: ", progname); + (void) fprintf(stderr, tformat(), *tp); + (void) fprintf(stderr, " ->"); + (void) fprintf(stderr, " sec %d", tmp->tm_sec); + (void) fprintf(stderr, " min %d", tmp->tm_min); + (void) fprintf(stderr, " hour %d", tmp->tm_hour); + (void) fprintf(stderr, " mday %d", tmp->tm_mday); + (void) fprintf(stderr, " mon %d", tmp->tm_mon); + (void) fprintf(stderr, " year %d", tmp->tm_year); + (void) fprintf(stderr, " -> "); + (void) fprintf(stderr, tformat(), t); + (void) fprintf(stderr, "\n"); + } + } + return tmp; + } + #endif /* !defined TYPECHECK */ + int main(argc, argv) int argc; *************** *** 266,272 **** show(argv[i], t, TRUE); if (t < cutlotime) t = cutlotime; ! tmp = localtime(&t); if (tmp != NULL) { tm = *tmp; (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1); --- 302,308 ---- show(argv[i], t, TRUE); if (t < cutlotime) t = cutlotime; ! tmp = my_localtime(&t); if (tmp != NULL) { tm = *tmp; (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1); *************** *** 399,405 **** register struct tm * tmp; char loab[MAX_STRING_LENGTH]; ! lotmp = localtime(&lot); if (lotmp != NULL) { lotm = *lotmp; (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1); --- 435,441 ---- register struct tm * tmp; char loab[MAX_STRING_LENGTH]; ! lotmp = my_localtime(&lot); if (lotmp != NULL) { lotm = *lotmp; (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1); *************** *** 414,420 **** ++t; else if (t >= hit) --t; ! tmp = localtime(&t); if (tmp != NULL) tm = *tmp; if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : --- 450,456 ---- ++t; else if (t >= hit) --t; ! tmp = my_localtime(&t); if (tmp != NULL) tm = *tmp; if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : *************** *** 477,483 **** } (void) printf(" = "); } ! tmp = localtime(&t); dumptime(tmp); if (tmp != NULL) { if (*abbr(tmp) != '\0') --- 513,519 ---- } (void) printf(" = "); } ! tmp = my_localtime(&t); dumptime(tmp); if (tmp != NULL) { if (*abbr(tmp) != '\0') ------- Makefile ------- *** /tmp/geta3221 Tue Jan 11 11:58:07 2005 --- /tmp/getb3221 Tue Jan 11 11:58:07 2005 *************** *** 1,4 **** ! # @(#)Makefile 7.102 # Change the line below for your time zone (after finding the zone you want in # the time zone files, or adding it to a time zone file). --- 1,4 ---- ! # @(#)Makefile 7.103 # Change the line below for your time zone (after finding the zone you want in # the time zone files, or adding it to a time zone file). *************** *** 396,404 **** typecheck: make clean ! for i in "long long" double unsigned; \ do \ ! make CFLAGS="-D_TIME_T \"-Dtime_t=$$i\"" ; \ ./zdump -v US/Eastern ; \ make clean ; \ done --- 396,404 ---- typecheck: make clean ! for i in "long long" unsigned double; \ do \ ! make CFLAGS="-DTYPECHECK -D_TIME_T \"-Dtime_t=$$i\"" ; \ ./zdump -v US/Eastern ; \ make clean ; \ done
It's not clear to me that the algorithm will always terminate if time_t is floating. For example, "++t; ++lo;" might have no effect if t is a floating-point number. Is it still your intent that the code be portable to K&R C? Personally I don't think it's worth your time worrying about it any more, but if you are worried, K&R C didn't have <float.h>, FLT_MAX, or DBL_MAX. The only solid way to check this is to actually use a K&R C compiler, but I haven't had easy access to one for years. Here are some warnings from "gcc -Wall -W -O" that you might want to attend to. The diagnostic for zdump.c:356 is a violation of the C standard, so it's the most important. localtime.c: In function `settzname': localtime.c:225: warning: traditional C rejects ISO C style function definitions localtime.c: In function `tzsetwall': localtime.c:959: warning: traditional C rejects ISO C style function definitions localtime.c: In function `tzset': localtime.c:980: warning: traditional C rejects ISO C style function definitions localtime.c: At top level: localtime.c:1034: warning: unused parameter 'offset' localtime.c: In function `localtime_r': localtime.c:1092: warning: declaration of 'tm' shadows a global declaration localtime.c:199: warning: shadowed declaration is here localtime.c: In function `gmtime_r': localtime.c:1154: warning: declaration of 'tm' shadows a global declaration localtime.c:199: warning: shadowed declaration is here localtime.c: In function `ctime_r': localtime.c:1336: warning: declaration of 'tm' shadows a global declaration localtime.c:199: warning: shadowed declaration is here localtime.c: In function `time2sub': localtime.c:1529: warning: declaration of 'i' shadows a previous local localtime.c:1440: warning: shadowed declaration is here localtime.c:1532: warning: comparison between signed and unsigned localtime.c:1439: warning: unused variable `bits' zdump.c: In function `main': zdump.c:247: warning: declaration of 'c' shadows a previous local zdump.c:199: warning: shadowed declaration is here zdump.c: At top level: zdump.c:356: warning: 'setabsolutes' was used with no prototype before its definition zdump.c:362:34: warning: traditional C rejects the "F" suffix zdump.c:363:33: warning: traditional C rejects the "F" suffix
participants (2)
-
Olson, Arthur David (NIH/NCI) -
Paul Eggert