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
62290001Sglebius#include <config.h>
6382498Sroberto#include "ntp_machine.h"
6454359Sroberto
65290001Sglebius#if !defined(HAVE_MKTIME) || ( !defined(HAVE_TIMEGM) && defined(WANT_TIMEGM) )
66106163Sroberto
67290001Sglebius#if SIZEOF_TIME_T >= 8
68290001Sglebius#error libntp supplied mktime()/timegm() do not support 64-bit time_t
69290001Sglebius#endif
70290001Sglebius
7154359Sroberto#ifndef DSTMINUTES
7254359Sroberto#define DSTMINUTES 60
7354359Sroberto#endif
7454359Sroberto
7554359Sroberto#define FALSE 0
7654359Sroberto#define TRUE 1
7754359Sroberto
7854359Sroberto/* some constants from tzfile.h */
7954359Sroberto#define SECSPERMIN      60
8054359Sroberto#define MINSPERHOUR     60
8154359Sroberto#define HOURSPERDAY     24
8254359Sroberto#define DAYSPERWEEK     7
8354359Sroberto#define DAYSPERNYEAR    365
8454359Sroberto#define DAYSPERLYEAR    366
8554359Sroberto#define SECSPERHOUR     (SECSPERMIN * MINSPERHOUR)
8654359Sroberto#define SECSPERDAY      ((long) SECSPERHOUR * HOURSPERDAY)
8754359Sroberto#define MONSPERYEAR     12
8854359Sroberto#define TM_YEAR_BASE    1900
8954359Sroberto#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
9054359Sroberto
9154359Srobertostatic int	mon_lengths[2][MONSPERYEAR] = {
9254359Sroberto	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
9354359Sroberto	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
9454359Sroberto};
9554359Sroberto
9654359Srobertostatic int	year_lengths[2] = {
9754359Sroberto	DAYSPERNYEAR, DAYSPERLYEAR
9854359Sroberto};
9954359Sroberto
10054359Sroberto/*
10154359Sroberto** Adapted from code provided by Robert Elz, who writes:
10254359Sroberto**	The "best" way to do mktime I think is based on an idea of Bob
10354359Sroberto**	Kridle's (so its said...) from a long time ago. (mtxinu!kridle now).
10454359Sroberto**	It does a binary search of the time_t space.  Since time_t's are
10554359Sroberto**	just 32 bits, its a max of 32 iterations (even at 64 bits it
10654359Sroberto**	would still be very reasonable).
10754359Sroberto*/
10854359Sroberto
10954359Sroberto#ifndef WRONG
11054359Sroberto#define WRONG	(-1)
11154359Sroberto#endif /* !defined WRONG */
11254359Sroberto
11354359Srobertostatic void
11454359Srobertonormalize(
11554359Sroberto	int * tensptr,
11654359Sroberto	int * unitsptr,
11754359Sroberto	int	base
11854359Sroberto	)
11954359Sroberto{
12054359Sroberto	if (*unitsptr >= base) {
12154359Sroberto		*tensptr += *unitsptr / base;
12254359Sroberto		*unitsptr %= base;
12354359Sroberto	} else if (*unitsptr < 0) {
12454359Sroberto		--*tensptr;
12554359Sroberto		*unitsptr += base;
12654359Sroberto		if (*unitsptr < 0) {
12754359Sroberto			*tensptr -= 1 + (-*unitsptr) / base;
12854359Sroberto			*unitsptr = base - (-*unitsptr) % base;
12954359Sroberto		}
13054359Sroberto	}
13154359Sroberto}
13254359Sroberto
13354359Srobertostatic struct tm *
13454359Srobertomkdst(
13554359Sroberto	struct tm *	tmp
13654359Sroberto	)
13754359Sroberto{
13854359Sroberto    /* jds */
13954359Sroberto    static struct tm tmbuf;
14054359Sroberto
14154359Sroberto    tmbuf = *tmp;
14254359Sroberto    tmbuf.tm_isdst = 1;
14354359Sroberto    tmbuf.tm_min += DSTMINUTES;
14454359Sroberto    normalize(&tmbuf.tm_hour, &tmbuf.tm_min, MINSPERHOUR);
14554359Sroberto    return &tmbuf;
14654359Sroberto}
14754359Sroberto
14854359Srobertostatic int
14954359Srobertotmcomp(
15054359Sroberto	register struct tm * atmp,
15154359Sroberto	register struct tm * btmp
15254359Sroberto	)
15354359Sroberto{
15454359Sroberto	register int	result;
15554359Sroberto
15654359Sroberto	/* compare down to the same day */
15754359Sroberto
15854359Sroberto	if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
15954359Sroberto	    (result = (atmp->tm_mon - btmp->tm_mon)) == 0)
16054359Sroberto	    result = (atmp->tm_mday - btmp->tm_mday);
16154359Sroberto
16254359Sroberto	if(result != 0)
16354359Sroberto	    return result;
16454359Sroberto
16554359Sroberto	/* get rid of one-sided dst bias */
16654359Sroberto
16754359Sroberto	if(atmp->tm_isdst == 1 && !btmp->tm_isdst)
16854359Sroberto	    btmp = mkdst(btmp);
16954359Sroberto	else if(btmp->tm_isdst == 1 && !atmp->tm_isdst)
17054359Sroberto	    atmp = mkdst(atmp);
17154359Sroberto
17254359Sroberto	/* compare the rest of the way */
17354359Sroberto
17454359Sroberto	if ((result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
17554359Sroberto	    (result = (atmp->tm_min - btmp->tm_min)) == 0)
17654359Sroberto	    result = atmp->tm_sec - btmp->tm_sec;
17754359Sroberto	return result;
17854359Sroberto}
17954359Sroberto
18054359Sroberto
18154359Srobertostatic time_t
18254359Srobertotime2(
18354359Sroberto	struct tm *	tmp,
184132451Sroberto	int * 		okayp,
185132451Sroberto	int		usezn
18654359Sroberto	)
18754359Sroberto{
18854359Sroberto	register int			dir;
18954359Sroberto	register int			bits;
19054359Sroberto	register int			i;
19154359Sroberto	register int			saved_seconds;
19254359Sroberto	time_t				t;
19354359Sroberto	struct tm			yourtm, mytm;
19454359Sroberto
19554359Sroberto	*okayp = FALSE;
19654359Sroberto	yourtm = *tmp;
19754359Sroberto	if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0)
19854359Sroberto		normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN);
19954359Sroberto	normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR);
20054359Sroberto	normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY);
20154359Sroberto	normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR);
20254359Sroberto	while (yourtm.tm_mday <= 0) {
20354359Sroberto		--yourtm.tm_year;
20454359Sroberto		yourtm.tm_mday +=
20554359Sroberto			year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)];
20654359Sroberto	}
20754359Sroberto	for ( ; ; ) {
20854359Sroberto		i = mon_lengths[isleap(yourtm.tm_year +
20954359Sroberto			TM_YEAR_BASE)][yourtm.tm_mon];
21054359Sroberto		if (yourtm.tm_mday <= i)
21154359Sroberto			break;
21254359Sroberto		yourtm.tm_mday -= i;
21354359Sroberto		if (++yourtm.tm_mon >= MONSPERYEAR) {
21454359Sroberto			yourtm.tm_mon = 0;
21554359Sroberto			++yourtm.tm_year;
21654359Sroberto		}
21754359Sroberto	}
21854359Sroberto	saved_seconds = yourtm.tm_sec;
21954359Sroberto	yourtm.tm_sec = 0;
22054359Sroberto	/*
22154359Sroberto	** Calculate the number of magnitude bits in a time_t
22254359Sroberto	** (this works regardless of whether time_t is
22354359Sroberto	** signed or unsigned, though lint complains if unsigned).
22454359Sroberto	*/
22554359Sroberto	for (bits = 0, t = 1; t > 0; ++bits, t <<= 1)
22654359Sroberto		;
22754359Sroberto	/*
22854359Sroberto	** If time_t is signed, then 0 is the median value,
22954359Sroberto	** if time_t is unsigned, then 1 << bits is median.
23054359Sroberto	*/
23154359Sroberto	t = (t < 0) ? 0 : ((time_t) 1 << bits);
23254359Sroberto	for ( ; ; ) {
233132451Sroberto		if (usezn)
234290001Sglebius			mytm = *localtime(&t);
235132451Sroberto		else
236290001Sglebius			mytm = *gmtime(&t);
23754359Sroberto		dir = tmcomp(&mytm, &yourtm);
23854359Sroberto		if (dir != 0) {
23954359Sroberto			if (bits-- < 0)
24054359Sroberto				return WRONG;
24154359Sroberto			if (bits < 0)
24254359Sroberto				--t;
24354359Sroberto			else if (dir > 0)
24454359Sroberto				t -= (time_t) 1 << bits;
24554359Sroberto			else	t += (time_t) 1 << bits;
24654359Sroberto			continue;
24754359Sroberto		}
24854359Sroberto		if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
24954359Sroberto			break;
25054359Sroberto
25154359Sroberto		return WRONG;
25254359Sroberto	}
25354359Sroberto	t += saved_seconds;
254132451Sroberto	if (usezn)
255132451Sroberto		*tmp = *localtime(&t);
256132451Sroberto	else
257132451Sroberto		*tmp = *gmtime(&t);
25854359Sroberto	*okayp = TRUE;
25954359Sroberto	return t;
26054359Sroberto}
261132451Sroberto#else
262132451Srobertoint mktime_bs;
263132451Sroberto#endif /* !HAVE_MKTIME || !HAVE_TIMEGM */
26454359Sroberto
265182007Sroberto#ifndef HAVE_MKTIME
26654359Srobertostatic time_t
26754359Srobertotime1(
26854359Sroberto	struct tm * tmp
26954359Sroberto	)
27054359Sroberto{
27154359Sroberto	register time_t			t;
27254359Sroberto	int				okay;
27354359Sroberto
27454359Sroberto	if (tmp->tm_isdst > 1)
27554359Sroberto		tmp->tm_isdst = 1;
276132451Sroberto	t = time2(tmp, &okay, 1);
27754359Sroberto	if (okay || tmp->tm_isdst < 0)
27854359Sroberto		return t;
27954359Sroberto
28054359Sroberto	return WRONG;
28154359Sroberto}
28254359Sroberto
28354359Srobertotime_t
28454359Srobertomktime(
28554359Sroberto	struct tm * tmp
28654359Sroberto	)
28754359Sroberto{
28854359Sroberto	return time1(tmp);
28954359Sroberto}
290132451Sroberto#endif /* !HAVE_MKTIME */
291132451Sroberto
292290001Sglebius#ifdef WANT_TIMEGM
293182007Sroberto#ifndef HAVE_TIMEGM
294132451Srobertotime_t
295132451Srobertotimegm(
296132451Sroberto	struct tm * tmp
297132451Sroberto	)
298132451Sroberto{
299132451Sroberto	register time_t			t;
300132451Sroberto	int				okay;
301132451Sroberto
302132451Sroberto	tmp->tm_isdst = 0;
303132451Sroberto	t = time2(tmp, &okay, 0);
304132451Sroberto	if (okay || tmp->tm_isdst < 0)
305132451Sroberto		return t;
306132451Sroberto
307132451Sroberto	return WRONG;
308132451Sroberto}
309132451Sroberto#endif /* !HAVE_TIMEGM */
310290001Sglebius#endif /* WANT_TIMEGM */
311