Revised mktime on systems with exotic time_t types

Olson, Arthur David (NIH/NCI) olsona at dc37a.nci.nih.gov
Thu Jan 13 15:03:35 UTC 2005


Thanks to Paul Eggert and Ken Pizzini for checking the proposed changes to
improve mktime on systems with exotic time_t types. Below find a revised
version of the changes; as usual for yours truly, these are relative to
what's currently available using ftp.

With providence, a new version of the time zone stuff incorporating these
changes and Paul's proposed data changes will go out next week.

				--ado

------- localtime.c -------
*** /tmp/geta10193	Thu Jan 13 09:58:48 2005
--- /tmp/getb10193	Thu Jan 13 09:58:48 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 at 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.89";
  #endif /* !defined NOID */
  #endif /* !defined lint */
  
***************
*** 1061,1067 ****
  		for (i = 1; i < sp->timecnt; ++i)
  			if (t < sp->ats[i])
  				break;
! 		i = sp->types[i - 1];
  	}
  	ttisp = &sp->ttis[i];
  	/*
--- 1056,1062 ----
  		for (i = 1; i < sp->timecnt; ++i)
  			if (t < sp->ats[i])
  				break;
! 		i = (int) sp->types[i - 1];
  	}
  	ttisp = &sp->ttis[i];
  	/*
***************
*** 1092,1102 ****
  */
  
  struct tm *
! localtime_r(timep, tm)
  const time_t * const	timep;
! struct tm *		tm;
  {
! 	return localsub(timep, 0L, tm);
  }
  
  /*
--- 1087,1097 ----
  */
  
  struct tm *
! localtime_r(timep, tmp)
  const time_t * const	timep;
! struct tm *		tmp;
  {
! 	return localsub(timep, 0L, tmp);
  }
  
  /*
***************
*** 1154,1164 ****
  */
  
  struct tm *
! gmtime_r(timep, tm)
  const time_t * const	timep;
! struct tm *		tm;
  {
! 	return gmtsub(timep, 0L, tm);
  }
  
  #ifdef STD_INSPIRED
--- 1149,1159 ----
  */
  
  struct tm *
! gmtime_r(timep, tmp)
  const time_t * const	timep;
! struct tm *		tmp;
  {
! 	return gmtsub(timep, 0L, tmp);
  }
  
  #ifdef STD_INSPIRED
***************
*** 1338,1346 ****
  const time_t * const	timep;
  char *			buf;
  {
! 	struct tm	tm;
  
! 	return asctime_r(localtime_r(timep, &tm), buf);
  }
  
  /*
--- 1333,1341 ----
  const time_t * const	timep;
  char *			buf;
  {
! 	struct tm	mytm;
  
! 	return asctime_r(localtime_r(timep, &mytm), buf);
  }
  
  /*
***************
*** 1441,1450 ****
  {
  	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;
--- 1436,1446 ----
  {
  	register const struct state *	sp;
  	register int			dir;
  	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)
--- 1514,1562 ----
  		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) DBL_MAX;
! 		else	hi = (time_t) FLT_MAX;
! 		lo = -hi;
! 	} else {
! 		lo = 1;
! 		for (i = 0; i < (int) 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)
--- 1586,1592 ----
  				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)

------- Makefile -------
*** /tmp/geta10212	Thu Jan 13 09:58:57 2005
--- /tmp/getb10212	Thu Jan 13 09:58:57 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

------- zdump.c -------
*** /tmp/geta10231	Thu Jan 13 09:59:07 2005
--- /tmp/getb10231	Thu Jan 13 09:59:07 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.61";
  
  /*
  ** This code has been made independent of the rest of the time
***************
*** 149,159 ****
  static long	delta P((struct tm * newp, struct tm * oldp));
  static void	dumptime P((const struct tm * tmp));
  static time_t	hunt P((char * name, time_t lot, time_t	hit));
! static void	setabsolutes();
  static void	show P((char * zone, time_t t, int v));
  static const char *	tformat P((void));
  static time_t	yeartot P((long y));
  
  int
  main(argc, argv)
  int	argc;
--- 149,195 ----
  static long	delta P((struct tm * newp, struct tm * oldp));
  static void	dumptime P((const struct tm * tmp));
  static time_t	hunt P((char * name, time_t lot, time_t	hit));
! static void	setabsolutes P((void));
  static void	show P((char * zone, time_t t, int v));
  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;
***************
*** 208,219 ****
  		if (cutarg != NULL) {
  			long	lo;
  			long	hi;
! 			char	c;
  
! 			if (sscanf(cutarg, "%ld%c", &hi, &c) == 1) {
  				cuthiyear = hi;
  			} else if (sscanf(cutarg, "%ld,%ld%c",
! 				&lo, &hi, &c) == 2) {
  					cutloyear = lo;
  					cuthiyear = hi;
  			} else {
--- 244,255 ----
  		if (cutarg != NULL) {
  			long	lo;
  			long	hi;
! 			char	dummy;
  
! 			if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
  				cuthiyear = hi;
  			} else if (sscanf(cutarg, "%ld,%ld%c",
! 				&lo, &hi, &dummy) == 2) {
  					cutloyear = lo;
  					cuthiyear = hi;
  			} else {
***************
*** 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')



More information about the tz mailing list