source: vendor/bash/3.1-p17/lib/sh/mktime.c

Last change on this file was 3231, checked in by bird, 18 years ago

eol style.

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