Blame missing_d/mktime.c

Packit 575503
/* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
Packit 575503
   This file is part of the GNU C Library.
Packit 575503
   Contributed by Paul Eggert (eggert@twinsun.com).
Packit 575503
Packit 575503
   The GNU C Library is free software; you can redistribute it and/or
Packit 575503
   modify it under the terms of the GNU Library General Public License as
Packit 575503
   published by the Free Software Foundation; either version 3 of the
Packit 575503
   License, or (at your option) any later version.
Packit 575503
Packit 575503
   The GNU C Library is distributed in the hope that it will be useful,
Packit 575503
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 575503
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 575503
   Library General Public License for more details.
Packit 575503
Packit 575503
   You should have received a copy of the GNU Library General Public
Packit 575503
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
Packit 575503
   write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Packit 575503
   Boston, MA 02110-1301, USA.  */
Packit 575503
Packit 575503
/* Define this to have a standalone program to test this implementation of
Packit 575503
   mktime.  */
Packit 575503
/* #define DEBUG 1 */
Packit 575503
Packit 575503
#ifdef HAVE_CONFIG_H
Packit 575503
#include <config.h>
Packit 575503
#endif
Packit 575503
Packit 575503
#ifdef _LIBC
Packit 575503
# define HAVE_LIMITS_H 1
Packit 575503
# define HAVE_LOCALTIME_R 1
Packit 575503
# define STDC_HEADERS 1
Packit 575503
#endif
Packit 575503
Packit 575503
/* Assume that leap seconds are possible, unless told otherwise.
Packit 575503
   If the host has a `zic' command with a `-L leapsecondfilename' option,
Packit 575503
   then it supports leap seconds; otherwise it probably doesn't.  */
Packit 575503
#ifndef LEAP_SECONDS_POSSIBLE
Packit 575503
#define LEAP_SECONDS_POSSIBLE 1
Packit 575503
#endif
Packit 575503
Packit 575503
#ifndef VMS
Packit 575503
#include <sys/types.h>		/* Some systems define `time_t' here.  */
Packit 575503
#else
Packit 575503
#include <stddef.h>
Packit 575503
#endif
Packit 575503
#include <time.h>
Packit 575503
Packit 575503
#if HAVE_LIMITS_H
Packit 575503
#include <limits.h>
Packit 575503
#endif
Packit 575503
Packit 575503
#if DEBUG
Packit 575503
#include <stdio.h>
Packit 575503
#if STDC_HEADERS
Packit 575503
#include <stdlib.h>
Packit 575503
#endif
Packit 575503
/* Make it work even if the system's libc has its own mktime routine.  */
Packit 575503
#define mktime my_mktime
Packit 575503
#endif /* DEBUG */
Packit 575503
Packit 575503
#ifndef __P
Packit 575503
#if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
Packit 575503
#define __P(args) args
Packit 575503
#else
Packit 575503
#define __P(args) ()
Packit 575503
#endif  /* GCC.  */
Packit 575503
#endif  /* Not __P.  */
Packit 575503
Packit 575503
#ifndef CHAR_BIT
Packit 575503
#define CHAR_BIT 8
Packit 575503
#endif
Packit 575503
Packit 575503
#ifndef INT_MIN
Packit 575503
#define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
Packit 575503
#endif
Packit 575503
#ifndef INT_MAX
Packit 575503
#define INT_MAX (~0 - INT_MIN)
Packit 575503
#endif
Packit 575503
Packit 575503
#ifndef TIME_T_MIN
Packit 575503
#define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
Packit 575503
		    : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
Packit 575503
#endif
Packit 575503
#ifndef TIME_T_MAX
Packit 575503
#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
Packit 575503
#endif
Packit 575503
Packit 575503
#define TM_YEAR_BASE 1900
Packit 575503
#define EPOCH_YEAR 1970
Packit 575503
Packit 575503
#ifndef __isleap
Packit 575503
/* Nonzero if YEAR is a leap year (every 4 years,
Packit 575503
   except every 100th isn't, and every 400th is).  */
Packit 575503
#define	__isleap(year)	\
Packit 575503
  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
Packit 575503
#endif
Packit 575503
Packit 575503
/* How many days come before each month (0-12).  */
Packit 575503
const unsigned short int __mon_yday[2][13] =
Packit 575503
  {
Packit 575503
    /* Normal years.  */
Packit 575503
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
Packit 575503
    /* Leap years.  */
Packit 575503
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
Packit 575503
  };
Packit 575503
Packit 575503
static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *));
Packit 575503
time_t __mktime_internal __P ((struct tm *,
Packit 575503
			       struct tm *(*) (const time_t *, struct tm *),
Packit 575503
			       time_t *));
Packit 575503
Packit 575503
Packit 575503
static struct tm *my_localtime_r __P ((const time_t *, struct tm *));
Packit 575503
static struct tm *
Packit 575503
my_localtime_r (t, tp)
Packit 575503
     const time_t *t;
Packit 575503
     struct tm *tp;
Packit 575503
{
Packit 575503
  struct tm *l = localtime (t);
Packit 575503
  if (! l)
Packit 575503
    return 0;
Packit 575503
  *tp = *l;
Packit 575503
  return tp;
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
Packit 575503
   measured in seconds, ignoring leap seconds.
Packit 575503
   YEAR uses the same numbering as TM->tm_year.
Packit 575503
   All values are in range, except possibly YEAR.
Packit 575503
   If overflow occurs, yield the low order bits of the correct answer.  */
Packit 575503
static time_t
Packit 575503
ydhms_tm_diff (year, yday, hour, min, sec, tp)
Packit 575503
     int year, yday, hour, min, sec;
Packit 575503
     const struct tm *tp;
Packit 575503
{
Packit 575503
  /* Compute intervening leap days correctly even if year is negative.
Packit 575503
     Take care to avoid int overflow.  time_t overflow is OK, since
Packit 575503
     only the low order bits of the correct time_t answer are needed.
Packit 575503
     Don't convert to time_t until after all divisions are done, since
Packit 575503
     time_t might be unsigned.  */
Packit 575503
  int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
Packit 575503
  int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
Packit 575503
  int a100 = a4 / 25 - (a4 % 25 < 0);
Packit 575503
  int b100 = b4 / 25 - (b4 % 25 < 0);
Packit 575503
  int a400 = a100 >> 2;
Packit 575503
  int b400 = b100 >> 2;
Packit 575503
  int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
Packit 575503
  time_t years = year - (time_t) tp->tm_year;
Packit 575503
  time_t days = (365 * years + intervening_leap_days
Packit 575503
		 + (yday - tp->tm_yday));
Packit 575503
  return (60 * (60 * (24 * days + (hour - tp->tm_hour))
Packit 575503
		+ (min - tp->tm_min))
Packit 575503
	  + (sec - tp->tm_sec));
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
static time_t localtime_offset;
Packit 575503
Packit 575503
/* Convert *TP to a time_t value.  */
Packit 575503
time_t
Packit 575503
mktime (tp)
Packit 575503
     struct tm *tp;
Packit 575503
{
Packit 575503
#ifdef _LIBC
Packit 575503
  /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
Packit 575503
     time zone names contained in the external variable `tzname' shall
Packit 575503
     be set as if the tzset() function had been called.  */
Packit 575503
  __tzset ();
Packit 575503
#endif
Packit 575503
Packit 575503
  return __mktime_internal (tp, my_localtime_r, &localtime_offset);
Packit 575503
}
Packit 575503
Packit 575503
/* Convert *TP to a time_t value, inverting
Packit 575503
   the monotonic and mostly-unit-linear conversion function CONVERT.
Packit 575503
   Use *OFFSET to keep track of a guess at the offset of the result,
Packit 575503
   compared to what the result would be for UTC without leap seconds.
Packit 575503
   If *OFFSET's guess is correct, only one CONVERT call is needed.  */
Packit 575503
time_t
Packit 575503
__mktime_internal (tp, convert, offset)
Packit 575503
     struct tm *tp;
Packit 575503
     struct tm *(*convert) __P ((const time_t *, struct tm *));
Packit 575503
     time_t *offset;
Packit 575503
{
Packit 575503
  time_t t, dt, t0;
Packit 575503
  struct tm tm;
Packit 575503
Packit 575503
  /* The maximum number of probes (calls to CONVERT) should be enough
Packit 575503
     to handle any combinations of time zone rule changes, solar time,
Packit 575503
     and leap seconds.  Posix.1 prohibits leap seconds, but some hosts
Packit 575503
     have them anyway.  */
Packit 575503
  int remaining_probes = 4;
Packit 575503
Packit 575503
  /* Time requested.  Copy it in case CONVERT modifies *TP; this can
Packit 575503
     occur if TP is localtime's returned value and CONVERT is localtime.  */
Packit 575503
  int sec = tp->tm_sec;
Packit 575503
  int min = tp->tm_min;
Packit 575503
  int hour = tp->tm_hour;
Packit 575503
  int mday = tp->tm_mday;
Packit 575503
  int mon = tp->tm_mon;
Packit 575503
  int year_requested = tp->tm_year;
Packit 575503
  int isdst = tp->tm_isdst;
Packit 575503
Packit 575503
  /* Ensure that mon is in range, and set year accordingly.  */
Packit 575503
  int mon_remainder = mon % 12;
Packit 575503
  int negative_mon_remainder = mon_remainder < 0;
Packit 575503
  int mon_years = mon / 12 - negative_mon_remainder;
Packit 575503
  int year = year_requested + mon_years;
Packit 575503
Packit 575503
  /* The other values need not be in range:
Packit 575503
     the remaining code handles minor overflows correctly,
Packit 575503
     assuming int and time_t arithmetic wraps around.
Packit 575503
     Major overflows are caught at the end.  */
Packit 575503
Packit 575503
  /* Calculate day of year from year, month, and day of month.
Packit 575503
     The result need not be in range.  */
Packit 575503
  int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
Packit 575503
	       [mon_remainder + 12 * negative_mon_remainder])
Packit 575503
	      + mday - 1);
Packit 575503
Packit 575503
#if LEAP_SECONDS_POSSIBLE
Packit 575503
  /* Handle out-of-range seconds specially,
Packit 575503
     since ydhms_tm_diff assumes every minute has 60 seconds.  */
Packit 575503
  int sec_requested = sec;
Packit 575503
  if (sec < 0)
Packit 575503
    sec = 0;
Packit 575503
  if (59 < sec)
Packit 575503
    sec = 59;
Packit 575503
#endif
Packit 575503
Packit 575503
  /* Invert CONVERT by probing.  First assume the same offset as last time.
Packit 575503
     Then repeatedly use the error to improve the guess.  */
Packit 575503
Packit 575503
  tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
Packit 575503
  tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
Packit 575503
  t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm;;
Packit 575503
Packit 575503
  for (t = t0 + *offset;
Packit 575503
       (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm)));
Packit 575503
       t += dt)
Packit 575503
    if (--remaining_probes == 0)
Packit 575503
      return -1;
Packit 575503
Packit 575503
  /* Check whether tm.tm_isdst has the requested value, if any.  */
Packit 575503
  if (0 <= isdst && 0 <= tm.tm_isdst)
Packit 575503
    {
Packit 575503
      int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
Packit 575503
      if (dst_diff)
Packit 575503
	{
Packit 575503
	  /* Move two hours in the direction indicated by the disagreement,
Packit 575503
	     probe some more, and switch to a new time if found.
Packit 575503
	     The largest known fallback due to daylight savings is two hours:
Packit 575503
	     once, in Newfoundland, 1988-10-30 02:00 -> 00:00.  */
Packit 575503
	  time_t ot = t - 2 * 60 * 60 * dst_diff;
Packit 575503
	  while (--remaining_probes != 0)
Packit 575503
	    {
Packit 575503
	      struct tm otm;
Packit 575503
	      if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
Packit 575503
					 (*convert) (&ot, &otm))))
Packit 575503
		{
Packit 575503
		  t = ot;
Packit 575503
		  tm = otm;
Packit 575503
		  break;
Packit 575503
		}
Packit 575503
	      if ((ot += dt) == t)
Packit 575503
		break;  /* Avoid a redundant probe.  */
Packit 575503
	    }
Packit 575503
	}
Packit 575503
    }
Packit 575503
Packit 575503
  *offset = t - t0;
Packit 575503
Packit 575503
#if LEAP_SECONDS_POSSIBLE
Packit 575503
  if (sec_requested != tm.tm_sec)
Packit 575503
    {
Packit 575503
      /* Adjust time to reflect the tm_sec requested, not the normalized value.
Packit 575503
	 Also, repair any damage from a false match due to a leap second.  */
Packit 575503
      t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
Packit 575503
      (*convert) (&t, &tm;;
Packit 575503
    }
Packit 575503
#endif
Packit 575503
Packit 575503
  if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
Packit 575503
    {
Packit 575503
      /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
Packit 575503
	 so check for major overflows.  A gross check suffices,
Packit 575503
	 since if t has overflowed, it is off by a multiple of
Packit 575503
	 TIME_T_MAX - TIME_T_MIN + 1.  So ignore any component of
Packit 575503
	 the difference that is bounded by a small value.  */
Packit 575503
Packit 575503
      double dyear = (double) year_requested + mon_years - tm.tm_year;
Packit 575503
      double dday = 366 * dyear + mday;
Packit 575503
      double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
Packit 575503
Packit 575503
      if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec))
Packit 575503
	return -1;
Packit 575503
    }
Packit 575503
Packit 575503
  *tp = tm;
Packit 575503
  return t;
Packit 575503
}
Packit 575503
Packit 575503
#ifdef weak_alias
Packit 575503
weak_alias (mktime, timelocal)
Packit 575503
#endif
Packit 575503

Packit 575503
#if DEBUG
Packit 575503
Packit 575503
static int
Packit 575503
not_equal_tm (a, b)
Packit 575503
     struct tm *a;
Packit 575503
     struct tm *b;
Packit 575503
{
Packit 575503
  return ((a->tm_sec ^ b->tm_sec)
Packit 575503
	  | (a->tm_min ^ b->tm_min)
Packit 575503
	  | (a->tm_hour ^ b->tm_hour)
Packit 575503
	  | (a->tm_mday ^ b->tm_mday)
Packit 575503
	  | (a->tm_mon ^ b->tm_mon)
Packit 575503
	  | (a->tm_year ^ b->tm_year)
Packit 575503
	  | (a->tm_mday ^ b->tm_mday)
Packit 575503
	  | (a->tm_yday ^ b->tm_yday)
Packit 575503
	  | (a->tm_isdst ^ b->tm_isdst));
Packit 575503
}
Packit 575503
Packit 575503
static void
Packit 575503
print_tm (tp)
Packit 575503
     struct tm *tp;
Packit 575503
{
Packit 575503
  printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
Packit 575503
	  tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
Packit 575503
	  tp->tm_hour, tp->tm_min, tp->tm_sec,
Packit 575503
	  tp->tm_yday, tp->tm_wday, tp->tm_isdst);
Packit 575503
}
Packit 575503
Packit 575503
static int
Packit 575503
check_result (tk, tmk, tl, tml)
Packit 575503
     time_t tk;
Packit 575503
     struct tm tmk;
Packit 575503
     time_t tl;
Packit 575503
     struct tm tml;
Packit 575503
{
Packit 575503
  if (tk != tl || not_equal_tm (&tmk, &tml))
Packit 575503
    {
Packit 575503
      printf ("mktime (");
Packit 575503
      print_tm (&tmk);
Packit 575503
      printf (")\nyields (");
Packit 575503
      print_tm (&tml);
Packit 575503
      printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
Packit 575503
      return 1;
Packit 575503
    }
Packit 575503
Packit 575503
  return 0;
Packit 575503
}
Packit 575503
Packit 575503
int
Packit 575503
main (argc, argv)
Packit 575503
     int argc;
Packit 575503
     char **argv;
Packit 575503
{
Packit 575503
  int status = 0;
Packit 575503
  struct tm tm, tmk, tml;
Packit 575503
  time_t tk, tl;
Packit 575503
  char trailer;
Packit 575503
Packit 575503
  if ((argc == 3 || argc == 4)
Packit 575503
      && (sscanf (argv[1], "%d-%d-%d%c",
Packit 575503
		  &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
Packit 575503
	  == 3)
Packit 575503
      && (sscanf (argv[2], "%d:%d:%d%c",
Packit 575503
		  &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
Packit 575503
	  == 3))
Packit 575503
    {
Packit 575503
      tm.tm_year -= TM_YEAR_BASE;
Packit 575503
      tm.tm_mon--;
Packit 575503
      tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
Packit 575503
      tmk = tm;
Packit 575503
      tl = mktime (&tmk);
Packit 575503
      tml = *localtime (&tl;;
Packit 575503
      printf ("mktime returns %ld == ", (long) tl);
Packit 575503
      print_tm (&tmk);
Packit 575503
      printf ("\n");
Packit 575503
      status = check_result (tl, tmk, tl, tml);
Packit 575503
    }
Packit 575503
  else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
Packit 575503
    {
Packit 575503
      time_t from = atol (argv[1]);
Packit 575503
      time_t by = atol (argv[2]);
Packit 575503
      time_t to = atol (argv[3]);
Packit 575503
Packit 575503
      if (argc == 4)
Packit 575503
	for (tl = from; tl <= to; tl += by)
Packit 575503
	  {
Packit 575503
	    tml = *localtime (&tl;;
Packit 575503
	    tmk = tml;
Packit 575503
	    tk = mktime (&tmk);
Packit 575503
	    status |= check_result (tk, tmk, tl, tml);
Packit 575503
	  }
Packit 575503
      else
Packit 575503
	for (tl = from; tl <= to; tl += by)
Packit 575503
	  {
Packit 575503
	    /* Null benchmark.  */
Packit 575503
	    tml = *localtime (&tl;;
Packit 575503
	    tmk = tml;
Packit 575503
	    tk = tl;
Packit 575503
	    status |= check_result (tk, tmk, tl, tml);
Packit 575503
	  }
Packit 575503
    }
Packit 575503
  else
Packit 575503
    printf ("Usage:\
Packit 575503
\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
Packit 575503
\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
Packit 575503
\t%s FROM BY TO - # Do not test those values (for benchmark).\n",
Packit 575503
	    argv[0], argv[0], argv[0]);
Packit 575503
Packit 575503
  return status;
Packit 575503
}
Packit 575503
Packit 575503
#endif /* DEBUG */
Packit 575503

Packit 575503
/*
Packit 575503
Local Variables:
Packit 575503
compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"
Packit 575503
End:
Packit 575503
*/