localtime.c revision 35285
117209Swollman/*
217209Swollman** This file is in the public domain, so clarified as of
317209Swollman** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).
417209Swollman*/
515923Sscrappy
62708Swollman#ifndef lint
72708Swollman#ifndef NOID
817209Swollmanstatic char	elsieid[] = "@(#)localtime.c	7.57";
92708Swollman#endif /* !defined NOID */
102708Swollman#endif /* !defined lint */
112708Swollman
122708Swollman/*
132708Swollman** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
142708Swollman** POSIX-style TZ environment variable handling from Guy Harris
152708Swollman** (guy@auspex.com).
162708Swollman*/
172708Swollman
182708Swollman/*LINTLIBRARY*/
192708Swollman
2018834Swollman#include <sys/types.h>
2118834Swollman#include <sys/stat.h>
2218834Swollman
232708Swollman#include "private.h"
242708Swollman#include "tzfile.h"
252708Swollman#include "fcntl.h"
2613545Sjulian#ifdef	_THREAD_SAFE
2713545Sjulian#include <pthread.h>
2813545Sjulian#include "pthread_private.h"
2913545Sjulian#endif
302708Swollman
319936Swollman/*
329936Swollman** SunOS 4.1.1 headers lack O_BINARY.
339936Swollman*/
342708Swollman
352708Swollman#ifdef O_BINARY
362708Swollman#define OPEN_MODE	(O_RDONLY | O_BINARY)
372708Swollman#endif /* defined O_BINARY */
382708Swollman#ifndef O_BINARY
392708Swollman#define OPEN_MODE	O_RDONLY
402708Swollman#endif /* !defined O_BINARY */
412708Swollman
422708Swollman#ifndef WILDABBR
432708Swollman/*
442708Swollman** Someone might make incorrect use of a time zone abbreviation:
452708Swollman**	1.	They might reference tzname[0] before calling tzset (explicitly
469936Swollman**		or implicitly).
472708Swollman**	2.	They might reference tzname[1] before calling tzset (explicitly
489936Swollman**		or implicitly).
492708Swollman**	3.	They might reference tzname[1] after setting to a time zone
502708Swollman**		in which Daylight Saving Time is never observed.
512708Swollman**	4.	They might reference tzname[0] after setting to a time zone
522708Swollman**		in which Standard Time is never observed.
532708Swollman**	5.	They might reference tm.TM_ZONE after calling offtime.
542708Swollman** What's best to do in the above cases is open to debate;
552708Swollman** for now, we just set things up so that in any of the five cases
562708Swollman** WILDABBR is used.  Another possibility:  initialize tzname[0] to the
572708Swollman** string "tzname[0] used before set", and similarly for the other cases.
582708Swollman** And another:  initialize tzname[0] to "ERA", with an explanation in the
592708Swollman** manual page of what this "time zone abbreviation" means (doing this so
602708Swollman** that tzname[0] has the "normal" length of three characters).
612708Swollman*/
622708Swollman#define WILDABBR	"   "
632708Swollman#endif /* !defined WILDABBR */
642708Swollman
659936Swollmanstatic char		wildabbr[] = "WILDABBR";
662708Swollman
679936Swollmanstatic const char	gmt[] = "GMT";
689936Swollman
692708Swollmanstruct ttinfo {				/* time type information */
702708Swollman	long		tt_gmtoff;	/* GMT offset in seconds */
712708Swollman	int		tt_isdst;	/* used to set tm_isdst */
722708Swollman	int		tt_abbrind;	/* abbreviation list index */
732708Swollman	int		tt_ttisstd;	/* TRUE if transition is std time */
749936Swollman	int		tt_ttisgmt;	/* TRUE if transition is GMT */
752708Swollman};
762708Swollman
772708Swollmanstruct lsinfo {				/* leap second information */
782708Swollman	time_t		ls_trans;	/* transition time */
792708Swollman	long		ls_corr;	/* correction to apply */
802708Swollman};
812708Swollman
822708Swollman#define BIGGEST(a, b)	(((a) > (b)) ? (a) : (b))
832708Swollman
842708Swollman#ifdef TZNAME_MAX
852708Swollman#define MY_TZNAME_MAX	TZNAME_MAX
862708Swollman#endif /* defined TZNAME_MAX */
872708Swollman#ifndef TZNAME_MAX
882708Swollman#define MY_TZNAME_MAX	255
892708Swollman#endif /* !defined TZNAME_MAX */
902708Swollman
912708Swollmanstruct state {
922708Swollman	int		leapcnt;
932708Swollman	int		timecnt;
942708Swollman	int		typecnt;
952708Swollman	int		charcnt;
962708Swollman	time_t		ats[TZ_MAX_TIMES];
972708Swollman	unsigned char	types[TZ_MAX_TIMES];
982708Swollman	struct ttinfo	ttis[TZ_MAX_TYPES];
999936Swollman	char		chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
1002708Swollman				(2 * (MY_TZNAME_MAX + 1)))];
1012708Swollman	struct lsinfo	lsis[TZ_MAX_LEAPS];
1022708Swollman};
1032708Swollman
1042708Swollmanstruct rule {
1052708Swollman	int		r_type;		/* type of rule--see below */
1062708Swollman	int		r_day;		/* day number of rule */
1072708Swollman	int		r_week;		/* week number of rule */
1082708Swollman	int		r_mon;		/* month number of rule */
1092708Swollman	long		r_time;		/* transition time of rule */
1102708Swollman};
1112708Swollman
1122708Swollman#define JULIAN_DAY		0	/* Jn - Julian day */
1132708Swollman#define DAY_OF_YEAR		1	/* n - day of year */
1142708Swollman#define MONTH_NTH_DAY_OF_WEEK	2	/* Mm.n.d - month, week, day of week */
1152708Swollman
1162708Swollman/*
1172708Swollman** Prototypes for static functions.
1182708Swollman*/
1192708Swollman
1202708Swollmanstatic long		detzcode P((const char * codep));
1212708Swollmanstatic const char *	getzname P((const char * strp));
1222708Swollmanstatic const char *	getnum P((const char * strp, int * nump, int min,
1232708Swollman				int max));
1242708Swollmanstatic const char *	getsecs P((const char * strp, long * secsp));
1252708Swollmanstatic const char *	getoffset P((const char * strp, long * offsetp));
1262708Swollmanstatic const char *	getrule P((const char * strp, struct rule * rulep));
1272708Swollmanstatic void		gmtload P((struct state * sp));
1282708Swollmanstatic void		gmtsub P((const time_t * timep, long offset,
1292708Swollman				struct tm * tmp));
1302708Swollmanstatic void		localsub P((const time_t * timep, long offset,
1312708Swollman				struct tm * tmp));
1322708Swollmanstatic int		increment_overflow P((int * number, int delta));
1332708Swollmanstatic int		normalize_overflow P((int * tensptr, int * unitsptr,
1342708Swollman				int base));
1352708Swollmanstatic void		settzname P((void));
1369936Swollmanstatic time_t		time1 P((struct tm * tmp,
1379936Swollman				void(*funcp) P((const time_t *,
1389936Swollman				long, struct tm *)),
1392708Swollman				long offset));
1409936Swollmanstatic time_t		time2 P((struct tm *tmp,
1419936Swollman				void(*funcp) P((const time_t *,
1429936Swollman				long, struct tm*)),
1432708Swollman				long offset, int * okayp));
1442708Swollmanstatic void		timesub P((const time_t * timep, long offset,
1452708Swollman				const struct state * sp, struct tm * tmp));
1462708Swollmanstatic int		tmcomp P((const struct tm * atmp,
1472708Swollman				const struct tm * btmp));
1482708Swollmanstatic time_t		transtime P((time_t janfirst, int year,
1492708Swollman				const struct rule * rulep, long offset));
1502708Swollmanstatic int		tzload P((const char * name, struct state * sp));
1512708Swollmanstatic int		tzparse P((const char * name, struct state * sp,
1522708Swollman				int lastditch));
1532708Swollman
1542708Swollman#ifdef ALL_STATE
1552708Swollmanstatic struct state *	lclptr;
1562708Swollmanstatic struct state *	gmtptr;
1572708Swollman#endif /* defined ALL_STATE */
1582708Swollman
1592708Swollman#ifndef ALL_STATE
1602708Swollmanstatic struct state	lclmem;
1612708Swollmanstatic struct state	gmtmem;
1622708Swollman#define lclptr		(&lclmem)
1632708Swollman#define gmtptr		(&gmtmem)
1642708Swollman#endif /* State Farm */
1652708Swollman
1669936Swollman#ifndef TZ_STRLEN_MAX
1679936Swollman#define TZ_STRLEN_MAX 255
1689936Swollman#endif /* !defined TZ_STRLEN_MAX */
1699936Swollman
1709936Swollmanstatic char		lcl_TZname[TZ_STRLEN_MAX + 1];
1712708Swollmanstatic int		lcl_is_set;
1722708Swollmanstatic int		gmt_is_set;
17313545Sjulian#ifdef	_THREAD_SAFE
17435026Sjbstatic struct pthread_mutex	_lcl_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
17535026Sjbstatic struct pthread_mutex	_gmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
17617706Sjulianstatic pthread_mutex_t		lcl_mutex   = &_lcl_mutexd;
17717706Sjulianstatic pthread_mutex_t		gmt_mutex   = &_gmt_mutexd;
17813545Sjulian#endif
1792708Swollman
1802708Swollmanchar *			tzname[2] = {
1819936Swollman	wildabbr,
1829936Swollman	wildabbr
1832708Swollman};
1842708Swollman
1859936Swollman/*
1869936Swollman** Section 4.12.3 of X3.159-1989 requires that
1879936Swollman**	Except for the strftime function, these functions [asctime,
1889936Swollman**	ctime, gmtime, localtime] return values in one of two static
1899936Swollman**	objects: a broken-down time structure and an array of char.
1909936Swollman** Thanks to Paul Eggert (eggert@twinsun.com) for noting this.
1919936Swollman*/
1929936Swollman
1939936Swollmanstatic struct tm	tm;
1949936Swollman
1952708Swollman#ifdef USG_COMPAT
1962708Swollmantime_t			timezone = 0;
1972708Swollmanint			daylight = 0;
1982708Swollman#endif /* defined USG_COMPAT */
1992708Swollman
2002708Swollman#ifdef ALTZONE
2012708Swollmantime_t			altzone = 0;
2022708Swollman#endif /* defined ALTZONE */
2032708Swollman
2042708Swollmanstatic long
2052708Swollmandetzcode(codep)
2062708Swollmanconst char * const	codep;
2072708Swollman{
2082708Swollman	register long	result;
2092708Swollman	register int	i;
2102708Swollman
21117209Swollman	result = (codep[0] & 0x80) ? ~0L : 0L;
2122708Swollman	for (i = 0; i < 4; ++i)
2132708Swollman		result = (result << 8) | (codep[i] & 0xff);
2142708Swollman	return result;
2152708Swollman}
2162708Swollman
2172708Swollmanstatic void
2189936Swollmansettzname P((void))
2192708Swollman{
22017209Swollman	register struct state * const	sp = lclptr;
22117209Swollman	register int			i;
2222708Swollman
2239936Swollman	tzname[0] = wildabbr;
2249936Swollman	tzname[1] = wildabbr;
2252708Swollman#ifdef USG_COMPAT
2262708Swollman	daylight = 0;
2272708Swollman	timezone = 0;
2282708Swollman#endif /* defined USG_COMPAT */
2292708Swollman#ifdef ALTZONE
2302708Swollman	altzone = 0;
2312708Swollman#endif /* defined ALTZONE */
2322708Swollman#ifdef ALL_STATE
2332708Swollman	if (sp == NULL) {
2349936Swollman		tzname[0] = tzname[1] = gmt;
2352708Swollman		return;
2362708Swollman	}
2372708Swollman#endif /* defined ALL_STATE */
2382708Swollman	for (i = 0; i < sp->typecnt; ++i) {
2392708Swollman		register const struct ttinfo * const	ttisp = &sp->ttis[i];
2402708Swollman
2412708Swollman		tzname[ttisp->tt_isdst] =
2429936Swollman			&sp->chars[ttisp->tt_abbrind];
2432708Swollman#ifdef USG_COMPAT
2442708Swollman		if (ttisp->tt_isdst)
2452708Swollman			daylight = 1;
2462708Swollman		if (i == 0 || !ttisp->tt_isdst)
2472708Swollman			timezone = -(ttisp->tt_gmtoff);
2482708Swollman#endif /* defined USG_COMPAT */
2492708Swollman#ifdef ALTZONE
2502708Swollman		if (i == 0 || ttisp->tt_isdst)
2512708Swollman			altzone = -(ttisp->tt_gmtoff);
2522708Swollman#endif /* defined ALTZONE */
2532708Swollman	}
2542708Swollman	/*
2552708Swollman	** And to get the latest zone names into tzname. . .
2562708Swollman	*/
2572708Swollman	for (i = 0; i < sp->timecnt; ++i) {
2582708Swollman		register const struct ttinfo * const	ttisp =
2592708Swollman							&sp->ttis[
2602708Swollman								sp->types[i]];
2612708Swollman
2622708Swollman		tzname[ttisp->tt_isdst] =
2639936Swollman			&sp->chars[ttisp->tt_abbrind];
2642708Swollman	}
2652708Swollman}
2662708Swollman
2672708Swollmanstatic int
2682708Swollmantzload(name, sp)
2692708Swollmanregister const char *		name;
2702708Swollmanregister struct state * const	sp;
2712708Swollman{
2722708Swollman	register const char *	p;
2732708Swollman	register int		i;
2742708Swollman	register int		fid;
2752708Swollman
2762708Swollman	if (name == NULL && (name = TZDEFAULT) == NULL)
2772708Swollman		return -1;
2782708Swollman	{
2799936Swollman		register int	doaccess;
28018834Swollman		struct stat	stab;
2819936Swollman		/*
2829936Swollman		** Section 4.9.1 of the C standard says that
2839936Swollman		** "FILENAME_MAX expands to an integral constant expression
28418834Swollman		** that is the size needed for an array of char large enough
2859936Swollman		** to hold the longest file name string that the implementation
2869936Swollman		** guarantees can be opened."
2879936Swollman		*/
2882708Swollman		char		fullname[FILENAME_MAX + 1];
2892708Swollman
2902708Swollman		if (name[0] == ':')
2912708Swollman			++name;
2922708Swollman		doaccess = name[0] == '/';
2932708Swollman		if (!doaccess) {
2942708Swollman			if ((p = TZDIR) == NULL)
2952708Swollman				return -1;
2962708Swollman			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
2972708Swollman				return -1;
2982708Swollman			(void) strcpy(fullname, p);
2992708Swollman			(void) strcat(fullname, "/");
3002708Swollman			(void) strcat(fullname, name);
3012708Swollman			/*
3022708Swollman			** Set doaccess if '.' (as in "../") shows up in name.
3032708Swollman			*/
3042708Swollman			if (strchr(name, '.') != NULL)
3052708Swollman				doaccess = TRUE;
3062708Swollman			name = fullname;
3072708Swollman		}
30824253Simp		if (doaccess && access(name, R_OK) != 0)
30924253Simp			return -1;
3102708Swollman		if ((fid = open(name, OPEN_MODE)) == -1)
3112708Swollman			return -1;
31218834Swollman		if ((fstat(fid, &stab) < 0) || !S_ISREG(stab.st_mode))
31318834Swollman			return -1;
3142708Swollman	}
3152708Swollman	{
3169936Swollman		struct tzhead *	tzhp;
3179936Swollman		char		buf[sizeof *sp + sizeof *tzhp];
3189936Swollman		int		ttisstdcnt;
3199936Swollman		int		ttisgmtcnt;
3202708Swollman
3212708Swollman		i = read(fid, buf, sizeof buf);
3229936Swollman		if (close(fid) != 0)
3232708Swollman			return -1;
3249936Swollman		p = buf;
3259936Swollman		p += sizeof tzhp->tzh_reserved;
3269936Swollman		ttisstdcnt = (int) detzcode(p);
3279936Swollman		p += 4;
3289936Swollman		ttisgmtcnt = (int) detzcode(p);
3299936Swollman		p += 4;
3309936Swollman		sp->leapcnt = (int) detzcode(p);
3319936Swollman		p += 4;
3329936Swollman		sp->timecnt = (int) detzcode(p);
3339936Swollman		p += 4;
3349936Swollman		sp->typecnt = (int) detzcode(p);
3359936Swollman		p += 4;
3369936Swollman		sp->charcnt = (int) detzcode(p);
3379936Swollman		p += 4;
3382708Swollman		if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
3392708Swollman			sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
3402708Swollman			sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
3412708Swollman			sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
3429936Swollman			(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
3439936Swollman			(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
3442708Swollman				return -1;
3459936Swollman		if (i - (p - buf) < sp->timecnt * 4 +	/* ats */
3469936Swollman			sp->timecnt +			/* types */
3479936Swollman			sp->typecnt * (4 + 2) +		/* ttinfos */
3489936Swollman			sp->charcnt +			/* chars */
3499936Swollman			sp->leapcnt * (4 + 4) +		/* lsinfos */
3509936Swollman			ttisstdcnt +			/* ttisstds */
3519936Swollman			ttisgmtcnt)			/* ttisgmts */
3522708Swollman				return -1;
3532708Swollman		for (i = 0; i < sp->timecnt; ++i) {
3542708Swollman			sp->ats[i] = detzcode(p);
3552708Swollman			p += 4;
3562708Swollman		}
3572708Swollman		for (i = 0; i < sp->timecnt; ++i) {
3582708Swollman			sp->types[i] = (unsigned char) *p++;
3592708Swollman			if (sp->types[i] >= sp->typecnt)
3602708Swollman				return -1;
3612708Swollman		}
3622708Swollman		for (i = 0; i < sp->typecnt; ++i) {
3632708Swollman			register struct ttinfo *	ttisp;
3642708Swollman
3652708Swollman			ttisp = &sp->ttis[i];
3662708Swollman			ttisp->tt_gmtoff = detzcode(p);
3672708Swollman			p += 4;
3682708Swollman			ttisp->tt_isdst = (unsigned char) *p++;
3692708Swollman			if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
3702708Swollman				return -1;
3712708Swollman			ttisp->tt_abbrind = (unsigned char) *p++;
3722708Swollman			if (ttisp->tt_abbrind < 0 ||
3732708Swollman				ttisp->tt_abbrind > sp->charcnt)
3742708Swollman					return -1;
3752708Swollman		}
3762708Swollman		for (i = 0; i < sp->charcnt; ++i)
3772708Swollman			sp->chars[i] = *p++;
3782708Swollman		sp->chars[i] = '\0';	/* ensure '\0' at end */
3792708Swollman		for (i = 0; i < sp->leapcnt; ++i) {
3802708Swollman			register struct lsinfo *	lsisp;
3812708Swollman
3822708Swollman			lsisp = &sp->lsis[i];
3832708Swollman			lsisp->ls_trans = detzcode(p);
3842708Swollman			p += 4;
3852708Swollman			lsisp->ls_corr = detzcode(p);
3862708Swollman			p += 4;
3872708Swollman		}
3882708Swollman		for (i = 0; i < sp->typecnt; ++i) {
3892708Swollman			register struct ttinfo *	ttisp;
3902708Swollman
3912708Swollman			ttisp = &sp->ttis[i];
3922708Swollman			if (ttisstdcnt == 0)
3932708Swollman				ttisp->tt_ttisstd = FALSE;
3942708Swollman			else {
3952708Swollman				ttisp->tt_ttisstd = *p++;
3962708Swollman				if (ttisp->tt_ttisstd != TRUE &&
3972708Swollman					ttisp->tt_ttisstd != FALSE)
3982708Swollman						return -1;
3992708Swollman			}
4002708Swollman		}
4019936Swollman		for (i = 0; i < sp->typecnt; ++i) {
4029936Swollman			register struct ttinfo *	ttisp;
4039936Swollman
4049936Swollman			ttisp = &sp->ttis[i];
4059936Swollman			if (ttisgmtcnt == 0)
4069936Swollman				ttisp->tt_ttisgmt = FALSE;
4079936Swollman			else {
4089936Swollman				ttisp->tt_ttisgmt = *p++;
4099936Swollman				if (ttisp->tt_ttisgmt != TRUE &&
4109936Swollman					ttisp->tt_ttisgmt != FALSE)
4119936Swollman						return -1;
4129936Swollman			}
4139936Swollman		}
4142708Swollman	}
4152708Swollman	return 0;
4162708Swollman}
4172708Swollman
4182708Swollmanstatic const int	mon_lengths[2][MONSPERYEAR] = {
4192708Swollman	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
4202708Swollman	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
4212708Swollman};
4222708Swollman
4232708Swollmanstatic const int	year_lengths[2] = {
4242708Swollman	DAYSPERNYEAR, DAYSPERLYEAR
4252708Swollman};
4262708Swollman
4272708Swollman/*
4282708Swollman** Given a pointer into a time zone string, scan until a character that is not
4292708Swollman** a valid character in a zone name is found.  Return a pointer to that
4302708Swollman** character.
4312708Swollman*/
4322708Swollman
4332708Swollmanstatic const char *
4342708Swollmangetzname(strp)
4352708Swollmanregister const char *	strp;
4362708Swollman{
4372708Swollman	register char	c;
4382708Swollman
43917209Swollman	while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
4402708Swollman		c != '+')
4412708Swollman			++strp;
4422708Swollman	return strp;
4432708Swollman}
4442708Swollman
4452708Swollman/*
4462708Swollman** Given a pointer into a time zone string, extract a number from that string.
4472708Swollman** Check that the number is within a specified range; if it is not, return
4482708Swollman** NULL.
4492708Swollman** Otherwise, return a pointer to the first character not part of the number.
4502708Swollman*/
4512708Swollman
4522708Swollmanstatic const char *
4532708Swollmangetnum(strp, nump, min, max)
4542708Swollmanregister const char *	strp;
4552708Swollmanint * const		nump;
4562708Swollmanconst int		min;
4572708Swollmanconst int		max;
4582708Swollman{
4592708Swollman	register char	c;
4602708Swollman	register int	num;
4612708Swollman
46217209Swollman	if (strp == NULL || !is_digit(c = *strp))
4632708Swollman		return NULL;
4642708Swollman	num = 0;
46517209Swollman	do {
4662708Swollman		num = num * 10 + (c - '0');
4672708Swollman		if (num > max)
4682708Swollman			return NULL;	/* illegal value */
46917209Swollman		c = *++strp;
47017209Swollman	} while (is_digit(c));
4712708Swollman	if (num < min)
4722708Swollman		return NULL;		/* illegal value */
4732708Swollman	*nump = num;
4742708Swollman	return strp;
4752708Swollman}
4762708Swollman
4772708Swollman/*
4782708Swollman** Given a pointer into a time zone string, extract a number of seconds,
4792708Swollman** in hh[:mm[:ss]] form, from the string.
4802708Swollman** If any error occurs, return NULL.
4812708Swollman** Otherwise, return a pointer to the first character not part of the number
4822708Swollman** of seconds.
4832708Swollman*/
4842708Swollman
4852708Swollmanstatic const char *
4862708Swollmangetsecs(strp, secsp)
4872708Swollmanregister const char *	strp;
4882708Swollmanlong * const		secsp;
4892708Swollman{
4902708Swollman	int	num;
4912708Swollman
4929936Swollman	/*
4939936Swollman	** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
4949936Swollman	** "M10.4.6/26", which does not conform to Posix,
4959936Swollman	** but which specifies the equivalent of
4969936Swollman	** ``02:00 on the first Sunday on or after 23 Oct''.
4979936Swollman	*/
4989936Swollman	strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
4992708Swollman	if (strp == NULL)
5002708Swollman		return NULL;
5019936Swollman	*secsp = num * (long) SECSPERHOUR;
5022708Swollman	if (*strp == ':') {
5032708Swollman		++strp;
5042708Swollman		strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
5052708Swollman		if (strp == NULL)
5062708Swollman			return NULL;
5072708Swollman		*secsp += num * SECSPERMIN;
5082708Swollman		if (*strp == ':') {
5092708Swollman			++strp;
5109936Swollman			/* `SECSPERMIN' allows for leap seconds.  */
5119936Swollman			strp = getnum(strp, &num, 0, SECSPERMIN);
5122708Swollman			if (strp == NULL)
5132708Swollman				return NULL;
5142708Swollman			*secsp += num;
5152708Swollman		}
5162708Swollman	}
5172708Swollman	return strp;
5182708Swollman}
5192708Swollman
5202708Swollman/*
5212708Swollman** Given a pointer into a time zone string, extract an offset, in
5222708Swollman** [+-]hh[:mm[:ss]] form, from the string.
5232708Swollman** If any error occurs, return NULL.
5242708Swollman** Otherwise, return a pointer to the first character not part of the time.
5252708Swollman*/
5262708Swollman
5272708Swollmanstatic const char *
5282708Swollmangetoffset(strp, offsetp)
5292708Swollmanregister const char *	strp;
5302708Swollmanlong * const		offsetp;
5312708Swollman{
53217209Swollman	register int	neg = 0;
5332708Swollman
5342708Swollman	if (*strp == '-') {
5352708Swollman		neg = 1;
5362708Swollman		++strp;
53717209Swollman	} else if (*strp == '+')
53817209Swollman		++strp;
5392708Swollman	strp = getsecs(strp, offsetp);
5402708Swollman	if (strp == NULL)
5412708Swollman		return NULL;		/* illegal time */
5422708Swollman	if (neg)
5432708Swollman		*offsetp = -*offsetp;
5442708Swollman	return strp;
5452708Swollman}
5462708Swollman
5472708Swollman/*
5482708Swollman** Given a pointer into a time zone string, extract a rule in the form
5492708Swollman** date[/time].  See POSIX section 8 for the format of "date" and "time".
5502708Swollman** If a valid rule is not found, return NULL.
5512708Swollman** Otherwise, return a pointer to the first character not part of the rule.
5522708Swollman*/
5532708Swollman
5542708Swollmanstatic const char *
5552708Swollmangetrule(strp, rulep)
5562708Swollmanconst char *			strp;
5572708Swollmanregister struct rule * const	rulep;
5582708Swollman{
5592708Swollman	if (*strp == 'J') {
5602708Swollman		/*
5612708Swollman		** Julian day.
5622708Swollman		*/
5632708Swollman		rulep->r_type = JULIAN_DAY;
5642708Swollman		++strp;
5652708Swollman		strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
5662708Swollman	} else if (*strp == 'M') {
5672708Swollman		/*
5682708Swollman		** Month, week, day.
5692708Swollman		*/
5702708Swollman		rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
5712708Swollman		++strp;
5722708Swollman		strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
5732708Swollman		if (strp == NULL)
5742708Swollman			return NULL;
5752708Swollman		if (*strp++ != '.')
5762708Swollman			return NULL;
5772708Swollman		strp = getnum(strp, &rulep->r_week, 1, 5);
5782708Swollman		if (strp == NULL)
5792708Swollman			return NULL;
5802708Swollman		if (*strp++ != '.')
5812708Swollman			return NULL;
5822708Swollman		strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
58317209Swollman	} else if (is_digit(*strp)) {
5842708Swollman		/*
5852708Swollman		** Day of year.
5862708Swollman		*/
5872708Swollman		rulep->r_type = DAY_OF_YEAR;
5882708Swollman		strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
5892708Swollman	} else	return NULL;		/* invalid format */
5902708Swollman	if (strp == NULL)
5912708Swollman		return NULL;
5922708Swollman	if (*strp == '/') {
5932708Swollman		/*
5942708Swollman		** Time specified.
5952708Swollman		*/
5962708Swollman		++strp;
5972708Swollman		strp = getsecs(strp, &rulep->r_time);
5982708Swollman	} else	rulep->r_time = 2 * SECSPERHOUR;	/* default = 2:00:00 */
5992708Swollman	return strp;
6002708Swollman}
6012708Swollman
6022708Swollman/*
6032708Swollman** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the
6042708Swollman** year, a rule, and the offset from GMT at the time that rule takes effect,
6052708Swollman** calculate the Epoch-relative time that rule takes effect.
6062708Swollman*/
6072708Swollman
6082708Swollmanstatic time_t
6092708Swollmantranstime(janfirst, year, rulep, offset)
6102708Swollmanconst time_t				janfirst;
6112708Swollmanconst int				year;
6122708Swollmanregister const struct rule * const	rulep;
6132708Swollmanconst long				offset;
6142708Swollman{
6152708Swollman	register int	leapyear;
6162708Swollman	register time_t	value;
6172708Swollman	register int	i;
6182708Swollman	int		d, m1, yy0, yy1, yy2, dow;
6192708Swollman
6209936Swollman	INITIALIZE(value);
6212708Swollman	leapyear = isleap(year);
6222708Swollman	switch (rulep->r_type) {
6232708Swollman
6242708Swollman	case JULIAN_DAY:
6252708Swollman		/*
6262708Swollman		** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
6272708Swollman		** years.
6282708Swollman		** In non-leap years, or if the day number is 59 or less, just
6292708Swollman		** add SECSPERDAY times the day number-1 to the time of
6302708Swollman		** January 1, midnight, to get the day.
6312708Swollman		*/
6322708Swollman		value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
6332708Swollman		if (leapyear && rulep->r_day >= 60)
6342708Swollman			value += SECSPERDAY;
6352708Swollman		break;
6362708Swollman
6372708Swollman	case DAY_OF_YEAR:
6382708Swollman		/*
6392708Swollman		** n - day of year.
6402708Swollman		** Just add SECSPERDAY times the day number to the time of
6412708Swollman		** January 1, midnight, to get the day.
6422708Swollman		*/
6432708Swollman		value = janfirst + rulep->r_day * SECSPERDAY;
6442708Swollman		break;
6452708Swollman
6462708Swollman	case MONTH_NTH_DAY_OF_WEEK:
6472708Swollman		/*
6482708Swollman		** Mm.n.d - nth "dth day" of month m.
6492708Swollman		*/
6502708Swollman		value = janfirst;
6512708Swollman		for (i = 0; i < rulep->r_mon - 1; ++i)
6522708Swollman			value += mon_lengths[leapyear][i] * SECSPERDAY;
6532708Swollman
6542708Swollman		/*
6552708Swollman		** Use Zeller's Congruence to get day-of-week of first day of
6562708Swollman		** month.
6572708Swollman		*/
6582708Swollman		m1 = (rulep->r_mon + 9) % 12 + 1;
6592708Swollman		yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
6602708Swollman		yy1 = yy0 / 100;
6612708Swollman		yy2 = yy0 % 100;
6622708Swollman		dow = ((26 * m1 - 2) / 10 +
6632708Swollman			1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
6642708Swollman		if (dow < 0)
6652708Swollman			dow += DAYSPERWEEK;
6662708Swollman
6672708Swollman		/*
6682708Swollman		** "dow" is the day-of-week of the first day of the month.  Get
6692708Swollman		** the day-of-month (zero-origin) of the first "dow" day of the
6702708Swollman		** month.
6712708Swollman		*/
6722708Swollman		d = rulep->r_day - dow;
6732708Swollman		if (d < 0)
6742708Swollman			d += DAYSPERWEEK;
6752708Swollman		for (i = 1; i < rulep->r_week; ++i) {
6762708Swollman			if (d + DAYSPERWEEK >=
6772708Swollman				mon_lengths[leapyear][rulep->r_mon - 1])
6782708Swollman					break;
6792708Swollman			d += DAYSPERWEEK;
6802708Swollman		}
6812708Swollman
6822708Swollman		/*
6832708Swollman		** "d" is the day-of-month (zero-origin) of the day we want.
6842708Swollman		*/
6852708Swollman		value += d * SECSPERDAY;
6862708Swollman		break;
6872708Swollman	}
6882708Swollman
6892708Swollman	/*
6902708Swollman	** "value" is the Epoch-relative time of 00:00:00 GMT on the day in
6912708Swollman	** question.  To get the Epoch-relative time of the specified local
6922708Swollman	** time on that day, add the transition time and the current offset
6932708Swollman	** from GMT.
6942708Swollman	*/
6952708Swollman	return value + rulep->r_time + offset;
6962708Swollman}
6972708Swollman
6982708Swollman/*
6992708Swollman** Given a POSIX section 8-style TZ string, fill in the rule tables as
7002708Swollman** appropriate.
7012708Swollman*/
7022708Swollman
7032708Swollmanstatic int
7042708Swollmantzparse(name, sp, lastditch)
7052708Swollmanconst char *			name;
7062708Swollmanregister struct state * const	sp;
7072708Swollmanconst int			lastditch;
7082708Swollman{
7092708Swollman	const char *			stdname;
7102708Swollman	const char *			dstname;
7119936Swollman	size_t				stdlen;
7129936Swollman	size_t				dstlen;
7132708Swollman	long				stdoffset;
7142708Swollman	long				dstoffset;
7152708Swollman	register time_t *		atp;
7162708Swollman	register unsigned char *	typep;
7172708Swollman	register char *			cp;
7182708Swollman	register int			load_result;
7192708Swollman
7209936Swollman	INITIALIZE(dstname);
7212708Swollman	stdname = name;
7222708Swollman	if (lastditch) {
7232708Swollman		stdlen = strlen(name);	/* length of standard zone name */
7242708Swollman		name += stdlen;
7252708Swollman		if (stdlen >= sizeof sp->chars)
7262708Swollman			stdlen = (sizeof sp->chars) - 1;
72721659Swollman		stdoffset = 0;
7282708Swollman	} else {
7292708Swollman		name = getzname(name);
7302708Swollman		stdlen = name - stdname;
7312708Swollman		if (stdlen < 3)
7322708Swollman			return -1;
73321659Swollman		if (*name == '\0')
73421659Swollman			return -1;	/* was "stdoffset = 0;" */
73521659Swollman		else {
73621659Swollman			name = getoffset(name, &stdoffset);
73721659Swollman			if (name == NULL)
73821659Swollman				return -1;
73921659Swollman		}
7402708Swollman	}
7412708Swollman	load_result = tzload(TZDEFRULES, sp);
7422708Swollman	if (load_result != 0)
7432708Swollman		sp->leapcnt = 0;		/* so, we're off a little */
7442708Swollman	if (*name != '\0') {
7452708Swollman		dstname = name;
7462708Swollman		name = getzname(name);
7472708Swollman		dstlen = name - dstname;	/* length of DST zone name */
7482708Swollman		if (dstlen < 3)
7492708Swollman			return -1;
7502708Swollman		if (*name != '\0' && *name != ',' && *name != ';') {
7512708Swollman			name = getoffset(name, &dstoffset);
7522708Swollman			if (name == NULL)
7532708Swollman				return -1;
7542708Swollman		} else	dstoffset = stdoffset - SECSPERHOUR;
7552708Swollman		if (*name == ',' || *name == ';') {
7562708Swollman			struct rule	start;
7572708Swollman			struct rule	end;
7582708Swollman			register int	year;
7592708Swollman			register time_t	janfirst;
7602708Swollman			time_t		starttime;
7612708Swollman			time_t		endtime;
7622708Swollman
7632708Swollman			++name;
7642708Swollman			if ((name = getrule(name, &start)) == NULL)
7652708Swollman				return -1;
7662708Swollman			if (*name++ != ',')
7672708Swollman				return -1;
7682708Swollman			if ((name = getrule(name, &end)) == NULL)
7692708Swollman				return -1;
7702708Swollman			if (*name != '\0')
7712708Swollman				return -1;
7722708Swollman			sp->typecnt = 2;	/* standard time and DST */
7732708Swollman			/*
7742708Swollman			** Two transitions per year, from EPOCH_YEAR to 2037.
7752708Swollman			*/
7762708Swollman			sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
7772708Swollman			if (sp->timecnt > TZ_MAX_TIMES)
7782708Swollman				return -1;
7792708Swollman			sp->ttis[0].tt_gmtoff = -dstoffset;
7802708Swollman			sp->ttis[0].tt_isdst = 1;
7812708Swollman			sp->ttis[0].tt_abbrind = stdlen + 1;
7822708Swollman			sp->ttis[1].tt_gmtoff = -stdoffset;
7832708Swollman			sp->ttis[1].tt_isdst = 0;
7842708Swollman			sp->ttis[1].tt_abbrind = 0;
7852708Swollman			atp = sp->ats;
7862708Swollman			typep = sp->types;
7872708Swollman			janfirst = 0;
7882708Swollman			for (year = EPOCH_YEAR; year <= 2037; ++year) {
7892708Swollman				starttime = transtime(janfirst, year, &start,
7902708Swollman					stdoffset);
7912708Swollman				endtime = transtime(janfirst, year, &end,
7922708Swollman					dstoffset);
7932708Swollman				if (starttime > endtime) {
7942708Swollman					*atp++ = endtime;
7952708Swollman					*typep++ = 1;	/* DST ends */
7962708Swollman					*atp++ = starttime;
7972708Swollman					*typep++ = 0;	/* DST begins */
7982708Swollman				} else {
7992708Swollman					*atp++ = starttime;
8002708Swollman					*typep++ = 0;	/* DST begins */
8012708Swollman					*atp++ = endtime;
8022708Swollman					*typep++ = 1;	/* DST ends */
8032708Swollman				}
8042708Swollman				janfirst += year_lengths[isleap(year)] *
8052708Swollman					SECSPERDAY;
8062708Swollman			}
8072708Swollman		} else {
8089936Swollman			register long	theirstdoffset;
8099936Swollman			register long	theirdstoffset;
8109936Swollman			register long	theiroffset;
8119936Swollman			register int	isdst;
8122708Swollman			register int	i;
8139936Swollman			register int	j;
8142708Swollman
8152708Swollman			if (*name != '\0')
8162708Swollman				return -1;
8172708Swollman			if (load_result != 0)
8182708Swollman				return -1;
8192708Swollman			/*
8209936Swollman			** Initial values of theirstdoffset and theirdstoffset.
8212708Swollman			*/
8229936Swollman			theirstdoffset = 0;
8239936Swollman			for (i = 0; i < sp->timecnt; ++i) {
8249936Swollman				j = sp->types[i];
8259936Swollman				if (!sp->ttis[j].tt_isdst) {
82617209Swollman					theirstdoffset =
82717209Swollman						-sp->ttis[j].tt_gmtoff;
8289936Swollman					break;
8292708Swollman				}
8302708Swollman			}
8319936Swollman			theirdstoffset = 0;
8329936Swollman			for (i = 0; i < sp->timecnt; ++i) {
8339936Swollman				j = sp->types[i];
8349936Swollman				if (sp->ttis[j].tt_isdst) {
83517209Swollman					theirdstoffset =
83617209Swollman						-sp->ttis[j].tt_gmtoff;
8379936Swollman					break;
8389936Swollman				}
8399936Swollman			}
8402708Swollman			/*
8419936Swollman			** Initially we're assumed to be in standard time.
8422708Swollman			*/
8439936Swollman			isdst = FALSE;
8449936Swollman			theiroffset = theirstdoffset;
8452708Swollman			/*
8469936Swollman			** Now juggle transition times and types
8479936Swollman			** tracking offsets as you do.
8482708Swollman			*/
8492708Swollman			for (i = 0; i < sp->timecnt; ++i) {
8509936Swollman				j = sp->types[i];
8519936Swollman				sp->types[i] = sp->ttis[j].tt_isdst;
8529936Swollman				if (sp->ttis[j].tt_ttisgmt) {
8539936Swollman					/* No adjustment to transition time */
8549936Swollman				} else {
8559936Swollman					/*
8569936Swollman					** If summer time is in effect, and the
8579936Swollman					** transition time was not specified as
8589936Swollman					** standard time, add the summer time
8599936Swollman					** offset to the transition time;
8609936Swollman					** otherwise, add the standard time
8619936Swollman					** offset to the transition time.
8629936Swollman					*/
8639936Swollman					/*
8649936Swollman					** Transitions from DST to DDST
8659936Swollman					** will effectively disappear since
8669936Swollman					** POSIX provides for only one DST
8679936Swollman					** offset.
8689936Swollman					*/
8699936Swollman					if (isdst && !sp->ttis[j].tt_ttisstd) {
8709936Swollman						sp->ats[i] += dstoffset -
8719936Swollman							theirdstoffset;
8729936Swollman					} else {
8739936Swollman						sp->ats[i] += stdoffset -
8749936Swollman							theirstdoffset;
8759936Swollman					}
8769936Swollman				}
8779936Swollman				theiroffset = -sp->ttis[j].tt_gmtoff;
8789936Swollman				if (sp->ttis[j].tt_isdst)
8799936Swollman					theirdstoffset = theiroffset;
8809936Swollman				else	theirstdoffset = theiroffset;
8812708Swollman			}
8829936Swollman			/*
8839936Swollman			** Finally, fill in ttis.
8849936Swollman			** ttisstd and ttisgmt need not be handled.
8859936Swollman			*/
8869936Swollman			sp->ttis[0].tt_gmtoff = -stdoffset;
8879936Swollman			sp->ttis[0].tt_isdst = FALSE;
8889936Swollman			sp->ttis[0].tt_abbrind = 0;
8899936Swollman			sp->ttis[1].tt_gmtoff = -dstoffset;
8909936Swollman			sp->ttis[1].tt_isdst = TRUE;
8919936Swollman			sp->ttis[1].tt_abbrind = stdlen + 1;
8922708Swollman		}
8932708Swollman	} else {
8942708Swollman		dstlen = 0;
8952708Swollman		sp->typecnt = 1;		/* only standard time */
8962708Swollman		sp->timecnt = 0;
8972708Swollman		sp->ttis[0].tt_gmtoff = -stdoffset;
8982708Swollman		sp->ttis[0].tt_isdst = 0;
8992708Swollman		sp->ttis[0].tt_abbrind = 0;
9002708Swollman	}
9012708Swollman	sp->charcnt = stdlen + 1;
9022708Swollman	if (dstlen != 0)
9032708Swollman		sp->charcnt += dstlen + 1;
9042708Swollman	if (sp->charcnt > sizeof sp->chars)
9052708Swollman		return -1;
9062708Swollman	cp = sp->chars;
9072708Swollman	(void) strncpy(cp, stdname, stdlen);
9082708Swollman	cp += stdlen;
9092708Swollman	*cp++ = '\0';
9102708Swollman	if (dstlen != 0) {
9112708Swollman		(void) strncpy(cp, dstname, dstlen);
9122708Swollman		*(cp + dstlen) = '\0';
9132708Swollman	}
9142708Swollman	return 0;
9152708Swollman}
9162708Swollman
9172708Swollmanstatic void
9182708Swollmangmtload(sp)
9192708Swollmanstruct state * const	sp;
9202708Swollman{
9219936Swollman	if (tzload(gmt, sp) != 0)
9229936Swollman		(void) tzparse(gmt, sp, TRUE);
9232708Swollman}
9242708Swollman
9252708Swollman#ifndef STD_INSPIRED
9269936Swollman/*
9279936Swollman** A non-static declaration of tzsetwall in a system header file
9289936Swollman** may cause a warning about this upcoming static declaration...
9299936Swollman*/
9302708Swollmanstatic
9312708Swollman#endif /* !defined STD_INSPIRED */
93213545Sjulian#ifdef	_THREAD_SAFE
9332708Swollmanvoid
93413545Sjuliantzsetwall_basic P((void))
93513545Sjulian#else
93613545Sjulianvoid
9379936Swollmantzsetwall P((void))
93813545Sjulian#endif
9392708Swollman{
9409936Swollman	if (lcl_is_set < 0)
9419936Swollman		return;
9429936Swollman	lcl_is_set = -1;
9439936Swollman
9442708Swollman#ifdef ALL_STATE
9452708Swollman	if (lclptr == NULL) {
9462708Swollman		lclptr = (struct state *) malloc(sizeof *lclptr);
9472708Swollman		if (lclptr == NULL) {
9482708Swollman			settzname();	/* all we can do */
9492708Swollman			return;
9502708Swollman		}
9512708Swollman	}
9522708Swollman#endif /* defined ALL_STATE */
9532708Swollman	if (tzload((char *) NULL, lclptr) != 0)
9542708Swollman		gmtload(lclptr);
9552708Swollman	settzname();
9562708Swollman}
9572708Swollman
95813545Sjulian#ifdef	_THREAD_SAFE
9592708Swollmanvoid
96013545Sjuliantzsetwall P((void))
96113545Sjulian{
96213545Sjulian	pthread_mutex_lock(&lcl_mutex);
96313545Sjulian	tzsetwall_basic();
96413545Sjulian	pthread_mutex_unlock(&lcl_mutex);
96513545Sjulian}
96613545Sjulian#endif
96713545Sjulian
96813545Sjulian#ifdef	_THREAD_SAFE
96913545Sjulianstatic void
97013545Sjuliantzset_basic P((void))
97113545Sjulian#else
97213545Sjulianvoid
9739936Swollmantzset P((void))
97413545Sjulian#endif
9752708Swollman{
9762708Swollman	register const char *	name;
9772708Swollman
9782708Swollman	name = getenv("TZ");
9792708Swollman	if (name == NULL) {
9802708Swollman		tzsetwall();
9812708Swollman		return;
9822708Swollman	}
9839936Swollman
9849936Swollman	if (lcl_is_set > 0  &&  strcmp(lcl_TZname, name) == 0)
9859936Swollman		return;
9869936Swollman	lcl_is_set = (strlen(name) < sizeof(lcl_TZname));
9879936Swollman	if (lcl_is_set)
9889936Swollman		(void) strcpy(lcl_TZname, name);
9899936Swollman
9902708Swollman#ifdef ALL_STATE
9912708Swollman	if (lclptr == NULL) {
9922708Swollman		lclptr = (struct state *) malloc(sizeof *lclptr);
9932708Swollman		if (lclptr == NULL) {
9942708Swollman			settzname();	/* all we can do */
9952708Swollman			return;
9962708Swollman		}
9972708Swollman	}
9982708Swollman#endif /* defined ALL_STATE */
9992708Swollman	if (*name == '\0') {
10002708Swollman		/*
10012708Swollman		** User wants it fast rather than right.
10022708Swollman		*/
10032708Swollman		lclptr->leapcnt = 0;		/* so, we're off a little */
10042708Swollman		lclptr->timecnt = 0;
10052708Swollman		lclptr->ttis[0].tt_gmtoff = 0;
10062708Swollman		lclptr->ttis[0].tt_abbrind = 0;
10079936Swollman		(void) strcpy(lclptr->chars, gmt);
10082708Swollman	} else if (tzload(name, lclptr) != 0)
10092708Swollman		if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
10102708Swollman			(void) gmtload(lclptr);
10112708Swollman	settzname();
10122708Swollman}
10132708Swollman
101413545Sjulian#ifdef	_THREAD_SAFE
101513545Sjulianvoid
101613545Sjuliantzset P((void))
101713545Sjulian{
101813545Sjulian	pthread_mutex_lock(&lcl_mutex);
101913545Sjulian	tzset_basic();
102013545Sjulian	pthread_mutex_unlock(&lcl_mutex);
102113545Sjulian}
102213545Sjulian#endif
102313545Sjulian
10242708Swollman/*
10252708Swollman** The easy way to behave "as if no library function calls" localtime
10262708Swollman** is to not call it--so we drop its guts into "localsub", which can be
10272708Swollman** freely called.  (And no, the PANS doesn't require the above behavior--
10282708Swollman** but it *is* desirable.)
10292708Swollman**
10302708Swollman** The unused offset argument is for the benefit of mktime variants.
10312708Swollman*/
10322708Swollman
10332708Swollman/*ARGSUSED*/
10342708Swollmanstatic void
10352708Swollmanlocalsub(timep, offset, tmp)
10362708Swollmanconst time_t * const	timep;
10372708Swollmanconst long		offset;
10382708Swollmanstruct tm * const	tmp;
10392708Swollman{
10409936Swollman	register struct state *		sp;
10412708Swollman	register const struct ttinfo *	ttisp;
10422708Swollman	register int			i;
10432708Swollman	const time_t			t = *timep;
10442708Swollman
10452708Swollman	sp = lclptr;
10462708Swollman#ifdef ALL_STATE
10472708Swollman	if (sp == NULL) {
10482708Swollman		gmtsub(timep, offset, tmp);
10492708Swollman		return;
10502708Swollman	}
10512708Swollman#endif /* defined ALL_STATE */
10522708Swollman	if (sp->timecnt == 0 || t < sp->ats[0]) {
10532708Swollman		i = 0;
10542708Swollman		while (sp->ttis[i].tt_isdst)
10552708Swollman			if (++i >= sp->typecnt) {
10562708Swollman				i = 0;
10572708Swollman				break;
10582708Swollman			}
10592708Swollman	} else {
10602708Swollman		for (i = 1; i < sp->timecnt; ++i)
10612708Swollman			if (t < sp->ats[i])
10622708Swollman				break;
10632708Swollman		i = sp->types[i - 1];
10642708Swollman	}
10652708Swollman	ttisp = &sp->ttis[i];
10662708Swollman	/*
10672708Swollman	** To get (wrong) behavior that's compatible with System V Release 2.0
10682708Swollman	** you'd replace the statement below with
10692708Swollman	**	t += ttisp->tt_gmtoff;
10702708Swollman	**	timesub(&t, 0L, sp, tmp);
10712708Swollman	*/
10722708Swollman	timesub(&t, ttisp->tt_gmtoff, sp, tmp);
10732708Swollman	tmp->tm_isdst = ttisp->tt_isdst;
10749936Swollman	tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
10752708Swollman#ifdef TM_ZONE
10769936Swollman	tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
10772708Swollman#endif /* defined TM_ZONE */
10782708Swollman}
10792708Swollman
108013545Sjulian#ifdef	_THREAD_SAFE
108119636Shsustruct tm *
108213545Sjulianlocaltime_r(timep, p_tm)
108313545Sjulianconst time_t * const	timep;
108413545Sjulianstruct tm *p_tm;
108513545Sjulian{
108613545Sjulian	pthread_mutex_lock(&lcl_mutex);
108713545Sjulian	tzset();
108813545Sjulian	localsub(timep, 0L, p_tm);
108913545Sjulian	pthread_mutex_unlock(&lcl_mutex);
109019636Shsu	return(p_tm);
109113545Sjulian}
109213545Sjulian#endif
109313545Sjulian
10942708Swollmanstruct tm *
10952708Swollmanlocaltime(timep)
10962708Swollmanconst time_t * const	timep;
10972708Swollman{
109813545Sjulian#ifdef	_THREAD_SAFE
109935026Sjb	static struct pthread_mutex _localtime_mutex = PTHREAD_MUTEX_STATIC_INITIALIZER;
110017706Sjulian	static pthread_mutex_t localtime_mutex = &_localtime_mutex;
110113545Sjulian	static pthread_key_t localtime_key = -1;
110213545Sjulian	struct tm *p_tm;
110313545Sjulian
110413545Sjulian	pthread_mutex_lock(&localtime_mutex);
110513545Sjulian	if (localtime_key < 0) {
110619636Shsu		if (pthread_key_create(&localtime_key, free) < 0) {
110713545Sjulian			pthread_mutex_unlock(&localtime_mutex);
110813545Sjulian			return(NULL);
110913545Sjulian		}
111013545Sjulian	}
111113545Sjulian	pthread_mutex_unlock(&localtime_mutex);
111222315Sjulian	p_tm = pthread_getspecific(localtime_key);
111322315Sjulian	if (p_tm == NULL) {
111422315Sjulian		if ((p_tm = (struct tm *)malloc(sizeof(struct tm))) == NULL)
111513545Sjulian			return(NULL);
111613545Sjulian		pthread_setspecific(localtime_key, p_tm);
111713545Sjulian	}
111813545Sjulian	pthread_mutex_lock(&lcl_mutex);
11199936Swollman	tzset();
112013545Sjulian	localsub(timep, 0L, p_tm);
112113545Sjulian	pthread_mutex_unlock(&lcl_mutex);
112213545Sjulian	return p_tm;
112313545Sjulian#else
112413545Sjulian	tzset();
11252708Swollman	localsub(timep, 0L, &tm);
11262708Swollman	return &tm;
112713545Sjulian#endif
11282708Swollman}
11292708Swollman
11302708Swollman/*
11312708Swollman** gmtsub is to gmtime as localsub is to localtime.
11322708Swollman*/
11332708Swollman
11342708Swollmanstatic void
11352708Swollmangmtsub(timep, offset, tmp)
11362708Swollmanconst time_t * const	timep;
11372708Swollmanconst long		offset;
11382708Swollmanstruct tm * const	tmp;
11392708Swollman{
114013545Sjulian#ifdef	_THREAD_SAFE
114113545Sjulian	pthread_mutex_lock(&gmt_mutex);
114213545Sjulian#endif
11432708Swollman	if (!gmt_is_set) {
11442708Swollman		gmt_is_set = TRUE;
11452708Swollman#ifdef ALL_STATE
11462708Swollman		gmtptr = (struct state *) malloc(sizeof *gmtptr);
11472708Swollman		if (gmtptr != NULL)
11482708Swollman#endif /* defined ALL_STATE */
11492708Swollman			gmtload(gmtptr);
11502708Swollman	}
115113545Sjulian#ifdef	_THREAD_SAFE
115213545Sjulian	pthread_mutex_unlock(&gmt_mutex);
115313545Sjulian#endif
11542708Swollman	timesub(timep, offset, gmtptr, tmp);
11552708Swollman#ifdef TM_ZONE
11562708Swollman	/*
11572708Swollman	** Could get fancy here and deliver something such as
11582708Swollman	** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero,
11592708Swollman	** but this is no time for a treasure hunt.
11602708Swollman	*/
11612708Swollman	if (offset != 0)
11629936Swollman		tmp->TM_ZONE = wildabbr;
11632708Swollman	else {
11642708Swollman#ifdef ALL_STATE
11652708Swollman		if (gmtptr == NULL)
11669936Swollman			tmp->TM_ZONE = gmt;
11672708Swollman		else	tmp->TM_ZONE = gmtptr->chars;
11682708Swollman#endif /* defined ALL_STATE */
11692708Swollman#ifndef ALL_STATE
11702708Swollman		tmp->TM_ZONE = gmtptr->chars;
11712708Swollman#endif /* State Farm */
11722708Swollman	}
11732708Swollman#endif /* defined TM_ZONE */
11742708Swollman}
11752708Swollman
11762708Swollmanstruct tm *
11772708Swollmangmtime(timep)
11782708Swollmanconst time_t * const	timep;
11792708Swollman{
118013545Sjulian#ifdef	_THREAD_SAFE
118135026Sjb	static struct pthread_mutex _gmtime_mutex = PTHREAD_MUTEX_STATIC_INITIALIZER;
118217706Sjulian	static pthread_mutex_t gmtime_mutex = &_gmtime_mutex;
118313545Sjulian	static pthread_key_t gmtime_key = -1;
118413545Sjulian	struct tm *p_tm;
118513545Sjulian
118613545Sjulian	pthread_mutex_lock(&gmtime_mutex);
118713545Sjulian	if (gmtime_key < 0) {
118819636Shsu		if (pthread_key_create(&gmtime_key, free) < 0) {
118913545Sjulian			pthread_mutex_unlock(&gmtime_mutex);
119013545Sjulian			return(NULL);
119113545Sjulian		}
119213545Sjulian	}
119313545Sjulian	pthread_mutex_unlock(&gmtime_mutex);
119433527Sjulian	/*
119533527Sjulian	 * Changed to follow draft 4 pthreads standard, which
119633527Sjulian	 * is what BSD currently has.
119733527Sjulian	 */
119833527Sjulian	if ((p_tm = pthread_getspecific(gmtime_key)) == NULL) {
119913545Sjulian		if ((p_tm = (struct tm *)malloc(sizeof(struct tm))) == NULL) {
120013545Sjulian			return(NULL);
120113545Sjulian		}
120213545Sjulian		pthread_setspecific(gmtime_key, p_tm);
120313545Sjulian	}
120413545Sjulian	gmtsub(timep, 0L, p_tm);
120513545Sjulian	return(p_tm);
120613545Sjulian#else
12072708Swollman	gmtsub(timep, 0L, &tm);
12082708Swollman	return &tm;
120913545Sjulian#endif
12102708Swollman}
12112708Swollman
121213545Sjulian#ifdef	_THREAD_SAFE
121319636Shsustruct tm *
121413545Sjuliangmtime_r(const time_t * timep, struct tm * tm)
121513545Sjulian{
121613545Sjulian	gmtsub(timep, 0L, tm);
121719636Shsu	return(tm);
121813545Sjulian}
121913545Sjulian#endif
122013545Sjulian
12212708Swollman#ifdef STD_INSPIRED
12222708Swollman
12232708Swollmanstruct tm *
12242708Swollmanofftime(timep, offset)
12252708Swollmanconst time_t * const	timep;
12262708Swollmanconst long		offset;
12272708Swollman{
12282708Swollman	gmtsub(timep, offset, &tm);
12292708Swollman	return &tm;
12302708Swollman}
12312708Swollman
12322708Swollman#endif /* defined STD_INSPIRED */
12332708Swollman
12342708Swollmanstatic void
12352708Swollmantimesub(timep, offset, sp, tmp)
12362708Swollmanconst time_t * const			timep;
12372708Swollmanconst long				offset;
12382708Swollmanregister const struct state * const	sp;
12392708Swollmanregister struct tm * const		tmp;
12402708Swollman{
12412708Swollman	register const struct lsinfo *	lp;
12422708Swollman	register long			days;
12432708Swollman	register long			rem;
12442708Swollman	register int			y;
12452708Swollman	register int			yleap;
12462708Swollman	register const int *		ip;
12472708Swollman	register long			corr;
12482708Swollman	register int			hit;
12492708Swollman	register int			i;
12502708Swollman
12512708Swollman	corr = 0;
12522708Swollman	hit = 0;
12532708Swollman#ifdef ALL_STATE
12542708Swollman	i = (sp == NULL) ? 0 : sp->leapcnt;
12552708Swollman#endif /* defined ALL_STATE */
12562708Swollman#ifndef ALL_STATE
12572708Swollman	i = sp->leapcnt;
12582708Swollman#endif /* State Farm */
12592708Swollman	while (--i >= 0) {
12602708Swollman		lp = &sp->lsis[i];
12612708Swollman		if (*timep >= lp->ls_trans) {
12622708Swollman			if (*timep == lp->ls_trans) {
12632708Swollman				hit = ((i == 0 && lp->ls_corr > 0) ||
12642708Swollman					lp->ls_corr > sp->lsis[i - 1].ls_corr);
12652708Swollman				if (hit)
12662708Swollman					while (i > 0 &&
12672708Swollman						sp->lsis[i].ls_trans ==
12682708Swollman						sp->lsis[i - 1].ls_trans + 1 &&
12692708Swollman						sp->lsis[i].ls_corr ==
12702708Swollman						sp->lsis[i - 1].ls_corr + 1) {
12712708Swollman							++hit;
12722708Swollman							--i;
12732708Swollman					}
12742708Swollman			}
12752708Swollman			corr = lp->ls_corr;
12762708Swollman			break;
12772708Swollman		}
12782708Swollman	}
12792708Swollman	days = *timep / SECSPERDAY;
12802708Swollman	rem = *timep % SECSPERDAY;
12812708Swollman#ifdef mc68k
12822708Swollman	if (*timep == 0x80000000) {
12832708Swollman		/*
12842708Swollman		** A 3B1 muffs the division on the most negative number.
12852708Swollman		*/
12862708Swollman		days = -24855;
12872708Swollman		rem = -11648;
12882708Swollman	}
12899936Swollman#endif /* defined mc68k */
12902708Swollman	rem += (offset - corr);
12912708Swollman	while (rem < 0) {
12922708Swollman		rem += SECSPERDAY;
12932708Swollman		--days;
12942708Swollman	}
12952708Swollman	while (rem >= SECSPERDAY) {
12962708Swollman		rem -= SECSPERDAY;
12972708Swollman		++days;
12982708Swollman	}
12992708Swollman	tmp->tm_hour = (int) (rem / SECSPERHOUR);
13002708Swollman	rem = rem % SECSPERHOUR;
13012708Swollman	tmp->tm_min = (int) (rem / SECSPERMIN);
130217209Swollman	/*
130317209Swollman	** A positive leap second requires a special
130417209Swollman	** representation.  This uses "... ??:59:60" et seq.
130517209Swollman	*/
130617209Swollman	tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
13072708Swollman	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
13082708Swollman	if (tmp->tm_wday < 0)
13092708Swollman		tmp->tm_wday += DAYSPERWEEK;
13102708Swollman	y = EPOCH_YEAR;
131117209Swollman#define LEAPS_THRU_END_OF(y)	((y) / 4 - (y) / 100 + (y) / 400)
131217209Swollman	while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
131317209Swollman		register int	newy;
131417209Swollman
131517209Swollman		newy = y + days / DAYSPERNYEAR;
131617209Swollman		if (days < 0)
131717209Swollman			--newy;
131817209Swollman		days -= (newy - y) * DAYSPERNYEAR +
131917209Swollman			LEAPS_THRU_END_OF(newy - 1) -
132017209Swollman			LEAPS_THRU_END_OF(y - 1);
132117209Swollman		y = newy;
132217209Swollman	}
13232708Swollman	tmp->tm_year = y - TM_YEAR_BASE;
13242708Swollman	tmp->tm_yday = (int) days;
13252708Swollman	ip = mon_lengths[yleap];
13262708Swollman	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
13272708Swollman		days = days - (long) ip[tmp->tm_mon];
13282708Swollman	tmp->tm_mday = (int) (days + 1);
13292708Swollman	tmp->tm_isdst = 0;
13302708Swollman#ifdef TM_GMTOFF
13312708Swollman	tmp->TM_GMTOFF = offset;
13322708Swollman#endif /* defined TM_GMTOFF */
13332708Swollman}
13342708Swollman
13352708Swollmanchar *
13362708Swollmanctime(timep)
13372708Swollmanconst time_t * const	timep;
13382708Swollman{
13399936Swollman/*
13409936Swollman** Section 4.12.3.2 of X3.159-1989 requires that
13419936Swollman**	The ctime funciton converts the calendar time pointed to by timer
13429936Swollman**	to local time in the form of a string.  It is equivalent to
13439936Swollman**		asctime(localtime(timer))
13449936Swollman*/
13452708Swollman	return asctime(localtime(timep));
13462708Swollman}
13472708Swollman
134835285Sphkchar *
134935285Sphkctime_r(timep, buf)
135035285Sphkconst time_t * const	timep;
135135285Sphkchar *buf;
135235285Sphk{
135335285Sphk        struct tm tm;
135435285Sphk	return asctime_r(localtime_r(timep, &tm), buf);
135535285Sphk}
135635285Sphk
13572708Swollman/*
13582708Swollman** Adapted from code provided by Robert Elz, who writes:
13592708Swollman**	The "best" way to do mktime I think is based on an idea of Bob
136017209Swollman**	Kridle's (so its said...) from a long time ago.
136117209Swollman**	[kridle@xinet.com as of 1996-01-16.]
13622708Swollman**	It does a binary search of the time_t space.  Since time_t's are
13632708Swollman**	just 32 bits, its a max of 32 iterations (even at 64 bits it
13642708Swollman**	would still be very reasonable).
13652708Swollman*/
13662708Swollman
13672708Swollman#ifndef WRONG
13682708Swollman#define WRONG	(-1)
13692708Swollman#endif /* !defined WRONG */
13702708Swollman
13712708Swollman/*
13722708Swollman** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).
13732708Swollman*/
13742708Swollman
13752708Swollmanstatic int
13762708Swollmanincrement_overflow(number, delta)
13772708Swollmanint *	number;
13782708Swollmanint	delta;
13792708Swollman{
13809936Swollman	int	number0;
13818870Srgrimes
13822708Swollman	number0 = *number;
13832708Swollman	*number += delta;
13842708Swollman	return (*number < number0) != (delta < 0);
13852708Swollman}
13862708Swollman
13872708Swollmanstatic int
13882708Swollmannormalize_overflow(tensptr, unitsptr, base)
13892708Swollmanint * const	tensptr;
13902708Swollmanint * const	unitsptr;
13912708Swollmanconst int	base;
13922708Swollman{
13932708Swollman	register int	tensdelta;
13942708Swollman
13952708Swollman	tensdelta = (*unitsptr >= 0) ?
13962708Swollman		(*unitsptr / base) :
13972708Swollman		(-1 - (-1 - *unitsptr) / base);
13982708Swollman	*unitsptr -= tensdelta * base;
13992708Swollman	return increment_overflow(tensptr, tensdelta);
14002708Swollman}
14012708Swollman
14022708Swollmanstatic int
14032708Swollmantmcomp(atmp, btmp)
14042708Swollmanregister const struct tm * const atmp;
14052708Swollmanregister const struct tm * const btmp;
14062708Swollman{
14072708Swollman	register int	result;
14082708Swollman
14092708Swollman	if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
14102708Swollman		(result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
14112708Swollman		(result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
14122708Swollman		(result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
14132708Swollman		(result = (atmp->tm_min - btmp->tm_min)) == 0)
14142708Swollman			result = atmp->tm_sec - btmp->tm_sec;
14152708Swollman	return result;
14162708Swollman}
14172708Swollman
14182708Swollmanstatic time_t
14192708Swollmantime2(tmp, funcp, offset, okayp)
14202708Swollmanstruct tm * const	tmp;
14219936Swollmanvoid (* const		funcp) P((const time_t*, long, struct tm*));
14222708Swollmanconst long		offset;
14232708Swollmanint * const		okayp;
14242708Swollman{
14252708Swollman	register const struct state *	sp;
14262708Swollman	register int			dir;
14272708Swollman	register int			bits;
14282708Swollman	register int			i, j ;
14292708Swollman	register int			saved_seconds;
14302708Swollman	time_t				newt;
14312708Swollman	time_t				t;
14322708Swollman	struct tm			yourtm, mytm;
14332708Swollman
14342708Swollman	*okayp = FALSE;
14352708Swollman	yourtm = *tmp;
14362708Swollman	if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
14372708Swollman		return WRONG;
14382708Swollman	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
14392708Swollman		return WRONG;
14402708Swollman	if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
14412708Swollman		return WRONG;
14422708Swollman	/*
14432708Swollman	** Turn yourtm.tm_year into an actual year number for now.
14442708Swollman	** It is converted back to an offset from TM_YEAR_BASE later.
14452708Swollman	*/
14462708Swollman	if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
14472708Swollman		return WRONG;
14482708Swollman	while (yourtm.tm_mday <= 0) {
14492708Swollman		if (increment_overflow(&yourtm.tm_year, -1))
14502708Swollman			return WRONG;
145117209Swollman		i = yourtm.tm_year + (1 < yourtm.tm_mon);
145217209Swollman		yourtm.tm_mday += year_lengths[isleap(i)];
14532708Swollman	}
14542708Swollman	while (yourtm.tm_mday > DAYSPERLYEAR) {
145517209Swollman		i = yourtm.tm_year + (1 < yourtm.tm_mon);
145617209Swollman		yourtm.tm_mday -= year_lengths[isleap(i)];
14572708Swollman		if (increment_overflow(&yourtm.tm_year, 1))
14582708Swollman			return WRONG;
14592708Swollman	}
14602708Swollman	for ( ; ; ) {
14612708Swollman		i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
14622708Swollman		if (yourtm.tm_mday <= i)
14632708Swollman			break;
14642708Swollman		yourtm.tm_mday -= i;
14652708Swollman		if (++yourtm.tm_mon >= MONSPERYEAR) {
14662708Swollman			yourtm.tm_mon = 0;
14672708Swollman			if (increment_overflow(&yourtm.tm_year, 1))
14682708Swollman				return WRONG;
14692708Swollman		}
14702708Swollman	}
14712708Swollman	if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
14722708Swollman		return WRONG;
14732708Swollman	if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
14742708Swollman		/*
14752708Swollman		** We can't set tm_sec to 0, because that might push the
14762708Swollman		** time below the minimum representable time.
14772708Swollman		** Set tm_sec to 59 instead.
14782708Swollman		** This assumes that the minimum representable time is
14792708Swollman		** not in the same minute that a leap second was deleted from,
14802708Swollman		** which is a safer assumption than using 58 would be.
14812708Swollman		*/
14822708Swollman		if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
14832708Swollman			return WRONG;
14842708Swollman		saved_seconds = yourtm.tm_sec;
14852708Swollman		yourtm.tm_sec = SECSPERMIN - 1;
14862708Swollman	} else {
14872708Swollman		saved_seconds = yourtm.tm_sec;
14882708Swollman		yourtm.tm_sec = 0;
14892708Swollman	}
14902708Swollman	/*
149117209Swollman	** Divide the search space in half
149217209Swollman	** (this works whether time_t is signed or unsigned).
14932708Swollman	*/
149417209Swollman	bits = TYPE_BIT(time_t) - 1;
14952708Swollman	/*
149617209Swollman	** If time_t is signed, then 0 is just above the median,
149717209Swollman	** assuming two's complement arithmetic.
149817209Swollman	** If time_t is unsigned, then (1 << bits) is just above the median.
14992708Swollman	*/
150017209Swollman	t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
15012708Swollman	for ( ; ; ) {
15022708Swollman		(*funcp)(&t, offset, &mytm);
15032708Swollman		dir = tmcomp(&mytm, &yourtm);
15042708Swollman		if (dir != 0) {
15052708Swollman			if (bits-- < 0)
15062708Swollman				return WRONG;
15072708Swollman			if (bits < 0)
150817209Swollman				--t; /* may be needed if new t is minimal */
15092708Swollman			else if (dir > 0)
151017209Swollman				t -= ((time_t) 1) << bits;
151117209Swollman			else	t += ((time_t) 1) << bits;
15122708Swollman			continue;
15132708Swollman		}
15142708Swollman		if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
15152708Swollman			break;
15162708Swollman		/*
15172708Swollman		** Right time, wrong type.
15182708Swollman		** Hunt for right time, right type.
15192708Swollman		** It's okay to guess wrong since the guess
15202708Swollman		** gets checked.
15212708Swollman		*/
15222708Swollman		/*
15232708Swollman		** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
15242708Swollman		*/
15252708Swollman		sp = (const struct state *)
15262708Swollman			(((void *) funcp == (void *) localsub) ?
15272708Swollman			lclptr : gmtptr);
15282708Swollman#ifdef ALL_STATE
15292708Swollman		if (sp == NULL)
15302708Swollman			return WRONG;
15312708Swollman#endif /* defined ALL_STATE */
153217209Swollman		for (i = sp->typecnt - 1; i >= 0; --i) {
15332708Swollman			if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
15342708Swollman				continue;
153517209Swollman			for (j = sp->typecnt - 1; j >= 0; --j) {
15362708Swollman				if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
15372708Swollman					continue;
15382708Swollman				newt = t + sp->ttis[j].tt_gmtoff -
15392708Swollman					sp->ttis[i].tt_gmtoff;
15402708Swollman				(*funcp)(&newt, offset, &mytm);
15412708Swollman				if (tmcomp(&mytm, &yourtm) != 0)
15422708Swollman					continue;
15432708Swollman				if (mytm.tm_isdst != yourtm.tm_isdst)
15442708Swollman					continue;
15452708Swollman				/*
15462708Swollman				** We have a match.
15472708Swollman				*/
15482708Swollman				t = newt;
15492708Swollman				goto label;
15502708Swollman			}
15512708Swollman		}
15522708Swollman		return WRONG;
15532708Swollman	}
15542708Swollmanlabel:
15552708Swollman	newt = t + saved_seconds;
15562708Swollman	if ((newt < t) != (saved_seconds < 0))
15572708Swollman		return WRONG;
15582708Swollman	t = newt;
15592708Swollman	(*funcp)(&t, offset, tmp);
15602708Swollman	*okayp = TRUE;
15612708Swollman	return t;
15622708Swollman}
15632708Swollman
15642708Swollmanstatic time_t
15652708Swollmantime1(tmp, funcp, offset)
15662708Swollmanstruct tm * const	tmp;
156717209Swollmanvoid (* const		funcp) P((const time_t *, long, struct tm *));
15682708Swollmanconst long		offset;
15692708Swollman{
15702708Swollman	register time_t			t;
15712708Swollman	register const struct state *	sp;
15722708Swollman	register int			samei, otheri;
15732708Swollman	int				okay;
15742708Swollman
15752708Swollman	if (tmp->tm_isdst > 1)
15762708Swollman		tmp->tm_isdst = 1;
15772708Swollman	t = time2(tmp, funcp, offset, &okay);
15782708Swollman#ifdef PCTS
15792708Swollman	/*
15802708Swollman	** PCTS code courtesy Grant Sullivan (grant@osf.org).
15812708Swollman	*/
15822708Swollman	if (okay)
15832708Swollman		return t;
15842708Swollman	if (tmp->tm_isdst < 0)
15852708Swollman		tmp->tm_isdst = 0;	/* reset to std and try again */
15862708Swollman#endif /* defined PCTS */
15872708Swollman#ifndef PCTS
15882708Swollman	if (okay || tmp->tm_isdst < 0)
15892708Swollman		return t;
15902708Swollman#endif /* !defined PCTS */
15912708Swollman	/*
15922708Swollman	** We're supposed to assume that somebody took a time of one type
15932708Swollman	** and did some math on it that yielded a "struct tm" that's bad.
15942708Swollman	** We try to divine the type they started from and adjust to the
15952708Swollman	** type they need.
15962708Swollman	*/
15972708Swollman	/*
15982708Swollman	** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
15992708Swollman	*/
16002708Swollman	sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
16012708Swollman		lclptr : gmtptr);
16022708Swollman#ifdef ALL_STATE
16032708Swollman	if (sp == NULL)
16042708Swollman		return WRONG;
16052708Swollman#endif /* defined ALL_STATE */
160617209Swollman	for (samei = sp->typecnt - 1; samei >= 0; --samei) {
16072708Swollman		if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
16082708Swollman			continue;
160917209Swollman		for (otheri = sp->typecnt - 1; otheri >= 0; --otheri) {
16102708Swollman			if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
16112708Swollman				continue;
16122708Swollman			tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
16132708Swollman					sp->ttis[samei].tt_gmtoff;
16142708Swollman			tmp->tm_isdst = !tmp->tm_isdst;
16152708Swollman			t = time2(tmp, funcp, offset, &okay);
16162708Swollman			if (okay)
16172708Swollman				return t;
16182708Swollman			tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
16192708Swollman					sp->ttis[samei].tt_gmtoff;
16202708Swollman			tmp->tm_isdst = !tmp->tm_isdst;
16212708Swollman		}
16222708Swollman	}
16232708Swollman	return WRONG;
16242708Swollman}
16252708Swollman
16262708Swollmantime_t
16272708Swollmanmktime(tmp)
16282708Swollmanstruct tm * const	tmp;
16292708Swollman{
163013545Sjulian	time_t mktime_return_value;
163113545Sjulian#ifdef	_THREAD_SAFE
163213545Sjulian	pthread_mutex_lock(&lcl_mutex);
163313545Sjulian#endif
16349936Swollman	tzset();
163513545Sjulian	mktime_return_value = time1(tmp, localsub, 0L);
163613545Sjulian#ifdef	_THREAD_SAFE
163713545Sjulian	pthread_mutex_unlock(&lcl_mutex);
163813545Sjulian#endif
163913545Sjulian	return(mktime_return_value);
16402708Swollman}
16412708Swollman
16422708Swollman#ifdef STD_INSPIRED
16432708Swollman
16442708Swollmantime_t
16452708Swollmantimelocal(tmp)
16462708Swollmanstruct tm * const	tmp;
16472708Swollman{
16482708Swollman	tmp->tm_isdst = -1;	/* in case it wasn't initialized */
16492708Swollman	return mktime(tmp);
16502708Swollman}
16512708Swollman
16522708Swollmantime_t
16532708Swollmantimegm(tmp)
16542708Swollmanstruct tm * const	tmp;
16552708Swollman{
16562708Swollman	tmp->tm_isdst = 0;
16572708Swollman	return time1(tmp, gmtsub, 0L);
16582708Swollman}
16592708Swollman
16602708Swollmantime_t
16612708Swollmantimeoff(tmp, offset)
16622708Swollmanstruct tm * const	tmp;
16632708Swollmanconst long		offset;
16642708Swollman{
16652708Swollman	tmp->tm_isdst = 0;
16662708Swollman	return time1(tmp, gmtsub, offset);
16672708Swollman}
16682708Swollman
16692708Swollman#endif /* defined STD_INSPIRED */
16702708Swollman
16712708Swollman#ifdef CMUCS
16722708Swollman
16732708Swollman/*
16742708Swollman** The following is supplied for compatibility with
16752708Swollman** previous versions of the CMUCS runtime library.
16762708Swollman*/
16772708Swollman
16782708Swollmanlong
16792708Swollmangtime(tmp)
16802708Swollmanstruct tm * const	tmp;
16812708Swollman{
16822708Swollman	const time_t	t = mktime(tmp);
16832708Swollman
16842708Swollman	if (t == WRONG)
16852708Swollman		return -1;
16862708Swollman	return t;
16872708Swollman}
16882708Swollman
16892708Swollman#endif /* defined CMUCS */
16902708Swollman
16912708Swollman/*
16922708Swollman** XXX--is the below the right way to conditionalize??
16932708Swollman*/
16942708Swollman
16952708Swollman#ifdef STD_INSPIRED
16962708Swollman
16972708Swollman/*
16982708Swollman** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599
16992708Swollman** shall correspond to "Wed Dec 31 23:59:59 GMT 1986", which
17002708Swollman** is not the case if we are accounting for leap seconds.
17012708Swollman** So, we provide the following conversion routines for use
17022708Swollman** when exchanging timestamps with POSIX conforming systems.
17032708Swollman*/
17042708Swollman
17052708Swollmanstatic long
17062708Swollmanleapcorr(timep)
17072708Swollmantime_t *	timep;
17082708Swollman{
17092708Swollman	register struct state *		sp;
17102708Swollman	register struct lsinfo *	lp;
17112708Swollman	register int			i;
17122708Swollman
17132708Swollman	sp = lclptr;
17142708Swollman	i = sp->leapcnt;
17152708Swollman	while (--i >= 0) {
17162708Swollman		lp = &sp->lsis[i];
17172708Swollman		if (*timep >= lp->ls_trans)
17182708Swollman			return lp->ls_corr;
17192708Swollman	}
17202708Swollman	return 0;
17212708Swollman}
17222708Swollman
17232708Swollmantime_t
17242708Swollmantime2posix(t)
17252708Swollmantime_t	t;
17262708Swollman{
17279936Swollman	tzset();
17282708Swollman	return t - leapcorr(&t);
17292708Swollman}
17302708Swollman
17312708Swollmantime_t
17322708Swollmanposix2time(t)
17332708Swollmantime_t	t;
17342708Swollman{
17352708Swollman	time_t	x;
17362708Swollman	time_t	y;
17372708Swollman
17389936Swollman	tzset();
17392708Swollman	/*
17402708Swollman	** For a positive leap second hit, the result
17412708Swollman	** is not unique.  For a negative leap second
17422708Swollman	** hit, the corresponding time doesn't exist,
17432708Swollman	** so we return an adjacent second.
17442708Swollman	*/
17452708Swollman	x = t + leapcorr(&t);
17462708Swollman	y = x - leapcorr(&x);
17472708Swollman	if (y < t) {
17482708Swollman		do {
17492708Swollman			x++;
17502708Swollman			y = x - leapcorr(&x);
17512708Swollman		} while (y < t);
17522708Swollman		if (t != y)
17532708Swollman			return x - 1;
17542708Swollman	} else if (y > t) {
17552708Swollman		do {
17562708Swollman			--x;
17572708Swollman			y = x - leapcorr(&x);
17582708Swollman		} while (y > t);
17592708Swollman		if (t != y)
17602708Swollman			return x + 1;
17612708Swollman	}
17622708Swollman	return x;
17632708Swollman}
17642708Swollman
17652708Swollman#endif /* defined STD_INSPIRED */
1766