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