xref: /freebsd/contrib/ntp/libntp/mktime.c (revision a466cc55)
1c0b746e5SOllivier Robert /*
2c0b746e5SOllivier Robert  * Copyright (c) 1987, 1989 Regents of the University of California.
3c0b746e5SOllivier Robert  * All rights reserved.
4c0b746e5SOllivier Robert  *
5c0b746e5SOllivier Robert  * This code is derived from software contributed to Berkeley by
6c0b746e5SOllivier Robert  * Arthur David Olson of the National Cancer Institute.
7c0b746e5SOllivier Robert  *
8c0b746e5SOllivier Robert  * Redistribution and use in source and binary forms, with or without
9c0b746e5SOllivier Robert  * modification, are permitted provided that the following conditions
10c0b746e5SOllivier Robert  * are met:
11c0b746e5SOllivier Robert  * 1. Redistributions of source code must retain the above copyright
12c0b746e5SOllivier Robert  *    notice, this list of conditions and the following disclaimer.
13c0b746e5SOllivier Robert  * 2. Redistributions in binary form must reproduce the above copyright
14c0b746e5SOllivier Robert  *    notice, this list of conditions and the following disclaimer in the
15c0b746e5SOllivier Robert  *    documentation and/or other materials provided with the distribution.
16c0b746e5SOllivier Robert  * 3. All advertising materials mentioning features or use of this software
17c0b746e5SOllivier Robert  *    must display the following acknowledgement:
18c0b746e5SOllivier Robert  *	This product includes software developed by the University of
19c0b746e5SOllivier Robert  *	California, Berkeley and its contributors.
20c0b746e5SOllivier Robert  * 4. Neither the name of the University nor the names of its contributors
21c0b746e5SOllivier Robert  *    may be used to endorse or promote products derived from this software
22c0b746e5SOllivier Robert  *    without specific prior written permission.
23c0b746e5SOllivier Robert  *
24c0b746e5SOllivier Robert  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25c0b746e5SOllivier Robert  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26c0b746e5SOllivier Robert  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27c0b746e5SOllivier Robert  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28c0b746e5SOllivier Robert  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29c0b746e5SOllivier Robert  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30c0b746e5SOllivier Robert  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31c0b746e5SOllivier Robert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32c0b746e5SOllivier Robert  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33c0b746e5SOllivier Robert  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34c0b746e5SOllivier Robert  * SUCH DAMAGE.  */
35c0b746e5SOllivier Robert 
36c0b746e5SOllivier Robert /*static char *sccsid = "from: @(#)ctime.c	5.26 (Berkeley) 2/23/91";*/
37c0b746e5SOllivier Robert 
38c0b746e5SOllivier Robert /*
39c0b746e5SOllivier Robert  * This implementation of mktime is lifted straight from the NetBSD (BSD 4.4)
40c0b746e5SOllivier Robert  * version.  I modified it slightly to divorce it from the internals of the
41c0b746e5SOllivier Robert  * ctime library.  Thus this version can't use details of the internal
42c0b746e5SOllivier Robert  * timezone state file to figure out strange unnormalized struct tm values,
43c0b746e5SOllivier Robert  * as might result from someone doing date math on the tm struct then passing
44c0b746e5SOllivier Robert  * it to mktime.
45c0b746e5SOllivier Robert  *
46c0b746e5SOllivier Robert  * It just does as well as it can at normalizing the tm input, then does a
47c0b746e5SOllivier Robert  * binary search of the time space using the system's localtime() function.
48c0b746e5SOllivier Robert  *
49c0b746e5SOllivier Robert  * The original binary search was defective in that it didn't consider the
50c0b746e5SOllivier Robert  * setting of tm_isdst when comparing tm values, causing the search to be
51c0b746e5SOllivier Robert  * flubbed for times near the dst/standard time changeover.  The original
52c0b746e5SOllivier Robert  * code seems to make up for this by grubbing through the timezone info
53c0b746e5SOllivier Robert  * whenever the binary search barfed.  Since I don't have that luxury in
54c0b746e5SOllivier Robert  * portable code, I have to take care of tm_isdst in the comparison routine.
55c0b746e5SOllivier Robert  * This requires knowing how many minutes offset dst is from standard time.
56c0b746e5SOllivier Robert  *
57c0b746e5SOllivier Robert  * So, if you live somewhere in the world where dst is not 60 minutes offset,
58c0b746e5SOllivier Robert  * and your vendor doesn't supply mktime(), you'll have to edit this variable
59c0b746e5SOllivier Robert  * by hand.  Sorry about that.
60c0b746e5SOllivier Robert  */
61c0b746e5SOllivier Robert 
622b15cb3dSCy Schubert #include <config.h>
63a466cc55SCy Schubert #include "ntp_types.h"
64c0b746e5SOllivier Robert 
652b15cb3dSCy Schubert #if !defined(HAVE_MKTIME) || ( !defined(HAVE_TIMEGM) && defined(WANT_TIMEGM) )
662b15cb3dSCy Schubert 
672b15cb3dSCy Schubert #if SIZEOF_TIME_T >= 8
682b15cb3dSCy Schubert #error libntp supplied mktime()/timegm() do not support 64-bit time_t
692b15cb3dSCy Schubert #endif
70ce265a54SOllivier Robert 
71c0b746e5SOllivier Robert #ifndef DSTMINUTES
72c0b746e5SOllivier Robert #define DSTMINUTES 60
73c0b746e5SOllivier Robert #endif
74c0b746e5SOllivier Robert 
75c0b746e5SOllivier Robert #define FALSE 0
76c0b746e5SOllivier Robert #define TRUE 1
77c0b746e5SOllivier Robert 
78c0b746e5SOllivier Robert /* some constants from tzfile.h */
79c0b746e5SOllivier Robert #define SECSPERMIN      60
80c0b746e5SOllivier Robert #define MINSPERHOUR     60
81c0b746e5SOllivier Robert #define HOURSPERDAY     24
82c0b746e5SOllivier Robert #define DAYSPERWEEK     7
83c0b746e5SOllivier Robert #define DAYSPERNYEAR    365
84c0b746e5SOllivier Robert #define DAYSPERLYEAR    366
85c0b746e5SOllivier Robert #define SECSPERHOUR     (SECSPERMIN * MINSPERHOUR)
86c0b746e5SOllivier Robert #define SECSPERDAY      ((long) SECSPERHOUR * HOURSPERDAY)
87c0b746e5SOllivier Robert #define MONSPERYEAR     12
88c0b746e5SOllivier Robert #define TM_YEAR_BASE    1900
89c0b746e5SOllivier Robert #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
90c0b746e5SOllivier Robert 
91c0b746e5SOllivier Robert static int	mon_lengths[2][MONSPERYEAR] = {
92c0b746e5SOllivier Robert 	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
93c0b746e5SOllivier Robert 	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
94c0b746e5SOllivier Robert };
95c0b746e5SOllivier Robert 
96c0b746e5SOllivier Robert static int	year_lengths[2] = {
97c0b746e5SOllivier Robert 	DAYSPERNYEAR, DAYSPERLYEAR
98c0b746e5SOllivier Robert };
99c0b746e5SOllivier Robert 
100c0b746e5SOllivier Robert /*
101c0b746e5SOllivier Robert ** Adapted from code provided by Robert Elz, who writes:
102c0b746e5SOllivier Robert **	The "best" way to do mktime I think is based on an idea of Bob
103c0b746e5SOllivier Robert **	Kridle's (so its said...) from a long time ago. (mtxinu!kridle now).
104c0b746e5SOllivier Robert **	It does a binary search of the time_t space.  Since time_t's are
105c0b746e5SOllivier Robert **	just 32 bits, its a max of 32 iterations (even at 64 bits it
106c0b746e5SOllivier Robert **	would still be very reasonable).
107c0b746e5SOllivier Robert */
108c0b746e5SOllivier Robert 
109c0b746e5SOllivier Robert #ifndef WRONG
110c0b746e5SOllivier Robert #define WRONG	(-1)
111c0b746e5SOllivier Robert #endif /* !defined WRONG */
112c0b746e5SOllivier Robert 
113c0b746e5SOllivier Robert static void
normalize(int * tensptr,int * unitsptr,int base)114c0b746e5SOllivier Robert normalize(
115c0b746e5SOllivier Robert 	int * tensptr,
116c0b746e5SOllivier Robert 	int * unitsptr,
117c0b746e5SOllivier Robert 	int	base
118c0b746e5SOllivier Robert 	)
119c0b746e5SOllivier Robert {
120c0b746e5SOllivier Robert 	if (*unitsptr >= base) {
121c0b746e5SOllivier Robert 		*tensptr += *unitsptr / base;
122c0b746e5SOllivier Robert 		*unitsptr %= base;
123c0b746e5SOllivier Robert 	} else if (*unitsptr < 0) {
124c0b746e5SOllivier Robert 		--*tensptr;
125c0b746e5SOllivier Robert 		*unitsptr += base;
126c0b746e5SOllivier Robert 		if (*unitsptr < 0) {
127c0b746e5SOllivier Robert 			*tensptr -= 1 + (-*unitsptr) / base;
128c0b746e5SOllivier Robert 			*unitsptr = base - (-*unitsptr) % base;
129c0b746e5SOllivier Robert 		}
130c0b746e5SOllivier Robert 	}
131c0b746e5SOllivier Robert }
132c0b746e5SOllivier Robert 
133c0b746e5SOllivier Robert static struct tm *
mkdst(struct tm * tmp)134c0b746e5SOllivier Robert mkdst(
135c0b746e5SOllivier Robert 	struct tm *	tmp
136c0b746e5SOllivier Robert 	)
137c0b746e5SOllivier Robert {
138c0b746e5SOllivier Robert     /* jds */
139c0b746e5SOllivier Robert     static struct tm tmbuf;
140c0b746e5SOllivier Robert 
141c0b746e5SOllivier Robert     tmbuf = *tmp;
142c0b746e5SOllivier Robert     tmbuf.tm_isdst = 1;
143c0b746e5SOllivier Robert     tmbuf.tm_min += DSTMINUTES;
144c0b746e5SOllivier Robert     normalize(&tmbuf.tm_hour, &tmbuf.tm_min, MINSPERHOUR);
145c0b746e5SOllivier Robert     return &tmbuf;
146c0b746e5SOllivier Robert }
147c0b746e5SOllivier Robert 
148c0b746e5SOllivier Robert static int
tmcomp(register struct tm * atmp,register struct tm * btmp)149c0b746e5SOllivier Robert tmcomp(
150c0b746e5SOllivier Robert 	register struct tm * atmp,
151c0b746e5SOllivier Robert 	register struct tm * btmp
152c0b746e5SOllivier Robert 	)
153c0b746e5SOllivier Robert {
154c0b746e5SOllivier Robert 	register int	result;
155c0b746e5SOllivier Robert 
156c0b746e5SOllivier Robert 	/* compare down to the same day */
157c0b746e5SOllivier Robert 
158c0b746e5SOllivier Robert 	if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
159c0b746e5SOllivier Robert 	    (result = (atmp->tm_mon - btmp->tm_mon)) == 0)
160c0b746e5SOllivier Robert 	    result = (atmp->tm_mday - btmp->tm_mday);
161c0b746e5SOllivier Robert 
162c0b746e5SOllivier Robert 	if(result != 0)
163c0b746e5SOllivier Robert 	    return result;
164c0b746e5SOllivier Robert 
165c0b746e5SOllivier Robert 	/* get rid of one-sided dst bias */
166c0b746e5SOllivier Robert 
167c0b746e5SOllivier Robert 	if(atmp->tm_isdst == 1 && !btmp->tm_isdst)
168c0b746e5SOllivier Robert 	    btmp = mkdst(btmp);
169c0b746e5SOllivier Robert 	else if(btmp->tm_isdst == 1 && !atmp->tm_isdst)
170c0b746e5SOllivier Robert 	    atmp = mkdst(atmp);
171c0b746e5SOllivier Robert 
172c0b746e5SOllivier Robert 	/* compare the rest of the way */
173c0b746e5SOllivier Robert 
174c0b746e5SOllivier Robert 	if ((result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
175c0b746e5SOllivier Robert 	    (result = (atmp->tm_min - btmp->tm_min)) == 0)
176c0b746e5SOllivier Robert 	    result = atmp->tm_sec - btmp->tm_sec;
177c0b746e5SOllivier Robert 	return result;
178c0b746e5SOllivier Robert }
179c0b746e5SOllivier Robert 
180c0b746e5SOllivier Robert 
181c0b746e5SOllivier Robert static time_t
time2(struct tm * tmp,int * okayp,int usezn)182c0b746e5SOllivier Robert time2(
183c0b746e5SOllivier Robert 	struct tm *	tmp,
1849c2daa00SOllivier Robert 	int * 		okayp,
1859c2daa00SOllivier Robert 	int		usezn
186c0b746e5SOllivier Robert 	)
187c0b746e5SOllivier Robert {
188c0b746e5SOllivier Robert 	register int			dir;
189c0b746e5SOllivier Robert 	register int			bits;
190c0b746e5SOllivier Robert 	register int			i;
191c0b746e5SOllivier Robert 	register int			saved_seconds;
192c0b746e5SOllivier Robert 	time_t				t;
193c0b746e5SOllivier Robert 	struct tm			yourtm, mytm;
194c0b746e5SOllivier Robert 
195c0b746e5SOllivier Robert 	*okayp = FALSE;
196c0b746e5SOllivier Robert 	yourtm = *tmp;
197c0b746e5SOllivier Robert 	if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0)
198c0b746e5SOllivier Robert 		normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN);
199c0b746e5SOllivier Robert 	normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR);
200c0b746e5SOllivier Robert 	normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY);
201c0b746e5SOllivier Robert 	normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR);
202c0b746e5SOllivier Robert 	while (yourtm.tm_mday <= 0) {
203c0b746e5SOllivier Robert 		--yourtm.tm_year;
204c0b746e5SOllivier Robert 		yourtm.tm_mday +=
205c0b746e5SOllivier Robert 			year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)];
206c0b746e5SOllivier Robert 	}
207c0b746e5SOllivier Robert 	for ( ; ; ) {
208c0b746e5SOllivier Robert 		i = mon_lengths[isleap(yourtm.tm_year +
209c0b746e5SOllivier Robert 			TM_YEAR_BASE)][yourtm.tm_mon];
210c0b746e5SOllivier Robert 		if (yourtm.tm_mday <= i)
211c0b746e5SOllivier Robert 			break;
212c0b746e5SOllivier Robert 		yourtm.tm_mday -= i;
213c0b746e5SOllivier Robert 		if (++yourtm.tm_mon >= MONSPERYEAR) {
214c0b746e5SOllivier Robert 			yourtm.tm_mon = 0;
215c0b746e5SOllivier Robert 			++yourtm.tm_year;
216c0b746e5SOllivier Robert 		}
217c0b746e5SOllivier Robert 	}
218c0b746e5SOllivier Robert 	saved_seconds = yourtm.tm_sec;
219c0b746e5SOllivier Robert 	yourtm.tm_sec = 0;
220c0b746e5SOllivier Robert 	/*
221c0b746e5SOllivier Robert 	** Calculate the number of magnitude bits in a time_t
222c0b746e5SOllivier Robert 	** (this works regardless of whether time_t is
223c0b746e5SOllivier Robert 	** signed or unsigned, though lint complains if unsigned).
224c0b746e5SOllivier Robert 	*/
225c0b746e5SOllivier Robert 	for (bits = 0, t = 1; t > 0; ++bits, t <<= 1)
226c0b746e5SOllivier Robert 		;
227c0b746e5SOllivier Robert 	/*
228c0b746e5SOllivier Robert 	** If time_t is signed, then 0 is the median value,
229c0b746e5SOllivier Robert 	** if time_t is unsigned, then 1 << bits is median.
230c0b746e5SOllivier Robert 	*/
231c0b746e5SOllivier Robert 	t = (t < 0) ? 0 : ((time_t) 1 << bits);
232c0b746e5SOllivier Robert 	for ( ; ; ) {
2339c2daa00SOllivier Robert 		if (usezn)
234c0b746e5SOllivier Robert 			mytm = *localtime(&t);
2359c2daa00SOllivier Robert 		else
2369c2daa00SOllivier Robert 			mytm = *gmtime(&t);
237c0b746e5SOllivier Robert 		dir = tmcomp(&mytm, &yourtm);
238c0b746e5SOllivier Robert 		if (dir != 0) {
239c0b746e5SOllivier Robert 			if (bits-- < 0)
240c0b746e5SOllivier Robert 				return WRONG;
241c0b746e5SOllivier Robert 			if (bits < 0)
242c0b746e5SOllivier Robert 				--t;
243c0b746e5SOllivier Robert 			else if (dir > 0)
244c0b746e5SOllivier Robert 				t -= (time_t) 1 << bits;
245c0b746e5SOllivier Robert 			else	t += (time_t) 1 << bits;
246c0b746e5SOllivier Robert 			continue;
247c0b746e5SOllivier Robert 		}
248c0b746e5SOllivier Robert 		if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
249c0b746e5SOllivier Robert 			break;
250c0b746e5SOllivier Robert 
251c0b746e5SOllivier Robert 		return WRONG;
252c0b746e5SOllivier Robert 	}
253c0b746e5SOllivier Robert 	t += saved_seconds;
2549c2daa00SOllivier Robert 	if (usezn)
255c0b746e5SOllivier Robert 		*tmp = *localtime(&t);
2569c2daa00SOllivier Robert 	else
2579c2daa00SOllivier Robert 		*tmp = *gmtime(&t);
258c0b746e5SOllivier Robert 	*okayp = TRUE;
259c0b746e5SOllivier Robert 	return t;
260c0b746e5SOllivier Robert }
2619c2daa00SOllivier Robert #else
262a466cc55SCy Schubert NONEMPTY_TRANSLATION_UNIT
2639c2daa00SOllivier Robert #endif /* !HAVE_MKTIME || !HAVE_TIMEGM */
264c0b746e5SOllivier Robert 
265ea906c41SOllivier Robert #ifndef HAVE_MKTIME
266c0b746e5SOllivier Robert static time_t
time1(struct tm * tmp)267c0b746e5SOllivier Robert time1(
268c0b746e5SOllivier Robert 	struct tm * tmp
269c0b746e5SOllivier Robert 	)
270c0b746e5SOllivier Robert {
271c0b746e5SOllivier Robert 	register time_t			t;
272c0b746e5SOllivier Robert 	int				okay;
273c0b746e5SOllivier Robert 
274c0b746e5SOllivier Robert 	if (tmp->tm_isdst > 1)
275c0b746e5SOllivier Robert 		tmp->tm_isdst = 1;
2769c2daa00SOllivier Robert 	t = time2(tmp, &okay, 1);
277c0b746e5SOllivier Robert 	if (okay || tmp->tm_isdst < 0)
278c0b746e5SOllivier Robert 		return t;
279c0b746e5SOllivier Robert 
280c0b746e5SOllivier Robert 	return WRONG;
281c0b746e5SOllivier Robert }
282c0b746e5SOllivier Robert 
283c0b746e5SOllivier Robert time_t
mktime(struct tm * tmp)284c0b746e5SOllivier Robert mktime(
285c0b746e5SOllivier Robert 	struct tm * tmp
286c0b746e5SOllivier Robert 	)
287c0b746e5SOllivier Robert {
288c0b746e5SOllivier Robert 	return time1(tmp);
289c0b746e5SOllivier Robert }
2909c2daa00SOllivier Robert #endif /* !HAVE_MKTIME */
2919c2daa00SOllivier Robert 
2922b15cb3dSCy Schubert #ifdef WANT_TIMEGM
293ea906c41SOllivier Robert #ifndef HAVE_TIMEGM
2949c2daa00SOllivier Robert time_t
timegm(struct tm * tmp)2959c2daa00SOllivier Robert timegm(
2969c2daa00SOllivier Robert 	struct tm * tmp
2979c2daa00SOllivier Robert 	)
2989c2daa00SOllivier Robert {
2999c2daa00SOllivier Robert 	register time_t			t;
3009c2daa00SOllivier Robert 	int				okay;
3019c2daa00SOllivier Robert 
3029c2daa00SOllivier Robert 	tmp->tm_isdst = 0;
3039c2daa00SOllivier Robert 	t = time2(tmp, &okay, 0);
3049c2daa00SOllivier Robert 	if (okay || tmp->tm_isdst < 0)
3059c2daa00SOllivier Robert 		return t;
3069c2daa00SOllivier Robert 
3079c2daa00SOllivier Robert 	return WRONG;
3089c2daa00SOllivier Robert }
3099c2daa00SOllivier Robert #endif /* !HAVE_TIMEGM */
3102b15cb3dSCy Schubert #endif /* WANT_TIMEGM */
311