Mktime on systems with exotic time_t types

Olson, Arthur David (NIH/NCI) olsona at dc37a.nci.nih.gov
Tue Jan 11 18:11:21 UTC 2005


Below find proposed changes to localtime.c to get mktime working correctly
on systems with exotic time_t types (such as unsigned or double).
Also included are changes to zdump.c and Makefile to arrange things that
doing a...
	make typecheck
...exercises mktime for a good range of values.

			--ado

------- localtime.c -------
*** /tmp/geta3175	Tue Jan 11 11:57:49 2005
--- /tmp/getb3175	Tue Jan 11 11:57:49 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.87";
  #endif /* !defined NOID */
  #endif /* !defined lint */
  
***************
*** 1445,1450 ****
--- 1440,1447 ----
  	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)
--- 1515,1565 ----
  		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) FLT_MAX;
! 		else	hi = (time_t) DBL_MAX;
! 		lo = -hi;
! 	} else {
! 		register int	i;
! 
! 		lo = 1;
! 		for (i = 0; i < 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)
--- 1589,1595 ----
  				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)

------- zdump.c -------
*** /tmp/geta3196	Tue Jan 11 11:57:58 2005
--- /tmp/getb3196	Tue Jan 11 11:57:58 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.60";
  
  /*
  ** This code has been made independent of the rest of the time
***************
*** 154,159 ****
--- 154,195 ----
  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;
***************
*** 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')

------- Makefile -------
*** /tmp/geta3221	Tue Jan 11 11:58:07 2005
--- /tmp/getb3221	Tue Jan 11 11:58:07 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



More information about the tz mailing list