Having dispensed with the easy stuff last week, below find the hard stuff. The goal is to better behavior of localtime and gmtime on systems where time_t is an exotic type--for example, a 64-bit integer or a double. A problem is that the tm_year field of a "struct tm" field is, by standard, an int--so there are some (very positive or very negative) time_t values that can't be broken down into a "struct tm". I've adopted existing practice here--localtime and gmtime return NULL in such cases. The system I'm using is a Sunblade 100 running gcc version 3.2.3. If I do a "make typecheck" using version 7.85 of localtime.c and then do a "make typecheck" using version 7.86, I get these differences: 29,30c29,30 < US/Eastern Sat Oct 16 08:29:52 -1583348 UTC = Sat Oct 16 03:29:52 -1583348 EST isdst=0 < US/Eastern Sun Oct 17 08:29:52 -1583348 UTC = Sun Oct 17 03:29:52 -1583348 EST isdst=0 --- > US/Eastern -9223372036854775808 = NULL > US/Eastern -9223372036854689408 = NULL 501,502c501,502 < US/Eastern Sun Mar 16 15:30:07 1587287 UTC = Sun Mar 16 10:30:07 1587287 EST isdst=0 < US/Eastern Mon Mar 17 15:30:07 1587287 UTC = Mon Mar 17 10:30:07 1587287 EST isdst=0 --- > US/Eastern 9223372036854689407 = NULL > US/Eastern 9223372036854775807 = NULL 524,525c524,525 < US/Eastern Sun Jun 23 20:45:52 5881512 UTC = Sat Jul 10 22:14:08 -5877573 EST isdst=0 < US/Eastern Sun Jun 23 20:45:52 5881512 UTC = Sat Jul 10 22:14:08 -5877573 EST isdst=0 --- > US/Eastern -1.79769e+308 = NULL > US/Eastern -1.79769e+308 = NULL 996,997c996,997 < US/Eastern Sat Jul 10 03:14:07 -5877573 UTC = Fri Jul 9 22:14:07 -5877573 EST isdst=0 < US/Eastern Sat Jul 10 03:14:07 -5877573 UTC = Fri Jul 9 22:14:07 -5877573 EST isdst=0 --- > US/Eastern 1.79769e+308 = NULL > US/Eastern 1.79769e+308 = NULL So...for this regression test at least, the only differences are the ones I'd expect. I'm eager for feedback. --ado ------- localtime.c ------- *** /tmp/geta22155 Sat Jan 1 13:17:31 2005 --- /tmp/getb22155 Sat Jan 1 13:17:31 2005 *************** *** 1,8 **** /* ! ** XXX--do the right thing if time_t is double and ! ** the value fed to gmtime or localtime is very very negative or ! ** very very positive (which causes problems with the days-and-rem logic). ! ** Also: do the right thing in mktime if time_t is double. */ /* --- 1,6 ---- /* ! ** XXX--have mktime et al. do the right thing when time_t is exotic ! ** (for example, double). */ /* *************** *** 12,18 **** #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)localtime.c 7.85"; #endif /* !defined NOID */ #endif /* !defined lint */ --- 10,16 ---- #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)localtime.c 7.86"; #endif /* !defined NOID */ #endif /* !defined lint */ *************** *** 141,146 **** --- 139,145 ---- static struct tm * localsub P((const time_t * timep, long offset, struct tm * tmp)); static int increment_overflow P((int * number, int delta)); + static int leaps_thru_end_of P((int y)); static int long_increment_overflow P((long * number, int delta)); static int long_normalize_overflow P((long * tensptr, int * unitsptr, int base)); *************** *** 1174,1179 **** --- 1173,1191 ---- #endif /* defined STD_INSPIRED */ + /* + ** Return the number of leap years through the end of the given year + ** where, to make the math easy, the answer for year zero is defined as zero. + */ + + static int + leaps_thru_end_of(y) + register const int y; + { + return (y >= 0) ? (y / 4 - y / 100 + y / 400) : + -(leaps_thru_end_of(-(y + 1)) + 1); + } + static struct tm * timesub(timep, offset, sp, tmp) const time_t * const timep; *************** *** 1182,1191 **** register struct tm * const tmp; { register const struct lsinfo * lp; ! register long days; register long rem; ! register long y; ! register int yleap; register const int * ip; register long corr; register int hit; --- 1194,1203 ---- register struct tm * const tmp; { register const struct lsinfo * lp; ! register time_t tdays; ! register int idays; /* unsigned would be so 2003 */ register long rem; ! int y; register const int * ip; register long corr; register int hit; *************** *** 1219,1246 **** break; } } ! days = *timep / SECSPERDAY; ! rem = *timep - ((time_t) days) * SECSPERDAY; ! #ifdef mc68k ! if (*timep == 0x80000000) { ! /* ! ** A 3B1 muffs the division on the most negative number. ! */ ! days = -24855; ! rem = -11648; } ! #endif /* defined mc68k */ ! rem += (offset - corr); while (rem < 0) { rem += SECSPERDAY; ! --days; } while (rem >= SECSPERDAY) { rem -= SECSPERDAY; ! ++days; } tmp->tm_hour = (int) (rem / SECSPERHOUR); ! rem = rem % SECSPERHOUR; tmp->tm_min = (int) (rem / SECSPERMIN); /* ** A positive leap second requires a special --- 1231,1308 ---- break; } } ! y = EPOCH_YEAR; ! tdays = *timep / SECSPERDAY; ! rem = *timep - tdays * SECSPERDAY; ! while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { ! int newy; ! register time_t tdelta; ! register int idelta; ! register int leapdays; ! ! tdelta = tdays / DAYSPERLYEAR; ! idelta = tdelta; ! if (tdelta - idelta >= 1 || idelta - tdelta >= 1) ! return NULL; ! if (idelta == 0) ! idelta = (tdays < 0) ? -1 : 1; ! newy = y; ! if (increment_overflow(&newy, idelta)) ! return NULL; ! leapdays = leaps_thru_end_of(newy - 1) - ! leaps_thru_end_of(y - 1); ! tdays -= ((time_t) newy - y) * DAYSPERNYEAR; ! tdays -= leapdays; ! y = newy; } ! { ! register long seconds; ! ! seconds = tdays * SECSPERDAY + 0.5; ! tdays = seconds / SECSPERDAY; ! rem += seconds - tdays * SECSPERDAY; ! } ! /* ! ** Given the range, we can now fearlessly cast... ! */ ! idays = tdays; ! rem += offset - corr; while (rem < 0) { rem += SECSPERDAY; ! --idays; } while (rem >= SECSPERDAY) { rem -= SECSPERDAY; ! ++idays; } + while (idays < 0) { + if (increment_overflow(&y, -1)) + return NULL; + idays += year_lengths[isleap(y)]; + } + while (idays >= year_lengths[isleap(y)]) { + idays -= year_lengths[isleap(y)]; + if (increment_overflow(&y, 1)) + return NULL; + } + tmp->tm_year = y; + if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) + return NULL; + tmp->tm_yday = idays; + /* + ** The "extra" mods below avoid overflow problems. + */ + tmp->tm_wday = EPOCH_WDAY + + ((y - EPOCH_YEAR) % DAYSPERWEEK) * + (DAYSPERNYEAR % DAYSPERWEEK) + + leaps_thru_end_of(y - 1) - + leaps_thru_end_of(EPOCH_YEAR - 1) + + idays; + tmp->tm_wday %= DAYSPERWEEK; + if (tmp->tm_wday < 0) + tmp->tm_wday += DAYSPERWEEK; tmp->tm_hour = (int) (rem / SECSPERHOUR); ! rem %= SECSPERHOUR; tmp->tm_min = (int) (rem / SECSPERMIN); /* ** A positive leap second requires a special *************** *** 1247,1275 **** ** representation. This uses "... ??:59:60" et seq. */ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; ! tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); ! if (tmp->tm_wday < 0) ! tmp->tm_wday += DAYSPERWEEK; ! y = EPOCH_YEAR; ! #define IPQ(i, p) ((i) / (p) - (((i) % (p)) < 0)) ! #define LEAPS_THRU_END_OF(y) (IPQ((y), 4) - IPQ((y), 100) + IPQ((y), 400)) ! while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) { ! register long newy; ! ! newy = y + days / DAYSPERNYEAR; ! if (days < 0) ! --newy; ! days -= (newy - y) * DAYSPERNYEAR + ! LEAPS_THRU_END_OF(newy - 1) - ! LEAPS_THRU_END_OF(y - 1); ! y = newy; ! } ! tmp->tm_year = y - TM_YEAR_BASE; ! tmp->tm_yday = (int) days; ! ip = mon_lengths[yleap]; ! for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon)) ! days = days - (long) ip[tmp->tm_mon]; ! tmp->tm_mday = (int) (days + 1); tmp->tm_isdst = 0; #ifdef TM_GMTOFF tmp->TM_GMTOFF = offset; --- 1309,1318 ---- ** representation. This uses "... ??:59:60" et seq. */ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; ! ip = mon_lengths[isleap(y)]; ! for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) ! idays -= ip[tmp->tm_mon]; ! tmp->tm_mday = (int) (idays + 1); tmp->tm_isdst = 0; #ifdef TM_GMTOFF tmp->TM_GMTOFF = offset; *************** *** 1486,1492 **** */ t = TYPE_SIGNED(time_t) ? 0 : (((unsigned long) 1) << bits); for ( ; ; ) { ! /* XXX */ (void) (*funcp)(&t, offset, &mytm); dir = tmcomp(&mytm, &yourtm); if (dir != 0) { if (bits-- < 0) --- 1529,1536 ---- */ 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) *************** *** 1524,1530 **** continue; newt = t + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; ! /* XXX */ (void) (*funcp)(&newt, offset, &mytm); if (tmcomp(&mytm, &yourtm) != 0) continue; if (mytm.tm_isdst != yourtm.tm_isdst) --- 1568,1575 ---- continue; 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)
participants (1)
-
Olson, Arthur David (NIH/NCI)