xref: /original-bsd/lib/libc/gen/ctime.c (revision a9452ba1)
125846b40Smckusick /*
2bac379f5Sbostic  * Copyright (c) 1987, 1989, 1993
3bac379f5Sbostic  *	The Regents of the University of California.  All rights reserved.
40f8ac513Sbostic  *
5da242259Sbostic  * This code is derived from software contributed to Berkeley by
69c5a654fSbostic  * Arthur David Olson of the National Cancer Institute.
7da242259Sbostic  *
87311dd13Sbostic  * %sccs.include.redist.c%
925846b40Smckusick  */
1025846b40Smckusick 
11f2f33c31Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*a9452ba1Sbostic static char sccsid[] = "@(#)ctime.c	8.2 (Berkeley) 03/20/94";
130f8ac513Sbostic #endif /* LIBC_SCCS and not lint */
1425846b40Smckusick 
1542802380Swnj /*
163d2a30d4Sbostic ** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
173d2a30d4Sbostic ** POSIX-style TZ environment variable handling from Guy Harris
183d2a30d4Sbostic ** (guy@auspex.com).
1942802380Swnj */
2042802380Swnj 
213d2a30d4Sbostic /*LINTLIBRARY*/
2242802380Swnj 
233d2a30d4Sbostic #include <sys/param.h>
243d2a30d4Sbostic #include <fcntl.h>
253d2a30d4Sbostic #include <time.h>
263d2a30d4Sbostic #include <tzfile.h>
273d2a30d4Sbostic #include <string.h>
283d2a30d4Sbostic #include <ctype.h>
293d2a30d4Sbostic #include <stdio.h>
30ae227ee3Sdonn #include <unistd.h>
313d2a30d4Sbostic 
323d2a30d4Sbostic #ifdef __STDC__
333d2a30d4Sbostic #include <stdlib.h>
343d2a30d4Sbostic 
353d2a30d4Sbostic #define P(s)		s
363d2a30d4Sbostic #define alloc_size_t	size_t
373d2a30d4Sbostic #define qsort_size_t	size_t
383d2a30d4Sbostic #define fread_size_t	size_t
393d2a30d4Sbostic #define fwrite_size_t	size_t
403d2a30d4Sbostic 
413d2a30d4Sbostic #else /* !defined __STDC__ */
423d2a30d4Sbostic 
43cad6ce04Sbostic #define P(s)		()
443d2a30d4Sbostic 
453d2a30d4Sbostic typedef char *		genericptr_t;
463d2a30d4Sbostic typedef unsigned	alloc_size_t;
473d2a30d4Sbostic typedef int		qsort_size_t;
483d2a30d4Sbostic typedef int		fread_size_t;
493d2a30d4Sbostic typedef int		fwrite_size_t;
503d2a30d4Sbostic 
513d2a30d4Sbostic extern char *	calloc();
523d2a30d4Sbostic extern char *	malloc();
533d2a30d4Sbostic extern char *	realloc();
543d2a30d4Sbostic extern char *	getenv();
553d2a30d4Sbostic 
563d2a30d4Sbostic #endif /* !defined __STDC__ */
573d2a30d4Sbostic 
583d2a30d4Sbostic extern time_t	time();
593d2a30d4Sbostic 
603d2a30d4Sbostic #define ACCESS_MODE	O_RDONLY
613d2a30d4Sbostic #define OPEN_MODE	O_RDONLY
623d2a30d4Sbostic 
633d2a30d4Sbostic #ifndef WILDABBR
643d2a30d4Sbostic /*
653d2a30d4Sbostic ** Someone might make incorrect use of a time zone abbreviation:
663d2a30d4Sbostic **	1.	They might reference tzname[0] before calling tzset (explicitly
673d2a30d4Sbostic **	 	or implicitly).
683d2a30d4Sbostic **	2.	They might reference tzname[1] before calling tzset (explicitly
693d2a30d4Sbostic **	 	or implicitly).
703d2a30d4Sbostic **	3.	They might reference tzname[1] after setting to a time zone
713d2a30d4Sbostic **		in which Daylight Saving Time is never observed.
723d2a30d4Sbostic **	4.	They might reference tzname[0] after setting to a time zone
733d2a30d4Sbostic **		in which Standard Time is never observed.
743d2a30d4Sbostic **	5.	They might reference tm.TM_ZONE after calling offtime.
753d2a30d4Sbostic ** What's best to do in the above cases is open to debate;
763d2a30d4Sbostic ** for now, we just set things up so that in any of the five cases
773d2a30d4Sbostic ** WILDABBR is used.  Another possibility:  initialize tzname[0] to the
783d2a30d4Sbostic ** string "tzname[0] used before set", and similarly for the other cases.
793d2a30d4Sbostic ** And another:  initialize tzname[0] to "ERA", with an explanation in the
803d2a30d4Sbostic ** manual page of what this "time zone abbreviation" means (doing this so
813d2a30d4Sbostic ** that tzname[0] has the "normal" length of three characters).
823d2a30d4Sbostic */
833d2a30d4Sbostic #define WILDABBR	"   "
843d2a30d4Sbostic #endif /* !defined WILDABBR */
8542802380Swnj 
86839b9482Sbostic #ifndef TRUE
87839b9482Sbostic #define TRUE		1
88839b9482Sbostic #define FALSE		0
893d2a30d4Sbostic #endif /* !defined TRUE */
90839b9482Sbostic 
913d2a30d4Sbostic static const char GMT[] = "GMT";
92839b9482Sbostic 
93839b9482Sbostic struct ttinfo {				/* time type information */
94839b9482Sbostic 	long		tt_gmtoff;	/* GMT offset in seconds */
95839b9482Sbostic 	int		tt_isdst;	/* used to set tm_isdst */
96839b9482Sbostic 	int		tt_abbrind;	/* abbreviation list index */
973d2a30d4Sbostic 	int		tt_ttisstd;	/* TRUE if transition is std time */
983d2a30d4Sbostic };
993d2a30d4Sbostic 
1003d2a30d4Sbostic struct lsinfo {				/* leap second information */
1013d2a30d4Sbostic 	time_t		ls_trans;	/* transition time */
1023d2a30d4Sbostic 	long		ls_corr;	/* correction to apply */
103839b9482Sbostic };
104839b9482Sbostic 
105839b9482Sbostic struct state {
1063d2a30d4Sbostic 	int		leapcnt;
107839b9482Sbostic 	int		timecnt;
108839b9482Sbostic 	int		typecnt;
109839b9482Sbostic 	int		charcnt;
110839b9482Sbostic 	time_t		ats[TZ_MAX_TIMES];
111839b9482Sbostic 	unsigned char	types[TZ_MAX_TIMES];
112839b9482Sbostic 	struct ttinfo	ttis[TZ_MAX_TYPES];
1133d2a30d4Sbostic 	char		chars[(TZ_MAX_CHARS + 1 > sizeof GMT) ?
1143d2a30d4Sbostic 				TZ_MAX_CHARS + 1 : sizeof GMT];
1153d2a30d4Sbostic 	struct lsinfo	lsis[TZ_MAX_LEAPS];
116839b9482Sbostic };
117839b9482Sbostic 
1183d2a30d4Sbostic struct rule {
1193d2a30d4Sbostic 	int		r_type;		/* type of rule--see below */
1203d2a30d4Sbostic 	int		r_day;		/* day number of rule */
1213d2a30d4Sbostic 	int		r_week;		/* week number of rule */
1223d2a30d4Sbostic 	int		r_mon;		/* month number of rule */
1233d2a30d4Sbostic 	long		r_time;		/* transition time of rule */
1243d2a30d4Sbostic };
125839b9482Sbostic 
1263d2a30d4Sbostic #define	JULIAN_DAY		0	/* Jn - Julian day */
1273d2a30d4Sbostic #define	DAY_OF_YEAR		1	/* n - day of year */
1283d2a30d4Sbostic #define	MONTH_NTH_DAY_OF_WEEK	2	/* Mm.n.d - month, week, day of week */
1293d2a30d4Sbostic 
1303d2a30d4Sbostic /*
1313d2a30d4Sbostic ** Prototypes for static functions.
1323d2a30d4Sbostic */
1333d2a30d4Sbostic 
1343d2a30d4Sbostic static long		detzcode P((const char * codep));
1353d2a30d4Sbostic static const char *	getzname P((const char * strp));
1363d2a30d4Sbostic static const char *	getnum P((const char * strp, int * nump, int min,
1373d2a30d4Sbostic 				int max));
1383d2a30d4Sbostic static const char *	getsecs P((const char * strp, long * secsp));
1393d2a30d4Sbostic static const char *	getoffset P((const char * strp, long * offsetp));
1403d2a30d4Sbostic static const char *	getrule P((const char * strp, struct rule * rulep));
1413d2a30d4Sbostic static void		gmtload P((struct state * sp));
1423d2a30d4Sbostic static void		gmtsub P((const time_t * timep, long offset,
1433d2a30d4Sbostic 				struct tm * tmp));
1443d2a30d4Sbostic static void		localsub P((const time_t * timep, long offset,
1453d2a30d4Sbostic 				struct tm * tmp));
1463d2a30d4Sbostic static void		normalize P((int * tensptr, int * unitsptr, int base));
1473d2a30d4Sbostic static void		settzname P((void));
1483d2a30d4Sbostic static time_t		time1 P((struct tm * tmp, void (* funcp)(),
1493d2a30d4Sbostic 				long offset));
1503d2a30d4Sbostic static time_t		time2 P((struct tm *tmp, void (* funcp)(),
1513d2a30d4Sbostic 				long offset, int * okayp));
1523d2a30d4Sbostic static void		timesub P((const time_t * timep, long offset,
1533d2a30d4Sbostic 				const struct state * sp, struct tm * tmp));
1543d2a30d4Sbostic static int		tmcomp P((const struct tm * atmp,
1553d2a30d4Sbostic 				const struct tm * btmp));
1563d2a30d4Sbostic static time_t		transtime P((time_t janfirst, int year,
1573d2a30d4Sbostic 				const struct rule * rulep, long offset));
1583d2a30d4Sbostic static int		tzload P((const char * name, struct state * sp));
1593d2a30d4Sbostic static int		tzparse P((const char * name, struct state * sp,
1603d2a30d4Sbostic 				int lastditch));
1613d2a30d4Sbostic 
1623d2a30d4Sbostic #ifdef ALL_STATE
1633d2a30d4Sbostic static struct state *	lclptr;
1643d2a30d4Sbostic static struct state *	gmtptr;
1653d2a30d4Sbostic #endif /* defined ALL_STATE */
1663d2a30d4Sbostic 
1673d2a30d4Sbostic #ifndef ALL_STATE
1683d2a30d4Sbostic static struct state	lclmem;
1693d2a30d4Sbostic static struct state	gmtmem;
1703d2a30d4Sbostic #define lclptr		(&lclmem)
1713d2a30d4Sbostic #define gmtptr		(&gmtmem)
1723d2a30d4Sbostic #endif /* State Farm */
1733d2a30d4Sbostic 
1743d2a30d4Sbostic static int		lcl_is_set;
1753d2a30d4Sbostic static int		gmt_is_set;
176839b9482Sbostic 
177839b9482Sbostic char *			tzname[2] = {
1783d2a30d4Sbostic 	WILDABBR,
1793d2a30d4Sbostic 	WILDABBR
180839b9482Sbostic };
181839b9482Sbostic 
182839b9482Sbostic #ifdef USG_COMPAT
183839b9482Sbostic time_t			timezone = 0;
184839b9482Sbostic int			daylight = 0;
1853d2a30d4Sbostic #endif /* defined USG_COMPAT */
1863d2a30d4Sbostic 
1873d2a30d4Sbostic #ifdef ALTZONE
1883d2a30d4Sbostic time_t			altzone = 0;
1893d2a30d4Sbostic #endif /* defined ALTZONE */
190839b9482Sbostic 
191839b9482Sbostic static long
detzcode(codep)192839b9482Sbostic detzcode(codep)
1933d2a30d4Sbostic const char * const	codep;
19442802380Swnj {
195839b9482Sbostic 	register long	result;
196839b9482Sbostic 	register int	i;
197839b9482Sbostic 
198839b9482Sbostic 	result = 0;
199839b9482Sbostic 	for (i = 0; i < 4; ++i)
200839b9482Sbostic 		result = (result << 8) | (codep[i] & 0xff);
201839b9482Sbostic 	return result;
20242802380Swnj }
20342802380Swnj 
2043d2a30d4Sbostic static void
settzname()2053d2a30d4Sbostic settzname()
20642802380Swnj {
2073d2a30d4Sbostic 	register const struct state * const	sp = lclptr;
2083d2a30d4Sbostic 	register int				i;
2093d2a30d4Sbostic 
2103d2a30d4Sbostic 	tzname[0] = WILDABBR;
2113d2a30d4Sbostic 	tzname[1] = WILDABBR;
2123d2a30d4Sbostic #ifdef USG_COMPAT
2133d2a30d4Sbostic 	daylight = 0;
2143d2a30d4Sbostic 	timezone = 0;
2153d2a30d4Sbostic #endif /* defined USG_COMPAT */
2163d2a30d4Sbostic #ifdef ALTZONE
2173d2a30d4Sbostic 	altzone = 0;
2183d2a30d4Sbostic #endif /* defined ALTZONE */
2193d2a30d4Sbostic #ifdef ALL_STATE
2203d2a30d4Sbostic 	if (sp == NULL) {
2213d2a30d4Sbostic 		tzname[0] = tzname[1] = GMT;
2223d2a30d4Sbostic 		return;
2233d2a30d4Sbostic 	}
2243d2a30d4Sbostic #endif /* defined ALL_STATE */
2253d2a30d4Sbostic 	for (i = 0; i < sp->typecnt; ++i) {
2263d2a30d4Sbostic 		register const struct ttinfo * const	ttisp = &sp->ttis[i];
2273d2a30d4Sbostic 
2283d2a30d4Sbostic 		tzname[ttisp->tt_isdst] =
2293d2a30d4Sbostic 			(char *) &sp->chars[ttisp->tt_abbrind];
2303d2a30d4Sbostic #ifdef USG_COMPAT
2313d2a30d4Sbostic 		if (ttisp->tt_isdst)
2323d2a30d4Sbostic 			daylight = 1;
2333d2a30d4Sbostic 		if (i == 0 || !ttisp->tt_isdst)
2343d2a30d4Sbostic 			timezone = -(ttisp->tt_gmtoff);
2353d2a30d4Sbostic #endif /* defined USG_COMPAT */
2363d2a30d4Sbostic #ifdef ALTZONE
2373d2a30d4Sbostic 		if (i == 0 || ttisp->tt_isdst)
2383d2a30d4Sbostic 			altzone = -(ttisp->tt_gmtoff);
2393d2a30d4Sbostic #endif /* defined ALTZONE */
2403d2a30d4Sbostic 	}
2413d2a30d4Sbostic 	/*
2423d2a30d4Sbostic 	** And to get the latest zone names into tzname. . .
2433d2a30d4Sbostic 	*/
2443d2a30d4Sbostic 	for (i = 0; i < sp->timecnt; ++i) {
2453d2a30d4Sbostic 		register const struct ttinfo * const	ttisp =
2463d2a30d4Sbostic 							&sp->ttis[sp->types[i]];
2473d2a30d4Sbostic 
2483d2a30d4Sbostic 		tzname[ttisp->tt_isdst] =
2493d2a30d4Sbostic 			(char *) &sp->chars[ttisp->tt_abbrind];
2503d2a30d4Sbostic 	}
2513d2a30d4Sbostic }
2523d2a30d4Sbostic 
2533d2a30d4Sbostic static int
tzload(name,sp)2543d2a30d4Sbostic tzload(name, sp)
2553d2a30d4Sbostic register const char *		name;
2563d2a30d4Sbostic register struct state * const	sp;
2573d2a30d4Sbostic {
2583d2a30d4Sbostic 	register const char *	p;
259839b9482Sbostic 	register int		i;
260839b9482Sbostic 	register int		fid;
261839b9482Sbostic 
2623d2a30d4Sbostic 	if (name == NULL && (name = TZDEFAULT) == NULL)
263839b9482Sbostic 		return -1;
264839b9482Sbostic 	{
2653d2a30d4Sbostic 		char		fullname[FILENAME_MAX + 1];
266839b9482Sbostic 
2673d2a30d4Sbostic 		if (name[0] == ':')
2683d2a30d4Sbostic 			++name;
2697e1c7d44Sbostic 		if (name[0] != '/') {
2703d2a30d4Sbostic 			if ((p = TZDIR) == NULL)
271839b9482Sbostic 				return -1;
272839b9482Sbostic 			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
273839b9482Sbostic 				return -1;
274839b9482Sbostic 			(void) strcpy(fullname, p);
275839b9482Sbostic 			(void) strcat(fullname, "/");
276839b9482Sbostic 			(void) strcat(fullname, name);
277839b9482Sbostic 			name = fullname;
278839b9482Sbostic 		}
2793d2a30d4Sbostic 		if ((fid = open(name, OPEN_MODE)) == -1)
280839b9482Sbostic 			return -1;
281839b9482Sbostic 	}
282839b9482Sbostic 	{
2833d2a30d4Sbostic 		register const struct tzhead *	tzhp;
2843d2a30d4Sbostic 		char				buf[sizeof *sp + sizeof *tzhp];
2853d2a30d4Sbostic 		int				ttisstdcnt;
286839b9482Sbostic 
287839b9482Sbostic 		i = read(fid, buf, sizeof buf);
288839b9482Sbostic 		if (close(fid) != 0 || i < sizeof *tzhp)
289839b9482Sbostic 			return -1;
290839b9482Sbostic 		tzhp = (struct tzhead *) buf;
2913d2a30d4Sbostic 		ttisstdcnt = (int) detzcode(tzhp->tzh_ttisstdcnt);
2923d2a30d4Sbostic 		sp->leapcnt = (int) detzcode(tzhp->tzh_leapcnt);
2933d2a30d4Sbostic 		sp->timecnt = (int) detzcode(tzhp->tzh_timecnt);
2943d2a30d4Sbostic 		sp->typecnt = (int) detzcode(tzhp->tzh_typecnt);
2953d2a30d4Sbostic 		sp->charcnt = (int) detzcode(tzhp->tzh_charcnt);
2963d2a30d4Sbostic 		if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
2973d2a30d4Sbostic 			sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
2983d2a30d4Sbostic 			sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
2993d2a30d4Sbostic 			sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
3003d2a30d4Sbostic 			(ttisstdcnt != sp->typecnt && ttisstdcnt != 0))
301839b9482Sbostic 				return -1;
302839b9482Sbostic 		if (i < sizeof *tzhp +
3033d2a30d4Sbostic 			sp->timecnt * (4 + sizeof (char)) +
3043d2a30d4Sbostic 			sp->typecnt * (4 + 2 * sizeof (char)) +
3053d2a30d4Sbostic 			sp->charcnt * sizeof (char) +
3063d2a30d4Sbostic 			sp->leapcnt * 2 * 4 +
3073d2a30d4Sbostic 			ttisstdcnt * sizeof (char))
308839b9482Sbostic 				return -1;
309839b9482Sbostic 		p = buf + sizeof *tzhp;
3103d2a30d4Sbostic 		for (i = 0; i < sp->timecnt; ++i) {
3113d2a30d4Sbostic 			sp->ats[i] = detzcode(p);
312839b9482Sbostic 			p += 4;
313839b9482Sbostic 		}
3143d2a30d4Sbostic 		for (i = 0; i < sp->timecnt; ++i) {
3153d2a30d4Sbostic 			sp->types[i] = (unsigned char) *p++;
3163d2a30d4Sbostic 			if (sp->types[i] >= sp->typecnt)
3173d2a30d4Sbostic 				return -1;
3183d2a30d4Sbostic 		}
3193d2a30d4Sbostic 		for (i = 0; i < sp->typecnt; ++i) {
320839b9482Sbostic 			register struct ttinfo *	ttisp;
321839b9482Sbostic 
3223d2a30d4Sbostic 			ttisp = &sp->ttis[i];
323839b9482Sbostic 			ttisp->tt_gmtoff = detzcode(p);
324839b9482Sbostic 			p += 4;
325839b9482Sbostic 			ttisp->tt_isdst = (unsigned char) *p++;
3263d2a30d4Sbostic 			if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
3273d2a30d4Sbostic 				return -1;
328839b9482Sbostic 			ttisp->tt_abbrind = (unsigned char) *p++;
3293d2a30d4Sbostic 			if (ttisp->tt_abbrind < 0 ||
3303d2a30d4Sbostic 				ttisp->tt_abbrind > sp->charcnt)
331839b9482Sbostic 					return -1;
3323d2a30d4Sbostic 		}
3333d2a30d4Sbostic 		for (i = 0; i < sp->charcnt; ++i)
3343d2a30d4Sbostic 			sp->chars[i] = *p++;
3353d2a30d4Sbostic 		sp->chars[i] = '\0';	/* ensure '\0' at end */
3363d2a30d4Sbostic 		for (i = 0; i < sp->leapcnt; ++i) {
3373d2a30d4Sbostic 			register struct lsinfo *	lsisp;
3383d2a30d4Sbostic 
3393d2a30d4Sbostic 			lsisp = &sp->lsis[i];
3403d2a30d4Sbostic 			lsisp->ls_trans = detzcode(p);
3413d2a30d4Sbostic 			p += 4;
3423d2a30d4Sbostic 			lsisp->ls_corr = detzcode(p);
3433d2a30d4Sbostic 			p += 4;
3443d2a30d4Sbostic 		}
3453d2a30d4Sbostic 		for (i = 0; i < sp->typecnt; ++i) {
346839b9482Sbostic 			register struct ttinfo *	ttisp;
347839b9482Sbostic 
3483d2a30d4Sbostic 			ttisp = &sp->ttis[i];
3493d2a30d4Sbostic 			if (ttisstdcnt == 0)
3503d2a30d4Sbostic 				ttisp->tt_ttisstd = FALSE;
3513d2a30d4Sbostic 			else {
3523d2a30d4Sbostic 				ttisp->tt_ttisstd = *p++;
3533d2a30d4Sbostic 				if (ttisp->tt_ttisstd != TRUE &&
3543d2a30d4Sbostic 					ttisp->tt_ttisstd != FALSE)
35553c215e7Sbostic 						return -1;
3563d2a30d4Sbostic 			}
3573d2a30d4Sbostic 		}
3583d2a30d4Sbostic 	}
35953c215e7Sbostic 	return 0;
36053c215e7Sbostic }
36153c215e7Sbostic 
3623d2a30d4Sbostic static const int	mon_lengths[2][MONSPERYEAR] = {
3633d2a30d4Sbostic 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
3643d2a30d4Sbostic 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
3653d2a30d4Sbostic };
3663d2a30d4Sbostic 
3673d2a30d4Sbostic static const int	year_lengths[2] = {
3683d2a30d4Sbostic 	DAYSPERNYEAR, DAYSPERLYEAR
3693d2a30d4Sbostic };
3703d2a30d4Sbostic 
3713d2a30d4Sbostic /*
3723d2a30d4Sbostic ** Given a pointer into a time zone string, scan until a character that is not
3733d2a30d4Sbostic ** a valid character in a zone name is found.  Return a pointer to that
3743d2a30d4Sbostic ** character.
3753d2a30d4Sbostic */
3763d2a30d4Sbostic 
3773d2a30d4Sbostic static const char *
getzname(strp)3783d2a30d4Sbostic getzname(strp)
3793d2a30d4Sbostic register const char *	strp;
380839b9482Sbostic {
3813d2a30d4Sbostic 	register char	c;
3823d2a30d4Sbostic 
3833d2a30d4Sbostic 	while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' &&
3843d2a30d4Sbostic 		c != '+')
3853d2a30d4Sbostic 			++strp;
3863d2a30d4Sbostic 	return strp;
3873d2a30d4Sbostic }
3883d2a30d4Sbostic 
3893d2a30d4Sbostic /*
3903d2a30d4Sbostic ** Given a pointer into a time zone string, extract a number from that string.
3913d2a30d4Sbostic ** Check that the number is within a specified range; if it is not, return
3923d2a30d4Sbostic ** NULL.
3933d2a30d4Sbostic ** Otherwise, return a pointer to the first character not part of the number.
3943d2a30d4Sbostic */
3953d2a30d4Sbostic 
3963d2a30d4Sbostic static const char *
getnum(strp,nump,min,max)3973d2a30d4Sbostic getnum(strp, nump, min, max)
3983d2a30d4Sbostic register const char *	strp;
3993d2a30d4Sbostic int * const		nump;
4003d2a30d4Sbostic const int		min;
4013d2a30d4Sbostic const int		max;
4023d2a30d4Sbostic {
4033d2a30d4Sbostic 	register char	c;
4043d2a30d4Sbostic 	register int	num;
4053d2a30d4Sbostic 
4063d2a30d4Sbostic 	if (strp == NULL || !isdigit(*strp))
4073d2a30d4Sbostic 		return NULL;
4083d2a30d4Sbostic 	num = 0;
4093d2a30d4Sbostic 	while ((c = *strp) != '\0' && isdigit(c)) {
4103d2a30d4Sbostic 		num = num * 10 + (c - '0');
4113d2a30d4Sbostic 		if (num > max)
4123d2a30d4Sbostic 			return NULL;	/* illegal value */
4133d2a30d4Sbostic 		++strp;
4143d2a30d4Sbostic 	}
4153d2a30d4Sbostic 	if (num < min)
4163d2a30d4Sbostic 		return NULL;		/* illegal value */
4173d2a30d4Sbostic 	*nump = num;
4183d2a30d4Sbostic 	return strp;
4193d2a30d4Sbostic }
4203d2a30d4Sbostic 
4213d2a30d4Sbostic /*
4223d2a30d4Sbostic ** Given a pointer into a time zone string, extract a number of seconds,
4233d2a30d4Sbostic ** in hh[:mm[:ss]] form, from the string.
4243d2a30d4Sbostic ** If any error occurs, return NULL.
4253d2a30d4Sbostic ** Otherwise, return a pointer to the first character not part of the number
4263d2a30d4Sbostic ** of seconds.
4273d2a30d4Sbostic */
4283d2a30d4Sbostic 
4293d2a30d4Sbostic static const char *
getsecs(strp,secsp)4303d2a30d4Sbostic getsecs(strp, secsp)
4313d2a30d4Sbostic register const char *	strp;
4323d2a30d4Sbostic long * const		secsp;
4333d2a30d4Sbostic {
4343d2a30d4Sbostic 	int	num;
4353d2a30d4Sbostic 
4363d2a30d4Sbostic 	strp = getnum(strp, &num, 0, HOURSPERDAY);
4373d2a30d4Sbostic 	if (strp == NULL)
4383d2a30d4Sbostic 		return NULL;
4393d2a30d4Sbostic 	*secsp = num * SECSPERHOUR;
4403d2a30d4Sbostic 	if (*strp == ':') {
4413d2a30d4Sbostic 		++strp;
4423d2a30d4Sbostic 		strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
4433d2a30d4Sbostic 		if (strp == NULL)
4443d2a30d4Sbostic 			return NULL;
4453d2a30d4Sbostic 		*secsp += num * SECSPERMIN;
4463d2a30d4Sbostic 		if (*strp == ':') {
4473d2a30d4Sbostic 			++strp;
4483d2a30d4Sbostic 			strp = getnum(strp, &num, 0, SECSPERMIN - 1);
4493d2a30d4Sbostic 			if (strp == NULL)
4503d2a30d4Sbostic 				return NULL;
4513d2a30d4Sbostic 			*secsp += num;
4523d2a30d4Sbostic 		}
4533d2a30d4Sbostic 	}
4543d2a30d4Sbostic 	return strp;
4553d2a30d4Sbostic }
4563d2a30d4Sbostic 
4573d2a30d4Sbostic /*
4583d2a30d4Sbostic ** Given a pointer into a time zone string, extract an offset, in
4593d2a30d4Sbostic ** [+-]hh[:mm[:ss]] form, from the string.
4603d2a30d4Sbostic ** If any error occurs, return NULL.
4613d2a30d4Sbostic ** Otherwise, return a pointer to the first character not part of the time.
4623d2a30d4Sbostic */
4633d2a30d4Sbostic 
4643d2a30d4Sbostic static const char *
getoffset(strp,offsetp)4653d2a30d4Sbostic getoffset(strp, offsetp)
4663d2a30d4Sbostic register const char *	strp;
4673d2a30d4Sbostic long * const		offsetp;
4683d2a30d4Sbostic {
4693d2a30d4Sbostic 	register int	neg;
4703d2a30d4Sbostic 
4713d2a30d4Sbostic 	if (*strp == '-') {
4723d2a30d4Sbostic 		neg = 1;
4733d2a30d4Sbostic 		++strp;
4743d2a30d4Sbostic 	} else if (isdigit(*strp) || *strp++ == '+')
4753d2a30d4Sbostic 		neg = 0;
4763d2a30d4Sbostic 	else	return NULL;		/* illegal offset */
4773d2a30d4Sbostic 	strp = getsecs(strp, offsetp);
4783d2a30d4Sbostic 	if (strp == NULL)
4793d2a30d4Sbostic 		return NULL;		/* illegal time */
4803d2a30d4Sbostic 	if (neg)
4813d2a30d4Sbostic 		*offsetp = -*offsetp;
4823d2a30d4Sbostic 	return strp;
4833d2a30d4Sbostic }
4843d2a30d4Sbostic 
4853d2a30d4Sbostic /*
4863d2a30d4Sbostic ** Given a pointer into a time zone string, extract a rule in the form
4873d2a30d4Sbostic ** date[/time].  See POSIX section 8 for the format of "date" and "time".
4883d2a30d4Sbostic ** If a valid rule is not found, return NULL.
4893d2a30d4Sbostic ** Otherwise, return a pointer to the first character not part of the rule.
4903d2a30d4Sbostic */
4913d2a30d4Sbostic 
4923d2a30d4Sbostic static const char *
getrule(strp,rulep)4933d2a30d4Sbostic getrule(strp, rulep)
4943d2a30d4Sbostic const char *			strp;
4953d2a30d4Sbostic register struct rule * const	rulep;
4963d2a30d4Sbostic {
4973d2a30d4Sbostic 	if (*strp == 'J') {
4983d2a30d4Sbostic 		/*
4993d2a30d4Sbostic 		** Julian day.
5003d2a30d4Sbostic 		*/
5013d2a30d4Sbostic 		rulep->r_type = JULIAN_DAY;
5023d2a30d4Sbostic 		++strp;
5033d2a30d4Sbostic 		strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
5043d2a30d4Sbostic 	} else if (*strp == 'M') {
5053d2a30d4Sbostic 		/*
5063d2a30d4Sbostic 		** Month, week, day.
5073d2a30d4Sbostic 		*/
5083d2a30d4Sbostic 		rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
5093d2a30d4Sbostic 		++strp;
5103d2a30d4Sbostic 		strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
5113d2a30d4Sbostic 		if (strp == NULL)
5123d2a30d4Sbostic 			return NULL;
5133d2a30d4Sbostic 		if (*strp++ != '.')
5143d2a30d4Sbostic 			return NULL;
5153d2a30d4Sbostic 		strp = getnum(strp, &rulep->r_week, 1, 5);
5163d2a30d4Sbostic 		if (strp == NULL)
5173d2a30d4Sbostic 			return NULL;
5183d2a30d4Sbostic 		if (*strp++ != '.')
5193d2a30d4Sbostic 			return NULL;
5203d2a30d4Sbostic 		strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
5213d2a30d4Sbostic 	} else if (isdigit(*strp)) {
5223d2a30d4Sbostic 		/*
5233d2a30d4Sbostic 		** Day of year.
5243d2a30d4Sbostic 		*/
5253d2a30d4Sbostic 		rulep->r_type = DAY_OF_YEAR;
5263d2a30d4Sbostic 		strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
5273d2a30d4Sbostic 	} else	return NULL;		/* invalid format */
5283d2a30d4Sbostic 	if (strp == NULL)
5293d2a30d4Sbostic 		return NULL;
5303d2a30d4Sbostic 	if (*strp == '/') {
5313d2a30d4Sbostic 		/*
5323d2a30d4Sbostic 		** Time specified.
5333d2a30d4Sbostic 		*/
5343d2a30d4Sbostic 		++strp;
5353d2a30d4Sbostic 		strp = getsecs(strp, &rulep->r_time);
5363d2a30d4Sbostic 	} else	rulep->r_time = 2 * SECSPERHOUR;	/* default = 2:00:00 */
5373d2a30d4Sbostic 	return strp;
5383d2a30d4Sbostic }
5393d2a30d4Sbostic 
5403d2a30d4Sbostic /*
5413d2a30d4Sbostic ** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the
5423d2a30d4Sbostic ** year, a rule, and the offset from GMT at the time that rule takes effect,
5433d2a30d4Sbostic ** calculate the Epoch-relative time that rule takes effect.
5443d2a30d4Sbostic */
5453d2a30d4Sbostic 
5463d2a30d4Sbostic static time_t
transtime(janfirst,year,rulep,offset)5473d2a30d4Sbostic transtime(janfirst, year, rulep, offset)
5483d2a30d4Sbostic const time_t				janfirst;
5493d2a30d4Sbostic const int				year;
5503d2a30d4Sbostic register const struct rule * const	rulep;
5513d2a30d4Sbostic const long				offset;
5523d2a30d4Sbostic {
5533d2a30d4Sbostic 	register int	leapyear;
5543d2a30d4Sbostic 	register time_t	value;
5553d2a30d4Sbostic 	register int	i;
5563d2a30d4Sbostic 	int		d, m1, yy0, yy1, yy2, dow;
5573d2a30d4Sbostic 
5583d2a30d4Sbostic 	leapyear = isleap(year);
5593d2a30d4Sbostic 	switch (rulep->r_type) {
5603d2a30d4Sbostic 
5613d2a30d4Sbostic 	case JULIAN_DAY:
5623d2a30d4Sbostic 		/*
5633d2a30d4Sbostic 		** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
5643d2a30d4Sbostic 		** years.
5653d2a30d4Sbostic 		** In non-leap years, or if the day number is 59 or less, just
5663d2a30d4Sbostic 		** add SECSPERDAY times the day number-1 to the time of
5673d2a30d4Sbostic 		** January 1, midnight, to get the day.
5683d2a30d4Sbostic 		*/
5693d2a30d4Sbostic 		value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
5703d2a30d4Sbostic 		if (leapyear && rulep->r_day >= 60)
5713d2a30d4Sbostic 			value += SECSPERDAY;
5723d2a30d4Sbostic 		break;
5733d2a30d4Sbostic 
5743d2a30d4Sbostic 	case DAY_OF_YEAR:
5753d2a30d4Sbostic 		/*
5763d2a30d4Sbostic 		** n - day of year.
5773d2a30d4Sbostic 		** Just add SECSPERDAY times the day number to the time of
5783d2a30d4Sbostic 		** January 1, midnight, to get the day.
5793d2a30d4Sbostic 		*/
5803d2a30d4Sbostic 		value = janfirst + rulep->r_day * SECSPERDAY;
5813d2a30d4Sbostic 		break;
5823d2a30d4Sbostic 
5833d2a30d4Sbostic 	case MONTH_NTH_DAY_OF_WEEK:
5843d2a30d4Sbostic 		/*
5853d2a30d4Sbostic 		** Mm.n.d - nth "dth day" of month m.
5863d2a30d4Sbostic 		*/
5873d2a30d4Sbostic 		value = janfirst;
5883d2a30d4Sbostic 		for (i = 0; i < rulep->r_mon - 1; ++i)
5893d2a30d4Sbostic 			value += mon_lengths[leapyear][i] * SECSPERDAY;
5903d2a30d4Sbostic 
5913d2a30d4Sbostic 		/*
5923d2a30d4Sbostic 		** Use Zeller's Congruence to get day-of-week of first day of
5933d2a30d4Sbostic 		** month.
5943d2a30d4Sbostic 		*/
5953d2a30d4Sbostic 		m1 = (rulep->r_mon + 9) % 12 + 1;
5963d2a30d4Sbostic 		yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
5973d2a30d4Sbostic 		yy1 = yy0 / 100;
5983d2a30d4Sbostic 		yy2 = yy0 % 100;
5993d2a30d4Sbostic 		dow = ((26 * m1 - 2) / 10 +
6003d2a30d4Sbostic 			1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
6013d2a30d4Sbostic 		if (dow < 0)
6023d2a30d4Sbostic 			dow += DAYSPERWEEK;
6033d2a30d4Sbostic 
6043d2a30d4Sbostic 		/*
6053d2a30d4Sbostic 		** "dow" is the day-of-week of the first day of the month.  Get
6063d2a30d4Sbostic 		** the day-of-month (zero-origin) of the first "dow" day of the
6073d2a30d4Sbostic 		** month.
6083d2a30d4Sbostic 		*/
6093d2a30d4Sbostic 		d = rulep->r_day - dow;
6103d2a30d4Sbostic 		if (d < 0)
6113d2a30d4Sbostic 			d += DAYSPERWEEK;
6123d2a30d4Sbostic 		for (i = 1; i < rulep->r_week; ++i) {
6133d2a30d4Sbostic 			if (d + DAYSPERWEEK >=
6143d2a30d4Sbostic 				mon_lengths[leapyear][rulep->r_mon - 1])
6153d2a30d4Sbostic 					break;
6163d2a30d4Sbostic 			d += DAYSPERWEEK;
6173d2a30d4Sbostic 		}
6183d2a30d4Sbostic 
6193d2a30d4Sbostic 		/*
6203d2a30d4Sbostic 		** "d" is the day-of-month (zero-origin) of the day we want.
6213d2a30d4Sbostic 		*/
6223d2a30d4Sbostic 		value += d * SECSPERDAY;
6233d2a30d4Sbostic 		break;
6243d2a30d4Sbostic 	}
6253d2a30d4Sbostic 
6263d2a30d4Sbostic 	/*
6273d2a30d4Sbostic 	** "value" is the Epoch-relative time of 00:00:00 GMT on the day in
6283d2a30d4Sbostic 	** question.  To get the Epoch-relative time of the specified local
6293d2a30d4Sbostic 	** time on that day, add the transition time and the current offset
6303d2a30d4Sbostic 	** from GMT.
6313d2a30d4Sbostic 	*/
6323d2a30d4Sbostic 	return value + rulep->r_time + offset;
6333d2a30d4Sbostic }
6343d2a30d4Sbostic 
6353d2a30d4Sbostic /*
6363d2a30d4Sbostic ** Given a POSIX section 8-style TZ string, fill in the rule tables as
6373d2a30d4Sbostic ** appropriate.
6383d2a30d4Sbostic */
6393d2a30d4Sbostic 
6403d2a30d4Sbostic static int
tzparse(name,sp,lastditch)6413d2a30d4Sbostic tzparse(name, sp, lastditch)
6423d2a30d4Sbostic const char *			name;
6433d2a30d4Sbostic register struct state * const	sp;
6443d2a30d4Sbostic const int			lastditch;
6453d2a30d4Sbostic {
6463d2a30d4Sbostic 	const char *			stdname;
6473d2a30d4Sbostic 	const char *			dstname;
6483d2a30d4Sbostic 	int				stdlen;
6493d2a30d4Sbostic 	int				dstlen;
6503d2a30d4Sbostic 	long				stdoffset;
6513d2a30d4Sbostic 	long				dstoffset;
6523d2a30d4Sbostic 	register time_t *		atp;
6533d2a30d4Sbostic 	register unsigned char *	typep;
6543d2a30d4Sbostic 	register char *			cp;
6553d2a30d4Sbostic 	register int			load_result;
6563d2a30d4Sbostic 
6573d2a30d4Sbostic 	stdname = name;
6583d2a30d4Sbostic 	if (lastditch) {
6593d2a30d4Sbostic 		stdlen = strlen(name);	/* length of standard zone name */
6603d2a30d4Sbostic 		name += stdlen;
6613d2a30d4Sbostic 		if (stdlen >= sizeof sp->chars)
6623d2a30d4Sbostic 			stdlen = (sizeof sp->chars) - 1;
6633d2a30d4Sbostic 	} else {
6643d2a30d4Sbostic 		name = getzname(name);
6653d2a30d4Sbostic 		stdlen = name - stdname;
6663d2a30d4Sbostic 		if (stdlen < 3)
6673d2a30d4Sbostic 			return -1;
6683d2a30d4Sbostic 	}
6693d2a30d4Sbostic 	if (*name == '\0')
670297a6844Sbostic 		return -1;
6713d2a30d4Sbostic 	else {
6723d2a30d4Sbostic 		name = getoffset(name, &stdoffset);
6733d2a30d4Sbostic 		if (name == NULL)
6743d2a30d4Sbostic 			return -1;
6753d2a30d4Sbostic 	}
6763d2a30d4Sbostic 	load_result = tzload(TZDEFRULES, sp);
6773d2a30d4Sbostic 	if (load_result != 0)
6783d2a30d4Sbostic 		sp->leapcnt = 0;		/* so, we're off a little */
6793d2a30d4Sbostic 	if (*name != '\0') {
6803d2a30d4Sbostic 		dstname = name;
6813d2a30d4Sbostic 		name = getzname(name);
6823d2a30d4Sbostic 		dstlen = name - dstname;	/* length of DST zone name */
6833d2a30d4Sbostic 		if (dstlen < 3)
6843d2a30d4Sbostic 			return -1;
6853d2a30d4Sbostic 		if (*name != '\0' && *name != ',' && *name != ';') {
6863d2a30d4Sbostic 			name = getoffset(name, &dstoffset);
6873d2a30d4Sbostic 			if (name == NULL)
6883d2a30d4Sbostic 				return -1;
6893d2a30d4Sbostic 		} else	dstoffset = stdoffset - SECSPERHOUR;
6903d2a30d4Sbostic 		if (*name == ',' || *name == ';') {
6913d2a30d4Sbostic 			struct rule	start;
6923d2a30d4Sbostic 			struct rule	end;
6933d2a30d4Sbostic 			register int	year;
6943d2a30d4Sbostic 			register time_t	janfirst;
6953d2a30d4Sbostic 			time_t		starttime;
6963d2a30d4Sbostic 			time_t		endtime;
6973d2a30d4Sbostic 
6983d2a30d4Sbostic 			++name;
6993d2a30d4Sbostic 			if ((name = getrule(name, &start)) == NULL)
7003d2a30d4Sbostic 				return -1;
7013d2a30d4Sbostic 			if (*name++ != ',')
7023d2a30d4Sbostic 				return -1;
7033d2a30d4Sbostic 			if ((name = getrule(name, &end)) == NULL)
7043d2a30d4Sbostic 				return -1;
7053d2a30d4Sbostic 			if (*name != '\0')
7063d2a30d4Sbostic 				return -1;
7073d2a30d4Sbostic 			sp->typecnt = 2;	/* standard time and DST */
7083d2a30d4Sbostic 			/*
7093d2a30d4Sbostic 			** Two transitions per year, from EPOCH_YEAR to 2037.
7103d2a30d4Sbostic 			*/
7113d2a30d4Sbostic 			sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
7123d2a30d4Sbostic 			if (sp->timecnt > TZ_MAX_TIMES)
7133d2a30d4Sbostic 				return -1;
7143d2a30d4Sbostic 			sp->ttis[0].tt_gmtoff = -dstoffset;
7153d2a30d4Sbostic 			sp->ttis[0].tt_isdst = 1;
7163d2a30d4Sbostic 			sp->ttis[0].tt_abbrind = stdlen + 1;
7173d2a30d4Sbostic 			sp->ttis[1].tt_gmtoff = -stdoffset;
7183d2a30d4Sbostic 			sp->ttis[1].tt_isdst = 0;
7193d2a30d4Sbostic 			sp->ttis[1].tt_abbrind = 0;
7203d2a30d4Sbostic 			atp = sp->ats;
7213d2a30d4Sbostic 			typep = sp->types;
7223d2a30d4Sbostic 			janfirst = 0;
7233d2a30d4Sbostic 			for (year = EPOCH_YEAR; year <= 2037; ++year) {
7243d2a30d4Sbostic 				starttime = transtime(janfirst, year, &start,
7253d2a30d4Sbostic 					stdoffset);
7263d2a30d4Sbostic 				endtime = transtime(janfirst, year, &end,
7273d2a30d4Sbostic 					dstoffset);
7283d2a30d4Sbostic 				if (starttime > endtime) {
7293d2a30d4Sbostic 					*atp++ = endtime;
7303d2a30d4Sbostic 					*typep++ = 1;	/* DST ends */
7313d2a30d4Sbostic 					*atp++ = starttime;
7323d2a30d4Sbostic 					*typep++ = 0;	/* DST begins */
7333d2a30d4Sbostic 				} else {
7343d2a30d4Sbostic 					*atp++ = starttime;
7353d2a30d4Sbostic 					*typep++ = 0;	/* DST begins */
7363d2a30d4Sbostic 					*atp++ = endtime;
7373d2a30d4Sbostic 					*typep++ = 1;	/* DST ends */
7383d2a30d4Sbostic 				}
7393d2a30d4Sbostic 				janfirst +=
7403d2a30d4Sbostic 					year_lengths[isleap(year)] * SECSPERDAY;
7413d2a30d4Sbostic 			}
7423d2a30d4Sbostic 		} else {
7433d2a30d4Sbostic 			int		sawstd;
7443d2a30d4Sbostic 			int		sawdst;
7453d2a30d4Sbostic 			long		stdfix;
7463d2a30d4Sbostic 			long		dstfix;
7473d2a30d4Sbostic 			long		oldfix;
7483d2a30d4Sbostic 			int		isdst;
7493d2a30d4Sbostic 			register int	i;
7503d2a30d4Sbostic 
7513d2a30d4Sbostic 			if (*name != '\0')
7523d2a30d4Sbostic 				return -1;
7533d2a30d4Sbostic 			if (load_result != 0)
7543d2a30d4Sbostic 				return -1;
7553d2a30d4Sbostic 			/*
7563d2a30d4Sbostic 			** Compute the difference between the real and
7573d2a30d4Sbostic 			** prototype standard and summer time offsets
7583d2a30d4Sbostic 			** from GMT, and put the real standard and summer
7593d2a30d4Sbostic 			** time offsets into the rules in place of the
7603d2a30d4Sbostic 			** prototype offsets.
7613d2a30d4Sbostic 			*/
7623d2a30d4Sbostic 			sawstd = FALSE;
7633d2a30d4Sbostic 			sawdst = FALSE;
7643d2a30d4Sbostic 			stdfix = 0;
7653d2a30d4Sbostic 			dstfix = 0;
7663d2a30d4Sbostic 			for (i = 0; i < sp->typecnt; ++i) {
7673d2a30d4Sbostic 				if (sp->ttis[i].tt_isdst) {
7683d2a30d4Sbostic 					oldfix = dstfix;
7693d2a30d4Sbostic 					dstfix =
7703d2a30d4Sbostic 					    sp->ttis[i].tt_gmtoff + dstoffset;
7713d2a30d4Sbostic 					if (sawdst && (oldfix != dstfix))
7723d2a30d4Sbostic 						return -1;
7733d2a30d4Sbostic 					sp->ttis[i].tt_gmtoff = -dstoffset;
7743d2a30d4Sbostic 					sp->ttis[i].tt_abbrind = stdlen + 1;
7753d2a30d4Sbostic 					sawdst = TRUE;
7763d2a30d4Sbostic 				} else {
7773d2a30d4Sbostic 					oldfix = stdfix;
7783d2a30d4Sbostic 					stdfix =
7793d2a30d4Sbostic 					    sp->ttis[i].tt_gmtoff + stdoffset;
7803d2a30d4Sbostic 					if (sawstd && (oldfix != stdfix))
7813d2a30d4Sbostic 						return -1;
7823d2a30d4Sbostic 					sp->ttis[i].tt_gmtoff = -stdoffset;
7833d2a30d4Sbostic 					sp->ttis[i].tt_abbrind = 0;
7843d2a30d4Sbostic 					sawstd = TRUE;
7853d2a30d4Sbostic 				}
7863d2a30d4Sbostic 			}
7873d2a30d4Sbostic 			/*
7883d2a30d4Sbostic 			** Make sure we have both standard and summer time.
7893d2a30d4Sbostic 			*/
7903d2a30d4Sbostic 			if (!sawdst || !sawstd)
7913d2a30d4Sbostic 				return -1;
7923d2a30d4Sbostic 			/*
7933d2a30d4Sbostic 			** Now correct the transition times by shifting
7943d2a30d4Sbostic 			** them by the difference between the real and
7953d2a30d4Sbostic 			** prototype offsets.  Note that this difference
7963d2a30d4Sbostic 			** can be different in standard and summer time;
7973d2a30d4Sbostic 			** the prototype probably has a 1-hour difference
7983d2a30d4Sbostic 			** between standard and summer time, but a different
7993d2a30d4Sbostic 			** difference can be specified in TZ.
8003d2a30d4Sbostic 			*/
8013d2a30d4Sbostic 			isdst = FALSE;	/* we start in standard time */
8023d2a30d4Sbostic 			for (i = 0; i < sp->timecnt; ++i) {
8033d2a30d4Sbostic 				register const struct ttinfo *	ttisp;
8043d2a30d4Sbostic 
8053d2a30d4Sbostic 				/*
8063d2a30d4Sbostic 				** If summer time is in effect, and the
8073d2a30d4Sbostic 				** transition time was not specified as
8083d2a30d4Sbostic 				** standard time, add the summer time
8093d2a30d4Sbostic 				** offset to the transition time;
8103d2a30d4Sbostic 				** otherwise, add the standard time offset
8113d2a30d4Sbostic 				** to the transition time.
8123d2a30d4Sbostic 				*/
8133d2a30d4Sbostic 				ttisp = &sp->ttis[sp->types[i]];
8143d2a30d4Sbostic 				sp->ats[i] +=
8153d2a30d4Sbostic 					(isdst && !ttisp->tt_ttisstd) ?
8163d2a30d4Sbostic 						dstfix : stdfix;
8173d2a30d4Sbostic 				isdst = ttisp->tt_isdst;
8183d2a30d4Sbostic 			}
8193d2a30d4Sbostic 		}
8203d2a30d4Sbostic 	} else {
8213d2a30d4Sbostic 		dstlen = 0;
8223d2a30d4Sbostic 		sp->typecnt = 1;		/* only standard time */
8233d2a30d4Sbostic 		sp->timecnt = 0;
8243d2a30d4Sbostic 		sp->ttis[0].tt_gmtoff = -stdoffset;
8253d2a30d4Sbostic 		sp->ttis[0].tt_isdst = 0;
8263d2a30d4Sbostic 		sp->ttis[0].tt_abbrind = 0;
8273d2a30d4Sbostic 	}
8283d2a30d4Sbostic 	sp->charcnt = stdlen + 1;
8293d2a30d4Sbostic 	if (dstlen != 0)
8303d2a30d4Sbostic 		sp->charcnt += dstlen + 1;
8313d2a30d4Sbostic 	if (sp->charcnt > sizeof sp->chars)
8323d2a30d4Sbostic 		return -1;
8333d2a30d4Sbostic 	cp = sp->chars;
8343d2a30d4Sbostic 	(void) strncpy(cp, stdname, stdlen);
8353d2a30d4Sbostic 	cp += stdlen;
8363d2a30d4Sbostic 	*cp++ = '\0';
8373d2a30d4Sbostic 	if (dstlen != 0) {
8383d2a30d4Sbostic 		(void) strncpy(cp, dstname, dstlen);
8393d2a30d4Sbostic 		*(cp + dstlen) = '\0';
8403d2a30d4Sbostic 	}
8413d2a30d4Sbostic 	return 0;
8423d2a30d4Sbostic }
8433d2a30d4Sbostic 
8443d2a30d4Sbostic static void
gmtload(sp)8453d2a30d4Sbostic gmtload(sp)
8463d2a30d4Sbostic struct state * const	sp;
8473d2a30d4Sbostic {
8483d2a30d4Sbostic 	if (tzload(GMT, sp) != 0)
8493d2a30d4Sbostic 		(void) tzparse(GMT, sp, TRUE);
850839b9482Sbostic }
851839b9482Sbostic 
852839b9482Sbostic void
tzset()853839b9482Sbostic tzset()
854839b9482Sbostic {
8553d2a30d4Sbostic 	register const char *	name;
8563d2a30d4Sbostic 	void tzsetwall();
857839b9482Sbostic 
858839b9482Sbostic 	name = getenv("TZ");
8593d2a30d4Sbostic 	if (name == NULL) {
8603d2a30d4Sbostic 		tzsetwall();
86153c215e7Sbostic 		return;
86253c215e7Sbostic 	}
8633d2a30d4Sbostic 	lcl_is_set = TRUE;
8643d2a30d4Sbostic #ifdef ALL_STATE
8653d2a30d4Sbostic 	if (lclptr == NULL) {
8663d2a30d4Sbostic 		lclptr = (struct state *) malloc(sizeof *lclptr);
8673d2a30d4Sbostic 		if (lclptr == NULL) {
8683d2a30d4Sbostic 			settzname();	/* all we can do */
8693d2a30d4Sbostic 			return;
8703d2a30d4Sbostic 		}
8713d2a30d4Sbostic 	}
8723d2a30d4Sbostic #endif /* defined ALL_STATE */
8733d2a30d4Sbostic 	if (*name == '\0') {
8743d2a30d4Sbostic 		/*
8753d2a30d4Sbostic 		** User wants it fast rather than right.
8763d2a30d4Sbostic 		*/
8773d2a30d4Sbostic 		lclptr->leapcnt = 0;		/* so, we're off a little */
8783d2a30d4Sbostic 		lclptr->timecnt = 0;
8793d2a30d4Sbostic 		lclptr->ttis[0].tt_gmtoff = 0;
8803d2a30d4Sbostic 		lclptr->ttis[0].tt_abbrind = 0;
8813d2a30d4Sbostic 		(void) strcpy(lclptr->chars, GMT);
8823d2a30d4Sbostic 	} else if (tzload(name, lclptr) != 0)
8833d2a30d4Sbostic 		if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
884297a6844Sbostic 			(void) gmtload(lclptr);
8853d2a30d4Sbostic 	settzname();
886839b9482Sbostic }
887839b9482Sbostic 
8883d2a30d4Sbostic void
tzsetwall()8893d2a30d4Sbostic tzsetwall()
890839b9482Sbostic {
8913d2a30d4Sbostic 	lcl_is_set = TRUE;
8923d2a30d4Sbostic #ifdef ALL_STATE
8933d2a30d4Sbostic 	if (lclptr == NULL) {
8943d2a30d4Sbostic 		lclptr = (struct state *) malloc(sizeof *lclptr);
8953d2a30d4Sbostic 		if (lclptr == NULL) {
8963d2a30d4Sbostic 			settzname();	/* all we can do */
8973d2a30d4Sbostic 			return;
8983d2a30d4Sbostic 		}
8993d2a30d4Sbostic 	}
9003d2a30d4Sbostic #endif /* defined ALL_STATE */
9013d2a30d4Sbostic 	if (tzload((char *) NULL, lclptr) != 0)
9023d2a30d4Sbostic 		gmtload(lclptr);
9033d2a30d4Sbostic 	settzname();
9043d2a30d4Sbostic }
905839b9482Sbostic 
9063d2a30d4Sbostic /*
9073d2a30d4Sbostic ** The easy way to behave "as if no library function calls" localtime
9083d2a30d4Sbostic ** is to not call it--so we drop its guts into "localsub", which can be
9093d2a30d4Sbostic ** freely called.  (And no, the PANS doesn't require the above behavior--
9103d2a30d4Sbostic ** but it *is* desirable.)
9113d2a30d4Sbostic **
9123d2a30d4Sbostic ** The unused offset argument is for the benefit of mktime variants.
9133d2a30d4Sbostic */
9143d2a30d4Sbostic 
9153d2a30d4Sbostic /*ARGSUSED*/
9163d2a30d4Sbostic static void
localsub(timep,offset,tmp)9173d2a30d4Sbostic localsub(timep, offset, tmp)
9183d2a30d4Sbostic const time_t * const	timep;
9193d2a30d4Sbostic const long		offset;
9203d2a30d4Sbostic struct tm * const	tmp;
9213d2a30d4Sbostic {
922ae227ee3Sdonn 	register struct state *	sp;
9233d2a30d4Sbostic 	register const struct ttinfo *	ttisp;
9243d2a30d4Sbostic 	register int			i;
9253d2a30d4Sbostic 	const time_t			t = *timep;
9263d2a30d4Sbostic 
9273d2a30d4Sbostic 	if (!lcl_is_set)
9283d2a30d4Sbostic 		tzset();
9293d2a30d4Sbostic 	sp = lclptr;
9303d2a30d4Sbostic #ifdef ALL_STATE
9313d2a30d4Sbostic 	if (sp == NULL) {
9323d2a30d4Sbostic 		gmtsub(timep, offset, tmp);
9333d2a30d4Sbostic 		return;
9343d2a30d4Sbostic 	}
9353d2a30d4Sbostic #endif /* defined ALL_STATE */
9363d2a30d4Sbostic 	if (sp->timecnt == 0 || t < sp->ats[0]) {
937839b9482Sbostic 		i = 0;
9383d2a30d4Sbostic 		while (sp->ttis[i].tt_isdst)
9393d2a30d4Sbostic 			if (++i >= sp->typecnt) {
940839b9482Sbostic 				i = 0;
941839b9482Sbostic 				break;
942839b9482Sbostic 			}
943839b9482Sbostic 	} else {
9443d2a30d4Sbostic 		for (i = 1; i < sp->timecnt; ++i)
9453d2a30d4Sbostic 			if (t < sp->ats[i])
946839b9482Sbostic 				break;
9473d2a30d4Sbostic 		i = sp->types[i - 1];
948839b9482Sbostic 	}
9493d2a30d4Sbostic 	ttisp = &sp->ttis[i];
950839b9482Sbostic 	/*
951839b9482Sbostic 	** To get (wrong) behavior that's compatible with System V Release 2.0
952839b9482Sbostic 	** you'd replace the statement below with
9533d2a30d4Sbostic 	**	t += ttisp->tt_gmtoff;
9543d2a30d4Sbostic 	**	timesub(&t, 0L, sp, tmp);
955839b9482Sbostic 	*/
9563d2a30d4Sbostic 	timesub(&t, ttisp->tt_gmtoff, sp, tmp);
957839b9482Sbostic 	tmp->tm_isdst = ttisp->tt_isdst;
9583d2a30d4Sbostic 	tzname[tmp->tm_isdst] = (char *) &sp->chars[ttisp->tt_abbrind];
9593d2a30d4Sbostic 	tmp->tm_zone = &sp->chars[ttisp->tt_abbrind];
960839b9482Sbostic }
961839b9482Sbostic 
962839b9482Sbostic struct tm *
localtime(timep)9633d2a30d4Sbostic localtime(timep)
9643d2a30d4Sbostic const time_t * const	timep;
965839b9482Sbostic {
9663d2a30d4Sbostic 	static struct tm	tm;
967839b9482Sbostic 
9683d2a30d4Sbostic 	localsub(timep, 0L, &tm);
9693d2a30d4Sbostic 	return &tm;
970839b9482Sbostic }
971839b9482Sbostic 
9723d2a30d4Sbostic /*
9733d2a30d4Sbostic ** gmtsub is to gmtime as localsub is to localtime.
9743d2a30d4Sbostic */
975839b9482Sbostic 
9763d2a30d4Sbostic static void
gmtsub(timep,offset,tmp)9773d2a30d4Sbostic gmtsub(timep, offset, tmp)
9783d2a30d4Sbostic const time_t * const	timep;
9793d2a30d4Sbostic const long		offset;
9803d2a30d4Sbostic struct tm * const	tmp;
9813d2a30d4Sbostic {
9823d2a30d4Sbostic 	if (!gmt_is_set) {
9833d2a30d4Sbostic 		gmt_is_set = TRUE;
9843d2a30d4Sbostic #ifdef ALL_STATE
9853d2a30d4Sbostic 		gmtptr = (struct state *) malloc(sizeof *gmtptr);
9863d2a30d4Sbostic 		if (gmtptr != NULL)
9873d2a30d4Sbostic #endif /* defined ALL_STATE */
9883d2a30d4Sbostic 			gmtload(gmtptr);
9893d2a30d4Sbostic 	}
9903d2a30d4Sbostic 	timesub(timep, offset, gmtptr, tmp);
9913d2a30d4Sbostic 	/*
9923d2a30d4Sbostic 	** Could get fancy here and deliver something such as
9933d2a30d4Sbostic 	** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero,
9943d2a30d4Sbostic 	** but this is no time for a treasure hunt.
9953d2a30d4Sbostic 	*/
9963d2a30d4Sbostic 	if (offset != 0)
9973d2a30d4Sbostic 		tmp->tm_zone = WILDABBR;
9983d2a30d4Sbostic 	else {
9993d2a30d4Sbostic #ifdef ALL_STATE
10003d2a30d4Sbostic 		if (gmtptr == NULL)
10013d2a30d4Sbostic 			tmp->TM_ZONE = GMT;
10023d2a30d4Sbostic 		else	tmp->TM_ZONE = gmtptr->chars;
10033d2a30d4Sbostic #endif /* defined ALL_STATE */
10043d2a30d4Sbostic #ifndef ALL_STATE
10053d2a30d4Sbostic 		tmp->tm_zone = gmtptr->chars;
10063d2a30d4Sbostic #endif /* State Farm */
10073d2a30d4Sbostic 	}
10083d2a30d4Sbostic }
1009839b9482Sbostic 
1010839b9482Sbostic struct tm *
gmtime(timep)10113d2a30d4Sbostic gmtime(timep)
10123d2a30d4Sbostic const time_t * const	timep;
1013839b9482Sbostic {
10143d2a30d4Sbostic 	static struct tm	tm;
10153d2a30d4Sbostic 
10163d2a30d4Sbostic 	gmtsub(timep, 0L, &tm);
10173d2a30d4Sbostic 	return &tm;
10183d2a30d4Sbostic }
10193d2a30d4Sbostic 
10203d2a30d4Sbostic static void
timesub(timep,offset,sp,tmp)10213d2a30d4Sbostic timesub(timep, offset, sp, tmp)
10223d2a30d4Sbostic const time_t * const			timep;
10233d2a30d4Sbostic const long				offset;
10243d2a30d4Sbostic register const struct state * const	sp;
10253d2a30d4Sbostic register struct tm * const		tmp;
10263d2a30d4Sbostic {
10273d2a30d4Sbostic 	register const struct lsinfo *	lp;
1028839b9482Sbostic 	register long			days;
1029839b9482Sbostic 	register long			rem;
1030839b9482Sbostic 	register int			y;
1031839b9482Sbostic 	register int			yleap;
10323d2a30d4Sbostic 	register const int *		ip;
10333d2a30d4Sbostic 	register long			corr;
10343d2a30d4Sbostic 	register int			hit;
10353d2a30d4Sbostic 	register int			i;
1036839b9482Sbostic 
10373d2a30d4Sbostic 	corr = 0;
10383d2a30d4Sbostic 	hit = FALSE;
10393d2a30d4Sbostic #ifdef ALL_STATE
10403d2a30d4Sbostic 	i = (sp == NULL) ? 0 : sp->leapcnt;
10413d2a30d4Sbostic #endif /* defined ALL_STATE */
10423d2a30d4Sbostic #ifndef ALL_STATE
10433d2a30d4Sbostic 	i = sp->leapcnt;
10443d2a30d4Sbostic #endif /* State Farm */
10453d2a30d4Sbostic 	while (--i >= 0) {
10463d2a30d4Sbostic 		lp = &sp->lsis[i];
10473d2a30d4Sbostic 		if (*timep >= lp->ls_trans) {
10483d2a30d4Sbostic 			if (*timep == lp->ls_trans)
10493d2a30d4Sbostic 				hit = ((i == 0 && lp->ls_corr > 0) ||
10503d2a30d4Sbostic 					lp->ls_corr > sp->lsis[i - 1].ls_corr);
10513d2a30d4Sbostic 			corr = lp->ls_corr;
10523d2a30d4Sbostic 			break;
10533d2a30d4Sbostic 		}
10543d2a30d4Sbostic 	}
10553d2a30d4Sbostic 	days = *timep / SECSPERDAY;
10563d2a30d4Sbostic 	rem = *timep % SECSPERDAY;
10573d2a30d4Sbostic #ifdef mc68k
10583d2a30d4Sbostic 	if (*timep == 0x80000000) {
10593d2a30d4Sbostic 		/*
10603d2a30d4Sbostic 		** A 3B1 muffs the division on the most negative number.
10613d2a30d4Sbostic 		*/
10623d2a30d4Sbostic 		days = -24855;
10633d2a30d4Sbostic 		rem = -11648;
10643d2a30d4Sbostic 	}
10653d2a30d4Sbostic #endif /* mc68k */
10663d2a30d4Sbostic 	rem += (offset - corr);
1067839b9482Sbostic 	while (rem < 0) {
10683d2a30d4Sbostic 		rem += SECSPERDAY;
1069839b9482Sbostic 		--days;
1070839b9482Sbostic 	}
10713d2a30d4Sbostic 	while (rem >= SECSPERDAY) {
10723d2a30d4Sbostic 		rem -= SECSPERDAY;
1073839b9482Sbostic 		++days;
1074839b9482Sbostic 	}
10753d2a30d4Sbostic 	tmp->tm_hour = (int) (rem / SECSPERHOUR);
10763d2a30d4Sbostic 	rem = rem % SECSPERHOUR;
10773d2a30d4Sbostic 	tmp->tm_min = (int) (rem / SECSPERMIN);
10783d2a30d4Sbostic 	tmp->tm_sec = (int) (rem % SECSPERMIN);
10793d2a30d4Sbostic 	if (hit)
10803d2a30d4Sbostic 		/*
10813d2a30d4Sbostic 		** A positive leap second requires a special
10823d2a30d4Sbostic 		** representation.  This uses "... ??:59:60".
10833d2a30d4Sbostic 		*/
10843d2a30d4Sbostic 		++(tmp->tm_sec);
10853d2a30d4Sbostic 	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
1086839b9482Sbostic 	if (tmp->tm_wday < 0)
10873d2a30d4Sbostic 		tmp->tm_wday += DAYSPERWEEK;
1088839b9482Sbostic 	y = EPOCH_YEAR;
1089839b9482Sbostic 	if (days >= 0)
1090839b9482Sbostic 		for ( ; ; ) {
1091839b9482Sbostic 			yleap = isleap(y);
1092839b9482Sbostic 			if (days < (long) year_lengths[yleap])
1093839b9482Sbostic 				break;
1094839b9482Sbostic 			++y;
1095839b9482Sbostic 			days = days - (long) year_lengths[yleap];
1096839b9482Sbostic 		}
1097839b9482Sbostic 	else do {
1098839b9482Sbostic 		--y;
1099839b9482Sbostic 		yleap = isleap(y);
1100839b9482Sbostic 		days = days + (long) year_lengths[yleap];
1101839b9482Sbostic 	} while (days < 0);
1102839b9482Sbostic 	tmp->tm_year = y - TM_YEAR_BASE;
1103839b9482Sbostic 	tmp->tm_yday = (int) days;
1104839b9482Sbostic 	ip = mon_lengths[yleap];
1105839b9482Sbostic 	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
1106839b9482Sbostic 		days = days - (long) ip[tmp->tm_mon];
1107839b9482Sbostic 	tmp->tm_mday = (int) (days + 1);
1108839b9482Sbostic 	tmp->tm_isdst = 0;
1109b810d286Sbostic 	tmp->tm_gmtoff = offset;
11103d2a30d4Sbostic }
11113d2a30d4Sbostic 
11123d2a30d4Sbostic /*
11133d2a30d4Sbostic ** A la X3J11
11143d2a30d4Sbostic */
11153d2a30d4Sbostic 
11163d2a30d4Sbostic char *
asctime(timeptr)11173d2a30d4Sbostic asctime(timeptr)
11183d2a30d4Sbostic register const struct tm *	timeptr;
11193d2a30d4Sbostic {
11203d2a30d4Sbostic 	static const char	wday_name[DAYSPERWEEK][3] = {
11213d2a30d4Sbostic 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
11223d2a30d4Sbostic 	};
11233d2a30d4Sbostic 	static const char	mon_name[MONSPERYEAR][3] = {
11243d2a30d4Sbostic 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
11253d2a30d4Sbostic 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
11263d2a30d4Sbostic 	};
11273d2a30d4Sbostic 	static char	result[26];
11283d2a30d4Sbostic 
11293d2a30d4Sbostic 	(void) sprintf(result, "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n",
11303d2a30d4Sbostic 		wday_name[timeptr->tm_wday],
11313d2a30d4Sbostic 		mon_name[timeptr->tm_mon],
11323d2a30d4Sbostic 		timeptr->tm_mday, timeptr->tm_hour,
11333d2a30d4Sbostic 		timeptr->tm_min, timeptr->tm_sec,
11343d2a30d4Sbostic 		TM_YEAR_BASE + timeptr->tm_year);
11353d2a30d4Sbostic 	return result;
11363d2a30d4Sbostic }
11373d2a30d4Sbostic 
11383d2a30d4Sbostic char *
ctime(timep)11393d2a30d4Sbostic ctime(timep)
11403d2a30d4Sbostic const time_t * const	timep;
11413d2a30d4Sbostic {
11423d2a30d4Sbostic 	return asctime(localtime(timep));
11433d2a30d4Sbostic }
11443d2a30d4Sbostic 
11453d2a30d4Sbostic /*
11463d2a30d4Sbostic ** Adapted from code provided by Robert Elz, who writes:
11473d2a30d4Sbostic **	The "best" way to do mktime I think is based on an idea of Bob
11483d2a30d4Sbostic **	Kridle's (so its said...) from a long time ago. (mtxinu!kridle now).
11493d2a30d4Sbostic **	It does a binary search of the time_t space.  Since time_t's are
11503d2a30d4Sbostic **	just 32 bits, its a max of 32 iterations (even at 64 bits it
11513d2a30d4Sbostic **	would still be very reasonable).
11523d2a30d4Sbostic */
11533d2a30d4Sbostic 
11543d2a30d4Sbostic #ifndef WRONG
11553d2a30d4Sbostic #define WRONG	(-1)
11563d2a30d4Sbostic #endif /* !defined WRONG */
11573d2a30d4Sbostic 
11583d2a30d4Sbostic static void
normalize(tensptr,unitsptr,base)11593d2a30d4Sbostic normalize(tensptr, unitsptr, base)
11603d2a30d4Sbostic int * const	tensptr;
11613d2a30d4Sbostic int * const	unitsptr;
11623d2a30d4Sbostic const int	base;
11633d2a30d4Sbostic {
11643d2a30d4Sbostic 	if (*unitsptr >= base) {
11653d2a30d4Sbostic 		*tensptr += *unitsptr / base;
11663d2a30d4Sbostic 		*unitsptr %= base;
11673d2a30d4Sbostic 	} else if (*unitsptr < 0) {
11681484bbd6Sbostic 		*tensptr -= 1 + (-(*unitsptr + 1)) / base;
11691484bbd6Sbostic 		*unitsptr = base - 1 - (-(*unitsptr + 1)) % base;
11703d2a30d4Sbostic 	}
11713d2a30d4Sbostic }
11723d2a30d4Sbostic 
11733d2a30d4Sbostic static int
tmcomp(atmp,btmp)11743d2a30d4Sbostic tmcomp(atmp, btmp)
11753d2a30d4Sbostic register const struct tm * const atmp;
11763d2a30d4Sbostic register const struct tm * const btmp;
11773d2a30d4Sbostic {
11783d2a30d4Sbostic 	register int	result;
11793d2a30d4Sbostic 
11803d2a30d4Sbostic 	if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
11813d2a30d4Sbostic 		(result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
11823d2a30d4Sbostic 		(result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
11833d2a30d4Sbostic 		(result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
11843d2a30d4Sbostic 		(result = (atmp->tm_min - btmp->tm_min)) == 0)
11853d2a30d4Sbostic 			result = atmp->tm_sec - btmp->tm_sec;
11863d2a30d4Sbostic 	return result;
11873d2a30d4Sbostic }
11883d2a30d4Sbostic 
11893d2a30d4Sbostic static time_t
time2(tmp,funcp,offset,okayp)11903d2a30d4Sbostic time2(tmp, funcp, offset, okayp)
11913d2a30d4Sbostic struct tm * const	tmp;
11923d2a30d4Sbostic void (* const		funcp)();
11933d2a30d4Sbostic const long		offset;
11943d2a30d4Sbostic int * const		okayp;
11953d2a30d4Sbostic {
11963d2a30d4Sbostic 	register const struct state *	sp;
11973d2a30d4Sbostic 	register int			dir;
11983d2a30d4Sbostic 	register int			bits;
11993d2a30d4Sbostic 	register int			i, j ;
12003d2a30d4Sbostic 	register int			saved_seconds;
12013d2a30d4Sbostic 	time_t				newt;
12023d2a30d4Sbostic 	time_t				t;
12033d2a30d4Sbostic 	struct tm			yourtm, mytm;
12043d2a30d4Sbostic 
12053d2a30d4Sbostic 	*okayp = FALSE;
12063d2a30d4Sbostic 	yourtm = *tmp;
12073d2a30d4Sbostic 	if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0)
12083d2a30d4Sbostic 		normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN);
12093d2a30d4Sbostic 	normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR);
12103d2a30d4Sbostic 	normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY);
12113d2a30d4Sbostic 	normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR);
12123d2a30d4Sbostic 	while (yourtm.tm_mday <= 0) {
12133d2a30d4Sbostic 		--yourtm.tm_year;
12143d2a30d4Sbostic 		yourtm.tm_mday +=
12153d2a30d4Sbostic 			year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)];
12163d2a30d4Sbostic 	}
1217*a9452ba1Sbostic 	while (yourtm.tm_mday > DAYSPERLYEAR) {
1218*a9452ba1Sbostic 		yourtm.tm_mday -=
1219*a9452ba1Sbostic 		    year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)];
1220*a9452ba1Sbostic 		++yourtm.tm_year;
1221*a9452ba1Sbostic 	}
12223d2a30d4Sbostic 	for ( ; ; ) {
12233d2a30d4Sbostic 		i = mon_lengths[isleap(yourtm.tm_year +
12243d2a30d4Sbostic 			TM_YEAR_BASE)][yourtm.tm_mon];
12253d2a30d4Sbostic 		if (yourtm.tm_mday <= i)
12263d2a30d4Sbostic 			break;
12273d2a30d4Sbostic 		yourtm.tm_mday -= i;
12283d2a30d4Sbostic 		if (++yourtm.tm_mon >= MONSPERYEAR) {
12293d2a30d4Sbostic 			yourtm.tm_mon = 0;
12303d2a30d4Sbostic 			++yourtm.tm_year;
12313d2a30d4Sbostic 		}
12323d2a30d4Sbostic 	}
12333d2a30d4Sbostic 	saved_seconds = yourtm.tm_sec;
12343d2a30d4Sbostic 	yourtm.tm_sec = 0;
12353d2a30d4Sbostic 	/*
12363d2a30d4Sbostic 	** Calculate the number of magnitude bits in a time_t
12373d2a30d4Sbostic 	** (this works regardless of whether time_t is
12383d2a30d4Sbostic 	** signed or unsigned, though lint complains if unsigned).
12393d2a30d4Sbostic 	*/
12403d2a30d4Sbostic 	for (bits = 0, t = 1; t > 0; ++bits, t <<= 1)
12413d2a30d4Sbostic 		;
12423d2a30d4Sbostic 	/*
12433d2a30d4Sbostic 	** If time_t is signed, then 0 is the median value,
12443d2a30d4Sbostic 	** if time_t is unsigned, then 1 << bits is median.
12453d2a30d4Sbostic 	*/
12463d2a30d4Sbostic 	t = (t < 0) ? 0 : ((time_t) 1 << bits);
12473d2a30d4Sbostic 	for ( ; ; ) {
12483d2a30d4Sbostic 		(*funcp)(&t, offset, &mytm);
12493d2a30d4Sbostic 		dir = tmcomp(&mytm, &yourtm);
12503d2a30d4Sbostic 		if (dir != 0) {
12513d2a30d4Sbostic 			if (bits-- < 0)
12523d2a30d4Sbostic 				return WRONG;
12533d2a30d4Sbostic 			if (bits < 0)
12543d2a30d4Sbostic 				--t;
12553d2a30d4Sbostic 			else if (dir > 0)
12563d2a30d4Sbostic 				t -= (time_t) 1 << bits;
12573d2a30d4Sbostic 			else	t += (time_t) 1 << bits;
12583d2a30d4Sbostic 			continue;
12593d2a30d4Sbostic 		}
12603d2a30d4Sbostic 		if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
12613d2a30d4Sbostic 			break;
12623d2a30d4Sbostic 		/*
12633d2a30d4Sbostic 		** Right time, wrong type.
12643d2a30d4Sbostic 		** Hunt for right time, right type.
12653d2a30d4Sbostic 		** It's okay to guess wrong since the guess
12663d2a30d4Sbostic 		** gets checked.
12673d2a30d4Sbostic 		*/
12683d2a30d4Sbostic 		sp = (const struct state *)
12693d2a30d4Sbostic 			((funcp == localsub) ? lclptr : gmtptr);
12703d2a30d4Sbostic #ifdef ALL_STATE
12713d2a30d4Sbostic 		if (sp == NULL)
12723d2a30d4Sbostic 			return WRONG;
12733d2a30d4Sbostic #endif /* defined ALL_STATE */
12743d2a30d4Sbostic 		for (i = 0; i < sp->typecnt; ++i) {
12753d2a30d4Sbostic 			if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
12763d2a30d4Sbostic 				continue;
12773d2a30d4Sbostic 			for (j = 0; j < sp->typecnt; ++j) {
12783d2a30d4Sbostic 				if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
12793d2a30d4Sbostic 					continue;
12803d2a30d4Sbostic 				newt = t + sp->ttis[j].tt_gmtoff -
12813d2a30d4Sbostic 					sp->ttis[i].tt_gmtoff;
12823d2a30d4Sbostic 				(*funcp)(&newt, offset, &mytm);
12833d2a30d4Sbostic 				if (tmcomp(&mytm, &yourtm) != 0)
12843d2a30d4Sbostic 					continue;
12853d2a30d4Sbostic 				if (mytm.tm_isdst != yourtm.tm_isdst)
12863d2a30d4Sbostic 					continue;
12873d2a30d4Sbostic 				/*
12883d2a30d4Sbostic 				** We have a match.
12893d2a30d4Sbostic 				*/
12903d2a30d4Sbostic 				t = newt;
12913d2a30d4Sbostic 				goto label;
12923d2a30d4Sbostic 			}
12933d2a30d4Sbostic 		}
12943d2a30d4Sbostic 		return WRONG;
12953d2a30d4Sbostic 	}
12963d2a30d4Sbostic label:
12973d2a30d4Sbostic 	t += saved_seconds;
12983d2a30d4Sbostic 	(*funcp)(&t, offset, tmp);
12993d2a30d4Sbostic 	*okayp = TRUE;
13003d2a30d4Sbostic 	return t;
13013d2a30d4Sbostic }
13023d2a30d4Sbostic 
13033d2a30d4Sbostic static time_t
time1(tmp,funcp,offset)13043d2a30d4Sbostic time1(tmp, funcp, offset)
13053d2a30d4Sbostic struct tm * const	tmp;
13063d2a30d4Sbostic void (* const		funcp)();
13073d2a30d4Sbostic const long		offset;
13083d2a30d4Sbostic {
13093d2a30d4Sbostic 	register time_t			t;
13103d2a30d4Sbostic 	register const struct state *	sp;
13113d2a30d4Sbostic 	register int			samei, otheri;
13123d2a30d4Sbostic 	int				okay;
13133d2a30d4Sbostic 
13143d2a30d4Sbostic 	if (tmp->tm_isdst > 1)
1315c65326f3Sbostic 		tmp->tm_isdst = 1;
13163d2a30d4Sbostic 	t = time2(tmp, funcp, offset, &okay);
13173d2a30d4Sbostic 	if (okay || tmp->tm_isdst < 0)
13183d2a30d4Sbostic 		return t;
13193d2a30d4Sbostic 	/*
13203d2a30d4Sbostic 	** We're supposed to assume that somebody took a time of one type
13213d2a30d4Sbostic 	** and did some math on it that yielded a "struct tm" that's bad.
13223d2a30d4Sbostic 	** We try to divine the type they started from and adjust to the
13233d2a30d4Sbostic 	** type they need.
13243d2a30d4Sbostic 	*/
13253d2a30d4Sbostic 	sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr);
13263d2a30d4Sbostic #ifdef ALL_STATE
13273d2a30d4Sbostic 	if (sp == NULL)
13283d2a30d4Sbostic 		return WRONG;
13293d2a30d4Sbostic #endif /* defined ALL_STATE */
13303d2a30d4Sbostic 	for (samei = 0; samei < sp->typecnt; ++samei) {
13313d2a30d4Sbostic 		if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
13323d2a30d4Sbostic 			continue;
13333d2a30d4Sbostic 		for (otheri = 0; otheri < sp->typecnt; ++otheri) {
13343d2a30d4Sbostic 			if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
13353d2a30d4Sbostic 				continue;
13363d2a30d4Sbostic 			tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
13373d2a30d4Sbostic 					sp->ttis[samei].tt_gmtoff;
13383d2a30d4Sbostic 			tmp->tm_isdst = !tmp->tm_isdst;
13393d2a30d4Sbostic 			t = time2(tmp, funcp, offset, &okay);
13403d2a30d4Sbostic 			if (okay)
13413d2a30d4Sbostic 				return t;
13423d2a30d4Sbostic 			tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
13433d2a30d4Sbostic 					sp->ttis[samei].tt_gmtoff;
13443d2a30d4Sbostic 			tmp->tm_isdst = !tmp->tm_isdst;
13453d2a30d4Sbostic 		}
13463d2a30d4Sbostic 	}
13473d2a30d4Sbostic 	return WRONG;
13483d2a30d4Sbostic }
13493d2a30d4Sbostic 
13503d2a30d4Sbostic time_t
mktime(tmp)13513d2a30d4Sbostic mktime(tmp)
13523d2a30d4Sbostic struct tm * const	tmp;
13533d2a30d4Sbostic {
13543d2a30d4Sbostic 	return time1(tmp, localsub, 0L);
13553d2a30d4Sbostic }
1356