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