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