Proposed strftime.c and localtime.c changes for long years

Olson, Arthur David (NIH/NCI) olsona at dc37a.nci.nih.gov
Tue Sep 7 12:21:29 UTC 2004


Attached are changes (based on those circulated on the list earlier by Paul
Eggert) to strftime.c and localtime.c; these changes allow for the
possibility that adding an int year to TM_YEAR_BASE might overflow an int,
so the relevant math is done with longs. There are also a couple of cosmetic
changes. Note that no changes to the way strftime handles two-digit year
output requests are being (deliberately) made at this point.

I hope to update the ftp bundle to incorporate these changes in about a
week; I'd welcome any feedback before then.

					--ado

------- localtime.c -------
*** /tmp/geta17327	Tue Sep  7 08:12:24 2004
--- /tmp/getb17327	Tue Sep  7 08:12:24 2004
***************
*** 5,11 ****
  
  #ifndef lint
  #ifndef NOID
! static char	elsieid[] = "@(#)localtime.c	7.78";
  #endif /* !defined NOID */
  #endif /* !defined lint */
  
--- 5,11 ----
  
  #ifndef lint
  #ifndef NOID
! static char	elsieid[] = "@(#)localtime.c	7.79";
  #endif /* !defined NOID */
  #endif /* !defined lint */
  
***************
*** 134,139 ****
--- 134,142 ----
  static void		localsub P((const time_t * timep, long offset,
  				struct tm * tmp));
  static int		increment_overflow P((int * number, int delta));
+ static int		long_increment_overflow P((long * number, int
delta));
+ static int		long_normalize_overflow P((long * tensptr,
+ 				int * unitsptr, int base));
  static int		normalize_overflow P((int * tensptr, int * unitsptr,
  				int base));
  static void		settzname P((void));
***************
*** 1149,1155 ****
  	register const struct lsinfo *	lp;
  	register long			days;
  	register long			rem;
! 	register int			y;
  	register int			yleap;
  	register const int *		ip;
  	register long			corr;
--- 1152,1158 ----
  	register const struct lsinfo *	lp;
  	register long			days;
  	register long			rem;
! 	register long			y;
  	register int			yleap;
  	register const int *		ip;
  	register long			corr;
***************
*** 1218,1224 ****
  	y = EPOCH_YEAR;
  #define LEAPS_THRU_END_OF(y)	((y) / 4 - (y) / 100 + (y) / 400)
  	while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
! 		register int	newy;
  
  		newy = y + days / DAYSPERNYEAR;
  		if (days < 0)
--- 1221,1227 ----
  	y = EPOCH_YEAR;
  #define LEAPS_THRU_END_OF(y)	((y) / 4 - (y) / 100 + (y) / 400)
  	while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
! 		register long	newy;
  
  		newy = y + days / DAYSPERNYEAR;
  		if (days < 0)
***************
*** 1259,1264 ****
--- 1262,1268 ----
  char *			buf;
  {
  	struct tm	tm;
+ 	extern char *	asctime_r();
  
  	return asctime_r(localtime_r(timep, &tm), buf);
  }
***************
*** 1294,1299 ****
--- 1298,1315 ----
  }
  
  static int
+ long_increment_overflow(number, delta)
+ long *	number;
+ int	delta;
+ {
+ 	long	number0;
+ 
+ 	number0 = *number;
+ 	*number += delta;
+ 	return (*number < number0) != (delta < 0);
+ }
+ 
+ static int
  normalize_overflow(tensptr, unitsptr, base)
  int * const	tensptr;
  int * const	unitsptr;
***************
*** 1309,1314 ****
--- 1325,1345 ----
  }
  
  static int
+ long_normalize_overflow(tensptr, unitsptr, base)
+ long * const	tensptr;
+ int * const	unitsptr;
+ const int	base;
+ {
+ 	register int	tensdelta;
+ 
+ 	tensdelta = (*unitsptr >= 0) ?
+ 		(*unitsptr / base) :
+ 		(-1 - (-1 - *unitsptr) / base);
+ 	*unitsptr -= tensdelta * base;
+ 	return long_increment_overflow(tensptr, tensdelta);
+ }
+ 
+ static int
  tmcomp(atmp, btmp)
  register const struct tm * const atmp;
  register const struct tm * const btmp;
***************
*** 1335,1342 ****
  	register const struct state *	sp;
  	register int			dir;
  	register int			bits;
! 	register int			i, j ;
  	register int			saved_seconds;
  	time_t				newt;
  	time_t				t;
  	struct tm			yourtm, mytm;
--- 1366,1375 ----
  	register const struct state *	sp;
  	register int			dir;
  	register int			bits;
! 	register int			i, j;
  	register int			saved_seconds;
+ 	register long			li;
+ 	long				y;
  	time_t				newt;
  	time_t				t;
  	struct tm			yourtm, mytm;
***************
*** 1352,1393 ****
  		return WRONG;
  	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour,
HOURSPERDAY))
  		return WRONG;
! 	if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon,
MONSPERYEAR))
  		return WRONG;
  	/*
! 	** Turn yourtm.tm_year into an actual year number for now.
  	** It is converted back to an offset from TM_YEAR_BASE later.
  	*/
! 	if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
  		return WRONG;
  	while (yourtm.tm_mday <= 0) {
! 		if (increment_overflow(&yourtm.tm_year, -1))
  			return WRONG;
! 		i = yourtm.tm_year + (1 < yourtm.tm_mon);
! 		yourtm.tm_mday += year_lengths[isleap(i)];
  	}
  	while (yourtm.tm_mday > DAYSPERLYEAR) {
! 		i = yourtm.tm_year + (1 < yourtm.tm_mon);
! 		yourtm.tm_mday -= year_lengths[isleap(i)];
! 		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;
  		yourtm.tm_mday -= i;
  		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_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
  		saved_seconds = 0;
! 	else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
  		/*
  		** We can't set tm_sec to 0, because that might push the
  		** time below the minimum representable time.
--- 1385,1438 ----
  		return WRONG;
  	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour,
HOURSPERDAY))
  		return WRONG;
! 	y = yourtm.tm_year;
! /*
! ** Hands off tm_year for a while.
! */
! #define tm_year	USE_Y_NOT_YOURTM_TM_YEAR
! 	if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
  		return WRONG;
  	/*
! 	** Turn y into an actual year number for now.
  	** It is converted back to an offset from TM_YEAR_BASE later.
  	*/
! 	if (long_increment_overflow(&y, TM_YEAR_BASE))
  		return WRONG;
  	while (yourtm.tm_mday <= 0) {
! 		if (long_increment_overflow(&y, -1))
  			return WRONG;
! 		li = y + (1 < yourtm.tm_mon);
! 		yourtm.tm_mday += year_lengths[isleap(li)];
  	}
  	while (yourtm.tm_mday > DAYSPERLYEAR) {
! 		li = y + (1 < yourtm.tm_mon);
! 		yourtm.tm_mday -= year_lengths[isleap(li)];
! 		if (long_increment_overflow(&y, 1))
  			return WRONG;
  	}
  	for ( ; ; ) {
! 		i = mon_lengths[isleap(y)][yourtm.tm_mon];
  		if (yourtm.tm_mday <= i)
  			break;
  		yourtm.tm_mday -= i;
  		if (++yourtm.tm_mon >= MONSPERYEAR) {
  			yourtm.tm_mon = 0;
! 			if (long_increment_overflow(&y, 1))
  				return WRONG;
  		}
  	}
! 	if (long_increment_overflow(&y, -TM_YEAR_BASE))
  		return WRONG;
+ /*
+ ** Hands back on tm_year.
+ */
+ #undef tm_year
+ 	yourtm.tm_year = y;
+ 	if (yourtm.tm_year != y)
+  		return WRONG;
  	if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
  		saved_seconds = 0;
! 	else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
  		/*
  		** We can't set tm_sec to 0, because that might push the
  		** time below the minimum representable time.
------- strftime.c -------
*** /tmp/geta17306	Tue Sep  7 08:12:07 2004
--- /tmp/getb17306	Tue Sep  7 08:12:07 2004
***************
*** 1,6 ****
  #ifndef lint
  #ifndef NOID
! static char	elsieid[] = "@(#)strftime.c	7.64";
  /*
  ** Based on the UCB version with the ID appearing below.
  ** This is ANSIish only when "multibyte character == plain character".
--- 1,6 ----
  #ifndef lint
  #ifndef NOID
! static char	elsieid[] = "@(#)strftime.c	7.66";
  /*
  ** Based on the UCB version with the ID appearing below.
  ** This is ANSIish only when "multibyte character == plain character".
***************
*** 108,113 ****
--- 108,114 ----
  
  static char *	_add P((const char *, char *, const char *));
  static char *	_conv P((int, const char *, char *, const char *));
+ static char *	_lconv P((long, const char *, char *, const char *));
  static char *	_fmt P((const char *, const struct tm *, char *, const char
*, int *));
  
  size_t strftime P((char *, size_t, const char *, const struct tm *));
***************
*** 210,217 ****
  				** something completely different.
  				** (ado, 1993-05-24)
  				*/
! 				pt = _conv((t->tm_year + TM_YEAR_BASE) /
100,
! 					"%02d", pt, ptlim);
  				continue;
  			case 'c':
  				{
--- 211,219 ----
  				** something completely different.
  				** (ado, 1993-05-24)
  				*/
! 				pt = _lconv((t->tm_year +
! 					(long) TM_YEAR_BASE) / 100,
! 					"%02ld", pt, ptlim);
  				continue;
  			case 'c':
  				{
***************
*** 379,390 ****
  ** (ado, 1996-01-02)
  */
  				{
! 					int	year;
  					int	yday;
  					int	wday;
  					int	w;
  
! 					year = t->tm_year + TM_YEAR_BASE;
  					yday = t->tm_yday;
  					wday = t->tm_wday;
  					for ( ; ; ) {
--- 381,393 ----
  ** (ado, 1996-01-02)
  */
  				{
! 					long	year;
  					int	yday;
  					int	wday;
  					int	w;
  
! 					year = t->tm_year;
! 					year += TM_YEAR_BASE;
  					yday = t->tm_yday;
  					wday = t->tm_wday;
  					for ( ; ; ) {
***************
*** 426,436 ****
  							DAYSPERNYEAR;
  					}
  #ifdef XPG4_1994_04_09
! 					if ((w == 52
! 					     && t->tm_mon == TM_JANUARY)
! 					    || (w == 1
! 						&& t->tm_mon ==
TM_DECEMBER))
! 						w = 53;
  #endif /* defined XPG4_1994_04_09 */
  					if (*format == 'V')
  						pt = _conv(w, "%02d",
--- 429,439 ----
  							DAYSPERNYEAR;
  					}
  #ifdef XPG4_1994_04_09
! 					if ((w == 52 &&
! 						t->tm_mon == TM_JANUARY) ||
! 						(w == 1 &&
! 						t->tm_mon == TM_DECEMBER))
! 							w = 53;
  #endif /* defined XPG4_1994_04_09 */
  					if (*format == 'V')
  						pt = _conv(w, "%02d",
***************
*** 437,446 ****
  							pt, ptlim);
  					else if (*format == 'g') {
  						*warnp = IN_ALL;
! 						pt = _conv(year % 100,
"%02d",
  							pt, ptlim);
- 					} else	pt = _conv(year, "%04d",
- 							pt, ptlim);
  				}
  				continue;
  			case 'v':
--- 440,449 ----
  							pt, ptlim);
  					else if (*format == 'g') {
  						*warnp = IN_ALL;
! 						pt = _conv(int(year % 100),
! 							"%02d", pt, ptlim);
! 					} else	pt = _lconv(year, "%04ld",
  							pt, ptlim);
  				}
  				continue;
  			case 'v':
***************
*** 477,488 ****
  				continue;
  			case 'y':
  				*warnp = IN_ALL;
! 				pt = _conv((t->tm_year + TM_YEAR_BASE) %
100,
  					"%02d", pt, ptlim);
  				continue;
  			case 'Y':
! 				pt = _conv(t->tm_year + TM_YEAR_BASE,
"%04d",
! 					pt, ptlim);
  				continue;
  			case 'Z':
  #ifdef TM_ZONE
--- 480,493 ----
  				continue;
  			case 'y':
  				*warnp = IN_ALL;
! 				pt = _conv(
! 					(int) ((t->tm_year +
! 					(long) TM_YEAR_BASE) % 100),
  					"%02d", pt, ptlim);
  				continue;
  			case 'Y':
! 				pt = _lconv(t->tm_year + (long)
TM_YEAR_BASE,
! 					"%04ld", pt, ptlim);
  				continue;
  			case 'Z':
  #ifdef TM_ZONE
***************
*** 582,587 ****
--- 587,605 ----
  	char	buf[INT_STRLEN_MAXIMUM(int) + 1];
  
  	(void) sprintf(buf, format, n);
+ 	return _add(buf, pt, ptlim);
+ }
+ 
+ static char *
+ _lconv(n, format, pt, ptlim)
+ const long		n;
+ const char * const	format;
+ char * const		pt;
+ const char * const	ptlim;
+ {
+ 	char	buf[INT_STRLEN_MAXIMUM(long) + 1];
+ 
+ 	(void) sprintf(buf, format, n);
  	return _add(buf, pt, ptlim);
  }
  



More information about the tz mailing list