yet another try at 64-bit changes

Arthur David Olson olsona at lecserver.nci.nih.gov
Thu Jun 30 14:59:52 UTC 2005


Below find the next try at 64-bit changes.
As before, zic writes a second instance of headers and data to time zone files;
the second instance has eight-byte transition times to cover far-future
(and far past) cases. Zic also puts a newline-enclosed POSIX-style time zone
string at the end of the file when possible (or, when a zone can't be
represented using POSIX, puts a newline-enclode empty string at the end of the
file). (Enclosing the string in newlines makes for meaningful output from the
"tail -1" command applied to time zone files.) When a POSIX-style string is
available, zic does *not* write 400 years worth of data.

The files that don't have a POSIX string at the end are:
	America/Godthab
	America/Santiago
	Antarctica/Palmer
	Asia/Tehran
	Asia/Jerusalem
	Asia/Tel_Aviv
	Chile/Continental
	Chile/EasterIsland
	Iran
	Israel
	Pacific/Easter
For zones such as America/Godthab, we use the previous dodge of writing 400
years worth of data to the time zone data file and then working modulo 400
in localtime.

Comparing current (tz) and proposed (tzexp2) disk usage:

	Script started on Thu 30 Jun 2005 10:51:51 AM EDT
	lecserver$ du -s -k ~/src/tz/tmp/etc/zoneinfo ~/src/tzexp2/tmp/etc/zoneinfo
	489	/mnt/olsona/src/tz/tmp/etc/zoneinfo
	779	/mnt/olsona/src/tzexp2/tmp/etc/zoneinfo
	lecserver$ exit

	script done on Thu 30 Jun 2005 10:52:06 AM EDT

There's still an increase; the increase is substantially less than without
the POSIX string dodge.

					--ado

diff -c old/Makefile new/Makefile
*** old/Makefile	Thu Jun 30 10:42:57 2005
--- new/Makefile	Thu Jun 30 10:42:58 2005
***************
*** 95,100 ****
--- 95,101 ----
  #  -DHAVE_SETTIMEOFDAY=1 if settimeofday has just 1 arg (SVR4)
  #  -DHAVE_SETTIMEOFDAY=2 if settimeofday uses 2nd arg (4.3BSD)
  #  -DHAVE_SETTIMEOFDAY=3 if settimeofday ignores 2nd arg (4.4BSD)
+ #  -DHAVE_STDINT_H=1 if you have a pre-C99 compiler with "stdint.h"
  #  -DHAVE_STRERROR=0 if your system lacks the strerror function
  #  -DHAVE_SYMLINK=0 if your system lacks the symlink function
  #  -DHAVE_SYS_STAT_H=0 if your compiler lacks a "sys/stat.h"
diff -c old/localtime.c new/localtime.c
*** old/localtime.c	Thu Jun 30 10:42:57 2005
--- new/localtime.c	Thu Jun 30 10:42:59 2005
***************
*** 111,116 ****
--- 111,118 ----
  	int		timecnt;
  	int		typecnt;
  	int		charcnt;
+ 	int		goback;
+ 	int		goahead;
  	time_t		ats[TZ_MAX_TIMES];
  	unsigned char	types[TZ_MAX_TIMES];
  	struct ttinfo	ttis[TZ_MAX_TYPES];
***************
*** 136,143 ****
  */
  
  static long		detzcode P((const char * codep));
  static const char *	getzname P((const char * strp));
! static const char *	getqzname P((const char * strp, const char delim));
  static const char *	getnum P((const char * strp, int * nump, int min,
  				int max));
  static const char *	getsecs P((const char * strp, long * secsp));
--- 138,147 ----
  */
  
  static long		detzcode P((const char * codep));
+ static time_t		detzcode64 P((const char * codep));
+ static int		differ_by_repeat P((time_t t1, time_t t0));
  static const char *	getzname P((const char * strp));
! static const char *	getqzname P((const char * strp, const int delim));
  static const char *	getnum P((const char * strp, int * nump, int min,
  				int max));
  static const char *	getsecs P((const char * strp, long * secsp));
***************
*** 174,180 ****
  				const struct tm * btmp));
  static time_t		transtime P((time_t janfirst, int year,
  				const struct rule * rulep, long offset));
! static int		tzload P((const char * name, struct state * sp));
  static int		tzparse P((const char * name, struct state * sp,
  				int lastditch));
  
--- 178,185 ----
  				const struct tm * btmp));
  static time_t		transtime P((time_t janfirst, int year,
  				const struct rule * rulep, long offset));
! static int		tzload P((const char * name, struct state * sp,
! 				int doextend));
  static int		tzparse P((const char * name, struct state * sp,
  				int lastditch));
  
***************
*** 235,240 ****
--- 240,258 ----
  	return result;
  }
  
+ static time_t
+ detzcode64(codep)
+ const char * const	codep;
+ {
+ 	register time_t	result;
+ 	register int	i;
+ 
+ 	result = (codep[0] & 0x80) ? ~0L : 0L;
+ 	for (i = 0; i < 8; ++i)
+ 		result = result * 256 + (codep[i] & 0xff);
+ 	return result;
+ }
+ 
  static void
  settzname P((void))
  {
***************
*** 304,316 ****
  }
  
  static int
! tzload(name, sp)
  register const char *		name;
  register struct state * const	sp;
  {
! 	register const char *	p;
! 	register int		i;
! 	register int		fid;
  
  	if (name == NULL && (name = TZDEFAULT) == NULL)
  		return -1;
--- 322,354 ----
  }
  
  static int
! differ_by_repeat(t1, t0)
! const time_t	t1;
! const time_t	t0;
! {
! 	if (TYPE_INTEGRAL(time_t) &&
! 		TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
! 			return 0;
! 	return t1 - t0 == SECSPERREPEAT;
! }
! 
! static int
! tzload(name, sp, doextend)
  register const char *		name;
  register struct state * const	sp;
+ register const int		doextend;
  {
! 	register const char *		p;
! 	register int			i;
! 	register int			fid;
! 	register int			stored;
! 	register int			nread;
! 	union {
! 		struct tzhead	tzhead;
! 		char		buf[2 * sizeof(struct tzhead) +
! 					2 * sizeof *sp +
! 					4 * TZ_MAX_TIMES];
! 	} u;
  
  	if (name == NULL && (name = TZDEFAULT) == NULL)
  		return -1;
***************
*** 348,365 ****
  		if ((fid = open(name, OPEN_MODE)) == -1)
  			return -1;
  	}
! 	{
! 		struct tzhead *	tzhp;
! 		union {
! 			struct tzhead	tzhead;
! 			char		buf[sizeof *sp + sizeof *tzhp];
! 		} u;
  		int		ttisstdcnt;
  		int		ttisgmtcnt;
  
- 		i = read(fid, u.buf, sizeof u.buf);
- 		if (close(fid) != 0)
- 			return -1;
  		ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
  		ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
  		sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
--- 386,398 ----
  		if ((fid = open(name, OPEN_MODE)) == -1)
  			return -1;
  	}
! 	nread = read(fid, u.buf, sizeof u.buf);
! 	if (close(fid) < 0 || nread <= 0)
! 		return -1;
! 	for (stored = 4; stored <= 8; stored *= 2) {
  		int		ttisstdcnt;
  		int		ttisgmtcnt;
  
  		ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
  		ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
  		sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
***************
*** 374,390 ****
  			(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
  			(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
  				return -1;
! 		if (i - (p - u.buf) < sp->timecnt * 4 +	/* ats */
  			sp->timecnt +			/* types */
! 			sp->typecnt * (4 + 2) +		/* ttinfos */
  			sp->charcnt +			/* chars */
! 			sp->leapcnt * (4 + 4) +		/* lsinfos */
  			ttisstdcnt +			/* ttisstds */
  			ttisgmtcnt)			/* ttisgmts */
  				return -1;
  		for (i = 0; i < sp->timecnt; ++i) {
! 			sp->ats[i] = detzcode(p);
! 			p += 4;
  		}
  		for (i = 0; i < sp->timecnt; ++i) {
  			sp->types[i] = (unsigned char) *p++;
--- 407,425 ----
  			(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
  			(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
  				return -1;
! 		if (nread - (p - u.buf) <
! 			sp->timecnt * stored +		/* ats */
  			sp->timecnt +			/* types */
! 			sp->typecnt * 6 +		/* ttinfos */
  			sp->charcnt +			/* chars */
! 			sp->leapcnt * (stored + 4) +	/* lsinfos */
  			ttisstdcnt +			/* ttisstds */
  			ttisgmtcnt)			/* ttisgmts */
  				return -1;
  		for (i = 0; i < sp->timecnt; ++i) {
! 			sp->ats[i] = (stored == 4) ?
! 				detzcode(p) : detzcode64(p);
! 			p += stored;
  		}
  		for (i = 0; i < sp->timecnt; ++i) {
  			sp->types[i] = (unsigned char) *p++;
***************
*** 412,419 ****
  			register struct lsinfo *	lsisp;
  
  			lsisp = &sp->lsis[i];
! 			lsisp->ls_trans = detzcode(p);
! 			p += 4;
  			lsisp->ls_corr = detzcode(p);
  			p += 4;
  		}
--- 447,455 ----
  			register struct lsinfo *	lsisp;
  
  			lsisp = &sp->lsis[i];
! 			lsisp->ls_trans = (stored == 4) ?
! 				detzcode(p) : detzcode64(p);
! 			p += stored;
  			lsisp->ls_corr = detzcode(p);
  			p += 4;
  		}
***************
*** 470,476 ****
--- 506,568 ----
  				}
  				break;
  			}
+ 		/*
+ 		** If this is an old file, we're done.
+ 		*/
+ 		if (u.tzhead.tzh_version[0] == '\0')
+ 			break;
+ 		nread -= p - u.buf;
+ 		for (i = 0; i < nread; ++i)
+ 			u.buf[i] = p[i];
+ 		/*
+ 		** If this is a narrow integer time_t system, we're done.
+ 		*/
+ 		if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t))
+ 			break;
  	}
+ 	if (doextend && nread > 2 &&
+ 		u.buf[0] == '\n' && u.buf[nread - 1] == '\n' &&
+ 		sp->typecnt + 2 <= TZ_MAX_TYPES) {
+ 			struct state	ts;
+ 			register int	result;
+ 
+ 			u.buf[nread - 1] = '\0';
+ 			result = tzparse(&u.buf[1], &ts, FALSE);
+ 			if (result == 0 && ts.typecnt == 2 &&
+ 				sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) {
+ 					for (i = 0; i < 2; ++i)
+ 						ts.ttis[i].tt_abbrind +=
+ 							sp->charcnt;
+ 					for (i = 0; i < ts.charcnt; ++i)
+ 						sp->chars[sp->charcnt++] =
+ 							ts.chars[i];
+ 					i = 0;
+ 					while (i < ts.timecnt &&
+ 						ts.ats[i] <=
+ 						sp->ats[sp->timecnt - 1])
+ 							++i;
+ 					while (i < ts.timecnt &&
+ 					    sp->timecnt < TZ_MAX_TIMES) {
+ 						sp->ats[sp->timecnt] =
+ 							ts.ats[i];
+ 						sp->types[sp->timecnt] =
+ 							sp->typecnt +
+ 							ts.types[i];
+ 						++sp->timecnt;
+ 						++i;
+ 					}
+ 					sp->ttis[sp->typecnt++] = ts.ttis[0];
+ 					sp->ttis[sp->typecnt++] = ts.ttis[1];
+ 			}
+ 	}
+ 	i = 2 * YEARSPERREPEAT;
+ 	sp->goback = sp->goahead = sp->timecnt > i;
+ 	sp->goback &= sp->types[i] == sp->types[0] &&
+ 		differ_by_repeat(sp->ats[i], sp->ats[0]);
+ 	sp->goahead &=
+ 		sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 1 - i] &&
+ 		differ_by_repeat(sp->ats[sp->timecnt - 1],
+ 			 sp->ats[sp->timecnt - 1 - i]);
  	return 0;
  }
  
***************
*** 503,521 ****
  
  /*
  ** Given a pointer into an extended time zone string, scan until the ending
! ** delimiter of the zone name is located.   Return a pointer to the delimiter.
  **
  ** As with getzname above, the legal character set is actually quite
  ** restricted, with other characters producing undefined results.
! ** We choose not to care - allowing almost anything to be in the zone abbrev.
  */
  
  static const char *
  getqzname(strp, delim)
  register const char *	strp;
! const char		delim;
  {
! 	register char	c;
  
  	while ((c = *strp) != '\0' && c != delim)
  		++strp;
--- 595,613 ----
  
  /*
  ** Given a pointer into an extended time zone string, scan until the ending
! ** delimiter of the zone name is located. Return a pointer to the delimiter.
  **
  ** As with getzname above, the legal character set is actually quite
  ** restricted, with other characters producing undefined results.
! ** We don't do any checking here; checking is done later in common-case code.
  */
  
  static const char *
  getqzname(strp, delim)
  register const char *	strp;
! const int		delim;
  {
! 	register int	c;
  
  	while ((c = *strp) != '\0' && c != delim)
  		++strp;
***************
*** 824,832 ****
  		if (name == NULL)
  			return -1;
  	}
! 	load_result = tzload(TZDEFRULES, sp);
  	if (load_result != 0)
  		sp->leapcnt = 0;		/* so, we're off a little */
  	if (*name != '\0') {
  		if (*name == '<') {
  			dstname = ++name;
--- 916,925 ----
  		if (name == NULL)
  			return -1;
  	}
! 	load_result = tzload(TZDEFRULES, sp, FALSE);
  	if (load_result != 0)
  		sp->leapcnt = 0;		/* so, we're off a little */
+ 	sp->timecnt = 0;
  	if (*name != '\0') {
  		if (*name == '<') {
  			dstname = ++name;
***************
*** 866,876 ****
  				return -1;
  			sp->typecnt = 2;	/* standard time and DST */
  			/*
! 			** Two transitions per year, from EPOCH_YEAR to 2037.
  			*/
- 			sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
- 			if (sp->timecnt > TZ_MAX_TIMES)
- 				return -1;
  			sp->ttis[0].tt_gmtoff = -dstoffset;
  			sp->ttis[0].tt_isdst = 1;
  			sp->ttis[0].tt_abbrind = stdlen + 1;
--- 959,966 ----
  				return -1;
  			sp->typecnt = 2;	/* standard time and DST */
  			/*
! 			** Two transitions per year, from EPOCH_YEAR forward.
  			*/
  			sp->ttis[0].tt_gmtoff = -dstoffset;
  			sp->ttis[0].tt_isdst = 1;
  			sp->ttis[0].tt_abbrind = stdlen + 1;
***************
*** 880,886 ****
  			atp = sp->ats;
  			typep = sp->types;
  			janfirst = 0;
! 			for (year = EPOCH_YEAR; year <= 2037; ++year) {
  				starttime = transtime(janfirst, year, &start,
  					stdoffset);
  				endtime = transtime(janfirst, year, &end,
--- 970,980 ----
  			atp = sp->ats;
  			typep = sp->types;
  			janfirst = 0;
! 			for (year = EPOCH_YEAR;
! 			    sp->timecnt + 2 <= TZ_MAX_TIMES;
! 			    ++year) {
! 			    	time_t	newfirst;
! 
  				starttime = transtime(janfirst, year, &start,
  					stdoffset);
  				endtime = transtime(janfirst, year, &end,
***************
*** 896,903 ****
  					*atp++ = endtime;
  					*typep++ = 1;	/* DST ends */
  				}
! 				janfirst += year_lengths[isleap(year)] *
  					SECSPERDAY;
  			}
  		} else {
  			register long	theirstdoffset;
--- 990,1002 ----
  					*atp++ = endtime;
  					*typep++ = 1;	/* DST ends */
  				}
! 				sp->timecnt += 2;
! 				newfirst = janfirst;
! 				newfirst += year_lengths[isleap(year)] *
  					SECSPERDAY;
+ 				if (newfirst <= janfirst)
+ 					break;
+ 				janfirst = newfirst;
  			}
  		} else {
  			register long	theirstdoffset;
***************
*** 1012,1018 ****
  gmtload(sp)
  struct state * const	sp;
  {
! 	if (tzload(gmt, sp) != 0)
  		(void) tzparse(gmt, sp, TRUE);
  }
  
--- 1111,1117 ----
  gmtload(sp)
  struct state * const	sp;
  {
! 	if (tzload(gmt, sp, TRUE) != 0)
  		(void) tzparse(gmt, sp, TRUE);
  }
  
***************
*** 1039,1045 ****
  		}
  	}
  #endif /* defined ALL_STATE */
! 	if (tzload((char *) NULL, lclptr) != 0)
  		gmtload(lclptr);
  	settzname();
  }
--- 1138,1144 ----
  		}
  	}
  #endif /* defined ALL_STATE */
! 	if (tzload((char *) NULL, lclptr, TRUE) != 0)
  		gmtload(lclptr);
  	settzname();
  }
***************
*** 1081,1087 ****
  		lclptr->ttis[0].tt_gmtoff = 0;
  		lclptr->ttis[0].tt_abbrind = 0;
  		(void) strcpy(lclptr->chars, gmt);
! 	} else if (tzload(name, lclptr) != 0)
  		if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
  			(void) gmtload(lclptr);
  	settzname();
--- 1180,1186 ----
  		lclptr->ttis[0].tt_gmtoff = 0;
  		lclptr->ttis[0].tt_abbrind = 0;
  		(void) strcpy(lclptr->chars, gmt);
! 	} else if (tzload(name, lclptr, TRUE) != 0)
  		if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
  			(void) gmtload(lclptr);
  	settzname();
***************
*** 1114,1119 ****
--- 1213,1257 ----
  	if (sp == NULL)
  		return gmtsub(timep, offset, tmp);
  #endif /* defined ALL_STATE */
+ 	if ((sp->goback && t < sp->ats[0]) ||
+ 		(sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+ 			time_t			newt = t;
+ 			register time_t		seconds;
+ 			register time_t		tcycles;
+ 			register int_fast64_t	icycles;
+ 
+ 			if (t < sp->ats[0])
+ 				seconds = sp->ats[0] - t;
+ 			else	seconds = t - sp->ats[sp->timecnt - 1];
+ 			--seconds;
+ 			tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
+ 			++tcycles;
+ 			icycles = tcycles;
+ 			if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
+ 				return NULL;
+ 			seconds = icycles;
+ 			seconds *= YEARSPERREPEAT;
+ 			seconds *= AVGSECSPERYEAR;
+ 			if (t < sp->ats[0])
+ 				newt += seconds;
+ 			else	newt -= seconds;
+ 			if (newt < sp->ats[0] ||
+ 				newt > sp->ats[sp->timecnt - 1])
+ 					return NULL;	/* "cannot happen" */
+ 			result = localsub(&newt, offset, tmp);
+ 			if (result == tmp) {
+ 				register time_t	newy;
+ 
+ 				newy = tmp->tm_year;
+ 				if (t < sp->ats[0])
+ 					newy -= icycles * YEARSPERREPEAT;
+ 				else	newy += icycles * YEARSPERREPEAT;
+ 				tmp->tm_year = newy;
+ 				if (tmp->tm_year != newy)
+ 					return NULL;
+ 			}
+ 			return result;
+ 	}
  	if (sp->timecnt == 0 || t < sp->ats[0]) {
  		i = 0;
  		while (sp->ttis[i].tt_isdst)
diff -c old/private.h new/private.h
*** old/private.h	Thu Jun 30 10:42:58 2005
--- new/private.h	Thu Jun 30 10:42:59 2005
***************
*** 48,53 ****
--- 48,57 ----
  #define HAVE_SETTIMEOFDAY	3
  #endif /* !defined HAVE_SETTIMEOFDAY */
  
+ #ifndef HAVE_STDINT_H
+ #define HAVE_STDINT_H		(199901 <= __STDC_VERSION__)
+ #endif /* !defined HAVE_STDINT_H */
+ 
  #ifndef HAVE_STRERROR
  #define HAVE_STRERROR		1
  #endif /* !defined HAVE_STRERROR */
***************
*** 89,95 ****
  #include "stdio.h"
  #include "errno.h"
  #include "string.h"
! #include "limits.h"	/* for CHAR_BIT */
  #include "time.h"
  #include "stdlib.h"
  
--- 93,99 ----
  #include "stdio.h"
  #include "errno.h"
  #include "string.h"
! #include "limits.h"	/* for CHAR_BIT et al. */
  #include "time.h"
  #include "stdlib.h"
  
***************
*** 124,129 ****
--- 128,152 ----
  /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
  #define is_digit(c) ((unsigned)(c) - '0' <= 9)
  
+ #if HAVE_STDINT_H
+ #include <stdint.h>
+ #endif /* !HAVE_STDINT_H */
+ 
+ #ifndef INT_FAST64_MAX
+ #ifdef LLONG_MAX
+ typedef long long	int_fast64_t;
+ #else /* !defined LLONG_MAX */
+ typedef long		int_fast64_t;
+ #endif /* !defined LLONG_MAX */
+ #endif /* !defined INT_FAST64_MAX */
+ 
+ #ifndef INT32_MAX
+ #define INT32_MAX 0x7fffffff
+ #endif /* !defined INT32_MAX */
+ #ifndef INT32_MIN
+ #define INT32_MIN (-1 - INT32_MAX)
+ #endif /* !defined INT32_MIN */
+ 
  /*
  ** Workarounds for compilers/systems.
  */
***************
*** 310,316 ****
--- 333,359 ----
  char *ctime_r P((time_t const *, char *));
  #endif /* HAVE_INCOMPATIBLE_CTIME_R */
  
+ #ifndef YEARSPERREPEAT
+ #define YEARSPERREPEAT		400	/* years before a Gregorian repeat */
+ #endif /* !defined YEARSPERREPEAT */
+ 
  /*
+ ** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
+ */
+ 
+ #ifndef AVGSECSPERYEAR
+ #define AVGSECSPERYEAR		31556952
+ #endif /* !defined AVGSECSPERYEAR */
+ 
+ #ifndef SECSPERREPEAT
+ #define SECSPERREPEAT		((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
+ #endif /* !defined SECSPERREPEAT */
+  
+ #ifndef SECSPERREPEAT_BITS
+ #define SECSPERREPEAT_BITS	34	/* ceil(log2(SECSPERREPEAT)) */
+ #endif /* !defined SECSPERREPEAT_BITS */
+ 
+ /*
  ** UNIX was a registered trademark of The Open Group in 2003.
  */
  
diff -c old/tzfile.5 new/tzfile.5
*** old/tzfile.5	Thu Jun 30 10:42:58 2005
--- new/tzfile.5	Thu Jun 30 10:42:59 2005
***************
*** 9,15 ****
  .IR tzset (3)
  begin with the magic characters "TZif" to identify then as
  time zone information files,
! followed by sixteen bytes reserved for future use,
  followed by six four-byte values of type
  .BR long ,
  written in a ``standard'' byte order
--- 9,17 ----
  .IR tzset (3)
  begin with the magic characters "TZif" to identify then as
  time zone information files,
! followed by a character identifying the version of the file's format
! (as of 2005, either an ASCII NUL or a '2')
! followed by fifteen bytes containing zeroes reserved for future use,
  followed by six four-byte values of type
  .BR long ,
  written in a ``standard'' byte order
***************
*** 131,136 ****
--- 133,148 ----
  .I tzh_timecnt
  is zero or the time argument is less than the first transition time recorded
  in the file.
+ .PP
+ For version-2-format time zone files,
+ the above header and data is followed by a second header and data,
+ identical in format except that
+ eight bytes are used for each transition time or leap second time.
+ After the second header and data comes a newline-encloded,
+ POSIX-TZ-environment-variable-style string for use in handling instants
+ after the last transition time stored in the file
+ (with nothing between the newlines if there is no POSIX representation for
+ such instants).
  .SH SEE ALSO
  newctime(3)
  .\" %W%
diff -c old/tzfile.h new/tzfile.h
*** old/tzfile.h	Thu Jun 30 10:42:58 2005
--- new/tzfile.h	Thu Jun 30 10:42:59 2005
***************
*** 49,55 ****
  
  struct tzhead {
  	char	tzh_magic[4];		/* TZ_MAGIC */
! 	char	tzh_reserved[16];	/* reserved for future use */
  	char	tzh_ttisgmtcnt[4];	/* coded number of trans. time flags */
  	char	tzh_ttisstdcnt[4];	/* coded number of trans. time flags */
  	char	tzh_leapcnt[4];		/* coded number of leap seconds */
--- 49,56 ----
  
  struct tzhead {
  	char	tzh_magic[4];		/* TZ_MAGIC */
! 	char	tzh_version[1];		/* '\0' or '2' as of 2005 */
! 	char	tzh_reserved[15];	/* reserved--must be zero */
  	char	tzh_ttisgmtcnt[4];	/* coded number of trans. time flags */
  	char	tzh_ttisstdcnt[4];	/* coded number of trans. time flags */
  	char	tzh_leapcnt[4];		/* coded number of leap seconds */
***************
*** 84,101 ****
  */
  
  /*
  ** In the current implementation, "tzset()" refuses to deal with files that
  ** exceed any of the limits below.
  */
  
  #ifndef TZ_MAX_TIMES
! /*
! ** The TZ_MAX_TIMES value below is enough to handle a bit more than a
! ** year's worth of solar time (corrected daily to the nearest second) or
! ** 138 years of Pacific Presidential Election time
! ** (where there are three time zone transitions every fourth year).
! */
! #define TZ_MAX_TIMES	370
  #endif /* !defined TZ_MAX_TIMES */
  
  #ifndef TZ_MAX_TYPES
--- 85,106 ----
  */
  
  /*
+ ** If tzh_version is '2' or greater, the above is followed by a second instance
+ ** of tzhead and a second instance of the data in which each coded transition
+ ** time uses 8 rather than 4 chars,
+ ** then a POSIX-TZ-environment-variable-style string for use in handling
+ ** instants after the last transition time stored in the file
+ ** (with nothing between the newlines if there is no POSIX representation for
+ ** such instants).
+ */
+ 
+ /*
  ** In the current implementation, "tzset()" refuses to deal with files that
  ** exceed any of the limits below.
  */
  
  #ifndef TZ_MAX_TIMES
! #define TZ_MAX_TIMES	1200
  #endif /* !defined TZ_MAX_TIMES */
  
  #ifndef TZ_MAX_TYPES
diff -c old/zdump.c new/zdump.c
*** old/zdump.c	Thu Jun 30 10:42:57 2005
--- new/zdump.c	Thu Jun 30 10:42:59 2005
***************
*** 12,17 ****
--- 12,18 ----
  #include "time.h"	/* for struct tm */
  #include "stdlib.h"	/* for exit, malloc, atoi */
  #include "float.h"	/* for FLT_MAX and DBL_MAX */
+ #include "ctype.h"	/* for isascii et al. */
  
  #ifndef ZDUMP_LO_YEAR
  #define ZDUMP_LO_YEAR	(-500)
***************
*** 147,153 ****
  static int	warned;
  
  static char *	abbr P((struct tm * tmp));
! static void	abbrok P((const char * abbr, const char * zone));
  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));
--- 148,154 ----
  static int	warned;
  
  static char *	abbr P((struct tm * tmp));
! static void	abbrok P((const char * abbrp, const char * zone));
  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));
***************
*** 194,222 ****
  #endif /* !defined TYPECHECK */
  
  static void
! abbrok(abbr, zone)
! const char * const	abbr;
  const char * const	zone;
  {
- 	register int		i;
  	register const char *	cp;
  	register char *		wp;
  
  	if (warned)
  		return;
! 	cp = abbr;
  	wp = NULL;
! 	while (isascii(*cp) && isalpha(*cp))
  		++cp;
! 	if (cp - abbr == 0)
  		wp = _("lacks alphabetic at start");
! 	if (cp - abbr < 3)
  		wp = _("has fewer than 3 alphabetics");
! 	if (cp - abbr > 6)
  		wp = _("has more than 6 alphabetics");
  	if (wp == NULL && (*cp == '+' || *cp == '-')) {
  		++cp;
! 		if (isascii(*cp) && isdigit(*cp))
  			if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
  				++cp;
  	}
--- 195,222 ----
  #endif /* !defined TYPECHECK */
  
  static void
! abbrok(abbrp, zone)
! const char * const	abbrp;
  const char * const	zone;
  {
  	register const char *	cp;
  	register char *		wp;
  
  	if (warned)
  		return;
! 	cp = abbrp;
  	wp = NULL;
! 	while (isascii((int) *cp) && isalpha((int) *cp))
  		++cp;
! 	if (cp - abbrp == 0)
  		wp = _("lacks alphabetic at start");
! 	if (cp - abbrp < 3)
  		wp = _("has fewer than 3 alphabetics");
! 	if (cp - abbrp > 6)
  		wp = _("has more than 6 alphabetics");
  	if (wp == NULL && (*cp == '+' || *cp == '-')) {
  		++cp;
! 		if (isascii((int) *cp) && isdigit((int) *cp))
  			if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
  				++cp;
  	}
***************
*** 227,233 ****
  	(void) fflush(stdout);
  	(void) fprintf(stderr,
  		"%s: warning: zone \"%s\" abbreviation \"%s\" %s\n",
! 		progname, zone, abbr, wp);
  	warned = TRUE;
  }
  
--- 227,233 ----
  	(void) fflush(stdout);
  	(void) fprintf(stderr,
  		"%s: warning: zone \"%s\" abbreviation \"%s\" %s\n",
! 		progname, zone, abbrp, wp);
  	warned = TRUE;
  }
  
diff -c old/zic.c new/zic.c
*** old/zic.c	Thu Jun 30 10:42:57 2005
--- new/zic.c	Thu Jun 30 10:42:59 2005
***************
*** 1,15 ****
  static char	elsieid[] = "%W%";
  
- /*
- ** Regardless of the type of time_t, we do our work using this type.
- */
- 
- typedef int	zic_t;
- 
  #include "private.h"
  #include "locale.h"
  #include "tzfile.h"
  
  #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
  #define ZIC_MAX_ABBR_LEN_WO_WARN	6
  #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
--- 1,13 ----
  static char	elsieid[] = "%W%";
  
  #include "private.h"
  #include "locale.h"
  #include "tzfile.h"
  
+ #define	ZIC_VERSION	'2'
+ 
+ typedef int_fast64_t	zic_t;
+ 
  #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
  #define ZIC_MAX_ABBR_LEN_WO_WARN	6
  #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
***************
*** 36,41 ****
--- 34,44 ----
  #define isascii(x) 1
  #endif
  
+ #define OFFSET_STRLEN_MAXIMUM	(7 + INT_STRLEN_MAXIMUM(long))
+ #define RULE_STRLEN_MAXIMUM	8	/* "Mdd.dd.d" */
+ 
+ #define end(cp)	(strchr((cp), '\0'))
+ 
  struct rule {
  	const char *	r_filename;
  	int		r_linenum;
***************
*** 44,49 ****
--- 47,54 ----
  	int		r_loyear;	/* for example, 1986 */
  	int		r_hiyear;	/* for example, 1986 */
  	const char *	r_yrtype;
+ 	int		r_lowasnum;
+ 	int		r_hiwasnum;
  
  	int		r_month;	/* 0..11 */
  
***************
*** 103,111 ****
  static void	associate P((void));
  static int	ciequal P((const char * ap, const char * bp));
  static void	convert P((long val, char * buf));
  static void	dolink P((const char * fromfile, const char * tofile));
  static void	doabbr P((char * abbr, const char * format,
! 			const char * letters, int isdst));
  static void	eat P((const char * name, int num));
  static void	eats P((const char * name, int num,
  			const char * rname, int rnum));
--- 108,117 ----
  static void	associate P((void));
  static int	ciequal P((const char * ap, const char * bp));
  static void	convert P((long val, char * buf));
+ static void	convert64 P((zic_t val, char * buf));
  static void	dolink P((const char * fromfile, const char * tofile));
  static void	doabbr P((char * abbr, const char * format,
! 			const char * letters, int isdst, int doquotes));
  static void	eat P((const char * name, int num));
  static void	eats P((const char * name, int num,
  			const char * rname, int rnum));
***************
*** 121,126 ****
--- 127,133 ----
  static int	inzcont P((char ** fields, int nfields));
  static int	inzone P((char ** fields, int nfields));
  static int	inzsub P((char ** fields, int nfields, int iscont));
+ static int	is32 P((zic_t x));
  static int	itsabbr P((const char * abbr, const char * word));
  static int	itsdir P((const char * name));
  static int	lowerit P((int c));
***************
*** 130,135 ****
--- 137,143 ----
  static long	oadd P((long t1, long t2));
  static void	outzone P((const struct zone * zp, int ntzones));
  static void	puttzcode P((long code, FILE * fp));
+ static void	puttzcode64 P((zic_t code, FILE * fp));
  static int	rcomp P((const void * leftp, const void * rightp));
  static zic_t	rpytime P((const struct rule * rp, int wantedy));
  static void	rulesub P((struct rule * rp,
***************
*** 136,145 ****
  			const char * loyearp, const char * hiyearp,
  			const char * typep, const char * monthp,
  			const char * dayp, const char * timep));
  static void	setboundaries P((void));
  static zic_t	tadd P((zic_t t1, long t2));
  static void	usage P((void));
! static void	writezone P((const char * name));
  static int	yearistype P((int year, const char * type));
  
  #if !HAVE_STRERROR
--- 144,158 ----
  			const char * loyearp, const char * hiyearp,
  			const char * typep, const char * monthp,
  			const char * dayp, const char * timep));
+ static int 	stringoffset P((char * result, long offset));
+ static int	stringrule P((char * result, const struct rule * rp,
+ 			long dstoff, long gmtoff));
+ static void 	stringzone P((char * result,
+ 			const struct zone * zp, int ntzones));
  static void	setboundaries P((void));
  static zic_t	tadd P((zic_t t1, long t2));
  static void	usage P((void));
! static void	writezone P((const char * name, const char * string));
  static int	yearistype P((int year, const char * type));
  
  #if !HAVE_STRERROR
***************
*** 150,162 ****
  static int		errors;
  static const char *	filename;
  static int		leapcnt;
  static int		linenum;
  static zic_t		max_time;
  static int		max_year;
- static int		max_year_representable;
  static zic_t		min_time;
  static int		min_year;
- static int		min_year_representable;
  static int		noise;
  static const char *	rfilename;
  static int		rlinenum;
--- 163,178 ----
  static int		errors;
  static const char *	filename;
  static int		leapcnt;
+ static int		leapseen;
+ static int		leapminyear;
+ static int		leapmaxyear;
  static int		linenum;
+ static int		max_abbrvar_len;
+ static int		max_format_len;
  static zic_t		max_time;
  static int		max_year;
  static zic_t		min_time;
  static int		min_year;
  static int		noise;
  static const char *	rfilename;
  static int		rlinenum;
***************
*** 453,459 ****
  usage P((void))
  {
  	(void) fprintf(stderr, _("%s: usage is %s \
! [ --version ] [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\
  \t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n"),
  		progname, progname);
  	(void) exit(EXIT_FAILURE);
--- 469,475 ----
  usage P((void))
  {
  	(void) fprintf(stderr, _("%s: usage is %s \
! [ --version ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\
  \t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n"),
  		progname, progname);
  	(void) exit(EXIT_FAILURE);
***************
*** 464,470 ****
  static const char *	directory;
  static const char *	leapsec;
  static const char *	yitcommand;
- static int		sflag = FALSE;
  
  int
  main(argc, argv)
--- 480,485 ----
***************
*** 486,491 ****
--- 501,511 ----
  	(void) textdomain(TZ_DOMAIN);
  #endif /* HAVE_GETTEXT */
  	progname = argv[0];
+ 	if (TYPE_BIT(zic_t) < 64) {
+ 		(void) fprintf(stderr, "%s: %s\n", progname,
+ 			_("wild compilation-time specification of zic_t"));
+ 		exit(EXIT_FAILURE);
+ 	}
  	for (i = 1; i < argc; ++i)
  		if (strcmp(argv[i], "--version") == 0) {
  			(void) printf("%s\n", elsieid);
***************
*** 549,555 ****
  				noise = TRUE;
  				break;
  			case 's':
! 				sflag = TRUE;
  				break;
  		}
  	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
--- 569,575 ----
  				noise = TRUE;
  				break;
  			case 's':
! 				(void) printf("%s: -s ignored\n", progname);
  				break;
  		}
  	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
***************
*** 671,724 ****
  	ifree(toname);
  }
  
! #ifndef INT_MAX
! #define INT_MAX	((int) (((unsigned)~0)>>1))
! #endif /* !defined INT_MAX */
  
- #ifndef INT_MIN
- #define INT_MIN	((int) ~(((unsigned)~0)>>1))
- #endif /* !defined INT_MIN */
- 
- /*
- ** The tz file format currently allows at most 32-bit quantities.
- ** This restriction should be removed before signed 32-bit values
- ** wrap around in 2038, but unfortunately this will require a
- ** change to the tz file format.
- */
- 
- #define MAX_BITS_IN_FILE	32
- #define TIME_T_BITS_IN_FILE	((TYPE_BIT(zic_t) < MAX_BITS_IN_FILE) ? \
- 					TYPE_BIT(zic_t) : MAX_BITS_IN_FILE)
- 
  static void
  setboundaries P((void))
  {
  	register int	i;
  
! 	if (TYPE_SIGNED(zic_t)) {
! 		min_time = -1;
! 		for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
! 			min_time *= 2;
! 		max_time = -(min_time + 1);
! 		if (sflag)
! 			min_time = 0;
! 	} else {
! 		min_time = 0;
! 		max_time = 2 - sflag;
! 		for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
! 			max_time *= 2;
! 		--max_time;
! 	}
! 	{
! 		time_t	t;
! 
! 		t = (time_t) min_time;
! 		min_year = TM_YEAR_BASE + gmtime(&t)->tm_year;
! 		t = (time_t) max_time;
! 		max_year = TM_YEAR_BASE + gmtime(&t)->tm_year;
! 	}
! 	min_year_representable = min_year;
! 	max_year_representable = max_year;
  }
  
  static int
--- 691,707 ----
  	ifree(toname);
  }
  
! #define TIME_T_BITS_IN_FILE	64
  
  static void
  setboundaries P((void))
  {
  	register int	i;
  
! 	min_time = -1;
! 	for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
! 		min_time *= 2;
! 	max_time = -(min_time + 1);
  }
  
  static int
***************
*** 993,998 ****
--- 976,983 ----
  		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
  	r.r_name = ecpyalloc(fields[RF_NAME]);
  	r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
+ 	if (max_abbrvar_len < strlen(r.r_abbrvar))
+ 		max_abbrvar_len = strlen(r.r_abbrvar);
  	rules = (struct rule *) (void *) erealloc((char *) rules,
  		(int) ((nrules + 1) * sizeof *rules));
  	rules[nrules++] = r;
***************
*** 1098,1103 ****
--- 1083,1090 ----
  	}
  	z.z_rule = ecpyalloc(fields[i_rule]);
  	z.z_format = ecpyalloc(fields[i_format]);
+ 	if (max_format_len < strlen(z.z_format))
+ 		max_format_len = strlen(z.z_format);
  	hasuntil = nfields > i_untilyear;
  	if (hasuntil) {
  		z.z_untilrule.r_filename = filename;
***************
*** 1159,1164 ****
--- 1146,1156 ----
  		error(_("invalid leaping year"));
  		return;
  	}
+ 	if (!leapseen || leapmaxyear < year)
+ 		leapmaxyear = year;
+ 	if (!leapseen || leapminyear < year)
+ 		leapminyear = year;
+ 	leapseen = TRUE;
  	j = EPOCH_YEAR;
  	while (j != year) {
  		if (year > j) {
***************
*** 1313,1319 ****
  	*/
  	cp = loyearp;
  	lp = byword(cp, begin_years);
! 	if (lp != NULL) switch ((int) lp->l_value) {
  		case YR_MINIMUM:
  			rp->r_loyear = INT_MIN;
  			break;
--- 1305,1312 ----
  	*/
  	cp = loyearp;
  	lp = byword(cp, begin_years);
! 	rp->r_lowasnum = lp == NULL;
! 	if (!rp->r_lowasnum) switch ((int) lp->l_value) {
  		case YR_MINIMUM:
  			rp->r_loyear = INT_MIN;
  			break;
***************
*** 1328,1341 ****
  	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
  		error(_("invalid starting year"));
  		return;
- 	} else if (noise) {
- 		if (rp->r_loyear < min_year_representable)
- 			warning(_("starting year too low to be represented"));
- 		else if (rp->r_loyear > max_year_representable)
- 			warning(_("starting year too high to be represented"));
  	}
  	cp = hiyearp;
! 	if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) {
  		case YR_MINIMUM:
  			rp->r_hiyear = INT_MIN;
  			break;
--- 1321,1331 ----
  	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
  		error(_("invalid starting year"));
  		return;
  	}
  	cp = hiyearp;
! 	lp = byword(cp, end_years);
! 	rp->r_hiwasnum = lp == NULL;
! 	if (!rp->r_hiwasnum) switch ((int) lp->l_value) {
  		case YR_MINIMUM:
  			rp->r_hiyear = INT_MIN;
  			break;
***************
*** 1353,1363 ****
  	} else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
  		error(_("invalid ending year"));
  		return;
- 	} else if (noise) {
- 		if (rp->r_loyear < min_year_representable)
- 			warning(_("ending year too low to be represented"));
- 		else if (rp->r_loyear > max_year_representable)
- 			warning(_("ending year too high to be represented"));
  	}
  	if (rp->r_loyear > rp->r_hiyear) {
  		error(_("starting year greater than ending year"));
--- 1343,1348 ----
***************
*** 1372,1379 ****
  		}
  		rp->r_yrtype = ecpyalloc(typep);
  	}
- 	if (rp->r_loyear < min_year && rp->r_loyear > 0)
- 		min_year = rp->r_loyear;
  	/*
  	** Day work.
  	** Accept things such as:
--- 1357,1362 ----
***************
*** 1427,1433 ****
  char * const	buf;
  {
  	register int	i;
! 	register long	shift;
  
  	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
  		buf[i] = val >> shift;
--- 1410,1416 ----
  char * const	buf;
  {
  	register int	i;
! 	register int	shift;
  
  	for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
  		buf[i] = val >> shift;
***************
*** 1434,1439 ****
--- 1417,1434 ----
  }
  
  static void
+ convert64(val, buf)
+ const zic_t	val;
+ char * const	buf;
+ {
+ 	register int	i;
+ 	register int	shift;
+ 
+ 	for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
+ 		buf[i] = val >> shift;
+ }
+ 
+ static void
  puttzcode(val, fp)
  const long	val;
  FILE * const	fp;
***************
*** 1444,1471 ****
  	(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
  }
  
  static int
  atcomp(avp, bvp)
! void *	avp;
! void *	bvp;
  {
! 	if (((struct attype *) avp)->at < ((struct attype *) bvp)->at)
! 		return -1;
! 	else if (((struct attype *) avp)->at > ((struct attype *) bvp)->at)
! 		return 1;
! 	else	return 0;
  }
  
  static void
! writezone(name)
  const char * const	name;
  {
! 	register FILE *		fp;
! 	register int		i, j;
! 	static char *		fullname;
! 	static struct tzhead	tzh;
! 	zic_t			ats[TZ_MAX_TIMES];
! 	unsigned char		types[TZ_MAX_TIMES];
  
  	/*
  	** Sort.
--- 1439,1488 ----
  	(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
  }
  
+ static void
+ puttzcode64(val, fp)
+ const zic_t	val;
+ FILE * const	fp;
+ {
+ 	char	buf[8];
+ 
+ 	convert64(val, buf);
+ 	(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
+ }
+ 
  static int
  atcomp(avp, bvp)
! const void *	avp;
! const void *	bvp;
  {
! 	const zic_t	a = ((const struct attype *) avp)->at;
! 	const zic_t	b = ((const struct attype *) bvp)->at;
! 
! 	return (a < b) ? -1 : (a > b);
  }
  
+ static int
+ is32(x)
+ const zic_t	x;
+ {
+ 	return INT32_MIN <= x && x <= INT32_MAX;
+ }
+ 
  static void
! writezone(name, string)
  const char * const	name;
+ const char * const	string;
  {
! 	register FILE *			fp;
! 	register int			i, j;
! 	register int			leapcnt32, leapi32;
! 	register int			timecnt32, timei32;
! 	register int			pass;
! 	static char *			fullname;
! 	static const struct tzhead	tzh0;
! 	static struct tzhead		tzh;
! 	zic_t				ats[TZ_MAX_TIMES];
! 	unsigned char			types[TZ_MAX_TIMES];
  
  	/*
  	** Sort.
***************
*** 1509,1514 ****
--- 1526,1561 ----
  		ats[i] = attypes[i].at;
  		types[i] = attypes[i].type;
  	}
+ 	/*
+ 	** Correct for leap seconds.
+ 	*/
+ 	for (i = 0; i < timecnt; ++i) {
+ 		j = leapcnt;
+ 		while (--j >= 0)
+ 			if (ats[i] >= trans[j]) {
+ 				ats[i] = tadd(ats[i], corr[j]);
+ 				break;
+ 			}
+ 	}
+ 	/*
+ 	** Figure out 32-bit-limited starts and counts.
+ 	*/
+ 	timecnt32 = timecnt;
+ 	timei32 = 0;
+ 	leapcnt32 = leapcnt;
+ 	leapi32 = 0;
+ 	while (timecnt32 > 0 && !is32(ats[timecnt32 - 1]))
+ 		--timecnt32;
+ 	while (timecnt32 > 0 && !is32(ats[timei32])) {
+ 		--timecnt32;
+ 		++timei32;
+ 	}
+ 	while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1]))
+ 		--leapcnt32;
+ 	while (leapcnt32 > 0 && !is32(trans[leapi32])) {
+ 		--leapcnt32;
+ 		++leapi32;
+ 	}
  	fullname = erealloc(fullname,
  		(int) (strlen(directory) + 1 + strlen(name) + 1));
  	(void) sprintf(fullname, "%s/%s", directory, name);
***************
*** 1533,1599 ****
  			(void) exit(EXIT_FAILURE);
  		}
  	}
! 	convert(eitol(typecnt), tzh.tzh_ttisgmtcnt);
! 	convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
! 	convert(eitol(leapcnt), tzh.tzh_leapcnt);
! 	convert(eitol(timecnt), tzh.tzh_timecnt);
! 	convert(eitol(typecnt), tzh.tzh_typecnt);
! 	convert(eitol(charcnt), tzh.tzh_charcnt);
! 	(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
  #define DO(field)	(void) fwrite((void *) tzh.field, \
  				(size_t) sizeof tzh.field, (size_t) 1, fp)
! 	DO(tzh_magic);
! 	DO(tzh_reserved);
! 	DO(tzh_ttisgmtcnt);
! 	DO(tzh_ttisstdcnt);
! 	DO(tzh_leapcnt);
! 	DO(tzh_timecnt);
! 	DO(tzh_typecnt);
! 	DO(tzh_charcnt);
  #undef DO
! 	for (i = 0; i < timecnt; ++i) {
! 		j = leapcnt;
! 		while (--j >= 0)
! 			if (ats[i] >= trans[j]) {
! 				ats[i] = tadd(ats[i], corr[j]);
! 				break;
! 			}
! 		puttzcode((long) ats[i], fp);
  	}
! 	if (timecnt > 0)
! 		(void) fwrite((void *) types, (size_t) sizeof types[0],
! 			(size_t) timecnt, fp);
! 	for (i = 0; i < typecnt; ++i) {
! 		puttzcode((long) gmtoffs[i], fp);
! 		(void) putc(isdsts[i], fp);
! 		(void) putc(abbrinds[i], fp);
! 	}
! 	if (charcnt != 0)
! 		(void) fwrite((void *) chars, (size_t) sizeof chars[0],
! 			(size_t) charcnt, fp);
! 	for (i = 0; i < leapcnt; ++i) {
! 		if (roll[i]) {
! 			if (timecnt == 0 || trans[i] < ats[0]) {
! 				j = 0;
! 				while (isdsts[j])
! 					if (++j >= typecnt) {
! 						j = 0;
! 						break;
! 					}
! 			} else {
! 				j = 1;
! 				while (j < timecnt && trans[i] >= ats[j])
! 					++j;
! 				j = types[j - 1];
! 			}
! 			puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp);
! 		} else	puttzcode((long) trans[i], fp);
! 		puttzcode((long) corr[i], fp);
! 	}
! 	for (i = 0; i < typecnt; ++i)
! 		(void) putc(ttisstds[i], fp);
! 	for (i = 0; i < typecnt; ++i)
! 		(void) putc(ttisgmts[i], fp);
  	if (ferror(fp) || fclose(fp)) {
  		(void) fprintf(stderr, _("%s: Error writing %s\n"),
  			progname, fullname);
--- 1580,1672 ----
  			(void) exit(EXIT_FAILURE);
  		}
  	}
! 	for (pass = 1; pass <= 2; ++pass) {
! 		register int	thistimei, thistimecnt;
! 		register int	thisleapi, thisleapcnt;
! 		register int	thistimelim, thisleaplim;
! 
! 		if (pass == 1) {
! 			thistimei = timei32;
! 			thistimecnt = timecnt32;
! 			thisleapi = leapi32;
! 			thisleapcnt = leapcnt32;
! 		} else {
! 			thistimei = 0;
! 			thistimecnt = timecnt;
! 			thisleapi = 0;
! 			thisleapcnt = leapcnt;
! 		}
! 		thistimelim = thistimei + thistimecnt;
! 		thisleaplim = thisleapi + thisleapcnt;
  #define DO(field)	(void) fwrite((void *) tzh.field, \
  				(size_t) sizeof tzh.field, (size_t) 1, fp)
! 		tzh = tzh0;
! 		(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
! 		tzh.tzh_version[0] = ZIC_VERSION;
! 		convert(eitol(typecnt), tzh.tzh_ttisgmtcnt);
! 		convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
! 		convert(eitol(thisleapcnt), tzh.tzh_leapcnt);
! 		convert(eitol(thistimecnt), tzh.tzh_timecnt);
! 		convert(eitol(typecnt), tzh.tzh_typecnt);
! 		convert(eitol(charcnt), tzh.tzh_charcnt);
! 		DO(tzh_magic);
! 		DO(tzh_version);
! 		DO(tzh_reserved);
! 		DO(tzh_ttisgmtcnt);
! 		DO(tzh_ttisstdcnt);
! 		DO(tzh_leapcnt);
! 		DO(tzh_timecnt);
! 		DO(tzh_typecnt);
! 		DO(tzh_charcnt);
  #undef DO
! 		for (i = thistimei; i < thistimelim; ++i)
! 			if (pass == 1)
! 				puttzcode((long) ats[i], fp);
! 			else	puttzcode64(ats[i], fp);
! 		if (thistimecnt > 0)
! 			(void) fwrite((void *) &types[thistimei],
! 				(size_t) sizeof types[0],
! 				(size_t) thistimecnt,
! 				fp);
! 		for (i = 0; i < typecnt; ++i) {
! 			puttzcode(gmtoffs[i], fp);
! 			(void) putc(isdsts[i], fp);
! 			(void) putc(abbrinds[i], fp);
! 		}
! 		if (charcnt != 0)
! 			(void) fwrite((void *) chars, (size_t) sizeof chars[0],
! 				(size_t) charcnt, fp);
! 		for (i = thisleapi; i < thisleaplim; ++i) {
! 			register zic_t	todo;
! 
! 			if (roll[i]) {
! 				if (timecnt == 0 || trans[i] < ats[0]) {
! 					j = 0;
! 					while (isdsts[j])
! 						if (++j >= typecnt) {
! 							j = 0;
! 							break;
! 						}
! 				} else {
! 					j = 1;
! 					while (j < timecnt &&
! 						trans[i] >= ats[j])
! 							++j;
! 					j = types[j - 1];
! 				}
! 				todo = tadd(trans[i], -gmtoffs[j]);
! 			} else	todo = trans[i];
! 			if (pass == 1)
! 				puttzcode((long) todo, fp);
! 			else	puttzcode64(todo, fp);
! 			puttzcode(corr[i], fp);
! 		}
! 		for (i = 0; i < typecnt; ++i)
! 			(void) putc(ttisstds[i], fp);
! 		for (i = 0; i < typecnt; ++i)
! 			(void) putc(ttisgmts[i], fp);
  	}
! 	(void) fprintf(fp, "\n%s\n", string);
  	if (ferror(fp) || fclose(fp)) {
  		(void) fprintf(stderr, _("%s: Error writing %s\n"),
  			progname, fullname);
***************
*** 1602,1626 ****
  }
  
  static void
! doabbr(abbr, format, letters, isdst)
  char * const		abbr;
  const char * const	format;
  const char * const	letters;
  const int		isdst;
  {
! 	if (strchr(format, '/') == NULL) {
  		if (letters == NULL)
  			(void) strcpy(abbr, format);
  		else	(void) sprintf(abbr, format, letters);
! 	} else if (isdst)
! 		(void) strcpy(abbr, strchr(format, '/') + 1);
! 	else {
! 		(void) strcpy(abbr, format);
! 		*strchr(abbr, '/') = '\0';
  	}
  }
  
  static void
  outzone(zpfirst, zonecount)
  const struct zone * const	zpfirst;
  const int			zonecount;
--- 1675,1901 ----
  }
  
  static void
! doabbr(abbr, format, letters, isdst, doquotes)
  char * const		abbr;
  const char * const	format;
  const char * const	letters;
  const int		isdst;
+ const int		doquotes;
  {
! 	register char *	cp;
! 	register char *	slashp;
! 	register int	len;
! 
! 	slashp = strchr(format, '/');
! 	if (slashp == NULL) {
  		if (letters == NULL)
  			(void) strcpy(abbr, format);
  		else	(void) sprintf(abbr, format, letters);
! 	} else if (isdst) {
! 		(void) strcpy(abbr, slashp + 1);
! 	} else {
! 		if (slashp > format)
! 			(void) strncpy(abbr, format,
! 				(unsigned) (slashp - format));
! 		abbr[slashp - format] = '\0';
  	}
+ 	if (!doquotes)
+ 		return;
+ 	for (cp = abbr; *cp != '\0'; ++cp)
+ 		if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL &&
+ 			strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL)
+ 				break;
+ 	len = strlen(abbr);
+ 	if (len == 3 && *cp == '\0')
+ 		return;
+ 	abbr[len + 2] = '\0';
+ 	abbr[len + 1] = '>';
+ 	for ( ; len > 0; --len)
+ 		abbr[len] = abbr[len - 1];
+ 	abbr[0] = '<';
  }
  
  static void
+ updateminmax(x)
+ const int	x;
+ {
+ 	if (min_year > x)
+ 		min_year = x;
+ 	if (max_year < x)
+ 		max_year = x;
+ }
+ 
+ static int
+ stringoffset(result, offset)
+ char *	result;
+ long	offset;
+ {
+ 	register int	hours;
+ 	register int	minutes;
+ 	register int	seconds;
+ 
+ 	result[0] = '\0';
+ 	if (offset < 0) {
+ 		(void) strcpy(result, "-");
+ 		offset = -offset;
+ 	}
+ 	seconds = offset % SECSPERMIN;
+ 	offset /= SECSPERMIN;
+ 	minutes = offset % MINSPERHOUR;
+ 	offset /= MINSPERHOUR;
+ 	hours = offset;
+ 	if (hours > 99) {
+ 		result[0] = '\0';
+ 		return -1;
+ 	}
+ 	(void) sprintf(end(result), "%d", hours);
+ 	if (minutes != 0 || seconds != 0) {
+ 		(void) sprintf(end(result), ":%02d", minutes);
+ 		if (seconds != 0)
+ 			(void) sprintf(end(result), ":%02d", seconds);
+ 	}
+ 	return 0;
+ }
+ 
+ static int
+ stringrule(result, rp, dstoff, gmtoff)
+ char *				result;
+ const struct rule * const	rp;
+ const long			dstoff;
+ const long			gmtoff;
+ {
+ 	register long	tod;
+ 
+ 	result = end(result);
+ 	if (rp->r_dycode == DC_DOM) {
+ 		register int	month, total;
+ 
+ 		if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
+ 			return -1;
+ 		total = 0;
+ 		for (month = 0; month < rp->r_month; ++month)
+ 			total += len_months[0][month];
+ 		(void) sprintf(result, "J%d", total + rp->r_dayofmonth);
+ 	} else {
+ 		register int	week;
+ 
+ 		if (rp->r_dycode == DC_DOWGEQ) {
+ 			week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
+ 			if ((week - 1) * DAYSPERWEEK + 1 != rp->r_dayofmonth)
+ 				return -1;
+ 		} else if (rp->r_dycode == DC_DOWLEQ) {
+ 			if (rp->r_dayofmonth == len_months[1][rp->r_month])
+ 				week = 5;
+ 			else {
+ 				week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
+ 				if (week * DAYSPERWEEK - 1 != rp->r_dayofmonth)
+ 					return -1;
+ 			}
+ 		} else	return -1;	/* "cannot happen" */
+ 		(void) sprintf(result, "M%d.%d.%d",
+ 			rp->r_month + 1, week, rp->r_wday);
+ 	}
+ 	tod = rp->r_tod;
+ 	if (rp->r_todisgmt)
+ 		tod += gmtoff;
+ 	else if (rp->r_todisstd && rp->r_stdoff == 0)
+ 		tod += dstoff;
+ 	if (tod < 0) {
+ 		result[0] = '\0';
+ 		return -1;
+ 	}
+ 	if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
+ 		(void) strcat(result, "/");
+ 		if (stringoffset(end(result), tod) != 0)
+ 			return -1;
+ 	}
+ 	return 0;
+ }
+ 
+ static void
+ stringzone(result, zpfirst, zonecount)
+ char *				result;
+ const struct zone * const	zpfirst;
+ const int			zonecount;
+ {
+ 	register const struct zone *	zp;
+ 	register struct rule *		rp;
+ 	register struct rule *		stdrp;
+ 	register struct rule *		dstrp;
+ 	register int			i;
+ 	register const char *		abbrvar;
+ 
+ 	result[0] = '\0';
+ 	zp = zpfirst + zonecount - 1;
+ 	stdrp = dstrp = NULL;
+ 	for (i = 0; i < zp->z_nrules; ++i) {
+ 		rp = &zp->z_rules[i];
+ 		if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX)
+ 			continue;
+ 		if (rp->r_yrtype != NULL)
+ 			continue;
+ 		if (rp->r_stdoff == 0) {
+ 			if (stdrp == NULL)
+ 				stdrp = rp;
+ 			else	return;
+ 		} else {
+ 			if (dstrp == NULL)
+ 				dstrp = rp;
+ 			else	return;
+ 		}
+ 	}
+ 	if (stdrp == NULL && dstrp == NULL) {
+ 		/*
+ 		** There are no rules running through "max".
+ 		** Let's find the latest rule.
+ 		*/
+ 		for (i = 0; i < zp->z_nrules; ++i) {
+ 			rp = &zp->z_rules[i];
+ 			if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear ||
+ 				(rp->r_hiyear == stdrp->r_hiyear &&
+ 				rp->r_month > stdrp->r_month))
+ 					stdrp = rp;
+ 		}
+ 		if (stdrp != NULL && stdrp->r_stdoff != 0)
+ 			return;	/* We end up in DST (a POSIX no-no). */
+ 		/*
+ 		** Horrid special case: if year is 2037,
+ 		** presume this is a zone handled on a year-by-year basis;
+ 		** do not try to apply a rule to the zone.
+ 		*/
+ 		if (stdrp != NULL && stdrp->r_hiyear == 2037)
+ 			return;
+ 	}
+ 	if (stdrp == NULL && zp->z_nrules != 0)
+ 		return;
+ 	abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
+ 	doabbr(result, zp->z_format, abbrvar, FALSE, TRUE);
+ 	if (stringoffset(end(result), -zp->z_gmtoff) != 0) {
+ 		result[0] = '\0';
+ 		return;
+ 	}
+ 	if (dstrp == NULL)
+ 		return;
+ 	doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE);
+ 	if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR)
+ 		if (stringoffset(end(result),
+ 			-(zp->z_gmtoff + dstrp->r_stdoff)) != 0) {
+ 				result[0] = '\0';
+ 				return;
+ 		}
+ 	(void) strcat(result, ",");
+ 	if (stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
+ 		result[0] = '\0';
+ 		return;
+ 	}
+ 	(void) strcat(result, ",");
+ 	if (stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
+ 		result[0] = '\0';
+ 		return;
+ 	}
+ }
+ 
+ static void
  outzone(zpfirst, zonecount)
  const struct zone * const	zpfirst;
  const int			zonecount;
***************
*** 1637,1644 ****
  	register int			startttisstd;
  	register int			startttisgmt;
  	register int			type;
! 	char				startbuf[BUFSIZ];
  
  	INITIALIZE(untiltime);
  	INITIALIZE(starttime);
  	/*
--- 1912,1928 ----
  	register int			startttisstd;
  	register int			startttisgmt;
  	register int			type;
! 	register char *			startbuf;
! 	register char *			ab;
! 	register char *			envvar;
! 	register int			max_abbr_len;
! 	register int			max_envvar_len;
  
+ 	max_abbr_len = 2 + max_format_len + max_abbrvar_len;
+ 	max_envvar_len = 2 * max_abbr_len + 5 * 9;
+ 	startbuf = emalloc(max_abbr_len + 1);
+ 	ab = emalloc(max_abbr_len + 1);
+ 	envvar = emalloc(max_envvar_len + 1);
  	INITIALIZE(untiltime);
  	INITIALIZE(starttime);
  	/*
***************
*** 1653,1659 ****
--- 1937,1985 ----
  	*/
  	startttisstd = FALSE;
  	startttisgmt = FALSE;
+ 	min_year = max_year = EPOCH_YEAR;
+ 	if (leapseen) {
+ 		updateminmax(leapminyear);
+ 		updateminmax(leapmaxyear);
+ 	}
  	for (i = 0; i < zonecount; ++i) {
+ 		zp = &zpfirst[i];
+ 		updateminmax(zp->z_untilrule.r_loyear);
+ 		for (j = 0; j < zp->z_nrules; ++j) {
+ 			rp = &zp->z_rules[j];
+ 			if (rp->r_lowasnum)
+ 				updateminmax(rp->r_loyear);
+ 			if (rp->r_hiwasnum)
+ 				updateminmax(rp->r_hiyear);
+ 		}
+ 	}
+ 	/*
+ 	** Generate lots of data if a rule can't cover all future times.
+ 	*/
+ 	stringzone(envvar, zpfirst, zonecount);
+ 	if (noise && envvar[0] == '\0') {
+ 		register char *	wp;
+ 
+ wp = ecpyalloc(_("no POSIX environment variable for zone"));
+ 		wp = ecatalloc(wp, " ");
+ 		wp = ecatalloc(wp, zpfirst->z_name); 
+ 		warning(wp);
+ 		ifree(wp);
+ 	}
+ 	if (envvar[0] == '\0') {
+ 		if (min_year >= INT_MIN + YEARSPERREPEAT)
+ 			min_year -= YEARSPERREPEAT;
+ 		else	min_year = INT_MIN;
+ 		if (max_year <= INT_MAX - YEARSPERREPEAT)
+ 			max_year += YEARSPERREPEAT;
+ 		else	max_year = INT_MAX;
+ 	}
+ 	/*
+ 	** For the benefit of older systems, generate data through 2037.
+ 	*/
+ 	if (max_year < 2037)
+ 		max_year = 2037;
+ 	for (i = 0; i < zonecount; ++i) {
  		/*
  		** A guess that may well be corrected later.
  		*/
***************
*** 1670,1676 ****
  		if (zp->z_nrules == 0) {
  			stdoff = zp->z_stdoff;
  			doabbr(startbuf, zp->z_format,
! 				(char *) NULL, stdoff != 0);
  			type = addtype(oadd(zp->z_gmtoff, stdoff),
  				startbuf, stdoff != 0, startttisstd,
  				startttisgmt);
--- 1996,2002 ----
  		if (zp->z_nrules == 0) {
  			stdoff = zp->z_stdoff;
  			doabbr(startbuf, zp->z_format,
! 				(char *) NULL, stdoff != 0, FALSE);
  			type = addtype(oadd(zp->z_gmtoff, stdoff),
  				startbuf, stdoff != 0, startttisstd,
  				startttisgmt);
***************
*** 1700,1706 ****
  				register int	k;
  				register zic_t	jtime, ktime;
  				register long	offset;
- 				char		buf[BUFSIZ];
  
  				INITIALIZE(ktime);
  				if (useuntil) {
--- 2026,2031 ----
***************
*** 1756,1779 ****
  							stdoff);
  						doabbr(startbuf, zp->z_format,
  							rp->r_abbrvar,
! 							rp->r_stdoff != 0);
  						continue;
  					}
  					if (*startbuf == '\0' &&
  						startoff == oadd(zp->z_gmtoff,
! 						stdoff))
  							doabbr(startbuf,
  								zp->z_format,
  								rp->r_abbrvar,
  								rp->r_stdoff !=
! 								0);
  				}
  				eats(zp->z_filename, zp->z_linenum,
  					rp->r_filename, rp->r_linenum);
! 				doabbr(buf, zp->z_format, rp->r_abbrvar,
! 					rp->r_stdoff != 0);
  				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
! 				type = addtype(offset, buf, rp->r_stdoff != 0,
  					rp->r_todisstd, rp->r_todisgmt);
  				addtt(ktime, type);
  			}
--- 2081,2107 ----
  							stdoff);
  						doabbr(startbuf, zp->z_format,
  							rp->r_abbrvar,
! 							rp->r_stdoff != 0,
! 							FALSE);
  						continue;
  					}
  					if (*startbuf == '\0' &&
  						startoff == oadd(zp->z_gmtoff,
! 						stdoff)) {
  							doabbr(startbuf,
  								zp->z_format,
  								rp->r_abbrvar,
  								rp->r_stdoff !=
! 								0,
! 								FALSE);
! 					}
  				}
  				eats(zp->z_filename, zp->z_linenum,
  					rp->r_filename, rp->r_linenum);
! 				doabbr(ab, zp->z_format, rp->r_abbrvar,
! 					rp->r_stdoff != 0, FALSE);
  				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
! 				type = addtype(offset, ab, rp->r_stdoff != 0,
  					rp->r_todisstd, rp->r_todisgmt);
  				addtt(ktime, type);
  			}
***************
*** 1806,1812 ****
  				starttime = tadd(starttime, -gmtoff);
  		}
  	}
! 	writezone(zpfirst->z_name);
  }
  
  static void
--- 2134,2143 ----
  				starttime = tadd(starttime, -gmtoff);
  		}
  	}
! 	writezone(zpfirst->z_name, envvar);
! 	ifree(startbuf);
! 	ifree(ab);
! 	ifree(envvar);
  }
  
  static void
***************
*** 2184,2191 ****
  will not work with pre-2004 versions of zic"));
  		}
  	}
- 	if (dayoff < 0 && !TYPE_SIGNED(zic_t))
- 		return min_time;
  	if (dayoff < min_time / SECSPERDAY)
  		return min_time;
  	if (dayoff > max_time / SECSPERDAY)
--- 2515,2520 ----
***************
*** 2210,2216 ****
  		*/
  		cp = string;
  		wp = NULL;
! 		while (isascii(*cp) && isalpha(*cp))
  			++cp;
  		if (cp - string == 0)
  wp = _("time zone abbreviation lacks alphabetic at start");
--- 2539,2545 ----
  		*/
  		cp = string;
  		wp = NULL;
! 		while (isascii((int) *cp) && isalpha((int) *cp))
  			++cp;
  		if (cp - string == 0)
  wp = _("time zone abbreviation lacks alphabetic at start");
***************
*** 2220,2226 ****
  wp = _("time zone abbreviation has too many alphabetics");
  		if (wp == NULL && (*cp == '+' || *cp == '-')) {
  			++cp;
! 			if (isascii(*cp) && isdigit(*cp))
  				if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
  					++cp;
  		}
--- 2549,2555 ----
  wp = _("time zone abbreviation has too many alphabetics");
  		if (wp == NULL && (*cp == '+' || *cp == '-')) {
  			++cp;
! 			if (isascii((int) *cp) && isdigit((int) *cp))
  				if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
  					++cp;
  		}



More information about the tz mailing list