Second part of localtime.c changes
Olson, Arthur David (NIH/NCI)
olsona at dc37a.nci.nih.gov
Sat Jan 1 18:27:36 UTC 2005
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)
More information about the tz
mailing list