localtime.c revision 17209
1139825Simp/*
236270Swpaul** This file is in the public domain, so clarified as of
336270Swpaul** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).
436270Swpaul*/
536270Swpaul
636270Swpaul#ifndef lint
736270Swpaul#ifndef NOID
836270Swpaulstatic char	elsieid[] = "@(#)localtime.c	7.57";
936270Swpaul#endif /* !defined NOID */
1036270Swpaul#endif /* !defined lint */
1136270Swpaul
1236270Swpaul/*
1336270Swpaul** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
1436270Swpaul** POSIX-style TZ environment variable handling from Guy Harris
1536270Swpaul** (guy@auspex.com).
1636270Swpaul*/
1736270Swpaul
1836270Swpaul/*LINTLIBRARY*/
1936270Swpaul
2036270Swpaul#include "private.h"
2136270Swpaul#include "tzfile.h"
2236270Swpaul#include "fcntl.h"
2336270Swpaul#ifdef	_THREAD_SAFE
2436270Swpaul#include <pthread.h>
2536270Swpaul#include "pthread_private.h"
2636270Swpaul#endif
2736270Swpaul
2836270Swpaul/*
2936270Swpaul** SunOS 4.1.1 headers lack O_BINARY.
3036270Swpaul*/
3136270Swpaul
3236270Swpaul#ifdef O_BINARY
33122678Sobrien#define OPEN_MODE	(O_RDONLY | O_BINARY)
34122678Sobrien#endif /* defined O_BINARY */
35122678Sobrien#ifndef O_BINARY
3636270Swpaul#define OPEN_MODE	O_RDONLY
3736270Swpaul#endif /* !defined O_BINARY */
3836270Swpaul
3936270Swpaul#ifndef WILDABBR
4036270Swpaul/*
4136270Swpaul** Someone might make incorrect use of a time zone abbreviation:
4239583Swpaul**	1.	They might reference tzname[0] before calling tzset (explicitly
4336270Swpaul**		or implicitly).
4436270Swpaul**	2.	They might reference tzname[1] before calling tzset (explicitly
4536270Swpaul**		or implicitly).
4636270Swpaul**	3.	They might reference tzname[1] after setting to a time zone
4739583Swpaul**		in which Daylight Saving Time is never observed.
4836270Swpaul**	4.	They might reference tzname[0] after setting to a time zone
4936270Swpaul**		in which Standard Time is never observed.
5036270Swpaul**	5.	They might reference tm.TM_ZONE after calling offtime.
5136270Swpaul** What's best to do in the above cases is open to debate;
5236270Swpaul** for now, we just set things up so that in any of the five cases
5336270Swpaul** WILDABBR is used.  Another possibility:  initialize tzname[0] to the
5436270Swpaul** string "tzname[0] used before set", and similarly for the other cases.
5536270Swpaul** And another:  initialize tzname[0] to "ERA", with an explanation in the
5636270Swpaul** manual page of what this "time zone abbreviation" means (doing this so
5736270Swpaul** that tzname[0] has the "normal" length of three characters).
5839583Swpaul*/
5936270Swpaul#define WILDABBR	"   "
6036270Swpaul#endif /* !defined WILDABBR */
6136270Swpaul
6236270Swpaulstatic char		wildabbr[] = "WILDABBR";
6336270Swpaul
6436270Swpaulstatic const char	gmt[] = "GMT";
6536270Swpaul
6639583Swpaulstruct ttinfo {				/* time type information */
6739583Swpaul	long		tt_gmtoff;	/* GMT offset in seconds */
6839583Swpaul	int		tt_isdst;	/* used to set tm_isdst */
6939583Swpaul	int		tt_abbrind;	/* abbreviation list index */
7039583Swpaul	int		tt_ttisstd;	/* TRUE if transition is std time */
7139583Swpaul	int		tt_ttisgmt;	/* TRUE if transition is GMT */
7239583Swpaul};
7339583Swpaul
7439583Swpaulstruct lsinfo {				/* leap second information */
7536270Swpaul	time_t		ls_trans;	/* transition time */
7636270Swpaul	long		ls_corr;	/* correction to apply */
7739583Swpaul};
7836270Swpaul
7936270Swpaul#define BIGGEST(a, b)	(((a) > (b)) ? (a) : (b))
8036270Swpaul
8136270Swpaul#ifdef TZNAME_MAX
8236270Swpaul#define MY_TZNAME_MAX	TZNAME_MAX
8336270Swpaul#endif /* defined TZNAME_MAX */
8436270Swpaul#ifndef TZNAME_MAX
8536270Swpaul#define MY_TZNAME_MAX	255
8636270Swpaul#endif /* !defined TZNAME_MAX */
8736270Swpaul
8836270Swpaulstruct state {
8936270Swpaul	int		leapcnt;
9036270Swpaul	int		timecnt;
9136270Swpaul	int		typecnt;
9236270Swpaul	int		charcnt;
9336270Swpaul	time_t		ats[TZ_MAX_TIMES];
9436270Swpaul	unsigned char	types[TZ_MAX_TIMES];
9536270Swpaul	struct ttinfo	ttis[TZ_MAX_TYPES];
9636270Swpaul	char		chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
9736270Swpaul				(2 * (MY_TZNAME_MAX + 1)))];
9836270Swpaul	struct lsinfo	lsis[TZ_MAX_LEAPS];
9936270Swpaul};
10036270Swpaul
10136270Swpaulstruct rule {
10236270Swpaul	int		r_type;		/* type of rule--see below */
10336270Swpaul	int		r_day;		/* day number of rule */
10436270Swpaul	int		r_week;		/* week number of rule */
10536270Swpaul	int		r_mon;		/* month number of rule */
10636270Swpaul	long		r_time;		/* transition time of rule */
10736270Swpaul};
10836270Swpaul
10936270Swpaul#define JULIAN_DAY		0	/* Jn - Julian day */
11036270Swpaul#define DAY_OF_YEAR		1	/* n - day of year */
11136270Swpaul#define MONTH_NTH_DAY_OF_WEEK	2	/* Mm.n.d - month, week, day of week */
11236270Swpaul
11336270Swpaul/*
11436270Swpaul** Prototypes for static functions.
11536270Swpaul*/
11636270Swpaul
11736270Swpaulstatic long		detzcode P((const char * codep));
11836270Swpaulstatic const char *	getzname P((const char * strp));
11936270Swpaulstatic const char *	getnum P((const char * strp, int * nump, int min,
12036270Swpaul				int max));
12136270Swpaulstatic const char *	getsecs P((const char * strp, long * secsp));
12236270Swpaulstatic const char *	getoffset P((const char * strp, long * offsetp));
12336270Swpaulstatic const char *	getrule P((const char * strp, struct rule * rulep));
12436270Swpaulstatic void		gmtload P((struct state * sp));
12536270Swpaulstatic void		gmtsub P((const time_t * timep, long offset,
12636270Swpaul				struct tm * tmp));
12736270Swpaulstatic void		localsub P((const time_t * timep, long offset,
12836270Swpaul				struct tm * tmp));
12936270Swpaulstatic int		increment_overflow P((int * number, int delta));
13036270Swpaulstatic int		normalize_overflow P((int * tensptr, int * unitsptr,
13136270Swpaul				int base));
13236270Swpaulstatic void		settzname P((void));
13336270Swpaulstatic time_t		time1 P((struct tm * tmp,
13436270Swpaul				void(*funcp) P((const time_t *,
13536270Swpaul				long, struct tm *)),
13636270Swpaul				long offset));
13736270Swpaulstatic time_t		time2 P((struct tm *tmp,
13836270Swpaul				void(*funcp) P((const time_t *,
13936270Swpaul				long, struct tm*)),
14036270Swpaul				long offset, int * okayp));
14136270Swpaulstatic void		timesub P((const time_t * timep, long offset,
14236270Swpaul				const struct state * sp, struct tm * tmp));
14336270Swpaulstatic int		tmcomp P((const struct tm * atmp,
14436270Swpaul				const struct tm * btmp));
14536270Swpaulstatic time_t		transtime P((time_t janfirst, int year,
14636270Swpaul				const struct rule * rulep, long offset));
14736270Swpaulstatic int		tzload P((const char * name, struct state * sp));
14836270Swpaulstatic int		tzparse P((const char * name, struct state * sp,
14936270Swpaul				int lastditch));
15036270Swpaul
15136270Swpaul#ifdef ALL_STATE
15236270Swpaulstatic struct state *	lclptr;
15336270Swpaulstatic struct state *	gmtptr;
15436270Swpaul#endif /* defined ALL_STATE */
15536270Swpaul
15636270Swpaul#ifndef ALL_STATE
15736270Swpaulstatic struct state	lclmem;
15836270Swpaulstatic struct state	gmtmem;
15936270Swpaul#define lclptr		(&lclmem)
16036270Swpaul#define gmtptr		(&gmtmem)
16136270Swpaul#endif /* State Farm */
16236270Swpaul
16336270Swpaul#ifndef TZ_STRLEN_MAX
16436270Swpaul#define TZ_STRLEN_MAX 255
16536270Swpaul#endif /* !defined TZ_STRLEN_MAX */
16636270Swpaul
16736270Swpaulstatic char		lcl_TZname[TZ_STRLEN_MAX + 1];
16836270Swpaulstatic int		lcl_is_set;
16936270Swpaulstatic int		gmt_is_set;
17036270Swpaul#ifdef	_THREAD_SAFE
17136270Swpaulstatic pthread_mutex_t  lcl_mutex   = PTHREAD_MUTEX_INITIALIZER;
17236270Swpaulstatic pthread_mutex_t  gmt_mutex   = PTHREAD_MUTEX_INITIALIZER;
17336270Swpaul#endif
17436270Swpaul
17536270Swpaulchar *			tzname[2] = {
17636270Swpaul	wildabbr,
17736270Swpaul	wildabbr
17836270Swpaul};
17936270Swpaul
18036270Swpaul/*
18136270Swpaul** Section 4.12.3 of X3.159-1989 requires that
18236270Swpaul**	Except for the strftime function, these functions [asctime,
18336270Swpaul**	ctime, gmtime, localtime] return values in one of two static
18436270Swpaul**	objects: a broken-down time structure and an array of char.
18536270Swpaul** Thanks to Paul Eggert (eggert@twinsun.com) for noting this.
186129878Sphk*/
18736270Swpaul
18836270Swpaulstatic struct tm	tm;
18936270Swpaul
19036270Swpaul#ifdef USG_COMPAT
19136270Swpaultime_t			timezone = 0;
19236270Swpaulint			daylight = 0;
19336270Swpaul#endif /* defined USG_COMPAT */
194147256Sbrooks
19536270Swpaul#ifdef ALTZONE
19636270Swpaultime_t			altzone = 0;
19736270Swpaul#endif /* defined ALTZONE */
19836270Swpaul
19936270Swpaulstatic long
20045155Swpauldetzcode(codep)
20148992Swpaulconst char * const	codep;
20248992Swpaul{
20348992Swpaul	register long	result;
20436270Swpaul	register int	i;
20550462Swpaul
20650462Swpaul	result = (codep[0] & 0x80) ? ~0L : 0L;
20750462Swpaul	for (i = 0; i < 4; ++i)
208119288Simp		result = (result << 8) | (codep[i] & 0xff);
209119288Simp	return result;
21036270Swpaul}
21139957Swpaul
21239957Swpaulstatic void
21339957Swpaulsettzname P((void))
21439957Swpaul{
21539957Swpaul	register struct state * const	sp = lclptr;
21639957Swpaul	register int			i;
21739957Swpaul
218181738Simp	tzname[0] = wildabbr;
21936270Swpaul	tzname[1] = wildabbr;
220113506Smdodd#ifdef USG_COMPAT
221113506Smdodd	daylight = 0;
22259758Speter	timezone = 0;
22359758Speter#endif /* defined USG_COMPAT */
224151545Simp#ifdef ALTZONE
22550462Swpaul	altzone = 0;
22650462Swpaul#endif /* defined ALTZONE */
22736270Swpaul#ifdef ALL_STATE
22836270Swpaul	if (sp == NULL) {
22936270Swpaul		tzname[0] = tzname[1] = gmt;
23036270Swpaul		return;
23136270Swpaul	}
23236270Swpaul#endif /* defined ALL_STATE */
23336270Swpaul	for (i = 0; i < sp->typecnt; ++i) {
23436270Swpaul		register const struct ttinfo * const	ttisp = &sp->ttis[i];
23536270Swpaul
23636270Swpaul		tzname[ttisp->tt_isdst] =
23736270Swpaul			&sp->chars[ttisp->tt_abbrind];
23836270Swpaul#ifdef USG_COMPAT
23936270Swpaul		if (ttisp->tt_isdst)
24036270Swpaul			daylight = 1;
24136270Swpaul		if (i == 0 || !ttisp->tt_isdst)
24236270Swpaul			timezone = -(ttisp->tt_gmtoff);
24336270Swpaul#endif /* defined USG_COMPAT */
24436270Swpaul#ifdef ALTZONE
24536270Swpaul		if (i == 0 || ttisp->tt_isdst)
24636270Swpaul			altzone = -(ttisp->tt_gmtoff);
24736270Swpaul#endif /* defined ALTZONE */
24837626Swpaul	}
24937626Swpaul	/*
25037626Swpaul	** And to get the latest zone names into tzname. . .
25137626Swpaul	*/
25237626Swpaul	for (i = 0; i < sp->timecnt; ++i) {
25337626Swpaul		register const struct ttinfo * const	ttisp =
25437626Swpaul							&sp->ttis[
25537626Swpaul								sp->types[i]];
25637626Swpaul
25737626Swpaul		tzname[ttisp->tt_isdst] =
25837626Swpaul			&sp->chars[ttisp->tt_abbrind];
25937626Swpaul	}
26036270Swpaul}
26136270Swpaul
26236270Swpaulstatic int
263142407Simptzload(name, sp)
264142407Simpregister const char *		name;
265142407Simpregister struct state * const	sp;
266142407Simp{
267142407Simp	register const char *	p;
268142407Simp	register int		i;
269142407Simp	register int		fid;
270142407Simp
271142407Simp	if (name == NULL && (name = TZDEFAULT) == NULL)
27236270Swpaul		return -1;
273142407Simp	{
274142407Simp		register int	doaccess;
275142407Simp		/*
27636270Swpaul		** Section 4.9.1 of the C standard says that
277142407Simp		** "FILENAME_MAX expands to an integral constant expression
278142407Simp		** that is the sie needed for an array of char large enough
279150171Sjhb		** to hold the longest file name string that the implementation
280142407Simp		** guarantees can be opened."
281142407Simp		*/
282150171Sjhb		char		fullname[FILENAME_MAX + 1];
283142407Simp
284199560Sjhb		if (name[0] == ':')
285188463Simp			++name;
286142407Simp		doaccess = name[0] == '/';
287142407Simp		if (!doaccess) {
28836270Swpaul			if ((p = TZDIR) == NULL)
289142407Simp				return -1;
290142407Simp			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
291142407Simp				return -1;
29236270Swpaul			(void) strcpy(fullname, p);
293142407Simp			(void) strcat(fullname, "/");
294142407Simp			(void) strcat(fullname, name);
295142407Simp			/*
296142407Simp			** Set doaccess if '.' (as in "../") shows up in name.
297142407Simp			*/
298142407Simp			if (strchr(name, '.') != NULL)
299142407Simp				doaccess = TRUE;
30036270Swpaul			name = fullname;
301142407Simp		}
302142407Simp		if (doaccess && access(name, R_OK) != 0)
303142407Simp			return -1;
304142407Simp		if ((fid = open(name, OPEN_MODE)) == -1)
305142407Simp			return -1;
306142407Simp	}
307142407Simp	{
308142407Simp		struct tzhead *	tzhp;
30936270Swpaul		char		buf[sizeof *sp + sizeof *tzhp];
310142407Simp		int		ttisstdcnt;
311142407Simp		int		ttisgmtcnt;
312142407Simp
313142407Simp		i = read(fid, buf, sizeof buf);
314142407Simp		if (close(fid) != 0)
315142407Simp			return -1;
316142407Simp		p = buf;
317142407Simp		p += sizeof tzhp->tzh_reserved;
318142407Simp		ttisstdcnt = (int) detzcode(p);
319142407Simp		p += 4;
32039583Swpaul		ttisgmtcnt = (int) detzcode(p);
32149010Swpaul		p += 4;
32249010Swpaul		sp->leapcnt = (int) detzcode(p);
32349010Swpaul		p += 4;
32449010Swpaul		sp->timecnt = (int) detzcode(p);
32549010Swpaul		p += 4;
32649010Swpaul		sp->typecnt = (int) detzcode(p);
32749010Swpaul		p += 4;
32849010Swpaul		sp->charcnt = (int) detzcode(p);
32948992Swpaul		p += 4;
33048992Swpaul		if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
33148992Swpaul			sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
33248992Swpaul			sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
33348992Swpaul			sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
33448992Swpaul			(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
33550462Swpaul			(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
33650462Swpaul				return -1;
33750462Swpaul		if (i - (p - buf) < sp->timecnt * 4 +	/* ats */
33850462Swpaul			sp->timecnt +			/* types */
33950462Swpaul			sp->typecnt * (4 + 2) +		/* ttinfos */
34050462Swpaul			sp->charcnt +			/* chars */
34150462Swpaul			sp->leapcnt * (4 + 4) +		/* lsinfos */
34250462Swpaul			ttisstdcnt +			/* ttisstds */
34350462Swpaul			ttisgmtcnt)			/* ttisgmts */
34450462Swpaul				return -1;
34548992Swpaul		for (i = 0; i < sp->timecnt; ++i) {
34648992Swpaul			sp->ats[i] = detzcode(p);
34748992Swpaul			p += 4;
34848992Swpaul		}
34951455Swpaul		for (i = 0; i < sp->timecnt; ++i) {
35048992Swpaul			sp->types[i] = (unsigned char) *p++;
35148992Swpaul			if (sp->types[i] >= sp->typecnt)
35248992Swpaul				return -1;
35348992Swpaul		}
35448992Swpaul		for (i = 0; i < sp->typecnt; ++i) {
35548992Swpaul			register struct ttinfo *	ttisp;
356113506Smdodd
35751473Swpaul			ttisp = &sp->ttis[i];
35848992Swpaul			ttisp->tt_gmtoff = detzcode(p);
35939583Swpaul			p += 4;
36041656Swpaul			ttisp->tt_isdst = (unsigned char) *p++;
36141656Swpaul			if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
36239583Swpaul				return -1;
36339583Swpaul			ttisp->tt_abbrind = (unsigned char) *p++;
36439583Swpaul			if (ttisp->tt_abbrind < 0 ||
36539583Swpaul				ttisp->tt_abbrind > sp->charcnt)
36639583Swpaul					return -1;
36739583Swpaul		}
36841656Swpaul		for (i = 0; i < sp->charcnt; ++i)
36941656Swpaul			sp->chars[i] = *p++;
37039583Swpaul		sp->chars[i] = '\0';	/* ensure '\0' at end */
37139583Swpaul		for (i = 0; i < sp->leapcnt; ++i) {
37239583Swpaul			register struct lsinfo *	lsisp;
37339583Swpaul
37439583Swpaul			lsisp = &sp->lsis[i];
37539583Swpaul			lsisp->ls_trans = detzcode(p);
37641656Swpaul			p += 4;
37741656Swpaul			lsisp->ls_corr = detzcode(p);
37839583Swpaul			p += 4;
37939583Swpaul		}
38039583Swpaul		for (i = 0; i < sp->typecnt; ++i) {
38139583Swpaul			register struct ttinfo *	ttisp;
38239583Swpaul
38339583Swpaul			ttisp = &sp->ttis[i];
38441656Swpaul			if (ttisstdcnt == 0)
38541656Swpaul				ttisp->tt_ttisstd = FALSE;
38641656Swpaul			else {
38739583Swpaul				ttisp->tt_ttisstd = *p++;
38839583Swpaul				if (ttisp->tt_ttisstd != TRUE &&
38939583Swpaul					ttisp->tt_ttisstd != FALSE)
39039583Swpaul						return -1;
39139583Swpaul			}
39239583Swpaul		}
39339583Swpaul		for (i = 0; i < sp->typecnt; ++i) {
39441656Swpaul			register struct ttinfo *	ttisp;
39541656Swpaul
39641656Swpaul			ttisp = &sp->ttis[i];
39739583Swpaul			if (ttisgmtcnt == 0)
39839583Swpaul				ttisp->tt_ttisgmt = FALSE;
39939583Swpaul			else {
40039583Swpaul				ttisp->tt_ttisgmt = *p++;
40139583Swpaul				if (ttisp->tt_ttisgmt != TRUE &&
40239583Swpaul					ttisp->tt_ttisgmt != FALSE)
40339583Swpaul						return -1;
40441656Swpaul			}
40541656Swpaul		}
40641656Swpaul	}
40739583Swpaul	return 0;
40839583Swpaul}
40939583Swpaul
41039583Swpaulstatic const int	mon_lengths[2][MONSPERYEAR] = {
41139583Swpaul	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
41239583Swpaul	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
413102336Salfred};
414102336Salfred
41541656Swpaulstatic const int	year_lengths[2] = {
41641656Swpaul	DAYSPERNYEAR, DAYSPERLYEAR
41741656Swpaul};
41839583Swpaul
41939583Swpaul/*
42039583Swpaul** Given a pointer into a time zone string, scan until a character that is not
42139583Swpaul** a valid character in a zone name is found.  Return a pointer to that
42239583Swpaul** character.
42339583Swpaul*/
42439583Swpaul
42539583Swpaulstatic const char *
42639583Swpaulgetzname(strp)
42739583Swpaulregister const char *	strp;
42839583Swpaul{
429102336Salfred	register char	c;
430102336Salfred
43141656Swpaul	while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
43241656Swpaul		c != '+')
43341656Swpaul			++strp;
43439583Swpaul	return strp;
43539583Swpaul}
43639583Swpaul
43739583Swpaul/*
43839583Swpaul** Given a pointer into a time zone string, extract a number from that string.
43939583Swpaul** Check that the number is within a specified range; if it is not, return
44039583Swpaul** NULL.
44139583Swpaul** Otherwise, return a pointer to the first character not part of the number.
44239583Swpaul*/
44339583Swpaul
44439583Swpaulstatic const char *
44539583Swpaulgetnum(strp, nump, min, max)
44641656Swpaulregister const char *	strp;
44741656Swpaulint * const		nump;
44841656Swpaulconst int		min;
44939583Swpaulconst int		max;
45039583Swpaul{
45139583Swpaul	register char	c;
45239583Swpaul	register int	num;
45339583Swpaul
45439583Swpaul	if (strp == NULL || !is_digit(c = *strp))
45539583Swpaul		return NULL;
45639583Swpaul	num = 0;
45739583Swpaul	do {
45839583Swpaul		num = num * 10 + (c - '0');
45939583Swpaul		if (num > max)
46039583Swpaul			return NULL;	/* illegal value */
46141656Swpaul		c = *++strp;
46241656Swpaul	} while (is_digit(c));
46341656Swpaul	if (num < min)
46439583Swpaul		return NULL;		/* illegal value */
46539583Swpaul	*nump = num;
46639583Swpaul	return strp;
46739583Swpaul}
46839583Swpaul
46939583Swpaul/*
47039583Swpaul** Given a pointer into a time zone string, extract a number of seconds,
47139583Swpaul** in hh[:mm[:ss]] form, from the string.
47239583Swpaul** If any error occurs, return NULL.
47339583Swpaul** Otherwise, return a pointer to the first character not part of the number
47439583Swpaul** of seconds.
47536270Swpaul*/
47636270Swpaul
47736270Swpaulstatic const char *
47839583Swpaulgetsecs(strp, secsp)
47939583Swpaulregister const char *	strp;
48041656Swpaullong * const		secsp;
48136270Swpaul{
48236270Swpaul	int	num;
48336270Swpaul
48436270Swpaul	/*
48536270Swpaul	** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
48636270Swpaul	** "M10.4.6/26", which does not conform to Posix,
48739583Swpaul	** but which specifies the equivalent of
48836270Swpaul	** ``02:00 on the first Sunday on or after 23 Oct''.
48936270Swpaul	*/
49036270Swpaul	strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
49136270Swpaul	if (strp == NULL)
49236270Swpaul		return NULL;
49336270Swpaul	*secsp = num * (long) SECSPERHOUR;
49439583Swpaul	if (*strp == ':') {
49536270Swpaul		++strp;
49639583Swpaul		strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
49736270Swpaul		if (strp == NULL)
49839583Swpaul			return NULL;
49939583Swpaul		*secsp += num * SECSPERMIN;
50039583Swpaul		if (*strp == ':') {
50139583Swpaul			++strp;
50236270Swpaul			/* `SECSPERMIN' allows for leap seconds.  */
50336270Swpaul			strp = getnum(strp, &num, 0, SECSPERMIN);
50436270Swpaul			if (strp == NULL)
50536270Swpaul				return NULL;
50636270Swpaul			*secsp += num;
50739583Swpaul		}
50836270Swpaul	}
50936270Swpaul	return strp;
51036270Swpaul}
51136270Swpaul
51239583Swpaul/*
51339583Swpaul** Given a pointer into a time zone string, extract an offset, in
51439583Swpaul** [+-]hh[:mm[:ss]] form, from the string.
51536270Swpaul** If any error occurs, return NULL.
51636270Swpaul** Otherwise, return a pointer to the first character not part of the time.
51736270Swpaul*/
51836270Swpaul
51936270Swpaulstatic const char *
52036270Swpaulgetoffset(strp, offsetp)
52136270Swpaulregister const char *	strp;
52239583Swpaullong * const		offsetp;
52339583Swpaul{
52441656Swpaul	register int	neg = 0;
52536270Swpaul
52636270Swpaul	if (*strp == '-') {
52736270Swpaul		neg = 1;
52836270Swpaul		++strp;
529162315Sglebius	} else if (*strp == '+')
53036270Swpaul		++strp;
53139583Swpaul	strp = getsecs(strp, offsetp);
53239583Swpaul	if (strp == NULL)
53336270Swpaul		return NULL;		/* illegal time */
53439583Swpaul	if (neg)
53536270Swpaul		*offsetp = -*offsetp;
53636270Swpaul	return strp;
53736270Swpaul}
53839583Swpaul
539162315Sglebius/*
540105599Sbrooks** Given a pointer into a time zone string, extract a rule in the form
54136270Swpaul** date[/time].  See POSIX section 8 for the format of "date" and "time".
54239583Swpaul** If a valid rule is not found, return NULL.
54336270Swpaul** Otherwise, return a pointer to the first character not part of the rule.
54436270Swpaul*/
54536270Swpaul
54636270Swpaulstatic const char *
54739583Swpaulgetrule(strp, rulep)
548162315Sglebiusconst char *			strp;
549105599Sbrooksregister struct rule * const	rulep;
55036270Swpaul{
55139583Swpaul	if (*strp == 'J') {
55236270Swpaul		/*
55336270Swpaul		** Julian day.
55436270Swpaul		*/
55536270Swpaul		rulep->r_type = JULIAN_DAY;
55636270Swpaul		++strp;
55736270Swpaul		strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
55839583Swpaul	} else if (*strp == 'M') {
559162315Sglebius		/*
560105599Sbrooks		** Month, week, day.
56136270Swpaul		*/
56239583Swpaul		rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
56336270Swpaul		++strp;
56436270Swpaul		strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
56536270Swpaul		if (strp == NULL)
56636270Swpaul			return NULL;
56739583Swpaul		if (*strp++ != '.')
56836270Swpaul			return NULL;
56939583Swpaul		strp = getnum(strp, &rulep->r_week, 1, 5);
57039583Swpaul		if (strp == NULL)
57139583Swpaul			return NULL;
57236270Swpaul		if (*strp++ != '.')
57339583Swpaul			return NULL;
57436501Swpaul		strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
57536270Swpaul	} else if (is_digit(*strp)) {
57636270Swpaul		/*
57736270Swpaul		** Day of year.
57836270Swpaul		*/
57936270Swpaul		rulep->r_type = DAY_OF_YEAR;
58036270Swpaul		strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
58136270Swpaul	} else	return NULL;		/* invalid format */
58236270Swpaul	if (strp == NULL)
58336270Swpaul		return NULL;
58436270Swpaul	if (*strp == '/') {
58536270Swpaul		/*
58636270Swpaul		** Time specified.
58736270Swpaul		*/
58839583Swpaul		++strp;
58939583Swpaul		strp = getsecs(strp, &rulep->r_time);
59039583Swpaul	} else	rulep->r_time = 2 * SECSPERHOUR;	/* default = 2:00:00 */
591102336Salfred	return strp;
592102336Salfred}
59339583Swpaul
59439583Swpaul/*
59539583Swpaul** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the
59639583Swpaul** year, a rule, and the offset from GMT at the time that rule takes effect,
59736270Swpaul** calculate the Epoch-relative time that rule takes effect.
59839583Swpaul*/
59939583Swpaul
60039583Swpaulstatic time_t
60139583Swpaultranstime(janfirst, year, rulep, offset)
60239583Swpaulconst time_t				janfirst;
60339583Swpaulconst int				year;
60439583Swpaulregister const struct rule * const	rulep;
60539583Swpaulconst long				offset;
60639583Swpaul{
60739583Swpaul	register int	leapyear;
60839583Swpaul	register time_t	value;
60939583Swpaul	register int	i;
61039583Swpaul	int		d, m1, yy0, yy1, yy2, dow;
611102336Salfred
612102336Salfred	INITIALIZE(value);
61339583Swpaul	leapyear = isleap(year);
61439583Swpaul	switch (rulep->r_type) {
61536270Swpaul
61636270Swpaul	case JULIAN_DAY:
61739583Swpaul		/*
61836270Swpaul		** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
61936270Swpaul		** years.
62039583Swpaul		** In non-leap years, or if the day number is 59 or less, just
62139583Swpaul		** add SECSPERDAY times the day number-1 to the time of
62236270Swpaul		** January 1, midnight, to get the day.
62336270Swpaul		*/
62436270Swpaul		value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
62536270Swpaul		if (leapyear && rulep->r_day >= 60)
62636270Swpaul			value += SECSPERDAY;
627102336Salfred		break;
628102336Salfred
62939583Swpaul	case DAY_OF_YEAR:
63036270Swpaul		/*
63136270Swpaul		** n - day of year.
63236270Swpaul		** Just add SECSPERDAY times the day number to the time of
63336270Swpaul		** January 1, midnight, to get the day.
63436270Swpaul		*/
63536270Swpaul		value = janfirst + rulep->r_day * SECSPERDAY;
63639583Swpaul		break;
63736270Swpaul
63839583Swpaul	case MONTH_NTH_DAY_OF_WEEK:
63936270Swpaul		/*
64039583Swpaul		** Mm.n.d - nth "dth day" of month m.
64136270Swpaul		*/
64239583Swpaul		value = janfirst;
64336270Swpaul		for (i = 0; i < rulep->r_mon - 1; ++i)
64436270Swpaul			value += mon_lengths[leapyear][i] * SECSPERDAY;
64536270Swpaul
646102336Salfred		/*
647102336Salfred		** Use Zeller's Congruence to get day-of-week of first day of
64839583Swpaul		** month.
64936270Swpaul		*/
65036270Swpaul		m1 = (rulep->r_mon + 9) % 12 + 1;
65136270Swpaul		yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
65267087Swpaul		yy1 = yy0 / 100;
65336270Swpaul		yy2 = yy0 % 100;
65436270Swpaul		dow = ((26 * m1 - 2) / 10 +
65539583Swpaul			1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
65636270Swpaul		if (dow < 0)
65736270Swpaul			dow += DAYSPERWEEK;
65836270Swpaul
65936270Swpaul		/*
66036270Swpaul		** "dow" is the day-of-week of the first day of the month.  Get
66136270Swpaul		** the day-of-month (zero-origin) of the first "dow" day of the
66236270Swpaul		** month.
66336270Swpaul		*/
66436270Swpaul		d = rulep->r_day - dow;
66536270Swpaul		if (d < 0)
66636270Swpaul			d += DAYSPERWEEK;
66736270Swpaul		for (i = 1; i < rulep->r_week; ++i) {
66839583Swpaul			if (d + DAYSPERWEEK >=
66936270Swpaul				mon_lengths[leapyear][rulep->r_mon - 1])
67039583Swpaul					break;
67136270Swpaul			d += DAYSPERWEEK;
67236270Swpaul		}
67336270Swpaul
67436270Swpaul		/*
67536270Swpaul		** "d" is the day-of-month (zero-origin) of the day we want.
67639583Swpaul		*/
67736270Swpaul		value += d * SECSPERDAY;
67836270Swpaul		break;
67936270Swpaul	}
68036270Swpaul
68139583Swpaul	/*
68239583Swpaul	** "value" is the Epoch-relative time of 00:00:00 GMT on the day in
68339583Swpaul	** question.  To get the Epoch-relative time of the specified local
68439583Swpaul	** time on that day, add the transition time and the current offset
68536270Swpaul	** from GMT.
68636270Swpaul	*/
68736270Swpaul	return value + rulep->r_time + offset;
68836270Swpaul}
68939583Swpaul
69036270Swpaul/*
69136270Swpaul** Given a POSIX section 8-style TZ string, fill in the rule tables as
69239583Swpaul** appropriate.
69339583Swpaul*/
69436270Swpaul
69536270Swpaulstatic int
69639583Swpaultzparse(name, sp, lastditch)
69739583Swpaulconst char *			name;
69836270Swpaulregister struct state * const	sp;
69936270Swpaulconst int			lastditch;
70039583Swpaul{
70136270Swpaul	const char *			stdname;
70236270Swpaul	const char *			dstname;
70336270Swpaul	size_t				stdlen;
70436270Swpaul	size_t				dstlen;
70536270Swpaul	long				stdoffset;
70636270Swpaul	long				dstoffset;
70736270Swpaul	register time_t *		atp;
70839583Swpaul	register unsigned char *	typep;
70939583Swpaul	register char *			cp;
71036270Swpaul	register int			load_result;
71136270Swpaul
71236270Swpaul	INITIALIZE(dstname);
71336270Swpaul	stdname = name;
71436270Swpaul	if (lastditch) {
71539583Swpaul		stdlen = strlen(name);	/* length of standard zone name */
71636270Swpaul		name += stdlen;
71739583Swpaul		if (stdlen >= sizeof sp->chars)
71836270Swpaul			stdlen = (sizeof sp->chars) - 1;
71936270Swpaul	} else {
72039583Swpaul		name = getzname(name);
72136270Swpaul		stdlen = name - stdname;
72236270Swpaul		if (stdlen < 3)
72336270Swpaul			return -1;
72436270Swpaul	}
72539583Swpaul	if (*name == '\0')
72639583Swpaul		return -1;	/* was "stdoffset = 0;" */
72736270Swpaul	else {
72836270Swpaul		name = getoffset(name, &stdoffset);
72936270Swpaul		if (name == NULL)
73039583Swpaul			return -1;
73136270Swpaul	}
73236270Swpaul	load_result = tzload(TZDEFRULES, sp);
73336270Swpaul	if (load_result != 0)
73436270Swpaul		sp->leapcnt = 0;		/* so, we're off a little */
73536270Swpaul	if (*name != '\0') {
73636270Swpaul		dstname = name;
73736270Swpaul		name = getzname(name);
738102336Salfred		dstlen = name - dstname;	/* length of DST zone name */
739102336Salfred		if (dstlen < 3)
74039583Swpaul			return -1;
74136270Swpaul		if (*name != '\0' && *name != ',' && *name != ';') {
74236270Swpaul			name = getoffset(name, &dstoffset);
74336270Swpaul			if (name == NULL)
74436270Swpaul				return -1;
74536270Swpaul		} else	dstoffset = stdoffset - SECSPERHOUR;
74639583Swpaul		if (*name == ',' || *name == ';') {
74736270Swpaul			struct rule	start;
74836270Swpaul			struct rule	end;
74936270Swpaul			register int	year;
75036270Swpaul			register time_t	janfirst;
75136270Swpaul			time_t		starttime;
75236270Swpaul			time_t		endtime;
75336270Swpaul
75436270Swpaul			++name;
75536270Swpaul			if ((name = getrule(name, &start)) == NULL)
75636270Swpaul				return -1;
75736270Swpaul			if (*name++ != ',')
75836270Swpaul				return -1;
75939583Swpaul			if ((name = getrule(name, &end)) == NULL)
76036270Swpaul				return -1;
76139583Swpaul			if (*name != '\0')
76236270Swpaul				return -1;
76336270Swpaul			sp->typecnt = 2;	/* standard time and DST */
76436270Swpaul			/*
76536270Swpaul			** Two transitions per year, from EPOCH_YEAR to 2037.
76636270Swpaul			*/
76739583Swpaul			sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
76836270Swpaul			if (sp->timecnt > TZ_MAX_TIMES)
76939583Swpaul				return -1;
77039583Swpaul			sp->ttis[0].tt_gmtoff = -dstoffset;
77139583Swpaul			sp->ttis[0].tt_isdst = 1;
77239583Swpaul			sp->ttis[0].tt_abbrind = stdlen + 1;
77339583Swpaul			sp->ttis[1].tt_gmtoff = -stdoffset;
77439583Swpaul			sp->ttis[1].tt_isdst = 0;
77536270Swpaul			sp->ttis[1].tt_abbrind = 0;
77639583Swpaul			atp = sp->ats;
77739583Swpaul			typep = sp->types;
77836270Swpaul			janfirst = 0;
77936270Swpaul			for (year = EPOCH_YEAR; year <= 2037; ++year) {
78036270Swpaul				starttime = transtime(janfirst, year, &start,
78136270Swpaul					stdoffset);
78239583Swpaul				endtime = transtime(janfirst, year, &end,
78336270Swpaul					dstoffset);
78436270Swpaul				if (starttime > endtime) {
78536270Swpaul					*atp++ = endtime;
78639583Swpaul					*typep++ = 1;	/* DST ends */
78736270Swpaul					*atp++ = starttime;
78836270Swpaul					*typep++ = 0;	/* DST begins */
78936270Swpaul				} else {
79036270Swpaul					*atp++ = starttime;
791102336Salfred					*typep++ = 0;	/* DST begins */
792102336Salfred					*atp++ = endtime;
79350462Swpaul					*typep++ = 1;	/* DST ends */
79450462Swpaul				}
79550462Swpaul				janfirst += year_lengths[isleap(year)] *
79636270Swpaul					SECSPERDAY;
79736270Swpaul			}
79836270Swpaul		} else {
79950462Swpaul			register long	theirstdoffset;
80036270Swpaul			register long	theirdstoffset;
80136270Swpaul			register long	theiroffset;
80250462Swpaul			register int	isdst;
80336270Swpaul			register int	i;
80439583Swpaul			register int	j;
80536270Swpaul
80636270Swpaul			if (*name != '\0')
80736270Swpaul				return -1;
80836270Swpaul			if (load_result != 0)
809102336Salfred				return -1;
810102336Salfred			/*
81150462Swpaul			** Initial values of theirstdoffset and theirdstoffset.
81250462Swpaul			*/
81350462Swpaul			theirstdoffset = 0;
81436270Swpaul			for (i = 0; i < sp->timecnt; ++i) {
81536270Swpaul				j = sp->types[i];
81636270Swpaul				if (!sp->ttis[j].tt_isdst) {
81750462Swpaul					theirstdoffset =
81836270Swpaul						-sp->ttis[j].tt_gmtoff;
81936270Swpaul					break;
82050462Swpaul				}
82136270Swpaul			}
82236270Swpaul			theirdstoffset = 0;
82336270Swpaul			for (i = 0; i < sp->timecnt; ++i) {
82439583Swpaul				j = sp->types[i];
82536270Swpaul				if (sp->ttis[j].tt_isdst) {
82650462Swpaul					theirdstoffset =
82736270Swpaul						-sp->ttis[j].tt_gmtoff;
82836270Swpaul					break;
829102336Salfred				}
830102336Salfred			}
83150462Swpaul			/*
83250462Swpaul			** Initially we're assumed to be in standard time.
83336270Swpaul			*/
83450462Swpaul			isdst = FALSE;
83536270Swpaul			theiroffset = theirstdoffset;
83650462Swpaul			/*
83750462Swpaul			** Now juggle transition times and types
83836270Swpaul			** tracking offsets as you do.
83950462Swpaul			*/
84050462Swpaul			for (i = 0; i < sp->timecnt; ++i) {
84136270Swpaul				j = sp->types[i];
84250462Swpaul				sp->types[i] = sp->ttis[j].tt_isdst;
84336270Swpaul				if (sp->ttis[j].tt_ttisgmt) {
84436270Swpaul					/* No adjustment to transition time */
84536270Swpaul				} else {
84636270Swpaul					/*
84736270Swpaul					** If summer time is in effect, and the
84836270Swpaul					** transition time was not specified as
84950462Swpaul					** standard time, add the summer time
85036270Swpaul					** offset to the transition time;
851102336Salfred					** otherwise, add the standard time
852102336Salfred					** offset to the transition time.
85336270Swpaul					*/
85436270Swpaul					/*
85536270Swpaul					** Transitions from DST to DDST
85650462Swpaul					** will effectively disappear since
85750462Swpaul					** POSIX provides for only one DST
85836270Swpaul					** offset.
85950462Swpaul					*/
86036270Swpaul					if (isdst && !sp->ttis[j].tt_ttisstd) {
86150462Swpaul						sp->ats[i] += dstoffset -
86239583Swpaul							theirdstoffset;
86336270Swpaul					} else {
86450462Swpaul						sp->ats[i] += stdoffset -
86539583Swpaul							theirstdoffset;
86636270Swpaul					}
86736270Swpaul				}
86836270Swpaul				theiroffset = -sp->ttis[j].tt_gmtoff;
86936270Swpaul				if (sp->ttis[j].tt_isdst)
87036270Swpaul					theirdstoffset = theiroffset;
87136270Swpaul				else	theirstdoffset = theiroffset;
87236464Swpaul			}
87336464Swpaul			/*
87436464Swpaul			** Finally, fill in ttis.
87536464Swpaul			** ttisstd and ttisgmt need not be handled.
87636464Swpaul			*/
87736464Swpaul			sp->ttis[0].tt_gmtoff = -stdoffset;
87836464Swpaul			sp->ttis[0].tt_isdst = FALSE;
87936464Swpaul			sp->ttis[0].tt_abbrind = 0;
88036464Swpaul			sp->ttis[1].tt_gmtoff = -dstoffset;
881123289Sobrien			sp->ttis[1].tt_isdst = TRUE;
882122625Sobrien			sp->ttis[1].tt_abbrind = stdlen + 1;
883123289Sobrien		}
88436270Swpaul	} else {
885123289Sobrien		dstlen = 0;
88636270Swpaul		sp->typecnt = 1;		/* only standard time */
88736464Swpaul		sp->timecnt = 0;
88836464Swpaul		sp->ttis[0].tt_gmtoff = -stdoffset;
88936464Swpaul		sp->ttis[0].tt_isdst = 0;
89036270Swpaul		sp->ttis[0].tt_abbrind = 0;
89136270Swpaul	}
89239583Swpaul	sp->charcnt = stdlen + 1;
89339583Swpaul	if (dstlen != 0)
89439583Swpaul		sp->charcnt += dstlen + 1;
89539583Swpaul	if (sp->charcnt > sizeof sp->chars)
89639583Swpaul		return -1;
89739583Swpaul	cp = sp->chars;
89839583Swpaul	(void) strncpy(cp, stdname, stdlen);
899102336Salfred	cp += stdlen;
900102336Salfred	*cp++ = '\0';
90139583Swpaul	if (dstlen != 0) {
90241656Swpaul		(void) strncpy(cp, dstname, dstlen);
90339583Swpaul		*(cp + dstlen) = '\0';
90439583Swpaul	}
90539583Swpaul	return 0;
90639583Swpaul}
90739583Swpaul
90839583Swpaulstatic void
90939583Swpaulgmtload(sp)
91039583Swpaulstruct state * const	sp;
91139583Swpaul{
91239583Swpaul	if (tzload(gmt, sp) != 0)
91339583Swpaul		(void) tzparse(gmt, sp, TRUE);
91439583Swpaul}
91539583Swpaul
91639583Swpaul#ifndef STD_INSPIRED
91739583Swpaul/*
91839583Swpaul** A non-static declaration of tzsetwall in a system header file
91939583Swpaul** may cause a warning about this upcoming static declaration...
92039583Swpaul*/
92139583Swpaulstatic
92239583Swpaul#endif /* !defined STD_INSPIRED */
92339583Swpaul#ifdef	_THREAD_SAFE
92439583Swpaulvoid
92539583Swpaultzsetwall_basic P((void))
92639583Swpaul#else
92739583Swpaulvoid
92839583Swpaultzsetwall P((void))
92939583Swpaul#endif
93039583Swpaul{
93139583Swpaul	if (lcl_is_set < 0)
932102336Salfred		return;
933102336Salfred	lcl_is_set = -1;
93436270Swpaul
93536270Swpaul#ifdef ALL_STATE
93636270Swpaul	if (lclptr == NULL) {
93736270Swpaul		lclptr = (struct state *) malloc(sizeof *lclptr);
93839583Swpaul		if (lclptr == NULL) {
93936270Swpaul			settzname();	/* all we can do */
94039583Swpaul			return;
941147256Sbrooks		}
94236270Swpaul	}
94339583Swpaul#endif /* defined ALL_STATE */
94439583Swpaul	if (tzload((char *) NULL, lclptr) != 0)
94541656Swpaul		gmtload(lclptr);
94639583Swpaul	settzname();
94739583Swpaul}
94839583Swpaul
94939583Swpaul#ifdef	_THREAD_SAFE
95039583Swpaulvoid
95136270Swpaultzsetwall P((void))
95236270Swpaul{
95336270Swpaul	pthread_mutex_lock(&lcl_mutex);
95439583Swpaul	tzsetwall_basic();
955195049Srwatson	pthread_mutex_unlock(&lcl_mutex);
95672084Sphk}
95736270Swpaul#endif
95836270Swpaul
95939583Swpaul#ifdef	_THREAD_SAFE
96039583Swpaulstatic void
96139583Swpaultzset_basic P((void))
96239583Swpaul#else
96339583Swpaulvoid
96439583Swpaultzset P((void))
96539583Swpaul#endif
96639583Swpaul{
96739583Swpaul	register const char *	name;
96839583Swpaul
96939583Swpaul	name = getenv("TZ");
97039583Swpaul	if (name == NULL) {
971122625Sobrien		tzsetwall();
97236270Swpaul		return;
97336270Swpaul	}
97436270Swpaul
97536270Swpaul	if (lcl_is_set > 0  &&  strcmp(lcl_TZname, name) == 0)
97636317Swpaul		return;
97736270Swpaul	lcl_is_set = (strlen(name) < sizeof(lcl_TZname));
978195049Srwatson	if (lcl_is_set)
97936270Swpaul		(void) strcpy(lcl_TZname, name);
98036270Swpaul
98139583Swpaul#ifdef ALL_STATE
98239583Swpaul	if (lclptr == NULL) {
98336270Swpaul		lclptr = (struct state *) malloc(sizeof *lclptr);
98436270Swpaul		if (lclptr == NULL) {
98536270Swpaul			settzname();	/* all we can do */
98636270Swpaul			return;
98739583Swpaul		}
98839583Swpaul	}
98939583Swpaul#endif /* defined ALL_STATE */
99039583Swpaul	if (*name == '\0') {
99139583Swpaul		/*
99239583Swpaul		** User wants it fast rather than right.
993102336Salfred		*/
994102336Salfred		lclptr->leapcnt = 0;		/* so, we're off a little */
99550468Swpaul		lclptr->timecnt = 0;
99650468Swpaul		lclptr->ttis[0].tt_gmtoff = 0;
99739583Swpaul		lclptr->ttis[0].tt_abbrind = 0;
99839583Swpaul		(void) strcpy(lclptr->chars, gmt);
99950468Swpaul	} else if (tzload(name, lclptr) != 0)
100039583Swpaul		if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
100150468Swpaul			(void) gmtload(lclptr);
100239583Swpaul	settzname();
100350468Swpaul}
100439583Swpaul
100550468Swpaul#ifdef	_THREAD_SAFE
100639583Swpaulvoid
100750468Swpaultzset P((void))
100850468Swpaul{
100939583Swpaul	pthread_mutex_lock(&lcl_mutex);
101050468Swpaul	tzset_basic();
101139583Swpaul	pthread_mutex_unlock(&lcl_mutex);
101250468Swpaul}
101339583Swpaul#endif
101450468Swpaul
101539583Swpaul/*
101650468Swpaul** The easy way to behave "as if no library function calls" localtime
101739583Swpaul** is to not call it--so we drop its guts into "localsub", which can be
101839583Swpaul** freely called.  (And no, the PANS doesn't require the above behavior--
101939583Swpaul** but it *is* desirable.)
1020102336Salfred**
1021102336Salfred** The unused offset argument is for the benefit of mktime variants.
102239583Swpaul*/
102336270Swpaul
102436270Swpaul/*ARGSUSED*/
102539583Swpaulstatic void
102636270Swpaullocalsub(timep, offset, tmp)
102736270Swpaulconst time_t * const	timep;
102839583Swpaulconst long		offset;
102950468Swpaulstruct tm * const	tmp;
103036270Swpaul{
103139583Swpaul	register struct state *		sp;
103236270Swpaul	register const struct ttinfo *	ttisp;
103336270Swpaul	register int			i;
103439583Swpaul	const time_t			t = *timep;
103539583Swpaul
103636270Swpaul	sp = lclptr;
103736270Swpaul#ifdef ALL_STATE
103839583Swpaul	if (sp == NULL) {
103939583Swpaul		gmtsub(timep, offset, tmp);
104036270Swpaul		return;
104136270Swpaul	}
104236270Swpaul#endif /* defined ALL_STATE */
104336270Swpaul	if (sp->timecnt == 0 || t < sp->ats[0]) {
104436270Swpaul		i = 0;
104539583Swpaul		while (sp->ttis[i].tt_isdst)
104645155Swpaul			if (++i >= sp->typecnt) {
104739583Swpaul				i = 0;
104836270Swpaul				break;
104939583Swpaul			}
105036270Swpaul	} else {
105136270Swpaul		for (i = 1; i < sp->timecnt; ++i)
105245155Swpaul			if (t < sp->ats[i])
105345155Swpaul				break;
105445155Swpaul		i = sp->types[i - 1];
105545155Swpaul	}
105636270Swpaul	ttisp = &sp->ttis[i];
105736270Swpaul	/*
105836270Swpaul	** To get (wrong) behavior that's compatible with System V Release 2.0
105936270Swpaul	** you'd replace the statement below with
106036270Swpaul	**	t += ttisp->tt_gmtoff;
106139583Swpaul	**	timesub(&t, 0L, sp, tmp);
106236270Swpaul	*/
106336270Swpaul	timesub(&t, ttisp->tt_gmtoff, sp, tmp);
106439583Swpaul	tmp->tm_isdst = ttisp->tt_isdst;
106539583Swpaul	tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
106636270Swpaul#ifdef TM_ZONE
106736270Swpaul	tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
106839583Swpaul#endif /* defined TM_ZONE */
106936270Swpaul}
107036270Swpaul
107139583Swpaul#ifdef	_THREAD_SAFE
107236270Swpaulint
107336270Swpaullocaltime_r(timep, p_tm)
107436270Swpaulconst time_t * const	timep;
107536270Swpaulstruct tm *p_tm;
107636270Swpaul{
107736270Swpaul	pthread_mutex_lock(&lcl_mutex);
107836270Swpaul	tzset();
107936270Swpaul	localsub(timep, 0L, p_tm);
108036270Swpaul	pthread_mutex_unlock(&lcl_mutex);
108139583Swpaul	return(0);
108236270Swpaul}
1083102336Salfred#endif
1084102336Salfred
108548992Swpaulstruct tm *
108636270Swpaullocaltime(timep)
108736270Swpaulconst time_t * const	timep;
108836270Swpaul{
108936270Swpaul#ifdef	_THREAD_SAFE
109036270Swpaul	static pthread_mutex_t localtime_mutex = PTHREAD_MUTEX_INITIALIZER;
109136270Swpaul	static pthread_key_t localtime_key = -1;
109248992Swpaul	struct tm *p_tm;
109348992Swpaul
109448992Swpaul	pthread_mutex_lock(&localtime_mutex);
1095142398Simp	if (localtime_key < 0) {
109648992Swpaul		if (pthread_keycreate(&localtime_key, free) < 0) {
109736270Swpaul			pthread_mutex_unlock(&localtime_mutex);
109836270Swpaul			return(NULL);
109936270Swpaul		}
110048992Swpaul	}
110136270Swpaul	pthread_mutex_unlock(&localtime_mutex);
110236270Swpaul	if (pthread_getspecific(localtime_key,(void **) &p_tm) != 0) {
1103102336Salfred		return(NULL);
1104102336Salfred	} else if (p_tm == NULL) {
110548992Swpaul		if ((p_tm = (struct tm *)malloc(sizeof(struct tm))) == NULL) {
110636270Swpaul			return(NULL);
110767087Swpaul		}
110839583Swpaul		pthread_setspecific(localtime_key, p_tm);
110939583Swpaul	}
111039583Swpaul	pthread_mutex_lock(&lcl_mutex);
111139583Swpaul	tzset();
111248992Swpaul	localsub(timep, 0L, p_tm);
1113147256Sbrooks	pthread_mutex_unlock(&lcl_mutex);
111436270Swpaul	return p_tm;
111548992Swpaul#else
111648992Swpaul	tzset();
111748992Swpaul	localsub(timep, 0L, &tm);
1118162315Sglebius	return &tm;
111948992Swpaul#endif
112039583Swpaul}
112139583Swpaul
112239583Swpaul/*
112339583Swpaul** gmtsub is to gmtime as localsub is to localtime.
112436270Swpaul*/
112539583Swpaul
112639583Swpaulstatic void
112736270Swpaulgmtsub(timep, offset, tmp)
112839583Swpaulconst time_t * const	timep;
1129105599Sbrooksconst long		offset;
1130112878Sjhbstruct tm * const	tmp;
113136270Swpaul{
113236270Swpaul#ifdef	_THREAD_SAFE
113393818Sjhb	pthread_mutex_lock(&gmt_mutex);
1134150171Sjhb#endif
113569583Swpaul	if (!gmt_is_set) {
113636270Swpaul		gmt_is_set = TRUE;
113736270Swpaul#ifdef ALL_STATE
113836270Swpaul		gmtptr = (struct state *) malloc(sizeof *gmtptr);
113972813Swpaul		if (gmtptr != NULL)
114036270Swpaul#endif /* defined ALL_STATE */
114139583Swpaul			gmtload(gmtptr);
114239583Swpaul	}
114348992Swpaul#ifdef	_THREAD_SAFE
1144127135Snjl	pthread_mutex_unlock(&gmt_mutex);
1145127135Snjl#endif
114648992Swpaul	timesub(timep, offset, gmtptr, tmp);
114748992Swpaul#ifdef TM_ZONE
114848992Swpaul	/*
114948992Swpaul	** Could get fancy here and deliver something such as
115048992Swpaul	** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero,
115148992Swpaul	** but this is no time for a treasure hunt.
115248992Swpaul	*/
1153127135Snjl	if (offset != 0)
1154127135Snjl		tmp->TM_ZONE = wildabbr;
115545155Swpaul	else {
115639583Swpaul#ifdef ALL_STATE
115748992Swpaul		if (gmtptr == NULL)
1158127135Snjl			tmp->TM_ZONE = gmt;
1159127135Snjl		else	tmp->TM_ZONE = gmtptr->chars;
116048992Swpaul#endif /* defined ALL_STATE */
116148992Swpaul#ifndef ALL_STATE
1162127135Snjl		tmp->TM_ZONE = gmtptr->chars;
1163127135Snjl#endif /* State Farm */
116436270Swpaul	}
116539583Swpaul#endif /* defined TM_ZONE */
116636270Swpaul}
116748992Swpaul
1168105599Sbrooksstruct tm *
116948992Swpaulgmtime(timep)
117048992Swpaulconst time_t * const	timep;
117148992Swpaul{
117248992Swpaul#ifdef	_THREAD_SAFE
117339583Swpaul	static pthread_mutex_t gmtime_mutex = PTHREAD_MUTEX_INITIALIZER;
117439583Swpaul	static pthread_key_t gmtime_key = -1;
117539583Swpaul	struct tm *p_tm;
117639583Swpaul
117739583Swpaul	pthread_mutex_lock(&gmtime_mutex);
117839583Swpaul	if (gmtime_key < 0) {
117939583Swpaul		if (pthread_keycreate(&gmtime_key, free) < 0) {
118048992Swpaul			pthread_mutex_unlock(&gmtime_mutex);
118139583Swpaul			return(NULL);
118248992Swpaul		}
118339583Swpaul	}
118436270Swpaul	pthread_mutex_unlock(&gmtime_mutex);
118536270Swpaul	if (pthread_getspecific(gmtime_key,(void **) &p_tm) != 0) {
118648992Swpaul		return(NULL);
1187127135Snjl	} else if (p_tm == NULL) {
118848992Swpaul		if ((p_tm = (struct tm *)malloc(sizeof(struct tm))) == NULL) {
118948992Swpaul			return(NULL);
119048992Swpaul		}
1191105599Sbrooks		pthread_setspecific(gmtime_key, p_tm);
119248992Swpaul	}
119336270Swpaul	gmtsub(timep, 0L, p_tm);
119436270Swpaul	return(p_tm);
119536270Swpaul#else
119636270Swpaul	gmtsub(timep, 0L, &tm);
119751439Swpaul	return &tm;
119836270Swpaul#endif
119951439Swpaul}
120051657Swpaul
120139583Swpaul#ifdef	_THREAD_SAFE
120251439Swpaulint
1203105599Sbrooksgmtime_r(const time_t * timep, struct tm * tm)
120448992Swpaul{
120536270Swpaul	gmtsub(timep, 0L, tm);
120636270Swpaul	return(0);
120736270Swpaul}
120839583Swpaul#endif
120939583Swpaul
121039583Swpaul#ifdef STD_INSPIRED
121143235Swpaul
121239583Swpaulstruct tm *
121339583Swpaulofftime(timep, offset)
121439583Swpaulconst time_t * const	timep;
121539583Swpaulconst long		offset;
121639583Swpaul{
121739583Swpaul	gmtsub(timep, offset, &tm);
121850468Swpaul	return &tm;
121939583Swpaul}
122039583Swpaul
122138030Swpaul#endif /* defined STD_INSPIRED */
122239583Swpaul
122339583Swpaulstatic void
1224147256Sbrookstimesub(timep, offset, sp, tmp)
1225105599Sbrooksconst time_t * const			timep;
122648992Swpaulconst long				offset;
122739583Swpaulregister const struct state * const	sp;
122839583Swpaulregister struct tm * const		tmp;
122939583Swpaul{
123039583Swpaul	register const struct lsinfo *	lp;
123139583Swpaul	register long			days;
123239583Swpaul	register long			rem;
123339583Swpaul	register int			y;
123439583Swpaul	register int			yleap;
123539583Swpaul	register const int *		ip;
123639583Swpaul	register long			corr;
123739583Swpaul	register int			hit;
123839583Swpaul	register int			i;
123939583Swpaul
124039583Swpaul	corr = 0;
124139583Swpaul	hit = 0;
124239583Swpaul#ifdef ALL_STATE
124339583Swpaul	i = (sp == NULL) ? 0 : sp->leapcnt;
124439583Swpaul#endif /* defined ALL_STATE */
124539583Swpaul#ifndef ALL_STATE
124639583Swpaul	i = sp->leapcnt;
1247147256Sbrooks#endif /* State Farm */
124839583Swpaul	while (--i >= 0) {
124939583Swpaul		lp = &sp->lsis[i];
125039583Swpaul		if (*timep >= lp->ls_trans) {
125139583Swpaul			if (*timep == lp->ls_trans) {
1252147256Sbrooks				hit = ((i == 0 && lp->ls_corr > 0) ||
1253147256Sbrooks					lp->ls_corr > sp->lsis[i - 1].ls_corr);
1254147256Sbrooks				if (hit)
1255147256Sbrooks					while (i > 0 &&
1256147256Sbrooks						sp->lsis[i].ls_trans ==
1257147256Sbrooks						sp->lsis[i - 1].ls_trans + 1 &&
125839583Swpaul						sp->lsis[i].ls_corr ==
1259121816Sbrooks						sp->lsis[i - 1].ls_corr + 1) {
1260150171Sjhb							++hit;
126139583Swpaul							--i;
126239583Swpaul					}
126339583Swpaul			}
126439583Swpaul			corr = lp->ls_corr;
126551439Swpaul			break;
1266169414Syar		}
1267169414Syar	}
1268150171Sjhb	days = *timep / SECSPERDAY;
126939583Swpaul	rem = *timep % SECSPERDAY;
127039583Swpaul#ifdef mc68k
127139583Swpaul	if (*timep == 0x80000000) {
127250468Swpaul		/*
127339583Swpaul		** A 3B1 muffs the division on the most negative number.
127439583Swpaul		*/
127536270Swpaul		days = -24855;
127650462Swpaul		rem = -11648;
127750462Swpaul	}
127850462Swpaul#endif /* defined mc68k */
1279213894Smarius	rem += (offset - corr);
1280213894Smarius	while (rem < 0) {
128136270Swpaul		rem += SECSPERDAY;
1282213894Smarius		--days;
1283213894Smarius	}
128445155Swpaul	while (rem >= SECSPERDAY) {
128545155Swpaul		rem -= SECSPERDAY;
128645155Swpaul		++days;
128745155Swpaul	}
128845155Swpaul	tmp->tm_hour = (int) (rem / SECSPERHOUR);
128945155Swpaul	rem = rem % SECSPERHOUR;
129045155Swpaul	tmp->tm_min = (int) (rem / SECSPERMIN);
129145166Swpaul	/*
129245155Swpaul	** A positive leap second requires a special
129345155Swpaul	** representation.  This uses "... ??:59:60" et seq.
129445155Swpaul	*/
129545155Swpaul	tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
129645155Swpaul	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
129736270Swpaul	if (tmp->tm_wday < 0)
129836270Swpaul		tmp->tm_wday += DAYSPERWEEK;
129939583Swpaul	y = EPOCH_YEAR;
130063090Sarchie#define LEAPS_THRU_END_OF(y)	((y) / 4 - (y) / 100 + (y) / 400)
130139583Swpaul	while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
1302147256Sbrooks		register int	newy;
130338030Swpaul
1304113609Snjl		newy = y + days / DAYSPERNYEAR;
1305150171Sjhb		if (days < 0)
1306166901Spiso			--newy;
1307112872Snjl		days -= (newy - y) * DAYSPERNYEAR +
1308112872Snjl			LEAPS_THRU_END_OF(newy - 1) -
1309112872Snjl			LEAPS_THRU_END_OF(y - 1);
1310113609Snjl		y = newy;
1311112872Snjl	}
1312112872Snjl	tmp->tm_year = y - TM_YEAR_BASE;
1313112872Snjl	tmp->tm_yday = (int) days;
131436270Swpaul	ip = mon_lengths[yleap];
1315112872Snjl	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
1316112872Snjl		days = days - (long) ip[tmp->tm_mon];
1317112872Snjl	tmp->tm_mday = (int) (days + 1);
131848992Swpaul	tmp->tm_isdst = 0;
131936270Swpaul#ifdef TM_GMTOFF
132036270Swpaul	tmp->TM_GMTOFF = offset;
1321113609Snjl#endif /* defined TM_GMTOFF */
1322113609Snjl}
1323113609Snjl
1324113609Snjlchar *
1325113609Snjlctime(timep)
1326113609Snjlconst time_t * const	timep;
1327113609Snjl{
1328102336Salfred/*
1329102336Salfred** Section 4.12.3.2 of X3.159-1989 requires that
133048992Swpaul**	The ctime funciton converts the calendar time pointed to by timer
133148992Swpaul**	to local time in the form of a string.  It is equivalent to
133248992Swpaul**		asctime(localtime(timer))
133348992Swpaul*/
133448992Swpaul	return asctime(localtime(timep));
133548992Swpaul}
1336112880Sjhb
1337147256Sbrooks/*
133848992Swpaul** Adapted from code provided by Robert Elz, who writes:
1339113609Snjl**	The "best" way to do mktime I think is based on an idea of Bob
1340113812Simp**	Kridle's (so its said...) from a long time ago.
1341199560Sjhb**	[kridle@xinet.com as of 1996-01-16.]
1342150171Sjhb**	It does a binary search of the time_t space.  Since time_t's are
1343113609Snjl**	just 32 bits, its a max of 32 iterations (even at 64 bits it
1344150171Sjhb**	would still be very reasonable).
1345150171Sjhb*/
1346150213Sru
1347113609Snjl#ifndef WRONG
1348112872Snjl#define WRONG	(-1)
1349113609Snjl#endif /* !defined WRONG */
135048992Swpaul
1351112872Snjl/*
1352112872Snjl** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).
135350462Swpaul*/
135450462Swpaul
135548992Swpaulstatic int
1356112872Snjlincrement_overflow(number, delta)
1357112872Snjlint *	number;
1358112872Snjlint	delta;
1359112872Snjl{
1360112872Snjl	int	number0;
1361112872Snjl
136248992Swpaul	number0 = *number;
1363151297Sru	*number += delta;
1364151297Sru	return (*number < number0) != (delta < 0);
1365151297Sru}
136667087Swpaul
136748992Swpaulstatic int
136848992Swpaulnormalize_overflow(tensptr, unitsptr, base)
136948992Swpaulint * const	tensptr;
137048992Swpaulint * const	unitsptr;
137136270Swpaulconst int	base;
137236270Swpaul{
137336270Swpaul	register int	tensdelta;
1374102336Salfred
1375102336Salfred	tensdelta = (*unitsptr >= 0) ?
137636270Swpaul		(*unitsptr / base) :
137736270Swpaul		(-1 - (-1 - *unitsptr) / base);
137836270Swpaul	*unitsptr -= tensdelta * base;
137936270Swpaul	return increment_overflow(tensptr, tensdelta);
138036270Swpaul}
138136270Swpaul
138236270Swpaulstatic int
138336270Swpaultmcomp(atmp, btmp)
138436270Swpaulregister const struct tm * const atmp;
138536270Swpaulregister const struct tm * const btmp;
138636270Swpaul{
138736270Swpaul	register int	result;
138836270Swpaul
138936270Swpaul	if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
139036270Swpaul		(result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
139136270Swpaul		(result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
139236270Swpaul		(result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
139336270Swpaul		(result = (atmp->tm_min - btmp->tm_min)) == 0)
139436270Swpaul			result = atmp->tm_sec - btmp->tm_sec;
139536270Swpaul	return result;
139636270Swpaul}
139736270Swpaul
139836270Swpaulstatic time_t
139936270Swpaultime2(tmp, funcp, offset, okayp)
140036270Swpaulstruct tm * const	tmp;
140136270Swpaulvoid (* const		funcp) P((const time_t*, long, struct tm*));
1402102336Salfredconst long		offset;
1403102336Salfredint * const		okayp;
140436270Swpaul{
140536270Swpaul	register const struct state *	sp;
140636270Swpaul	register int			dir;
140736270Swpaul	register int			bits;
140836270Swpaul	register int			i, j ;
140936270Swpaul	register int			saved_seconds;
141036270Swpaul	time_t				newt;
141136270Swpaul	time_t				t;
141236270Swpaul	struct tm			yourtm, mytm;
141340795Swpaul
141436270Swpaul	*okayp = FALSE;
141537626Swpaul	yourtm = *tmp;
141639583Swpaul	if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
141739583Swpaul		return WRONG;
141840795Swpaul	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
141936270Swpaul		return WRONG;
142036270Swpaul	if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
142136270Swpaul		return WRONG;
142236270Swpaul	/*
142336270Swpaul	** Turn yourtm.tm_year into an actual year number for now.
142436270Swpaul	** It is converted back to an offset from TM_YEAR_BASE later.
142536270Swpaul	*/
142636270Swpaul	if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
142736270Swpaul		return WRONG;
142836270Swpaul	while (yourtm.tm_mday <= 0) {
142936270Swpaul		if (increment_overflow(&yourtm.tm_year, -1))
143036270Swpaul			return WRONG;
143136270Swpaul		i = yourtm.tm_year + (1 < yourtm.tm_mon);
143236270Swpaul		yourtm.tm_mday += year_lengths[isleap(i)];
143336270Swpaul	}
1434102336Salfred	while (yourtm.tm_mday > DAYSPERLYEAR) {
1435102336Salfred		i = yourtm.tm_year + (1 < yourtm.tm_mon);
143636270Swpaul		yourtm.tm_mday -= year_lengths[isleap(i)];
143737626Swpaul		if (increment_overflow(&yourtm.tm_year, 1))
143836270Swpaul			return WRONG;
143936270Swpaul	}
144036270Swpaul	for ( ; ; ) {
1441150171Sjhb		i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
144287846Sluigi		if (yourtm.tm_mday <= i)
144336270Swpaul			break;
144436270Swpaul		yourtm.tm_mday -= i;
144536270Swpaul		if (++yourtm.tm_mon >= MONSPERYEAR) {
144636270Swpaul			yourtm.tm_mon = 0;
144736270Swpaul			if (increment_overflow(&yourtm.tm_year, 1))
144836270Swpaul				return WRONG;
144937626Swpaul		}
145037626Swpaul	}
145156060Swpaul	if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
145236270Swpaul		return WRONG;
145336270Swpaul	if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
145436270Swpaul		/*
145536270Swpaul		** We can't set tm_sec to 0, because that might push the
145636270Swpaul		** time below the minimum representable time.
145736270Swpaul		** Set tm_sec to 59 instead.
145836270Swpaul		** This assumes that the minimum representable time is
145936270Swpaul		** not in the same minute that a leap second was deleted from,
146036270Swpaul		** which is a safer assumption than using 58 would be.
146136270Swpaul		*/
146236270Swpaul		if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
146336270Swpaul			return WRONG;
146436270Swpaul		saved_seconds = yourtm.tm_sec;
146536270Swpaul		yourtm.tm_sec = SECSPERMIN - 1;
146636270Swpaul	} else {
146736270Swpaul		saved_seconds = yourtm.tm_sec;
146836270Swpaul		yourtm.tm_sec = 0;
146936270Swpaul	}
147036270Swpaul	/*
147136270Swpaul	** Divide the search space in half
147236270Swpaul	** (this works whether time_t is signed or unsigned).
147336270Swpaul	*/
147436270Swpaul	bits = TYPE_BIT(time_t) - 1;
147536270Swpaul	/*
147636270Swpaul	** If time_t is signed, then 0 is just above the median,
147736270Swpaul	** assuming two's complement arithmetic.
1478102336Salfred	** If time_t is unsigned, then (1 << bits) is just above the median.
1479102336Salfred	*/
148036270Swpaul	t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
148136270Swpaul	for ( ; ; ) {
148236270Swpaul		(*funcp)(&t, offset, &mytm);
148336270Swpaul		dir = tmcomp(&mytm, &yourtm);
148436270Swpaul		if (dir != 0) {
148536270Swpaul			if (bits-- < 0)
148636270Swpaul				return WRONG;
148736270Swpaul			if (bits < 0)
148837626Swpaul				--t; /* may be needed if new t is minimal */
148936270Swpaul			else if (dir > 0)
149036270Swpaul				t -= ((time_t) 1) << bits;
1491147256Sbrooks			else	t += ((time_t) 1) << bits;
149236270Swpaul			continue;
1493122689Ssam		}
1494122689Ssam		if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
149556060Swpaul			break;
149656060Swpaul		/*
149756060Swpaul		** Right time, wrong type.
149856060Swpaul		** Hunt for right time, right type.
149936270Swpaul		** It's okay to guess wrong since the guess
150036270Swpaul		** gets checked.
150136270Swpaul		*/
150236270Swpaul		/*
150336270Swpaul		** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
150439583Swpaul		*/
150539583Swpaul		sp = (const struct state *)
150639583Swpaul			(((void *) funcp == (void *) localsub) ?
150739583Swpaul			lclptr : gmtptr);
150839583Swpaul#ifdef ALL_STATE
150939583Swpaul		if (sp == NULL)
151039583Swpaul			return WRONG;
151136270Swpaul#endif /* defined ALL_STATE */
151236270Swpaul		for (i = sp->typecnt - 1; i >= 0; --i) {
151336270Swpaul			if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
151436270Swpaul				continue;
151536270Swpaul			for (j = sp->typecnt - 1; j >= 0; --j) {
151636270Swpaul				if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
151737626Swpaul					continue;
151837626Swpaul				newt = t + sp->ttis[j].tt_gmtoff -
151937626Swpaul					sp->ttis[i].tt_gmtoff;
152037626Swpaul				(*funcp)(&newt, offset, &mytm);
152137626Swpaul				if (tmcomp(&mytm, &yourtm) != 0)
152237626Swpaul					continue;
1523106936Ssam				if (mytm.tm_isdst != yourtm.tm_isdst)
152439583Swpaul					continue;
1525152315Sru				/*
152637626Swpaul				** We have a match.
152737626Swpaul				*/
152837626Swpaul				t = newt;
152937626Swpaul				goto label;
153037626Swpaul			}
1531106936Ssam		}
1532106936Ssam		return WRONG;
1533106936Ssam	}
1534122689Ssamlabel:
1535106936Ssam	newt = t + saved_seconds;
1536122689Ssam	if ((newt < t) != (saved_seconds < 0))
153736270Swpaul		return WRONG;
153836270Swpaul	t = newt;
153936270Swpaul	(*funcp)(&t, offset, tmp);
154036270Swpaul	*okayp = TRUE;
154136270Swpaul	return t;
154236270Swpaul}
154336270Swpaul
154436270Swpaulstatic time_t
154536270Swpaultime1(tmp, funcp, offset)
154636270Swpaulstruct tm * const	tmp;
154736270Swpaulvoid (* const		funcp) P((const time_t *, long, struct tm *));
154836270Swpaulconst long		offset;
1549102336Salfred{
1550102336Salfred	register time_t			t;
155136270Swpaul	register const struct state *	sp;
155236270Swpaul	register int			samei, otheri;
155336270Swpaul	int				okay;
155436270Swpaul
155536270Swpaul	if (tmp->tm_isdst > 1)
155656060Swpaul		tmp->tm_isdst = 1;
155736270Swpaul	t = time2(tmp, funcp, offset, &okay);
155856060Swpaul#ifdef PCTS
155936270Swpaul	/*
156056060Swpaul	** PCTS code courtesy Grant Sullivan (grant@osf.org).
156136270Swpaul	*/
156236270Swpaul	if (okay)
156336270Swpaul		return t;
156439583Swpaul	if (tmp->tm_isdst < 0)
156536270Swpaul		tmp->tm_isdst = 0;	/* reset to std and try again */
156656060Swpaul#endif /* defined PCTS */
156756060Swpaul#ifndef PCTS
156839583Swpaul	if (okay || tmp->tm_isdst < 0)
156936270Swpaul		return t;
157036270Swpaul#endif /* !defined PCTS */
157136270Swpaul	/*
157236270Swpaul	** We're supposed to assume that somebody took a time of one type
1573102336Salfred	** and did some math on it that yielded a "struct tm" that's bad.
1574102336Salfred	** We try to divine the type they started from and adjust to the
157536270Swpaul	** type they need.
157636270Swpaul	*/
157736270Swpaul	/*
157836270Swpaul	** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
157936270Swpaul	*/
158036270Swpaul	sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
158136270Swpaul		lclptr : gmtptr);
158236270Swpaul#ifdef ALL_STATE
158336270Swpaul	if (sp == NULL)
158436270Swpaul		return WRONG;
158536270Swpaul#endif /* defined ALL_STATE */
158636270Swpaul	for (samei = sp->typecnt - 1; samei >= 0; --samei) {
158736270Swpaul		if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
158836270Swpaul			continue;
158936270Swpaul		for (otheri = sp->typecnt - 1; otheri >= 0; --otheri) {
159036270Swpaul			if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
159136270Swpaul				continue;
159236270Swpaul			tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
159336270Swpaul					sp->ttis[samei].tt_gmtoff;
159436270Swpaul			tmp->tm_isdst = !tmp->tm_isdst;
159536270Swpaul			t = time2(tmp, funcp, offset, &okay);
159636270Swpaul			if (okay)
159736270Swpaul				return t;
159836270Swpaul			tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
159936270Swpaul					sp->ttis[samei].tt_gmtoff;
160037626Swpaul			tmp->tm_isdst = !tmp->tm_isdst;
160137626Swpaul		}
160236270Swpaul	}
160336270Swpaul	return WRONG;
160436270Swpaul}
160536270Swpaul
160636270Swpaultime_t
160736270Swpaulmktime(tmp)
160836270Swpaulstruct tm * const	tmp;
160936270Swpaul{
161036270Swpaul	time_t mktime_return_value;
161136270Swpaul#ifdef	_THREAD_SAFE
161236270Swpaul	pthread_mutex_lock(&lcl_mutex);
161336270Swpaul#endif
161436270Swpaul	tzset();
161536270Swpaul	mktime_return_value = time1(tmp, localsub, 0L);
161636270Swpaul#ifdef	_THREAD_SAFE
161736270Swpaul	pthread_mutex_unlock(&lcl_mutex);
161836270Swpaul#endif
161936270Swpaul	return(mktime_return_value);
162036270Swpaul}
162136270Swpaul
162236270Swpaul#ifdef STD_INSPIRED
162336270Swpaul
162436270Swpaultime_t
162536270Swpaultimelocal(tmp)
1626102336Salfredstruct tm * const	tmp;
1627102336Salfred{
162836270Swpaul	tmp->tm_isdst = -1;	/* in case it wasn't initialized */
162936270Swpaul	return mktime(tmp);
163036270Swpaul}
163136270Swpaul
163236270Swpaultime_t
163336270Swpaultimegm(tmp)
163436270Swpaulstruct tm * const	tmp;
163536270Swpaul{
1636147256Sbrooks	tmp->tm_isdst = 0;
163736270Swpaul	return time1(tmp, gmtsub, 0L);
163836270Swpaul}
1639199560Sjhb
164036270Swpaultime_t
164136270Swpaultimeoff(tmp, offset)
1642148887Srwatsonstruct tm * const	tmp;
164336270Swpaulconst long		offset;
164436270Swpaul{
164536270Swpaul	tmp->tm_isdst = 0;
164636270Swpaul	return time1(tmp, gmtsub, offset);
164736270Swpaul}
164839583Swpaul
164936270Swpaul#endif /* defined STD_INSPIRED */
165039583Swpaul
165151439Swpaul#ifdef CMUCS
165236270Swpaul
165339583Swpaul/*
165436270Swpaul** The following is supplied for compatibility with
165536270Swpaul** previous versions of the CMUCS runtime library.
165639583Swpaul*/
165736270Swpaul
165836270Swpaullong
165936270Swpaulgtime(tmp)
166036270Swpaulstruct tm * const	tmp;
166136270Swpaul{
166236270Swpaul	const time_t	t = mktime(tmp);
1663102336Salfred
1664102336Salfred	if (t == WRONG)
166536270Swpaul		return -1;
166636270Swpaul	return t;
166736270Swpaul}
166836270Swpaul
166936270Swpaul#endif /* defined CMUCS */
167036270Swpaul
167136270Swpaul/*
167239627Swpaul** XXX--is the below the right way to conditionalize??
1673162315Sglebius*/
167441656Swpaul
167536270Swpaul#ifdef STD_INSPIRED
167639583Swpaul
167737626Swpaul/*
1678150171Sjhb** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599
167939583Swpaul** shall correspond to "Wed Dec 31 23:59:59 GMT 1986", which
168036270Swpaul** is not the case if we are accounting for leap seconds.
168136270Swpaul** So, we provide the following conversion routines for use
168236270Swpaul** when exchanging timestamps with POSIX conforming systems.
168336270Swpaul*/
1684102336Salfred
1685102336Salfredstatic long
168636270Swpaulleapcorr(timep)
168736270Swpaultime_t *	timep;
168836270Swpaul{
168936270Swpaul	register struct state *		sp;
169036270Swpaul	register struct lsinfo *	lp;
169136270Swpaul	register int			i;
169236270Swpaul
169336270Swpaul	sp = lclptr;
169439583Swpaul	i = sp->leapcnt;
169539583Swpaul	while (--i >= 0) {
169636270Swpaul		lp = &sp->lsis[i];
1697162315Sglebius		if (*timep >= lp->ls_trans)
169836270Swpaul			return lp->ls_corr;
169936270Swpaul	}
170036270Swpaul	return 0;
170136270Swpaul}
1702102336Salfred
1703102336Salfredtime_t
170439583Swpaultime2posix(t)
170536270Swpaultime_t	t;
170636270Swpaul{
170736270Swpaul	tzset();
170836270Swpaul	return t - leapcorr(&t);
170936270Swpaul}
171036270Swpaul
171136270Swpaultime_t
171236270Swpaulposix2time(t)
171339583Swpaultime_t	t;
171467087Swpaul{
171536270Swpaul	time_t	x;
171636270Swpaul	time_t	y;
171739583Swpaul
171839583Swpaul	tzset();
171936270Swpaul	/*
172036270Swpaul	** For a positive leap second hit, the result
172136270Swpaul	** is not unique.  For a negative leap second
172236270Swpaul	** hit, the corresponding time doesn't exist,
1723147256Sbrooks	** so we return an adjacent second.
172436270Swpaul	*/
172536270Swpaul	x = t + leapcorr(&t);
172636270Swpaul	y = x - leapcorr(&x);
172739583Swpaul	if (y < t) {
1728162315Sglebius		do {
172939583Swpaul			x++;
173039583Swpaul			y = x - leapcorr(&x);
173139583Swpaul		} while (y < t);
173239583Swpaul		if (t != y)
173336270Swpaul			return x - 1;
173436270Swpaul	} else if (y > t) {
173536270Swpaul		do {
173636270Swpaul			--x;
173736270Swpaul			y = x - leapcorr(&x);
173836270Swpaul		} while (y > t);
173936270Swpaul		if (t != y)
174036270Swpaul			return x + 1;
174139583Swpaul	}
174239583Swpaul	return x;
174336270Swpaul}
174436270Swpaul
174536270Swpaul#endif /* defined STD_INSPIRED */
174636270Swpaul