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#if DEBUG
309
310static int
311not_equal_tm (a, b)
312     struct tm *a;
313     struct tm *b;
314{
315  return ((a->tm_sec ^ b->tm_sec)
316	  | (a->tm_min ^ b->tm_min)
317	  | (a->tm_hour ^ b->tm_hour)
318	  | (a->tm_mday ^ b->tm_mday)
319	  | (a->tm_mon ^ b->tm_mon)
320	  | (a->tm_year ^ b->tm_year)
321	  | (a->tm_mday ^ b->tm_mday)
322	  | (a->tm_yday ^ b->tm_yday)
323	  | (a->tm_isdst ^ b->tm_isdst));
324}
325
326static void
327print_tm (tp)
328     struct tm *tp;
329{
330  printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
331	  tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
332	  tp->tm_hour, tp->tm_min, tp->tm_sec,
333	  tp->tm_yday, tp->tm_wday, tp->tm_isdst);
334}
335
336static int
337check_result (tk, tmk, tl, tml)
338     time_t tk;
339     struct tm tmk;
340     time_t tl;
341     struct tm tml;
342{
343  if (tk != tl || not_equal_tm (&tmk, &tml))
344    {
345      printf ("mktime (");
346      print_tm (&tmk);
347      printf (")\nyields (");
348      print_tm (&tml);
349      printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
350      return 1;
351    }
352
353  return 0;
354}
355
356int
357main (argc, argv)
358     int argc;
359     char **argv;
360{
361  int status = 0;
362  struct tm tm, tmk, tml;
363  time_t tk, tl;
364  char trailer;
365
366  if ((argc == 3 || argc == 4)
367      && (sscanf (argv[1], "%d-%d-%d%c",
368		  &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
369	  == 3)
370      && (sscanf (argv[2], "%d:%d:%d%c",
371		  &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
372	  == 3))
373    {
374      tm.tm_year -= TM_YEAR_BASE;
375      tm.tm_mon--;
376      tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
377      tmk = tm;
378      tl = mktime (&tmk);
379      tml = *localtime (&tl);
380      printf ("mktime returns %ld == ", (long) tl);
381      print_tm (&tmk);
382      printf ("\n");
383      status = check_result (tl, tmk, tl, tml);
384    }
385  else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
386    {
387      time_t from = atol (argv[1]);
388      time_t by = atol (argv[2]);
389      time_t to = atol (argv[3]);
390
391      if (argc == 4)
392	for (tl = from; tl <= to; tl += by)
393	  {
394	    tml = *localtime (&tl);
395	    tmk = tml;
396	    tk = mktime (&tmk);
397	    status |= check_result (tk, tmk, tl, tml);
398	  }
399      else
400	for (tl = from; tl <= to; tl += by)
401	  {
402	    /* Null benchmark.  */
403	    tml = *localtime (&tl);
404	    tmk = tml;
405	    tk = tl;
406	    status |= check_result (tk, tmk, tl, tml);
407	  }
408    }
409  else
410    printf ("Usage:\
411\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
412\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
413\t%s FROM BY TO - # Do not test those values (for benchmark).\n",
414	    argv[0], argv[0], argv[0]);
415
416  return status;
417}
418
419#endif /* DEBUG */
420
421/*
422Local Variables:
423compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"
424End:
425*/
426