mktime bugs around the edges

Paul Eggert eggert at lotl.twinsun.com
Sun Apr 3 21:32:06 UTC 1994


Here are some mktime bugs in tz-1994e.
(Bugs that PCTS should have caught :-)

mktime fails on the earliest representable time (Fri Dec 13 20:45:52 1901 GMT).

mktime does not fail, as it should, with one plus the last
representable time (i.e. with Tue Jan 19 03:14:08 2038 GMT).
It wraps around and reports Fri Dec 13 20:45:52 1901 GMT instead.

When normalizing times, mktime assumes that every minute contains 60
seconds.  This is incorrect if leap seconds are in use.

mktime does not detect arithmetic overflow when normalizing
nonsensically large inputs.

Here is a patch.

===================================================================
RCS file: localtime.c,v
retrieving revision 1994.5.1.1
retrieving revision 1994.5.1.2
diff -c2 -r1994.5.1.1 -r1994.5.1.2
*** localtime.c	1994/04/03 02:54:41	1994.5.1.1
--- localtime.c	1994/04/03 21:25:32	1994.5.1.2
***************
*** 114,118 ****
  static void		localsub P((const time_t * timep, long offset,
  				struct tm * tmp));
! static void		normalize P((int * tensptr, int * unitsptr, int base));
  static void		settzname P((void));
  static time_t		time1 P((struct tm * tmp,
--- 114,120 ----
  static void		localsub P((const time_t * timep, long offset,
  				struct tm * tmp));
! static int		increment_overflow P((int * number, int delta));
! static int		normalize_overflow P((int * tensptr, int * unitsptr,
! 				int base));
  static void		settzname P((void));
  static time_t		time1 P((struct tm * tmp,
***************
*** 1155,1160 ****
  */
  
! static void
! normalize(tensptr, unitsptr, base)
  int * const	tensptr;
  int * const	unitsptr;
--- 1157,1172 ----
  */
  
! static int
! increment_overflow(number, delta)
! int *	number;
! int	delta;
! {
! 	int number0 = *number;
! 	*number = number0 + delta;
! 	return (*number < number0) != (delta < 0);
! }
! 
! static int
! normalize_overflow(tensptr, unitsptr, base)
  int * const	tensptr;
  int * const	unitsptr;
***************
*** 1166,1171 ****
  		(*unitsptr / base) :
  		(-1 - (-1 - *unitsptr) / base);
- 	*tensptr += tensdelta;
  	*unitsptr -= tensdelta * base;
  }
  
--- 1178,1183 ----
  		(*unitsptr / base) :
  		(-1 - (-1 - *unitsptr) / base);
  	*unitsptr -= tensdelta * base;
+ 	return increment_overflow(tensptr, tensdelta);
  }
  
***************
*** 1204,1225 ****
  	*okayp = FALSE;
  	yourtm = *tmp;
! 	if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0)
! 		normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN);
! 	normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR);
! 	normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY);
! 	normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR);
  	while (yourtm.tm_mday <= 0) {
! 		--yourtm.tm_year;
! 		yourtm.tm_mday +=
! 			year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)];
  	}
  	while (yourtm.tm_mday > DAYSPERLYEAR) {
! 		yourtm.tm_mday -=
! 			year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)];
! 		++yourtm.tm_year;
  	}
  	for ( ; ; ) {
! 		i = mon_lengths[isleap(yourtm.tm_year +
! 			TM_YEAR_BASE)][yourtm.tm_mon];
  		if (yourtm.tm_mday <= i)
  			break;
--- 1216,1236 ----
  	*okayp = FALSE;
  	yourtm = *tmp;
! 	if (   normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)
! 	    || normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)
! 	    || normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR)
! 	    || increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
! 		return WRONG;
  	while (yourtm.tm_mday <= 0) {
! 		if (increment_overflow(&yourtm.tm_year, -1))
! 			return WRONG;
! 		yourtm.tm_mday += year_lengths[isleap(yourtm.tm_year)];
  	}
  	while (yourtm.tm_mday > DAYSPERLYEAR) {
! 		yourtm.tm_mday -= year_lengths[isleap(yourtm.tm_year)];
! 		if (increment_overflow(&yourtm.tm_year, 1))
! 			return WRONG;
  	}
  	for ( ; ; ) {
! 		i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
  		if (yourtm.tm_mday <= i)
  			break;
***************
*** 1227,1235 ****
  		if (++yourtm.tm_mon >= MONSPERYEAR) {
  			yourtm.tm_mon = 0;
! 			++yourtm.tm_year;
  		}
  	}
! 	saved_seconds = yourtm.tm_sec;
! 	yourtm.tm_sec = 0;
  	/*
  	** Calculate the number of magnitude bits in a time_t
--- 1238,1264 ----
  		if (++yourtm.tm_mon >= MONSPERYEAR) {
  			yourtm.tm_mon = 0;
! 			if (increment_overflow(&yourtm.tm_year, 1))
! 				return WRONG;
  		}
  	}
! 	if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
! 		return WRONG;
! 	if (yourtm.tm_year < EPOCH_YEAR - TM_YEAR_BASE) {
! 		/*
! 		** We can't set tm_sec to 0, because that might push the
! 		** time below the minimum representable time.
! 		** Set tm_sec to 59 instead.
! 		** This assumes that the minimum representable time is
! 		** not in the same minute that a leap second was deleted from,
! 		** which is a safer assumption than using 58 would be.
! 		*/
! 		if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
! 			return WRONG;
! 		saved_seconds = yourtm.tm_sec;
! 		yourtm.tm_sec = SECSPERMIN - 1;
! 	} else {
! 		saved_seconds = yourtm.tm_sec;
! 		yourtm.tm_sec = 0;
! 	}
  	/*
  	** Calculate the number of magnitude bits in a time_t
***************
*** 1298,1302 ****
  	}
  label:
! 	t += saved_seconds;
  	(*funcp)(&t, offset, tmp);
  	*okayp = TRUE;
--- 1327,1334 ----
  	}
  label:
! 	newt = t + saved_seconds;
! 	if ((newt < t) != (saved_seconds < 0))
! 		return WRONG;
! 	t = newt;
  	(*funcp)(&t, offset, tmp);
  	*okayp = TRUE;



More information about the tz mailing list