Back to index

nagios-plugins  1.4.16
mktime.c
Go to the documentation of this file.
00001 /* Convert a `struct tm' to a time_t value.
00002    Copyright (C) 1993-1999, 2002-2007, 2009-2010 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Paul Eggert <eggert@twinsun.com>.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 3, or (at your option)
00009    any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License along
00017    with this program; if not, write to the Free Software Foundation,
00018    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
00019 
00020 /* Define this to have a standalone program to test this implementation of
00021    mktime.  */
00022 /* #define DEBUG 1 */
00023 
00024 #ifndef _LIBC
00025 # include <config.h>
00026 #endif
00027 
00028 /* Assume that leap seconds are possible, unless told otherwise.
00029    If the host has a `zic' command with a `-L leapsecondfilename' option,
00030    then it supports leap seconds; otherwise it probably doesn't.  */
00031 #ifndef LEAP_SECONDS_POSSIBLE
00032 # define LEAP_SECONDS_POSSIBLE 1
00033 #endif
00034 
00035 #include <time.h>
00036 
00037 #include <limits.h>
00038 
00039 #include <string.h>             /* For the real memcpy prototype.  */
00040 
00041 #if DEBUG
00042 # include <stdio.h>
00043 # include <stdlib.h>
00044 /* Make it work even if the system's libc has its own mktime routine.  */
00045 # define mktime my_mktime
00046 #endif /* DEBUG */
00047 
00048 /* Shift A right by B bits portably, by dividing A by 2**B and
00049    truncating towards minus infinity.  A and B should be free of side
00050    effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
00051    INT_BITS is the number of useful bits in an int.  GNU code can
00052    assume that INT_BITS is at least 32.
00053 
00054    ISO C99 says that A >> B is implementation-defined if A < 0.  Some
00055    implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
00056    right in the usual way when A < 0, so SHR falls back on division if
00057    ordinary A >> B doesn't seem to be the usual signed shift.  */
00058 #define SHR(a, b)       \
00059   (-1 >> 1 == -1        \
00060    ? (a) >> (b)         \
00061    : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
00062 
00063 /* The extra casts in the following macros work around compiler bugs,
00064    e.g., in Cray C 5.0.3.0.  */
00065 
00066 /* True if the arithmetic type T is an integer type.  bool counts as
00067    an integer.  */
00068 #define TYPE_IS_INTEGER(t) ((t) 1.5 == 1)
00069 
00070 /* True if negative values of the signed integer type T use two's
00071    complement, ones' complement, or signed magnitude representation,
00072    respectively.  Much GNU code assumes two's complement, but some
00073    people like to be portable to all possible C hosts.  */
00074 #define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1)
00075 #define TYPE_ONES_COMPLEMENT(t) ((t) ~ (t) 0 == 0)
00076 #define TYPE_SIGNED_MAGNITUDE(t) ((t) ~ (t) 0 < (t) -1)
00077 
00078 /* True if the arithmetic type T is signed.  */
00079 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
00080 
00081 /* The maximum and minimum values for the integer type T.  These
00082    macros have undefined behavior if T is signed and has padding bits.
00083    If this is a problem for you, please let us know how to fix it for
00084    your host.  */
00085 #define TYPE_MINIMUM(t) \
00086   ((t) (! TYPE_SIGNED (t) \
00087         ? (t) 0 \
00088         : TYPE_SIGNED_MAGNITUDE (t) \
00089         ? ~ (t) 0 \
00090         : ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1)))
00091 #define TYPE_MAXIMUM(t) \
00092   ((t) (! TYPE_SIGNED (t) \
00093         ? (t) -1 \
00094         : ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))))
00095 
00096 #ifndef TIME_T_MIN
00097 # define TIME_T_MIN TYPE_MINIMUM (time_t)
00098 #endif
00099 #ifndef TIME_T_MAX
00100 # define TIME_T_MAX TYPE_MAXIMUM (time_t)
00101 #endif
00102 #define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1)
00103 
00104 /* Verify a requirement at compile-time (unlike assert, which is runtime).  */
00105 #define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
00106 
00107 verify (time_t_is_integer, TYPE_IS_INTEGER (time_t));
00108 verify (twos_complement_arithmetic, TYPE_TWOS_COMPLEMENT (int));
00109 /* The code also assumes that signed integer overflow silently wraps
00110    around, but this assumption can't be stated without causing a
00111    diagnostic on some hosts.  */
00112 
00113 #define EPOCH_YEAR 1970
00114 #define TM_YEAR_BASE 1900
00115 verify (base_year_is_a_multiple_of_100, TM_YEAR_BASE % 100 == 0);
00116 
00117 /* Return 1 if YEAR + TM_YEAR_BASE is a leap year.  */
00118 static inline int
00119 leapyear (long int year)
00120 {
00121   /* Don't add YEAR to TM_YEAR_BASE, as that might overflow.
00122      Also, work even if YEAR is negative.  */
00123   return
00124     ((year & 3) == 0
00125      && (year % 100 != 0
00126          || ((year / 100) & 3) == (- (TM_YEAR_BASE / 100) & 3)));
00127 }
00128 
00129 /* How many days come before each month (0-12).  */
00130 #ifndef _LIBC
00131 static
00132 #endif
00133 const unsigned short int __mon_yday[2][13] =
00134   {
00135     /* Normal years.  */
00136     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
00137     /* Leap years.  */
00138     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
00139   };
00140 
00141 
00142 #ifndef _LIBC
00143 /* Portable standalone applications should supply a <time.h> that
00144    declares a POSIX-compliant localtime_r, for the benefit of older
00145    implementations that lack localtime_r or have a nonstandard one.
00146    See the gnulib time_r module for one way to implement this.  */
00147 # undef __localtime_r
00148 # define __localtime_r localtime_r
00149 # define __mktime_internal mktime_internal
00150 # include "mktime-internal.h"
00151 #endif
00152 
00153 /* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) -
00154    (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks
00155    were not adjusted between the time stamps.
00156 
00157    The YEAR values uses the same numbering as TP->tm_year.  Values
00158    need not be in the usual range.  However, YEAR1 must not be less
00159    than 2 * INT_MIN or greater than 2 * INT_MAX.
00160 
00161    The result may overflow.  It is the caller's responsibility to
00162    detect overflow.  */
00163 
00164 static inline time_t
00165 ydhms_diff (long int year1, long int yday1, int hour1, int min1, int sec1,
00166             int year0, int yday0, int hour0, int min0, int sec0)
00167 {
00168   verify (C99_integer_division, -1 / 2 == 0);
00169 #if 0 /* This assertion fails on 32-bit systems with 64-bit time_t, such as
00170          NetBSD 5 on i386.  */
00171   verify (long_int_year_and_yday_are_wide_enough,
00172           INT_MAX <= LONG_MAX / 2 || TIME_T_MAX <= UINT_MAX);
00173 #endif
00174 
00175   /* Compute intervening leap days correctly even if year is negative.
00176      Take care to avoid integer overflow here.  */
00177   int a4 = SHR (year1, 2) + SHR (TM_YEAR_BASE, 2) - ! (year1 & 3);
00178   int b4 = SHR (year0, 2) + SHR (TM_YEAR_BASE, 2) - ! (year0 & 3);
00179   int a100 = a4 / 25 - (a4 % 25 < 0);
00180   int b100 = b4 / 25 - (b4 % 25 < 0);
00181   int a400 = SHR (a100, 2);
00182   int b400 = SHR (b100, 2);
00183   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
00184 
00185   /* Compute the desired time in time_t precision.  Overflow might
00186      occur here.  */
00187   time_t tyear1 = year1;
00188   time_t years = tyear1 - year0;
00189   time_t days = 365 * years + yday1 - yday0 + intervening_leap_days;
00190   time_t hours = 24 * days + hour1 - hour0;
00191   time_t minutes = 60 * hours + min1 - min0;
00192   time_t seconds = 60 * minutes + sec1 - sec0;
00193   return seconds;
00194 }
00195 
00196 
00197 /* Return a time_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC),
00198    assuming that *T corresponds to *TP and that no clock adjustments
00199    occurred between *TP and the desired time.
00200    If TP is null, return a value not equal to *T; this avoids false matches.
00201    If overflow occurs, yield the minimal or maximal value, except do not
00202    yield a value equal to *T.  */
00203 static time_t
00204 guess_time_tm (long int year, long int yday, int hour, int min, int sec,
00205                const time_t *t, const struct tm *tp)
00206 {
00207   if (tp)
00208     {
00209       time_t d = ydhms_diff (year, yday, hour, min, sec,
00210                              tp->tm_year, tp->tm_yday,
00211                              tp->tm_hour, tp->tm_min, tp->tm_sec);
00212       time_t t1 = *t + d;
00213       if ((t1 < *t) == (TYPE_SIGNED (time_t) ? d < 0 : TIME_T_MAX / 2 < d))
00214         return t1;
00215     }
00216 
00217   /* Overflow occurred one way or another.  Return the nearest result
00218      that is actually in range, except don't report a zero difference
00219      if the actual difference is nonzero, as that would cause a false
00220      match; and don't oscillate between two values, as that would
00221      confuse the spring-forward gap detector.  */
00222   return (*t < TIME_T_MIDPOINT
00223           ? (*t <= TIME_T_MIN + 1 ? *t + 1 : TIME_T_MIN)
00224           : (TIME_T_MAX - 1 <= *t ? *t - 1 : TIME_T_MAX));
00225 }
00226 
00227 /* Use CONVERT to convert *T to a broken down time in *TP.
00228    If *T is out of range for conversion, adjust it so that
00229    it is the nearest in-range value and then convert that.  */
00230 static struct tm *
00231 ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
00232                 time_t *t, struct tm *tp)
00233 {
00234   struct tm *r = convert (t, tp);
00235 
00236   if (!r && *t)
00237     {
00238       time_t bad = *t;
00239       time_t ok = 0;
00240 
00241       /* BAD is a known unconvertible time_t, and OK is a known good one.
00242          Use binary search to narrow the range between BAD and OK until
00243          they differ by 1.  */
00244       while (bad != ok + (bad < 0 ? -1 : 1))
00245         {
00246           time_t mid = *t = (bad < 0
00247                              ? bad + ((ok - bad) >> 1)
00248                              : ok + ((bad - ok) >> 1));
00249           r = convert (t, tp);
00250           if (r)
00251             ok = mid;
00252           else
00253             bad = mid;
00254         }
00255 
00256       if (!r && ok)
00257         {
00258           /* The last conversion attempt failed;
00259              revert to the most recent successful attempt.  */
00260           *t = ok;
00261           r = convert (t, tp);
00262         }
00263     }
00264 
00265   return r;
00266 }
00267 
00268 
00269 /* Convert *TP to a time_t value, inverting
00270    the monotonic and mostly-unit-linear conversion function CONVERT.
00271    Use *OFFSET to keep track of a guess at the offset of the result,
00272    compared to what the result would be for UTC without leap seconds.
00273    If *OFFSET's guess is correct, only one CONVERT call is needed.
00274    This function is external because it is used also by timegm.c.  */
00275 time_t
00276 __mktime_internal (struct tm *tp,
00277                    struct tm *(*convert) (const time_t *, struct tm *),
00278                    time_t *offset)
00279 {
00280   time_t t, gt, t0, t1, t2;
00281   struct tm tm;
00282 
00283   /* The maximum number of probes (calls to CONVERT) should be enough
00284      to handle any combinations of time zone rule changes, solar time,
00285      leap seconds, and oscillations around a spring-forward gap.
00286      POSIX.1 prohibits leap seconds, but some hosts have them anyway.  */
00287   int remaining_probes = 6;
00288 
00289   /* Time requested.  Copy it in case CONVERT modifies *TP; this can
00290      occur if TP is localtime's returned value and CONVERT is localtime.  */
00291   int sec = tp->tm_sec;
00292   int min = tp->tm_min;
00293   int hour = tp->tm_hour;
00294   int mday = tp->tm_mday;
00295   int mon = tp->tm_mon;
00296   int year_requested = tp->tm_year;
00297   /* Normalize the value.  */
00298   int isdst = ((tp->tm_isdst >> (8 * sizeof (tp->tm_isdst) - 1))
00299                | (tp->tm_isdst != 0));
00300 
00301   /* 1 if the previous probe was DST.  */
00302   int dst2;
00303 
00304   /* Ensure that mon is in range, and set year accordingly.  */
00305   int mon_remainder = mon % 12;
00306   int negative_mon_remainder = mon_remainder < 0;
00307   int mon_years = mon / 12 - negative_mon_remainder;
00308   long int lyear_requested = year_requested;
00309   long int year = lyear_requested + mon_years;
00310 
00311   /* The other values need not be in range:
00312      the remaining code handles minor overflows correctly,
00313      assuming int and time_t arithmetic wraps around.
00314      Major overflows are caught at the end.  */
00315 
00316   /* Calculate day of year from year, month, and day of month.
00317      The result need not be in range.  */
00318   int mon_yday = ((__mon_yday[leapyear (year)]
00319                    [mon_remainder + 12 * negative_mon_remainder])
00320                   - 1);
00321   long int lmday = mday;
00322   long int yday = mon_yday + lmday;
00323 
00324   time_t guessed_offset = *offset;
00325 
00326   int sec_requested = sec;
00327 
00328   if (LEAP_SECONDS_POSSIBLE)
00329     {
00330       /* Handle out-of-range seconds specially,
00331          since ydhms_tm_diff assumes every minute has 60 seconds.  */
00332       if (sec < 0)
00333         sec = 0;
00334       if (59 < sec)
00335         sec = 59;
00336     }
00337 
00338   /* Invert CONVERT by probing.  First assume the same offset as last
00339      time.  */
00340 
00341   t0 = ydhms_diff (year, yday, hour, min, sec,
00342                    EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset);
00343 
00344   if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
00345     {
00346       /* time_t isn't large enough to rule out overflows, so check
00347          for major overflows.  A gross check suffices, since if t0
00348          has overflowed, it is off by a multiple of TIME_T_MAX -
00349          TIME_T_MIN + 1.  So ignore any component of the difference
00350          that is bounded by a small value.  */
00351 
00352       /* Approximate log base 2 of the number of time units per
00353          biennium.  A biennium is 2 years; use this unit instead of
00354          years to avoid integer overflow.  For example, 2 average
00355          Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds,
00356          which is 63113904 seconds, and rint (log2 (63113904)) is
00357          26.  */
00358       int ALOG2_SECONDS_PER_BIENNIUM = 26;
00359       int ALOG2_MINUTES_PER_BIENNIUM = 20;
00360       int ALOG2_HOURS_PER_BIENNIUM = 14;
00361       int ALOG2_DAYS_PER_BIENNIUM = 10;
00362       int LOG2_YEARS_PER_BIENNIUM = 1;
00363 
00364       int approx_requested_biennia =
00365         (SHR (year_requested, LOG2_YEARS_PER_BIENNIUM)
00366          - SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM)
00367          + SHR (mday, ALOG2_DAYS_PER_BIENNIUM)
00368          + SHR (hour, ALOG2_HOURS_PER_BIENNIUM)
00369          + SHR (min, ALOG2_MINUTES_PER_BIENNIUM)
00370          + (LEAP_SECONDS_POSSIBLE
00371             ? 0
00372             : SHR (sec, ALOG2_SECONDS_PER_BIENNIUM)));
00373 
00374       int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM);
00375       int diff = approx_biennia - approx_requested_biennia;
00376       int abs_diff = diff < 0 ? - diff : diff;
00377 
00378       /* IRIX 4.0.5 cc miscaculates TIME_T_MIN / 3: it erroneously
00379          gives a positive value of 715827882.  Setting a variable
00380          first then doing math on it seems to work.
00381          (ghazi@caip.rutgers.edu) */
00382       time_t time_t_max = TIME_T_MAX;
00383       time_t time_t_min = TIME_T_MIN;
00384       time_t overflow_threshold =
00385         (time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM;
00386 
00387       if (overflow_threshold < abs_diff)
00388         {
00389           /* Overflow occurred.  Try repairing it; this might work if
00390              the time zone offset is enough to undo the overflow.  */
00391           time_t repaired_t0 = -1 - t0;
00392           approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM);
00393           diff = approx_biennia - approx_requested_biennia;
00394           abs_diff = diff < 0 ? - diff : diff;
00395           if (overflow_threshold < abs_diff)
00396             return -1;
00397           guessed_offset += repaired_t0 - t0;
00398           t0 = repaired_t0;
00399         }
00400     }
00401 
00402   /* Repeatedly use the error to improve the guess.  */
00403 
00404   for (t = t1 = t2 = t0, dst2 = 0;
00405        (gt = guess_time_tm (year, yday, hour, min, sec, &t,
00406                             ranged_convert (convert, &t, &tm)),
00407         t != gt);
00408        t1 = t2, t2 = t, t = gt, dst2 = tm.tm_isdst != 0)
00409     if (t == t1 && t != t2
00410         && (tm.tm_isdst < 0
00411             || (isdst < 0
00412                 ? dst2 <= (tm.tm_isdst != 0)
00413                 : (isdst != 0) != (tm.tm_isdst != 0))))
00414       /* We can't possibly find a match, as we are oscillating
00415          between two values.  The requested time probably falls
00416          within a spring-forward gap of size GT - T.  Follow the common
00417          practice in this case, which is to return a time that is GT - T
00418          away from the requested time, preferring a time whose
00419          tm_isdst differs from the requested value.  (If no tm_isdst
00420          was requested and only one of the two values has a nonzero
00421          tm_isdst, prefer that value.)  In practice, this is more
00422          useful than returning -1.  */
00423       goto offset_found;
00424     else if (--remaining_probes == 0)
00425       return -1;
00426 
00427   /* We have a match.  Check whether tm.tm_isdst has the requested
00428      value, if any.  */
00429   if (isdst != tm.tm_isdst && 0 <= isdst && 0 <= tm.tm_isdst)
00430     {
00431       /* tm.tm_isdst has the wrong value.  Look for a neighboring
00432          time with the right value, and use its UTC offset.
00433 
00434          Heuristic: probe the adjacent timestamps in both directions,
00435          looking for the desired isdst.  This should work for all real
00436          time zone histories in the tz database.  */
00437 
00438       /* Distance between probes when looking for a DST boundary.  In
00439          tzdata2003a, the shortest period of DST is 601200 seconds
00440          (e.g., America/Recife starting 2000-10-08 01:00), and the
00441          shortest period of non-DST surrounded by DST is 694800
00442          seconds (Africa/Tunis starting 1943-04-17 01:00).  Use the
00443          minimum of these two values, so we don't miss these short
00444          periods when probing.  */
00445       int stride = 601200;
00446 
00447       /* The longest period of DST in tzdata2003a is 536454000 seconds
00448          (e.g., America/Jujuy starting 1946-10-01 01:00).  The longest
00449          period of non-DST is much longer, but it makes no real sense
00450          to search for more than a year of non-DST, so use the DST
00451          max.  */
00452       int duration_max = 536454000;
00453 
00454       /* Search in both directions, so the maximum distance is half
00455          the duration; add the stride to avoid off-by-1 problems.  */
00456       int delta_bound = duration_max / 2 + stride;
00457 
00458       int delta, direction;
00459 
00460       for (delta = stride; delta < delta_bound; delta += stride)
00461         for (direction = -1; direction <= 1; direction += 2)
00462           {
00463             time_t ot = t + delta * direction;
00464             if ((ot < t) == (direction < 0))
00465               {
00466                 struct tm otm;
00467                 ranged_convert (convert, &ot, &otm);
00468                 if (otm.tm_isdst == isdst)
00469                   {
00470                     /* We found the desired tm_isdst.
00471                        Extrapolate back to the desired time.  */
00472                     t = guess_time_tm (year, yday, hour, min, sec, &ot, &otm);
00473                     ranged_convert (convert, &t, &tm);
00474                     goto offset_found;
00475                   }
00476               }
00477           }
00478     }
00479 
00480  offset_found:
00481   *offset = guessed_offset + t - t0;
00482 
00483   if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec)
00484     {
00485       /* Adjust time to reflect the tm_sec requested, not the normalized value.
00486          Also, repair any damage from a false match due to a leap second.  */
00487       int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec;
00488       t1 = t + sec_requested;
00489       t2 = t1 + sec_adjustment;
00490       if (((t1 < t) != (sec_requested < 0))
00491           | ((t2 < t1) != (sec_adjustment < 0))
00492           | ! convert (&t2, &tm))
00493         return -1;
00494       t = t2;
00495     }
00496 
00497   *tp = tm;
00498   return t;
00499 }
00500 
00501 
00502 /* FIXME: This should use a signed type wide enough to hold any UTC
00503    offset in seconds.  'int' should be good enough for GNU code.  We
00504    can't fix this unilaterally though, as other modules invoke
00505    __mktime_internal.  */
00506 static time_t localtime_offset;
00507 
00508 /* Convert *TP to a time_t value.  */
00509 time_t
00510 mktime (struct tm *tp)
00511 {
00512 #ifdef _LIBC
00513   /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
00514      time zone names contained in the external variable `tzname' shall
00515      be set as if the tzset() function had been called.  */
00516   __tzset ();
00517 #endif
00518 
00519   return __mktime_internal (tp, __localtime_r, &localtime_offset);
00520 }
00521 
00522 #ifdef weak_alias
00523 weak_alias (mktime, timelocal)
00524 #endif
00525 
00526 #ifdef _LIBC
00527 libc_hidden_def (mktime)
00528 libc_hidden_weak (timelocal)
00529 #endif
00530 
00531 #if DEBUG
00532 
00533 static int
00534 not_equal_tm (const struct tm *a, const struct tm *b)
00535 {
00536   return ((a->tm_sec ^ b->tm_sec)
00537           | (a->tm_min ^ b->tm_min)
00538           | (a->tm_hour ^ b->tm_hour)
00539           | (a->tm_mday ^ b->tm_mday)
00540           | (a->tm_mon ^ b->tm_mon)
00541           | (a->tm_year ^ b->tm_year)
00542           | (a->tm_yday ^ b->tm_yday)
00543           | (a->tm_isdst ^ b->tm_isdst));
00544 }
00545 
00546 static void
00547 print_tm (const struct tm *tp)
00548 {
00549   if (tp)
00550     printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
00551             tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
00552             tp->tm_hour, tp->tm_min, tp->tm_sec,
00553             tp->tm_yday, tp->tm_wday, tp->tm_isdst);
00554   else
00555     printf ("0");
00556 }
00557 
00558 static int
00559 check_result (time_t tk, struct tm tmk, time_t tl, const struct tm *lt)
00560 {
00561   if (tk != tl || !lt || not_equal_tm (&tmk, lt))
00562     {
00563       printf ("mktime (");
00564       print_tm (lt);
00565       printf (")\nyields (");
00566       print_tm (&tmk);
00567       printf (") == %ld, should be %ld\n", (long int) tk, (long int) tl);
00568       return 1;
00569     }
00570 
00571   return 0;
00572 }
00573 
00574 int
00575 main (int argc, char **argv)
00576 {
00577   int status = 0;
00578   struct tm tm, tmk, tml;
00579   struct tm *lt;
00580   time_t tk, tl, tl1;
00581   char trailer;
00582 
00583   if ((argc == 3 || argc == 4)
00584       && (sscanf (argv[1], "%d-%d-%d%c",
00585                   &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
00586           == 3)
00587       && (sscanf (argv[2], "%d:%d:%d%c",
00588                   &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
00589           == 3))
00590     {
00591       tm.tm_year -= TM_YEAR_BASE;
00592       tm.tm_mon--;
00593       tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
00594       tmk = tm;
00595       tl = mktime (&tmk);
00596       lt = localtime (&tl);
00597       if (lt)
00598         {
00599           tml = *lt;
00600           lt = &tml;
00601         }
00602       printf ("mktime returns %ld == ", (long int) tl);
00603       print_tm (&tmk);
00604       printf ("\n");
00605       status = check_result (tl, tmk, tl, lt);
00606     }
00607   else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
00608     {
00609       time_t from = atol (argv[1]);
00610       time_t by = atol (argv[2]);
00611       time_t to = atol (argv[3]);
00612 
00613       if (argc == 4)
00614         for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
00615           {
00616             lt = localtime (&tl);
00617             if (lt)
00618               {
00619                 tmk = tml = *lt;
00620                 tk = mktime (&tmk);
00621                 status |= check_result (tk, tmk, tl, &tml);
00622               }
00623             else
00624               {
00625                 printf ("localtime (%ld) yields 0\n", (long int) tl);
00626                 status = 1;
00627               }
00628             tl1 = tl + by;
00629             if ((tl1 < tl) != (by < 0))
00630               break;
00631           }
00632       else
00633         for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
00634           {
00635             /* Null benchmark.  */
00636             lt = localtime (&tl);
00637             if (lt)
00638               {
00639                 tmk = tml = *lt;
00640                 tk = tl;
00641                 status |= check_result (tk, tmk, tl, &tml);
00642               }
00643             else
00644               {
00645                 printf ("localtime (%ld) yields 0\n", (long int) tl);
00646                 status = 1;
00647               }
00648             tl1 = tl + by;
00649             if ((tl1 < tl) != (by < 0))
00650               break;
00651           }
00652     }
00653   else
00654     printf ("Usage:\
00655 \t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
00656 \t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
00657 \t%s FROM BY TO - # Do not test those values (for benchmark).\n",
00658             argv[0], argv[0], argv[0]);
00659 
00660   return status;
00661 }
00662 
00663 #endif /* DEBUG */
00664 
00665 /*
00666 Local Variables:
00667 compile-command: "gcc -DDEBUG -Wall -W -O -g mktime.c -o mktime"
00668 End:
00669 */