]>
git.ipfire.org Git - thirdparty/glibc.git/blob - 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).
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.
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.
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. */
20 /* Define this to have a standalone program to test this implementation of
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
35 #include <sys/types.h> /* Some systems define `time_t' here. */
38 #if __STDC__ || __GNU_LIBRARY__ || STDC_HEADERS
44 #if __STDC__ || __GNU_LIBRARY__ || STDC_HEADERS
47 /* Make it work even if the system's libc has its own mktime routine. */
48 #define mktime my_mktime
52 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
53 #define __P(args) args
64 #define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
67 #define INT_MAX (~0 - INT_MIN)
71 #define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
72 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
75 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
78 #define TM_YEAR_BASE 1900
79 #define EPOCH_YEAR 1970
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))
88 /* Prototype for the internal function to get information based on TZ. */
89 extern void __tzset_internal
__P ((int always
));
91 /* How many days come before each month (0-12). */
92 const unsigned short int __mon_yday
[2][13] =
95 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
97 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
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
*),
106 #if ! HAVE_LOCALTIME_R && ! defined (localtime_r)
108 #define localtime_r __localtime_r
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
*));
118 struct tm
*l
= localtime (t
);
125 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
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. */
134 ydhms_tm_diff (year
, yday
, hour
, min
, sec
, tp
)
135 int year
, yday
, hour
, min
, sec
;
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
));
159 static time_t localtime_offset
;
161 /* Convert *TP to a time_t value. */
167 /* Update internal database according to current TZ setting. */
168 __tzset_internal (1);
171 return __mktime_internal (tp
, localtime_r
, &localtime_offset
);
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. */
180 __mktime_internal (tp
, convert
, offset
)
182 struct tm
*(*convert
) __P ((const time_t *, struct tm
*));
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
192 int remaining_probes
= 4;
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
;
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
;
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. */
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
])
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
;
231 /* Invert CONVERT by probing. First assume the same offset as last time.
232 Then repeatedly use the error to improve the guess. */
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
);
238 for (t
= t0
+ *offset
;
239 (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
, (*convert
) (&t
, &tm
)));
241 if (--remaining_probes
== 0)
244 /* Check whether tm.tm_isdst has the requested value, if any. */
245 if (0 <= isdst
&& 0 <= tm
.tm_isdst
)
247 int dst_diff
= (isdst
!= 0) - (tm
.tm_isdst
!= 0);
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)
258 if (! (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
,
259 (*convert
) (&ot
, &otm
))))
266 break; /* Avoid a redundant probe. */
273 #if LEAP_SECONDS_POSSIBLE
274 if (sec_requested
!= tm
.tm_sec
)
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
);
283 if (TIME_T_MAX
/ INT_MAX
/ 366 / 24 / 60 / 60 < 3)
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. */
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
;
295 if (TIME_T_MAX
/ 3 - TIME_T_MIN
/ 3 < (dsec
< 0 ? - dsec
: dsec
))
304 weak_alias (mktime
, timelocal
)
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
));
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
);
336 check_result (tk
, tmk
, tl
, tml
)
342 if (tk
!= tl
|| not_equal_tm (&tmk
, &tml
))
346 printf (")\nyields (");
348 printf (") == %ld, should be %ld\n", (long) tl
, (long) tk
);
361 struct tm tm
, tmk
, tml
;
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
)
369 && (sscanf (argv
[2], "%d:%d:%d%c",
370 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
, &trailer
)
373 tm
.tm_year
-= TM_YEAR_BASE
;
375 tm
.tm_isdst
= argc
== 3 ? -1 : atoi (argv
[3]);
378 tml
= *localtime (&tl
);
379 printf ("mktime returns %ld == ", (long) tl
);
382 status
= check_result (tl
, tmk
, tl
, tml
);
384 else if (argc
== 4 || (argc
== 5 && strcmp (argv
[4], "-") == 0))
386 time_t from
= atol (argv
[1]);
387 time_t by
= atol (argv
[2]);
388 time_t to
= atol (argv
[3]);
391 for (tl
= from
; tl
<= to
; tl
+= by
)
393 tml
= *localtime (&tl
);
396 status
|= check_result (tk
, tmk
, tl
, tml
);
399 for (tl
= from
; tl
<= to
; tl
+= by
)
401 /* Null benchmark. */
402 tml
= *localtime (&tl
);
405 status
|= check_result (tk
, tmk
, tl
, tml
);
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]);
422 compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"