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