154359Sroberto/*
254359Sroberto * Copyright (c) 1987, 1989 Regents of the University of California.
354359Sroberto * All rights reserved.
454359Sroberto *
554359Sroberto * This code is derived from software contributed to Berkeley by
654359Sroberto * Arthur David Olson of the National Cancer Institute.
754359Sroberto *
854359Sroberto * Redistribution and use in source and binary forms, with or without
954359Sroberto * modification, are permitted provided that the following conditions
1054359Sroberto * are met:
1154359Sroberto * 1. Redistributions of source code must retain the above copyright
1254359Sroberto *    notice, this list of conditions and the following disclaimer.
1354359Sroberto * 2. Redistributions in binary form must reproduce the above copyright
1454359Sroberto *    notice, this list of conditions and the following disclaimer in the
1554359Sroberto *    documentation and/or other materials provided with the distribution.
1654359Sroberto * 3. All advertising materials mentioning features or use of this software
1754359Sroberto *    must display the following acknowledgement:
1854359Sroberto *	This product includes software developed by the University of
1954359Sroberto *	California, Berkeley and its contributors.
2054359Sroberto * 4. Neither the name of the University nor the names of its contributors
2154359Sroberto *    may be used to endorse or promote products derived from this software
2254359Sroberto *    without specific prior written permission.
2354359Sroberto *
2454359Sroberto * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2554359Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2654359Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2754359Sroberto * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2854359Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2954359Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3054359Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3154359Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3254359Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3354359Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3454359Sroberto * SUCH DAMAGE.  */
3554359Sroberto
3654359Sroberto/*static char *sccsid = "from: @(#)ctime.c	5.26 (Berkeley) 2/23/91";*/
3754359Sroberto
3854359Sroberto/*
3954359Sroberto * This implementation of mktime is lifted straight from the NetBSD (BSD 4.4)
4054359Sroberto * version.  I modified it slightly to divorce it from the internals of the
4154359Sroberto * ctime library.  Thus this version can't use details of the internal
4254359Sroberto * timezone state file to figure out strange unnormalized struct tm values,
4354359Sroberto * as might result from someone doing date math on the tm struct then passing
4454359Sroberto * it to mktime.
4554359Sroberto *
4654359Sroberto * It just does as well as it can at normalizing the tm input, then does a
4754359Sroberto * binary search of the time space using the system's localtime() function.
4854359Sroberto *
4954359Sroberto * The original binary search was defective in that it didn't consider the
5054359Sroberto * setting of tm_isdst when comparing tm values, causing the search to be
5154359Sroberto * flubbed for times near the dst/standard time changeover.  The original
5254359Sroberto * code seems to make up for this by grubbing through the timezone info
5354359Sroberto * whenever the binary search barfed.  Since I don't have that luxury in
5454359Sroberto * portable code, I have to take care of tm_isdst in the comparison routine.
5554359Sroberto * This requires knowing how many minutes offset dst is from standard time.
5654359Sroberto *
5754359Sroberto * So, if you live somewhere in the world where dst is not 60 minutes offset,
5854359Sroberto * and your vendor doesn't supply mktime(), you'll have to edit this variable
5954359Sroberto * by hand.  Sorry about that.
6054359Sroberto */
6154359Sroberto
6282498Sroberto#include "ntp_machine.h"
6354359Sroberto
64182007Sroberto#if !defined(HAVE_MKTIME) || !defined(HAVE_TIMEGM)
65106163Sroberto
6654359Sroberto#ifndef DSTMINUTES
6754359Sroberto#define DSTMINUTES 60
6854359Sroberto#endif
6954359Sroberto
7054359Sroberto#define FALSE 0
7154359Sroberto#define TRUE 1
7254359Sroberto
7354359Sroberto/* some constants from tzfile.h */
7454359Sroberto#define SECSPERMIN      60
7554359Sroberto#define MINSPERHOUR     60
7654359Sroberto#define HOURSPERDAY     24
7754359Sroberto#define DAYSPERWEEK     7
7854359Sroberto#define DAYSPERNYEAR    365
7954359Sroberto#define DAYSPERLYEAR    366
8054359Sroberto#define SECSPERHOUR     (SECSPERMIN * MINSPERHOUR)
8154359Sroberto#define SECSPERDAY      ((long) SECSPERHOUR * HOURSPERDAY)
8254359Sroberto#define MONSPERYEAR     12
8354359Sroberto#define TM_YEAR_BASE    1900
8454359Sroberto#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
8554359Sroberto
8654359Srobertostatic int	mon_lengths[2][MONSPERYEAR] = {
8754359Sroberto	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
8854359Sroberto	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
8954359Sroberto};
9054359Sroberto
9154359Srobertostatic int	year_lengths[2] = {
9254359Sroberto	DAYSPERNYEAR, DAYSPERLYEAR
9354359Sroberto};
9454359Sroberto
9554359Sroberto/*
9654359Sroberto** Adapted from code provided by Robert Elz, who writes:
9754359Sroberto**	The "best" way to do mktime I think is based on an idea of Bob
9854359Sroberto**	Kridle's (so its said...) from a long time ago. (mtxinu!kridle now).
9954359Sroberto**	It does a binary search of the time_t space.  Since time_t's are
10054359Sroberto**	just 32 bits, its a max of 32 iterations (even at 64 bits it
10154359Sroberto**	would still be very reasonable).
10254359Sroberto*/
10354359Sroberto
10454359Sroberto#ifndef WRONG
10554359Sroberto#define WRONG	(-1)
10654359Sroberto#endif /* !defined WRONG */
10754359Sroberto
10854359Srobertostatic void
10954359Srobertonormalize(
11054359Sroberto	int * tensptr,
11154359Sroberto	int * unitsptr,
11254359Sroberto	int	base
11354359Sroberto	)
11454359Sroberto{
11554359Sroberto	if (*unitsptr >= base) {
11654359Sroberto		*tensptr += *unitsptr / base;
11754359Sroberto		*unitsptr %= base;
11854359Sroberto	} else if (*unitsptr < 0) {
11954359Sroberto		--*tensptr;
12054359Sroberto		*unitsptr += base;
12154359Sroberto		if (*unitsptr < 0) {
12254359Sroberto			*tensptr -= 1 + (-*unitsptr) / base;
12354359Sroberto			*unitsptr = base - (-*unitsptr) % base;
12454359Sroberto		}
12554359Sroberto	}
12654359Sroberto}
12754359Sroberto
12854359Srobertostatic struct tm *
12954359Srobertomkdst(
13054359Sroberto	struct tm *	tmp
13154359Sroberto	)
13254359Sroberto{
13354359Sroberto    /* jds */
13454359Sroberto    static struct tm tmbuf;
13554359Sroberto
13654359Sroberto    tmbuf = *tmp;
13754359Sroberto    tmbuf.tm_isdst = 1;
13854359Sroberto    tmbuf.tm_min += DSTMINUTES;
13954359Sroberto    normalize(&tmbuf.tm_hour, &tmbuf.tm_min, MINSPERHOUR);
14054359Sroberto    return &tmbuf;
14154359Sroberto}
14254359Sroberto
14354359Srobertostatic int
14454359Srobertotmcomp(
14554359Sroberto	register struct tm * atmp,
14654359Sroberto	register struct tm * btmp
14754359Sroberto	)
14854359Sroberto{
14954359Sroberto	register int	result;
15054359Sroberto
15154359Sroberto	/* compare down to the same day */
15254359Sroberto
15354359Sroberto	if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
15454359Sroberto	    (result = (atmp->tm_mon - btmp->tm_mon)) == 0)
15554359Sroberto	    result = (atmp->tm_mday - btmp->tm_mday);
15654359Sroberto
15754359Sroberto	if(result != 0)
15854359Sroberto	    return result;
15954359Sroberto
16054359Sroberto	/* get rid of one-sided dst bias */
16154359Sroberto
16254359Sroberto	if(atmp->tm_isdst == 1 && !btmp->tm_isdst)
16354359Sroberto	    btmp = mkdst(btmp);
16454359Sroberto	else if(btmp->tm_isdst == 1 && !atmp->tm_isdst)
16554359Sroberto	    atmp = mkdst(atmp);
16654359Sroberto
16754359Sroberto	/* compare the rest of the way */
16854359Sroberto
16954359Sroberto	if ((result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
17054359Sroberto	    (result = (atmp->tm_min - btmp->tm_min)) == 0)
17154359Sroberto	    result = atmp->tm_sec - btmp->tm_sec;
17254359Sroberto	return result;
17354359Sroberto}
17454359Sroberto
17554359Sroberto
17654359Srobertostatic time_t
17754359Srobertotime2(
17854359Sroberto	struct tm *	tmp,
179132451Sroberto	int * 		okayp,
180132451Sroberto	int		usezn
18154359Sroberto	)
18254359Sroberto{
18354359Sroberto	register int			dir;
18454359Sroberto	register int			bits;
18554359Sroberto	register int			i;
18654359Sroberto	register int			saved_seconds;
18754359Sroberto	time_t				t;
18854359Sroberto	struct tm			yourtm, mytm;
18954359Sroberto
19054359Sroberto	*okayp = FALSE;
19154359Sroberto	yourtm = *tmp;
19254359Sroberto	if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0)
19354359Sroberto		normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN);
19454359Sroberto	normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR);
19554359Sroberto	normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY);
19654359Sroberto	normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR);
19754359Sroberto	while (yourtm.tm_mday <= 0) {
19854359Sroberto		--yourtm.tm_year;
19954359Sroberto		yourtm.tm_mday +=
20054359Sroberto			year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)];
20154359Sroberto	}
20254359Sroberto	for ( ; ; ) {
20354359Sroberto		i = mon_lengths[isleap(yourtm.tm_year +
20454359Sroberto			TM_YEAR_BASE)][yourtm.tm_mon];
20554359Sroberto		if (yourtm.tm_mday <= i)
20654359Sroberto			break;
20754359Sroberto		yourtm.tm_mday -= i;
20854359Sroberto		if (++yourtm.tm_mon >= MONSPERYEAR) {
20954359Sroberto			yourtm.tm_mon = 0;
21054359Sroberto			++yourtm.tm_year;
21154359Sroberto		}
21254359Sroberto	}
21354359Sroberto	saved_seconds = yourtm.tm_sec;
21454359Sroberto	yourtm.tm_sec = 0;
21554359Sroberto	/*
21654359Sroberto	** Calculate the number of magnitude bits in a time_t
21754359Sroberto	** (this works regardless of whether time_t is
21854359Sroberto	** signed or unsigned, though lint complains if unsigned).
21954359Sroberto	*/
22054359Sroberto	for (bits = 0, t = 1; t > 0; ++bits, t <<= 1)
22154359Sroberto		;
22254359Sroberto	/*
22354359Sroberto	** If time_t is signed, then 0 is the median value,
22454359Sroberto	** if time_t is unsigned, then 1 << bits is median.
22554359Sroberto	*/
22654359Sroberto	t = (t < 0) ? 0 : ((time_t) 1 << bits);
22754359Sroberto	for ( ; ; ) {
228132451Sroberto		if (usezn)
229132451Sroberto	        	mytm = *localtime(&t);
230132451Sroberto		else
231132451Sroberto	        	mytm = *gmtime(&t);
23254359Sroberto		dir = tmcomp(&mytm, &yourtm);
23354359Sroberto		if (dir != 0) {
23454359Sroberto			if (bits-- < 0)
23554359Sroberto				return WRONG;
23654359Sroberto			if (bits < 0)
23754359Sroberto				--t;
23854359Sroberto			else if (dir > 0)
23954359Sroberto				t -= (time_t) 1 << bits;
24054359Sroberto			else	t += (time_t) 1 << bits;
24154359Sroberto			continue;
24254359Sroberto		}
24354359Sroberto		if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
24454359Sroberto			break;
24554359Sroberto
24654359Sroberto		return WRONG;
24754359Sroberto	}
24854359Sroberto	t += saved_seconds;
249132451Sroberto	if (usezn)
250132451Sroberto		*tmp = *localtime(&t);
251132451Sroberto	else
252132451Sroberto		*tmp = *gmtime(&t);
25354359Sroberto	*okayp = TRUE;
25454359Sroberto	return t;
25554359Sroberto}
256132451Sroberto#else
257132451Srobertoint mktime_bs;
258132451Sroberto#endif /* !HAVE_MKTIME || !HAVE_TIMEGM */
25954359Sroberto
260182007Sroberto#ifndef HAVE_MKTIME
26154359Srobertostatic time_t
26254359Srobertotime1(
26354359Sroberto	struct tm * tmp
26454359Sroberto	)
26554359Sroberto{
26654359Sroberto	register time_t			t;
26754359Sroberto	int				okay;
26854359Sroberto
26954359Sroberto	if (tmp->tm_isdst > 1)
27054359Sroberto		tmp->tm_isdst = 1;
271132451Sroberto	t = time2(tmp, &okay, 1);
27254359Sroberto	if (okay || tmp->tm_isdst < 0)
27354359Sroberto		return t;
27454359Sroberto
27554359Sroberto	return WRONG;
27654359Sroberto}
27754359Sroberto
27854359Srobertotime_t
27954359Srobertomktime(
28054359Sroberto	struct tm * tmp
28154359Sroberto	)
28254359Sroberto{
28354359Sroberto	return time1(tmp);
28454359Sroberto}
285132451Sroberto#endif /* !HAVE_MKTIME */
286132451Sroberto
287182007Sroberto#ifndef HAVE_TIMEGM
288132451Srobertotime_t
289132451Srobertotimegm(
290132451Sroberto	struct tm * tmp
291132451Sroberto	)
292132451Sroberto{
293132451Sroberto	register time_t			t;
294132451Sroberto	int				okay;
295132451Sroberto
296132451Sroberto	tmp->tm_isdst = 0;
297132451Sroberto	t = time2(tmp, &okay, 0);
298132451Sroberto	if (okay || tmp->tm_isdst < 0)
299132451Sroberto		return t;
300132451Sroberto
301132451Sroberto	return WRONG;
302132451Sroberto}
303132451Sroberto#endif /* !HAVE_TIMEGM */
304