source: trunk/third/findutils/lib/mktime.c @ 18890

Revision 18890, 14.8 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18889, which included commits to RCS files with non-trunk default branches.
Line 
1/* Convert a `struct tm' to a time_t value.
2   Copyright (C) 1993, 94, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
3   This file is part of the GNU C Library.
4   Contributed by Paul Eggert (eggert@twinsun.com).
5
6   The GNU C Library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Library General Public License as
8   published by the Free Software Foundation; either version 2 of the
9   License, or (at your option) any later version.
10
11   The GNU C Library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Library General Public License for more details.
15
16   You should have received a copy of the GNU Library General Public
17   License along with the GNU C Library; see the file COPYING.LIB.  If not,
18   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19   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#ifdef _LIBC
30# define HAVE_LIMITS_H 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/* The extra casts work around common compiler bugs.  */
70#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
71/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
72   It is necessary at least when t == time_t.  */
73#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
74                              ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
75#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
76
77#ifndef INT_MIN
78# define INT_MIN TYPE_MINIMUM (int)
79#endif
80#ifndef INT_MAX
81# define INT_MAX TYPE_MAXIMUM (int)
82#endif
83
84#ifndef TIME_T_MIN
85# define TIME_T_MIN TYPE_MINIMUM (time_t)
86#endif
87#ifndef TIME_T_MAX
88# define TIME_T_MAX TYPE_MAXIMUM (time_t)
89#endif
90
91#define TM_YEAR_BASE 1900
92#define EPOCH_YEAR 1970
93
94#ifndef __isleap
95/* Nonzero if YEAR is a leap year (every 4 years,
96   except every 100th isn't, and every 400th is).  */
97# define __isleap(year) \
98  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
99#endif
100
101/* How many days come before each month (0-12).  */
102const unsigned short int __mon_yday[2][13] =
103  {
104    /* Normal years.  */
105    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
106    /* Leap years.  */
107    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
108  };
109
110
111#ifdef _LIBC
112# define my_mktime_localtime_r __localtime_r
113#else
114/* If we're a mktime substitute in a GNU program, then prefer
115   localtime to localtime_r, since many localtime_r implementations
116   are buggy.  */
117static struct tm *
118my_mktime_localtime_r (const time_t *t, struct tm *tp)
119{
120  struct tm *l = localtime (t);
121  if (! l)
122    return 0;
123  *tp = *l;
124  return tp;
125}
126#endif /* ! _LIBC */
127
128
129/* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
130   measured in seconds, ignoring leap seconds.
131   YEAR uses the same numbering as TM->tm_year.
132   All values are in range, except possibly YEAR.
133   If TP is null, return a nonzero value.
134   If overflow occurs, yield the low order bits of the correct answer.  */
135static time_t
136ydhms_tm_diff (int year, int yday, int hour, int min, int sec,
137               const struct tm *tp)
138{
139  if (!tp)
140    return 1;
141  else
142    {
143      /* Compute intervening leap days correctly even if year is negative.
144         Take care to avoid int overflow.  time_t overflow is OK, since
145         only the low order bits of the correct time_t answer are needed.
146         Don't convert to time_t until after all divisions are done, since
147         time_t might be unsigned.  */
148      int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
149      int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
150      int a100 = a4 / 25 - (a4 % 25 < 0);
151      int b100 = b4 / 25 - (b4 % 25 < 0);
152      int a400 = a100 >> 2;
153      int b400 = b100 >> 2;
154      int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
155      time_t years = year - (time_t) tp->tm_year;
156      time_t days = (365 * years + intervening_leap_days
157                     + (yday - tp->tm_yday));
158      return (60 * (60 * (24 * days + (hour - tp->tm_hour))
159                    + (min - tp->tm_min))
160              + (sec - tp->tm_sec));
161    }
162}
163
164/* Use CONVERT to convert *T to a broken down time in *TP.
165   If *T is out of range for conversion, adjust it so that
166   it is the nearest in-range value and then convert that.  */
167static struct tm *
168ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
169                time_t *t, struct tm *tp)
170{
171  struct tm *r;
172
173  if (! (r = (*convert) (t, tp)) && *t)
174    {
175      time_t bad = *t;
176      time_t ok = 0;
177      struct tm tm;
178
179      /* BAD is a known unconvertible time_t, and OK is a known good one.
180         Use binary search to narrow the range between BAD and OK until
181         they differ by 1.  */
182      while (bad != ok + (bad < 0 ? -1 : 1))
183        {
184          time_t mid = *t = (bad < 0
185                             ? bad + ((ok - bad) >> 1)
186                             : ok + ((bad - ok) >> 1));
187          if ((r = (*convert) (t, tp)))
188            {
189              tm = *r;
190              ok = mid;
191            }
192          else
193            bad = mid;
194        }
195
196      if (!r && ok)
197        {
198          /* The last conversion attempt failed;
199             revert to the most recent successful attempt.  */
200          *t = ok;
201          *tp = tm;
202          r = tp;
203        }
204    }
205
206  return r;
207}
208
209
210/* Convert *TP to a time_t value, inverting
211   the monotonic and mostly-unit-linear conversion function CONVERT.
212   Use *OFFSET to keep track of a guess at the offset of the result,
213   compared to what the result would be for UTC without leap seconds.
214   If *OFFSET's guess is correct, only one CONVERT call is needed.  */
215time_t
216__mktime_internal (struct tm *tp,
217                   struct tm *(*convert) (const time_t *, struct tm *),
218                   time_t *offset)
219{
220  time_t t, dt, t0, t1, t2;
221  struct tm tm;
222
223  /* The maximum number of probes (calls to CONVERT) should be enough
224     to handle any combinations of time zone rule changes, solar time,
225     leap seconds, and oscillations around a spring-forward gap.
226     POSIX.1 prohibits leap seconds, but some hosts have them anyway.  */
227  int remaining_probes = 6;
228
229  /* Time requested.  Copy it in case CONVERT modifies *TP; this can
230     occur if TP is localtime's returned value and CONVERT is localtime.  */
231  int sec = tp->tm_sec;
232  int min = tp->tm_min;
233  int hour = tp->tm_hour;
234  int mday = tp->tm_mday;
235  int mon = tp->tm_mon;
236  int year_requested = tp->tm_year;
237  int isdst = tp->tm_isdst;
238
239  /* Ensure that mon is in range, and set year accordingly.  */
240  int mon_remainder = mon % 12;
241  int negative_mon_remainder = mon_remainder < 0;
242  int mon_years = mon / 12 - negative_mon_remainder;
243  int year = year_requested + mon_years;
244
245  /* The other values need not be in range:
246     the remaining code handles minor overflows correctly,
247     assuming int and time_t arithmetic wraps around.
248     Major overflows are caught at the end.  */
249
250  /* Calculate day of year from year, month, and day of month.
251     The result need not be in range.  */
252  int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
253               [mon_remainder + 12 * negative_mon_remainder])
254              + mday - 1);
255
256  int sec_requested = sec;
257#if LEAP_SECONDS_POSSIBLE
258  /* Handle out-of-range seconds specially,
259     since ydhms_tm_diff assumes every minute has 60 seconds.  */
260  if (sec < 0)
261    sec = 0;
262  if (59 < sec)
263    sec = 59;
264#endif
265
266  /* Invert CONVERT by probing.  First assume the same offset as last time.
267     Then repeatedly use the error to improve the guess.  */
268
269  tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
270  tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
271  t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
272
273  for (t = t1 = t2 = t0 + *offset;
274       (dt = ydhms_tm_diff (year, yday, hour, min, sec,
275                            ranged_convert (convert, &t, &tm)));
276       t1 = t2, t2 = t, t += dt)
277    if (t == t1 && t != t2
278        && (isdst < 0 || tm.tm_isdst < 0
279            || (isdst != 0) != (tm.tm_isdst != 0)))
280      /* We can't possibly find a match, as we are oscillating
281         between two values.  The requested time probably falls
282         within a spring-forward gap of size DT.  Follow the common
283         practice in this case, which is to return a time that is DT
284         away from the requested time, preferring a time whose
285         tm_isdst differs from the requested value.  In practice,
286         this is more useful than returning -1.  */
287      break;
288    else if (--remaining_probes == 0)
289      return -1;
290
291  /* If we have a match, check whether tm.tm_isdst has the requested
292     value, if any.  */
293  if (dt == 0 && isdst != tm.tm_isdst && 0 <= isdst && 0 <= tm.tm_isdst)
294    {
295      /* tm.tm_isdst has the wrong value.  Look for a neighboring
296         time with the right value, and use its UTC offset.
297         Heuristic: probe the previous three calendar quarters (approximately),
298         looking for the desired isdst.  This isn't perfect,
299         but it's good enough in practice.  */
300      int quarter = 7889238; /* seconds per average 1/4 Gregorian year */
301      int i;
302
303      /* If we're too close to the time_t limit, look in future quarters.  */
304      if (t < TIME_T_MIN + 3 * quarter)
305        quarter = -quarter;
306
307      for (i = 1; i <= 3; i++)
308        {
309          time_t ot = t - i * quarter;
310          struct tm otm;
311          ranged_convert (convert, &ot, &otm);
312          if (otm.tm_isdst == isdst)
313            {
314              /* We found the desired tm_isdst.
315                 Extrapolate back to the desired time.  */
316              t = ot + ydhms_tm_diff (year, yday, hour, min, sec, &otm);
317              ranged_convert (convert, &t, &tm);
318              break;
319            }
320        }
321    }
322
323  *offset = t - t0;
324
325#if LEAP_SECONDS_POSSIBLE
326  if (sec_requested != tm.tm_sec)
327    {
328      /* Adjust time to reflect the tm_sec requested, not the normalized value.
329         Also, repair any damage from a false match due to a leap second.  */
330      t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
331      if (! (*convert) (&t, &tm))
332        return -1;
333    }
334#endif
335
336  if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
337    {
338      /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
339         so check for major overflows.  A gross check suffices,
340         since if t has overflowed, it is off by a multiple of
341         TIME_T_MAX - TIME_T_MIN + 1.  So ignore any component of
342         the difference that is bounded by a small value.  */
343
344      double dyear = (double) year_requested + mon_years - tm.tm_year;
345      double dday = 366 * dyear + mday;
346      double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
347
348      /* On Irix4.0.5 cc, dividing TIME_T_MIN by 3 does not produce
349         correct results, ie., it erroneously gives a positive value
350         of 715827882.  Setting a variable first then doing math on it
351         seems to work.  (ghazi@caip.rutgers.edu) */
352
353      const time_t time_t_max = TIME_T_MAX;
354      const time_t time_t_min = TIME_T_MIN;
355
356      if (time_t_max / 3 - time_t_min / 3 < (dsec < 0 ? - dsec : dsec))
357        return -1;
358    }
359
360  *tp = tm;
361  return t;
362}
363
364
365static time_t localtime_offset;
366
367/* Convert *TP to a time_t value.  */
368time_t
369mktime (tp)
370     struct tm *tp;
371{
372#ifdef _LIBC
373  /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
374     time zone names contained in the external variable `tzname' shall
375     be set as if the tzset() function had been called.  */
376  __tzset ();
377#endif
378
379  return __mktime_internal (tp, my_mktime_localtime_r, &localtime_offset);
380}
381
382#ifdef weak_alias
383weak_alias (mktime, timelocal)
384#endif
385
386#if DEBUG
387
388static int
389not_equal_tm (a, b)
390     struct tm *a;
391     struct tm *b;
392{
393  return ((a->tm_sec ^ b->tm_sec)
394          | (a->tm_min ^ b->tm_min)
395          | (a->tm_hour ^ b->tm_hour)
396          | (a->tm_mday ^ b->tm_mday)
397          | (a->tm_mon ^ b->tm_mon)
398          | (a->tm_year ^ b->tm_year)
399          | (a->tm_mday ^ b->tm_mday)
400          | (a->tm_yday ^ b->tm_yday)
401          | (a->tm_isdst ^ b->tm_isdst));
402}
403
404static void
405print_tm (tp)
406     struct tm *tp;
407{
408  if (tp)
409    printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
410            tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
411            tp->tm_hour, tp->tm_min, tp->tm_sec,
412            tp->tm_yday, tp->tm_wday, tp->tm_isdst);
413  else
414    printf ("0");
415}
416
417static int
418check_result (tk, tmk, tl, lt)
419     time_t tk;
420     struct tm tmk;
421     time_t tl;
422     struct tm *lt;
423{
424  if (tk != tl || !lt || not_equal_tm (&tmk, lt))
425    {
426      printf ("mktime (");
427      print_tm (&tmk);
428      printf (")\nyields (");
429      print_tm (lt);
430      printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
431      return 1;
432    }
433
434  return 0;
435}
436
437int
438main (argc, argv)
439     int argc;
440     char **argv;
441{
442  int status = 0;
443  struct tm tm, tmk, tml;
444  struct tm *lt;
445  time_t tk, tl;
446  char trailer;
447
448  if ((argc == 3 || argc == 4)
449      && (sscanf (argv[1], "%d-%d-%d%c",
450                  &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
451          == 3)
452      && (sscanf (argv[2], "%d:%d:%d%c",
453                  &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
454          == 3))
455    {
456      tm.tm_year -= TM_YEAR_BASE;
457      tm.tm_mon--;
458      tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
459      tmk = tm;
460      tl = mktime (&tmk);
461      lt = localtime (&tl);
462      if (lt)
463        {
464          tml = *lt;
465          lt = &tml;
466        }
467      printf ("mktime returns %ld == ", (long) tl);
468      print_tm (&tmk);
469      printf ("\n");
470      status = check_result (tl, tmk, tl, lt);
471    }
472  else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
473    {
474      time_t from = atol (argv[1]);
475      time_t by = atol (argv[2]);
476      time_t to = atol (argv[3]);
477
478      if (argc == 4)
479        for (tl = from; tl <= to; tl += by)
480          {
481            lt = localtime (&tl);
482            if (lt)
483              {
484                tmk = tml = *lt;
485                tk = mktime (&tmk);
486                status |= check_result (tk, tmk, tl, tml);
487              }
488            else
489              {
490                printf ("localtime (%ld) yields 0\n", (long) tl);
491                status = 1;
492              }
493          }
494      else
495        for (tl = from; tl <= to; tl += by)
496          {
497            /* Null benchmark.  */
498            lt = localtime (&tl);
499            if (lt)
500              {
501                tmk = tml = *lt;
502                tk = tl;
503                status |= check_result (tk, tmk, tl, tml);
504              }
505            else
506              {
507                printf ("localtime (%ld) yields 0\n", (long) tl);
508                status = 1;
509              }
510          }
511    }
512  else
513    printf ("Usage:\
514\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
515\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
516\t%s FROM BY TO - # Do not test those values (for benchmark).\n",
517            argv[0], argv[0], argv[0]);
518
519  return status;
520}
521
522#endif /* DEBUG */
523
524/*
525Local Variables:
526compile-command: "gcc -DDEBUG -DHAVE_LIMITS_H -DSTDC_HEADERS -Wall -W -O -g mktime.c -o mktime"
527End:
528*/
Note: See TracBrowser for help on using the repository browser.