]> git.ipfire.org Git - thirdparty/glibc.git/blob - time/mktime.c
update from main archive 970201
[thirdparty/glibc.git] / time / mktime.c
1 /* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Paul Eggert (eggert@twinsun.com).
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
19
20 /* Define this to have a standalone program to test this implementation of
21 mktime. */
22 /* #define DEBUG 1 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #ifdef _LIBC
29 # define HAVE_LIMITS_H 1
30 # define HAVE_LOCALTIME_R 1
31 # define STDC_HEADERS 1
32 #endif
33
34 /* Assume that leap seconds are possible, unless told otherwise.
35 If the host has a `zic' command with a `-L leapsecondfilename' option,
36 then it supports leap seconds; otherwise it probably doesn't. */
37 #ifndef LEAP_SECONDS_POSSIBLE
38 #define LEAP_SECONDS_POSSIBLE 1
39 #endif
40
41 #include <sys/types.h> /* Some systems define `time_t' here. */
42 #include <time.h>
43
44 #if HAVE_LIMITS_H
45 #include <limits.h>
46 #endif
47
48 #if DEBUG
49 #include <stdio.h>
50 #if STDC_HEADERS
51 #include <stdlib.h>
52 #endif
53 /* Make it work even if the system's libc has its own mktime routine. */
54 #define mktime my_mktime
55 #endif /* DEBUG */
56
57 #ifndef __P
58 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
59 #define __P(args) args
60 #else
61 #define __P(args) ()
62 #endif /* GCC. */
63 #endif /* Not __P. */
64
65 #ifndef CHAR_BIT
66 #define CHAR_BIT 8
67 #endif
68
69 #ifndef INT_MIN
70 #define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
71 #endif
72 #ifndef INT_MAX
73 #define INT_MAX (~0 - INT_MIN)
74 #endif
75
76 #ifndef TIME_T_MIN
77 #define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
78 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
79 #endif
80 #ifndef TIME_T_MAX
81 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
82 #endif
83
84 #define TM_YEAR_BASE 1900
85 #define EPOCH_YEAR 1970
86
87 #ifndef __isleap
88 /* Nonzero if YEAR is a leap year (every 4 years,
89 except every 100th isn't, and every 400th is). */
90 #define __isleap(year) \
91 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
92 #endif
93
94 /* How many days come before each month (0-12). */
95 const unsigned short int __mon_yday[2][13] =
96 {
97 /* Normal years. */
98 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
99 /* Leap years. */
100 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
101 };
102
103 static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *));
104 time_t __mktime_internal __P ((struct tm *,
105 struct tm *(*) (const time_t *, struct tm *),
106 time_t *));
107
108
109 #ifdef _LIBC
110 #define localtime_r __localtime_r
111 #else
112 #if ! HAVE_LOCALTIME_R && ! defined (localtime_r)
113 /* Approximate localtime_r as best we can in its absence. */
114 #define localtime_r my_localtime_r
115 static struct tm *localtime_r __P ((const time_t *, struct tm *));
116 static struct tm *
117 localtime_r (t, tp)
118 const time_t *t;
119 struct tm *tp;
120 {
121 struct tm *l = localtime (t);
122 if (! l)
123 return 0;
124 *tp = *l;
125 return tp;
126 }
127 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
128 #endif /* ! _LIBC */
129
130
131 /* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
132 measured in seconds, ignoring leap seconds.
133 YEAR uses the same numbering as TM->tm_year.
134 All values are in range, except possibly YEAR.
135 If overflow occurs, yield the low order bits of the correct answer. */
136 static time_t
137 ydhms_tm_diff (year, yday, hour, min, sec, tp)
138 int year, yday, hour, min, sec;
139 const struct tm *tp;
140 {
141 /* Compute intervening leap days correctly even if year is negative.
142 Take care to avoid int overflow. time_t overflow is OK, since
143 only the low order bits of the correct time_t answer are needed.
144 Don't convert to time_t until after all divisions are done, since
145 time_t might be unsigned. */
146 int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
147 int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
148 int a100 = a4 / 25 - (a4 % 25 < 0);
149 int b100 = b4 / 25 - (b4 % 25 < 0);
150 int a400 = a100 >> 2;
151 int b400 = b100 >> 2;
152 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
153 time_t years = year - (time_t) tp->tm_year;
154 time_t days = (365 * years + intervening_leap_days
155 + (yday - tp->tm_yday));
156 return (60 * (60 * (24 * days + (hour - tp->tm_hour))
157 + (min - tp->tm_min))
158 + (sec - tp->tm_sec));
159 }
160
161
162 static time_t localtime_offset;
163
164 /* Convert *TP to a time_t value. */
165 time_t
166 mktime (tp)
167 struct tm *tp;
168 {
169 #ifdef _LIBC
170 /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
171 time zone names contained in the external variable `tzname' shall
172 be set as if the tzset() function had been called. */
173 __tzset ();
174 #endif
175
176 return __mktime_internal (tp, localtime_r, &localtime_offset);
177 }
178
179 /* Convert *TP to a time_t value, inverting
180 the monotonic and mostly-unit-linear conversion function CONVERT.
181 Use *OFFSET to keep track of a guess at the offset of the result,
182 compared to what the result would be for UTC without leap seconds.
183 If *OFFSET's guess is correct, only one CONVERT call is needed. */
184 time_t
185 __mktime_internal (tp, convert, offset)
186 struct tm *tp;
187 struct tm *(*convert) __P ((const time_t *, struct tm *));
188 time_t *offset;
189 {
190 time_t t, dt, t0;
191 struct tm tm;
192
193 /* The maximum number of probes (calls to CONVERT) should be enough
194 to handle any combinations of time zone rule changes, solar time,
195 and leap seconds. Posix.1 prohibits leap seconds, but some hosts
196 have them anyway. */
197 int remaining_probes = 4;
198
199 /* Time requested. Copy it in case CONVERT modifies *TP; this can
200 occur if TP is localtime's returned value and CONVERT is localtime. */
201 int sec = tp->tm_sec;
202 int min = tp->tm_min;
203 int hour = tp->tm_hour;
204 int mday = tp->tm_mday;
205 int mon = tp->tm_mon;
206 int year_requested = tp->tm_year;
207 int isdst = tp->tm_isdst;
208
209 /* Ensure that mon is in range, and set year accordingly. */
210 int mon_remainder = mon % 12;
211 int negative_mon_remainder = mon_remainder < 0;
212 int mon_years = mon / 12 - negative_mon_remainder;
213 int year = year_requested + mon_years;
214
215 /* The other values need not be in range:
216 the remaining code handles minor overflows correctly,
217 assuming int and time_t arithmetic wraps around.
218 Major overflows are caught at the end. */
219
220 /* Calculate day of year from year, month, and day of month.
221 The result need not be in range. */
222 int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
223 [mon_remainder + 12 * negative_mon_remainder])
224 + mday - 1);
225
226 #if LEAP_SECONDS_POSSIBLE
227 /* Handle out-of-range seconds specially,
228 since ydhms_tm_diff assumes every minute has 60 seconds. */
229 int sec_requested = sec;
230 if (sec < 0)
231 sec = 0;
232 if (59 < sec)
233 sec = 59;
234 #endif
235
236 /* Invert CONVERT by probing. First assume the same offset as last time.
237 Then repeatedly use the error to improve the guess. */
238
239 tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
240 tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
241 t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
242
243 for (t = t0 + *offset;
244 (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm)));
245 t += dt)
246 if (--remaining_probes == 0)
247 return -1;
248
249 /* Check whether tm.tm_isdst has the requested value, if any. */
250 if (0 <= isdst && 0 <= tm.tm_isdst)
251 {
252 int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
253 if (dst_diff)
254 {
255 /* Move two hours in the direction indicated by the disagreement,
256 probe some more, and switch to a new time if found.
257 The largest known fallback due to daylight savings is two hours:
258 once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
259 time_t ot = t - 2 * 60 * 60 * dst_diff;
260 while (--remaining_probes != 0)
261 {
262 struct tm otm;
263 if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
264 (*convert) (&ot, &otm))))
265 {
266 t = ot;
267 tm = otm;
268 break;
269 }
270 if ((ot += dt) == t)
271 break; /* Avoid a redundant probe. */
272 }
273 }
274 }
275
276 *offset = t - t0;
277
278 #if LEAP_SECONDS_POSSIBLE
279 if (sec_requested != tm.tm_sec)
280 {
281 /* Adjust time to reflect the tm_sec requested, not the normalized value.
282 Also, repair any damage from a false match due to a leap second. */
283 t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
284 (*convert) (&t, &tm);
285 }
286 #endif
287
288 if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
289 {
290 /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
291 so check for major overflows. A gross check suffices,
292 since if t has overflowed, it is off by a multiple of
293 TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
294 the difference that is bounded by a small value. */
295
296 double dyear = (double) year_requested + mon_years - tm.tm_year;
297 double dday = 366 * dyear + mday;
298 double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
299
300 if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec))
301 return -1;
302 }
303
304 *tp = tm;
305 return t;
306 }
307
308 #ifdef weak_alias
309 weak_alias (mktime, timelocal)
310 #endif
311 \f
312 #if DEBUG
313
314 static int
315 not_equal_tm (a, b)
316 struct tm *a;
317 struct tm *b;
318 {
319 return ((a->tm_sec ^ b->tm_sec)
320 | (a->tm_min ^ b->tm_min)
321 | (a->tm_hour ^ b->tm_hour)
322 | (a->tm_mday ^ b->tm_mday)
323 | (a->tm_mon ^ b->tm_mon)
324 | (a->tm_year ^ b->tm_year)
325 | (a->tm_mday ^ b->tm_mday)
326 | (a->tm_yday ^ b->tm_yday)
327 | (a->tm_isdst ^ b->tm_isdst));
328 }
329
330 static void
331 print_tm (tp)
332 struct tm *tp;
333 {
334 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
335 tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
336 tp->tm_hour, tp->tm_min, tp->tm_sec,
337 tp->tm_yday, tp->tm_wday, tp->tm_isdst);
338 }
339
340 static int
341 check_result (tk, tmk, tl, tml)
342 time_t tk;
343 struct tm tmk;
344 time_t tl;
345 struct tm tml;
346 {
347 if (tk != tl || not_equal_tm (&tmk, &tml))
348 {
349 printf ("mktime (");
350 print_tm (&tmk);
351 printf (")\nyields (");
352 print_tm (&tml);
353 printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
354 return 1;
355 }
356
357 return 0;
358 }
359
360 int
361 main (argc, argv)
362 int argc;
363 char **argv;
364 {
365 int status = 0;
366 struct tm tm, tmk, tml;
367 time_t tk, tl;
368 char trailer;
369
370 if ((argc == 3 || argc == 4)
371 && (sscanf (argv[1], "%d-%d-%d%c",
372 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
373 == 3)
374 && (sscanf (argv[2], "%d:%d:%d%c",
375 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
376 == 3))
377 {
378 tm.tm_year -= TM_YEAR_BASE;
379 tm.tm_mon--;
380 tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
381 tmk = tm;
382 tl = mktime (&tmk);
383 tml = *localtime (&tl);
384 printf ("mktime returns %ld == ", (long) tl);
385 print_tm (&tmk);
386 printf ("\n");
387 status = check_result (tl, tmk, tl, tml);
388 }
389 else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
390 {
391 time_t from = atol (argv[1]);
392 time_t by = atol (argv[2]);
393 time_t to = atol (argv[3]);
394
395 if (argc == 4)
396 for (tl = from; tl <= to; tl += by)
397 {
398 tml = *localtime (&tl);
399 tmk = tml;
400 tk = mktime (&tmk);
401 status |= check_result (tk, tmk, tl, tml);
402 }
403 else
404 for (tl = from; tl <= to; tl += by)
405 {
406 /* Null benchmark. */
407 tml = *localtime (&tl);
408 tmk = tml;
409 tk = tl;
410 status |= check_result (tk, tmk, tl, tml);
411 }
412 }
413 else
414 printf ("Usage:\
415 \t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
416 \t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
417 \t%s FROM BY TO - # Do not test those values (for benchmark).\n",
418 argv[0], argv[0], argv[0]);
419
420 return status;
421 }
422
423 #endif /* DEBUG */
424 \f
425 /*
426 Local Variables:
427 compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"
428 End:
429 */