Blame lib/mktime.c

Packit Service fdd496
/* Convert a 'struct tm' to a time_t value.
Packit Service fdd496
   Copyright (C) 1993-2017 Free Software Foundation, Inc.
Packit Service fdd496
   This file is part of the GNU C Library.
Packit Service fdd496
   Contributed by Paul Eggert <eggert@twinsun.com>.
Packit Service fdd496
Packit Service fdd496
   The GNU C Library is free software; you can redistribute it and/or
Packit Service fdd496
   modify it under the terms of the GNU General Public
Packit Service fdd496
   License as published by the Free Software Foundation; either
Packit Service fdd496
   version 3 of the License, or (at your option) any later version.
Packit Service fdd496
Packit Service fdd496
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service fdd496
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service fdd496
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service fdd496
   General Public License for more details.
Packit Service fdd496
Packit Service fdd496
   You should have received a copy of the GNU General Public
Packit Service fdd496
   License along with the GNU C Library; if not, see
Packit Service fdd496
   <http://www.gnu.org/licenses/>.  */
Packit Service fdd496
Packit Service fdd496
/* Define this to 1 to have a standalone program to test this implementation of
Packit Service fdd496
   mktime.  */
Packit Service fdd496
#ifndef DEBUG_MKTIME
Packit Service fdd496
# define DEBUG_MKTIME 0
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
/* The following macros influence what gets defined when this file is compiled:
Packit Service fdd496
Packit Service fdd496
   Macro/expression            Which gnulib module    This compilation unit
Packit Service fdd496
                                                      should define
Packit Service fdd496
Packit Service fdd496
   NEED_MKTIME_WORKING         mktime                 rpl_mktime
Packit Service fdd496
   || NEED_MKTIME_WINDOWS
Packit Service fdd496
Packit Service fdd496
   NEED_MKTIME_INTERNAL        mktime-internal        mktime_internal
Packit Service fdd496
Packit Service fdd496
   DEBUG_MKTIME                (defined manually)     my_mktime, main
Packit Service fdd496
 */
Packit Service fdd496
Packit Service fdd496
#if !defined _LIBC && !DEBUG_MKTIME
Packit Service fdd496
# include <config.h>
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
/* Assume that leap seconds are possible, unless told otherwise.
Packit Service fdd496
   If the host has a 'zic' command with a '-L leapsecondfilename' option,
Packit Service fdd496
   then it supports leap seconds; otherwise it probably doesn't.  */
Packit Service fdd496
#ifndef LEAP_SECONDS_POSSIBLE
Packit Service fdd496
# define LEAP_SECONDS_POSSIBLE 1
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
#include <time.h>
Packit Service fdd496
Packit Service fdd496
#include <limits.h>
Packit Service fdd496
#include <stdbool.h>
Packit Service fdd496
Packit Service fdd496
#include <intprops.h>
Packit Service fdd496
#include <verify.h>
Packit Service fdd496
Packit Service fdd496
#if DEBUG_MKTIME
Packit Service fdd496
# include <stdio.h>
Packit Service fdd496
# include <stdlib.h>
Packit Service fdd496
# include <string.h>
Packit Service fdd496
/* Make it work even if the system's libc has its own mktime routine.  */
Packit Service fdd496
# undef mktime
Packit Service fdd496
# define mktime my_mktime
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
#if NEED_MKTIME_WINDOWS /* on native Windows */
Packit Service fdd496
# include <stdlib.h>
Packit Service fdd496
# include <string.h>
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
#if NEED_MKTIME_WORKING || NEED_MKTIME_INTERNAL || DEBUG_MKTIME
Packit Service fdd496
Packit Service fdd496
/* A signed type that can represent an integer number of years
Packit Service fdd496
   multiplied by three times the number of seconds in a year.  It is
Packit Service fdd496
   needed when converting a tm_year value times the number of seconds
Packit Service fdd496
   in a year.  The factor of three comes because these products need
Packit Service fdd496
   to be subtracted from each other, and sometimes with an offset
Packit Service fdd496
   added to them, without worrying about overflow.
Packit Service fdd496
Packit Service fdd496
   Much of the code uses long_int to represent time_t values, to
Packit Service fdd496
   lessen the hassle of dealing with platforms where time_t is
Packit Service fdd496
   unsigned, and because long_int should suffice to represent all
Packit Service fdd496
   time_t values that mktime can generate even on platforms where
Packit Service fdd496
   time_t is excessively wide.  */
Packit Service fdd496
Packit Service fdd496
#if INT_MAX <= LONG_MAX / 3 / 366 / 24 / 60 / 60
Packit Service fdd496
typedef long int long_int;
Packit Service fdd496
#else
Packit Service fdd496
typedef long long int long_int;
Packit Service fdd496
#endif
Packit Service fdd496
verify (INT_MAX <= TYPE_MAXIMUM (long_int) / 3 / 366 / 24 / 60 / 60);
Packit Service fdd496
Packit Service fdd496
/* Shift A right by B bits portably, by dividing A by 2**B and
Packit Service fdd496
   truncating towards minus infinity.  B should be in the range 0 <= B
Packit Service fdd496
   <= LONG_INT_BITS - 2, where LONG_INT_BITS is the number of useful
Packit Service fdd496
   bits in a long_int.  LONG_INT_BITS is at least 32.
Packit Service fdd496
Packit Service fdd496
   ISO C99 says that A >> B is implementation-defined if A < 0.  Some
Packit Service fdd496
   implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
Packit Service fdd496
   right in the usual way when A < 0, so SHR falls back on division if
Packit Service fdd496
   ordinary A >> B doesn't seem to be the usual signed shift.  */
Packit Service fdd496
Packit Service fdd496
static long_int
Packit Service fdd496
shr (long_int a, int b)
Packit Service fdd496
{
Packit Service fdd496
  long_int one = 1;
Packit Service fdd496
  return (-one >> 1 == -1
Packit Service fdd496
	  ? a >> b
Packit Service fdd496
	  : a / (one << b) - (a % (one << b) < 0));
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Bounds for the intersection of time_t and long_int.  */
Packit Service fdd496
Packit Service fdd496
static long_int const mktime_min
Packit Service fdd496
  = ((TYPE_SIGNED (time_t) && TYPE_MINIMUM (time_t) < TYPE_MINIMUM (long_int))
Packit Service fdd496
     ? TYPE_MINIMUM (long_int) : TYPE_MINIMUM (time_t));
Packit Service fdd496
static long_int const mktime_max
Packit Service fdd496
  = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (time_t)
Packit Service fdd496
     ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (time_t));
Packit Service fdd496
Packit Service fdd496
verify (TYPE_IS_INTEGER (time_t));
Packit Service fdd496
Packit Service fdd496
#define EPOCH_YEAR 1970
Packit Service fdd496
#define TM_YEAR_BASE 1900
Packit Service fdd496
verify (TM_YEAR_BASE % 100 == 0);
Packit Service fdd496
Packit Service fdd496
/* Is YEAR + TM_YEAR_BASE a leap year?  */
Packit Service fdd496
static bool
Packit Service fdd496
leapyear (long_int year)
Packit Service fdd496
{
Packit Service fdd496
  /* Don't add YEAR to TM_YEAR_BASE, as that might overflow.
Packit Service fdd496
     Also, work even if YEAR is negative.  */
Packit Service fdd496
  return
Packit Service fdd496
    ((year & 3) == 0
Packit Service fdd496
     && (year % 100 != 0
Packit Service fdd496
	 || ((year / 100) & 3) == (- (TM_YEAR_BASE / 100) & 3)));
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* How many days come before each month (0-12).  */
Packit Service fdd496
#ifndef _LIBC
Packit Service fdd496
static
Packit Service fdd496
#endif
Packit Service fdd496
const unsigned short int __mon_yday[2][13] =
Packit Service fdd496
  {
Packit Service fdd496
    /* Normal years.  */
Packit Service fdd496
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
Packit Service fdd496
    /* Leap years.  */
Packit Service fdd496
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
Packit Service fdd496
  };
Packit Service fdd496
Packit Service fdd496
Packit Service fdd496
#ifdef _LIBC
Packit Service fdd496
typedef time_t mktime_offset_t;
Packit Service fdd496
#else
Packit Service fdd496
/* Portable standalone applications should supply a <time.h> that
Packit Service fdd496
   declares a POSIX-compliant localtime_r, for the benefit of older
Packit Service fdd496
   implementations that lack localtime_r or have a nonstandard one.
Packit Service fdd496
   See the gnulib time_r module for one way to implement this.  */
Packit Service fdd496
# undef __localtime_r
Packit Service fdd496
# define __localtime_r localtime_r
Packit Service fdd496
# define __mktime_internal mktime_internal
Packit Service fdd496
# include "mktime-internal.h"
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
/* Do the values A and B differ according to the rules for tm_isdst?
Packit Service fdd496
   A and B differ if one is zero and the other positive.  */
Packit Service fdd496
static bool
Packit Service fdd496
isdst_differ (int a, int b)
Packit Service fdd496
{
Packit Service fdd496
  return (!a != !b) && (0 <= a) && (0 <= b);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) -
Packit Service fdd496
   (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks
Packit Service fdd496
   were not adjusted between the timestamps.
Packit Service fdd496
Packit Service fdd496
   The YEAR values uses the same numbering as TP->tm_year.  Values
Packit Service fdd496
   need not be in the usual range.  However, YEAR1 must not overflow
Packit Service fdd496
   when multiplied by three times the number of seconds in a year, and
Packit Service fdd496
   likewise for YDAY1 and three times the number of seconds in a day.  */
Packit Service fdd496
Packit Service fdd496
static long_int
Packit Service fdd496
ydhms_diff (long_int year1, long_int yday1, int hour1, int min1, int sec1,
Packit Service fdd496
	    int year0, int yday0, int hour0, int min0, int sec0)
Packit Service fdd496
{
Packit Service fdd496
  verify (-1 / 2 == 0);
Packit Service fdd496
Packit Service fdd496
  /* Compute intervening leap days correctly even if year is negative.
Packit Service fdd496
     Take care to avoid integer overflow here.  */
Packit Service fdd496
  int a4 = shr (year1, 2) + shr (TM_YEAR_BASE, 2) - ! (year1 & 3);
Packit Service fdd496
  int b4 = shr (year0, 2) + shr (TM_YEAR_BASE, 2) - ! (year0 & 3);
Packit Service fdd496
  int a100 = a4 / 25 - (a4 % 25 < 0);
Packit Service fdd496
  int b100 = b4 / 25 - (b4 % 25 < 0);
Packit Service fdd496
  int a400 = shr (a100, 2);
Packit Service fdd496
  int b400 = shr (b100, 2);
Packit Service fdd496
  int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
Packit Service fdd496
Packit Service fdd496
  /* Compute the desired time without overflowing.  */
Packit Service fdd496
  long_int years = year1 - year0;
Packit Service fdd496
  long_int days = 365 * years + yday1 - yday0 + intervening_leap_days;
Packit Service fdd496
  long_int hours = 24 * days + hour1 - hour0;
Packit Service fdd496
  long_int minutes = 60 * hours + min1 - min0;
Packit Service fdd496
  long_int seconds = 60 * minutes + sec1 - sec0;
Packit Service fdd496
  return seconds;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return the average of A and B, even if A + B would overflow.
Packit Service fdd496
   Round toward positive infinity.  */
Packit Service fdd496
static long_int
Packit Service fdd496
long_int_avg (long_int a, long_int b)
Packit Service fdd496
{
Packit Service fdd496
  return shr (a, 1) + shr (b, 1) + ((a | b) & 1);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return a time_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC),
Packit Service fdd496
   assuming that T corresponds to *TP and that no clock adjustments
Packit Service fdd496
   occurred between *TP and the desired time.
Packit Service fdd496
   Although T and the returned value are of type long_int,
Packit Service fdd496
   they represent time_t values and must be in time_t range.
Packit Service fdd496
   If TP is null, return a value not equal to T; this avoids false matches.
Packit Service fdd496
   YEAR and YDAY must not be so large that multiplying them by three times the
Packit Service fdd496
   number of seconds in a year (or day, respectively) would overflow long_int.
Packit Service fdd496
   If the returned value would be out of range, yield the minimal or
Packit Service fdd496
   maximal in-range value, except do not yield a value equal to T.  */
Packit Service fdd496
static long_int
Packit Service fdd496
guess_time_tm (long_int year, long_int yday, int hour, int min, int sec,
Packit Service fdd496
	       long_int t, const struct tm *tp)
Packit Service fdd496
{
Packit Service fdd496
  if (tp)
Packit Service fdd496
    {
Packit Service fdd496
      long_int result;
Packit Service fdd496
      long_int d = ydhms_diff (year, yday, hour, min, sec,
Packit Service fdd496
			       tp->tm_year, tp->tm_yday,
Packit Service fdd496
			       tp->tm_hour, tp->tm_min, tp->tm_sec);
Packit Service fdd496
      if (! INT_ADD_WRAPV (t, d, &result))
Packit Service fdd496
	return result;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Overflow occurred one way or another.  Return the nearest result
Packit Service fdd496
     that is actually in range, except don't report a zero difference
Packit Service fdd496
     if the actual difference is nonzero, as that would cause a false
Packit Service fdd496
     match; and don't oscillate between two values, as that would
Packit Service fdd496
     confuse the spring-forward gap detector.  */
Packit Service fdd496
  return (t < long_int_avg (mktime_min, mktime_max)
Packit Service fdd496
	  ? (t <= mktime_min + 1 ? t + 1 : mktime_min)
Packit Service fdd496
	  : (mktime_max - 1 <= t ? t - 1 : mktime_max));
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Use CONVERT to convert T to a struct tm value in *TM.  T must be in
Packit Service fdd496
   range for time_t.  Return TM if successful, NULL if T is out of
Packit Service fdd496
   range for CONVERT.  */
Packit Service fdd496
static struct tm *
Packit Service fdd496
convert_time (struct tm *(*convert) (const time_t *, struct tm *),
Packit Service fdd496
	      long_int t, struct tm *tm)
Packit Service fdd496
{
Packit Service fdd496
  time_t x = t;
Packit Service fdd496
  return convert (&x, tm);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Use CONVERT to convert *T to a broken down time in *TP.
Packit Service fdd496
   If *T is out of range for conversion, adjust it so that
Packit Service fdd496
   it is the nearest in-range value and then convert that.
Packit Service fdd496
   A value is in range if it fits in both time_t and long_int.  */
Packit Service fdd496
static struct tm *
Packit Service fdd496
ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
Packit Service fdd496
		long_int *t, struct tm *tp)
Packit Service fdd496
{
Packit Service fdd496
  struct tm *r;
Packit Service fdd496
  if (*t < mktime_min)
Packit Service fdd496
    *t = mktime_min;
Packit Service fdd496
  else if (mktime_max < *t)
Packit Service fdd496
    *t = mktime_max;
Packit Service fdd496
  r = convert_time (convert, *t, tp);
Packit Service fdd496
Packit Service fdd496
  if (!r && *t)
Packit Service fdd496
    {
Packit Service fdd496
      long_int bad = *t;
Packit Service fdd496
      long_int ok = 0;
Packit Service fdd496
Packit Service fdd496
      /* BAD is a known unconvertible value, and OK is a known good one.
Packit Service fdd496
	 Use binary search to narrow the range between BAD and OK until
Packit Service fdd496
	 they differ by 1.  */
Packit Service fdd496
      while (true)
Packit Service fdd496
	{
Packit Service fdd496
	  long_int mid = long_int_avg (ok, bad);
Packit Service fdd496
	  if (mid != ok && mid != bad)
Packit Service fdd496
	    break;
Packit Service fdd496
	  r = convert_time (convert, mid, tp);
Packit Service fdd496
	  if (r)
Packit Service fdd496
	    ok = mid;
Packit Service fdd496
	  else
Packit Service fdd496
	    bad = mid;
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      if (!r && ok)
Packit Service fdd496
	{
Packit Service fdd496
	  /* The last conversion attempt failed;
Packit Service fdd496
	     revert to the most recent successful attempt.  */
Packit Service fdd496
	  r = convert_time (convert, ok, tp);
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return r;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Convert *TP to a time_t value, inverting
Packit Service fdd496
   the monotonic and mostly-unit-linear conversion function CONVERT.
Packit Service fdd496
   Use *OFFSET to keep track of a guess at the offset of the result,
Packit Service fdd496
   compared to what the result would be for UTC without leap seconds.
Packit Service fdd496
   If *OFFSET's guess is correct, only one CONVERT call is needed.
Packit Service fdd496
   This function is external because it is used also by timegm.c.  */
Packit Service fdd496
time_t
Packit Service fdd496
__mktime_internal (struct tm *tp,
Packit Service fdd496
		   struct tm *(*convert) (const time_t *, struct tm *),
Packit Service fdd496
		   mktime_offset_t *offset)
Packit Service fdd496
{
Packit Service fdd496
  long_int t, gt, t0, t1, t2, dt;
Packit Service fdd496
  struct tm tm;
Packit Service fdd496
Packit Service fdd496
  /* The maximum number of probes (calls to CONVERT) should be enough
Packit Service fdd496
     to handle any combinations of time zone rule changes, solar time,
Packit Service fdd496
     leap seconds, and oscillations around a spring-forward gap.
Packit Service fdd496
     POSIX.1 prohibits leap seconds, but some hosts have them anyway.  */
Packit Service fdd496
  int remaining_probes = 6;
Packit Service fdd496
Packit Service fdd496
  /* Time requested.  Copy it in case CONVERT modifies *TP; this can
Packit Service fdd496
     occur if TP is localtime's returned value and CONVERT is localtime.  */
Packit Service fdd496
  int sec = tp->tm_sec;
Packit Service fdd496
  int min = tp->tm_min;
Packit Service fdd496
  int hour = tp->tm_hour;
Packit Service fdd496
  int mday = tp->tm_mday;
Packit Service fdd496
  int mon = tp->tm_mon;
Packit Service fdd496
  int year_requested = tp->tm_year;
Packit Service fdd496
  int isdst = tp->tm_isdst;
Packit Service fdd496
Packit Service fdd496
  /* 1 if the previous probe was DST.  */
Packit Service fdd496
  int dst2;
Packit Service fdd496
Packit Service fdd496
  /* Ensure that mon is in range, and set year accordingly.  */
Packit Service fdd496
  int mon_remainder = mon % 12;
Packit Service fdd496
  int negative_mon_remainder = mon_remainder < 0;
Packit Service fdd496
  int mon_years = mon / 12 - negative_mon_remainder;
Packit Service fdd496
  long_int lyear_requested = year_requested;
Packit Service fdd496
  long_int year = lyear_requested + mon_years;
Packit Service fdd496
Packit Service fdd496
  /* The other values need not be in range:
Packit Service fdd496
     the remaining code handles overflows correctly.  */
Packit Service fdd496
Packit Service fdd496
  /* Calculate day of year from year, month, and day of month.
Packit Service fdd496
     The result need not be in range.  */
Packit Service fdd496
  int mon_yday = ((__mon_yday[leapyear (year)]
Packit Service fdd496
		   [mon_remainder + 12 * negative_mon_remainder])
Packit Service fdd496
		  - 1);
Packit Service fdd496
  long_int lmday = mday;
Packit Service fdd496
  long_int yday = mon_yday + lmday;
Packit Service fdd496
Packit Service fdd496
  int negative_offset_guess;
Packit Service fdd496
Packit Service fdd496
  int sec_requested = sec;
Packit Service fdd496
Packit Service fdd496
  if (LEAP_SECONDS_POSSIBLE)
Packit Service fdd496
    {
Packit Service fdd496
      /* Handle out-of-range seconds specially,
Packit Service fdd496
	 since ydhms_tm_diff assumes every minute has 60 seconds.  */
Packit Service fdd496
      if (sec < 0)
Packit Service fdd496
	sec = 0;
Packit Service fdd496
      if (59 < sec)
Packit Service fdd496
	sec = 59;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Invert CONVERT by probing.  First assume the same offset as last
Packit Service fdd496
     time.  */
Packit Service fdd496
Packit Service fdd496
  INT_SUBTRACT_WRAPV (0, *offset, &negative_offset_guess);
Packit Service fdd496
  t0 = ydhms_diff (year, yday, hour, min, sec,
Packit Service fdd496
		   EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, negative_offset_guess);
Packit Service fdd496
Packit Service fdd496
  /* Repeatedly use the error to improve the guess.  */
Packit Service fdd496
Packit Service fdd496
  for (t = t1 = t2 = t0, dst2 = 0;
Packit Service fdd496
       (gt = guess_time_tm (year, yday, hour, min, sec, t,
Packit Service fdd496
			    ranged_convert (convert, &t, &tm)),
Packit Service fdd496
	t != gt);
Packit Service fdd496
       t1 = t2, t2 = t, t = gt, dst2 = tm.tm_isdst != 0)
Packit Service fdd496
    if (t == t1 && t != t2
Packit Service fdd496
	&& (tm.tm_isdst < 0
Packit Service fdd496
	    || (isdst < 0
Packit Service fdd496
		? dst2 <= (tm.tm_isdst != 0)
Packit Service fdd496
		: (isdst != 0) != (tm.tm_isdst != 0))))
Packit Service fdd496
      /* We can't possibly find a match, as we are oscillating
Packit Service fdd496
	 between two values.  The requested time probably falls
Packit Service fdd496
	 within a spring-forward gap of size GT - T.  Follow the common
Packit Service fdd496
	 practice in this case, which is to return a time that is GT - T
Packit Service fdd496
	 away from the requested time, preferring a time whose
Packit Service fdd496
	 tm_isdst differs from the requested value.  (If no tm_isdst
Packit Service fdd496
	 was requested and only one of the two values has a nonzero
Packit Service fdd496
	 tm_isdst, prefer that value.)  In practice, this is more
Packit Service fdd496
	 useful than returning -1.  */
Packit Service fdd496
      goto offset_found;
Packit Service fdd496
    else if (--remaining_probes == 0)
Packit Service fdd496
      return -1;
Packit Service fdd496
Packit Service fdd496
  /* We have a match.  Check whether tm.tm_isdst has the requested
Packit Service fdd496
     value, if any.  */
Packit Service fdd496
  if (isdst_differ (isdst, tm.tm_isdst))
Packit Service fdd496
    {
Packit Service fdd496
      /* tm.tm_isdst has the wrong value.  Look for a neighboring
Packit Service fdd496
	 time with the right value, and use its UTC offset.
Packit Service fdd496
Packit Service fdd496
	 Heuristic: probe the adjacent timestamps in both directions,
Packit Service fdd496
	 looking for the desired isdst.  This should work for all real
Packit Service fdd496
	 time zone histories in the tz database.  */
Packit Service fdd496
Packit Service fdd496
      /* Distance between probes when looking for a DST boundary.  In
Packit Service fdd496
	 tzdata2003a, the shortest period of DST is 601200 seconds
Packit Service fdd496
	 (e.g., America/Recife starting 2000-10-08 01:00), and the
Packit Service fdd496
	 shortest period of non-DST surrounded by DST is 694800
Packit Service fdd496
	 seconds (Africa/Tunis starting 1943-04-17 01:00).  Use the
Packit Service fdd496
	 minimum of these two values, so we don't miss these short
Packit Service fdd496
	 periods when probing.  */
Packit Service fdd496
      int stride = 601200;
Packit Service fdd496
Packit Service fdd496
      /* The longest period of DST in tzdata2003a is 536454000 seconds
Packit Service fdd496
	 (e.g., America/Jujuy starting 1946-10-01 01:00).  The longest
Packit Service fdd496
	 period of non-DST is much longer, but it makes no real sense
Packit Service fdd496
	 to search for more than a year of non-DST, so use the DST
Packit Service fdd496
	 max.  */
Packit Service fdd496
      int duration_max = 536454000;
Packit Service fdd496
Packit Service fdd496
      /* Search in both directions, so the maximum distance is half
Packit Service fdd496
	 the duration; add the stride to avoid off-by-1 problems.  */
Packit Service fdd496
      int delta_bound = duration_max / 2 + stride;
Packit Service fdd496
Packit Service fdd496
      int delta, direction;
Packit Service fdd496
Packit Service fdd496
      for (delta = stride; delta < delta_bound; delta += stride)
Packit Service fdd496
	for (direction = -1; direction <= 1; direction += 2)
Packit Service fdd496
	  {
Packit Service fdd496
	    long_int ot;
Packit Service fdd496
	    if (! INT_ADD_WRAPV (t, delta * direction, &ot))
Packit Service fdd496
	      {
Packit Service fdd496
		struct tm otm;
Packit Service fdd496
		ranged_convert (convert, &ot, &otm;;
Packit Service fdd496
		if (! isdst_differ (isdst, otm.tm_isdst))
Packit Service fdd496
		  {
Packit Service fdd496
		    /* We found the desired tm_isdst.
Packit Service fdd496
		       Extrapolate back to the desired time.  */
Packit Service fdd496
		    t = guess_time_tm (year, yday, hour, min, sec, ot, &otm;;
Packit Service fdd496
		    ranged_convert (convert, &t, &tm;;
Packit Service fdd496
		    goto offset_found;
Packit Service fdd496
		  }
Packit Service fdd496
	      }
Packit Service fdd496
	  }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
 offset_found:
Packit Service fdd496
  /* Set *OFFSET to the low-order bits of T - T0 - NEGATIVE_OFFSET_GUESS.
Packit Service fdd496
     This is just a heuristic to speed up the next mktime call, and
Packit Service fdd496
     correctness is unaffected if integer overflow occurs here.  */
Packit Service fdd496
  INT_SUBTRACT_WRAPV (t, t0, &dt);
Packit Service fdd496
  INT_SUBTRACT_WRAPV (dt, negative_offset_guess, offset);
Packit Service fdd496
Packit Service fdd496
  if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec)
Packit Service fdd496
    {
Packit Service fdd496
      /* Adjust time to reflect the tm_sec requested, not the normalized value.
Packit Service fdd496
	 Also, repair any damage from a false match due to a leap second.  */
Packit Service fdd496
      long_int sec_adjustment = sec == 0 && tm.tm_sec == 60;
Packit Service fdd496
      sec_adjustment -= sec;
Packit Service fdd496
      sec_adjustment += sec_requested;
Packit Service fdd496
      if (INT_ADD_WRAPV (t, sec_adjustment, &t)
Packit Service fdd496
	  || ! (mktime_min <= t && t <= mktime_max)
Packit Service fdd496
	  || ! convert_time (convert, t, &tm))
Packit Service fdd496
	return -1;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  *tp = tm;
Packit Service fdd496
  return t;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
#endif /* NEED_MKTIME_WORKING || NEED_MKTIME_INTERNAL || DEBUG_MKTIME */
Packit Service fdd496
Packit Service fdd496
#if NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS || DEBUG_MKTIME
Packit Service fdd496
Packit Service fdd496
# if NEED_MKTIME_WORKING || DEBUG_MKTIME
Packit Service fdd496
static mktime_offset_t localtime_offset;
Packit Service fdd496
# endif
Packit Service fdd496
Packit Service fdd496
/* Convert *TP to a time_t value.  */
Packit Service fdd496
time_t
Packit Service fdd496
mktime (struct tm *tp)
Packit Service fdd496
{
Packit Service fdd496
# if NEED_MKTIME_WINDOWS
Packit Service fdd496
  /* Rectify the value of the environment variable TZ.
Packit Service fdd496
     There are four possible kinds of such values:
Packit Service fdd496
       - Traditional US time zone names, e.g. "PST8PDT".  Syntax: see
Packit Service fdd496
         <https://msdn.microsoft.com/en-us/library/90s5c885.aspx>
Packit Service fdd496
       - Time zone names based on geography, that contain one or more
Packit Service fdd496
         slashes, e.g. "Europe/Moscow".
Packit Service fdd496
       - Time zone names based on geography, without slashes, e.g.
Packit Service fdd496
         "Singapore".
Packit Service fdd496
       - Time zone names that contain explicit DST rules.  Syntax: see
Packit Service fdd496
         <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03>
Packit Service fdd496
     The Microsoft CRT understands only the first kind.  It produces incorrect
Packit Service fdd496
     results if the value of TZ is of the other kinds.
Packit Service fdd496
     But in a Cygwin environment, /etc/profile.d/tzset.sh sets TZ to a value
Packit Service fdd496
     of the second kind for most geographies, or of the first kind in a few
Packit Service fdd496
     other geographies.  If it is of the second kind, neutralize it.  For the
Packit Service fdd496
     Microsoft CRT, an absent or empty TZ means the time zone that the user
Packit Service fdd496
     has set in the Windows Control Panel.
Packit Service fdd496
     If the value of TZ is of the third or fourth kind -- Cygwin programs
Packit Service fdd496
     understand these syntaxes as well --, it does not matter whether we
Packit Service fdd496
     neutralize it or not, since these values occur only when a Cygwin user
Packit Service fdd496
     has set TZ explicitly; this case is 1. rare and 2. under the user's
Packit Service fdd496
     responsibility.  */
Packit Service fdd496
  const char *tz = getenv ("TZ");
Packit Service fdd496
  if (tz != NULL && strchr (tz, '/') != NULL)
Packit Service fdd496
    _putenv ("TZ=");
Packit Service fdd496
# endif
Packit Service fdd496
Packit Service fdd496
# if NEED_MKTIME_WORKING || DEBUG_MKTIME
Packit Service fdd496
#  ifdef _LIBC
Packit Service fdd496
  /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
Packit Service fdd496
     time zone names contained in the external variable 'tzname' shall
Packit Service fdd496
     be set as if the tzset() function had been called.  */
Packit Service fdd496
  __tzset ();
Packit Service fdd496
#  elif HAVE_TZSET
Packit Service fdd496
  tzset ();
Packit Service fdd496
#  endif
Packit Service fdd496
Packit Service fdd496
  return __mktime_internal (tp, __localtime_r, &localtime_offset);
Packit Service fdd496
# else
Packit Service fdd496
#  undef mktime
Packit Service fdd496
  return mktime (tp);
Packit Service fdd496
# endif
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
#endif /* NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS || DEBUG_MKTIME */
Packit Service fdd496
Packit Service fdd496
#ifdef weak_alias
Packit Service fdd496
weak_alias (mktime, timelocal)
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
#ifdef _LIBC
Packit Service fdd496
libc_hidden_def (mktime)
Packit Service fdd496
libc_hidden_weak (timelocal)
Packit Service fdd496
#endif
Packit Service fdd496

Packit Service fdd496
#if DEBUG_MKTIME
Packit Service fdd496
Packit Service fdd496
static int
Packit Service fdd496
not_equal_tm (const struct tm *a, const struct tm *b)
Packit Service fdd496
{
Packit Service fdd496
  return ((a->tm_sec ^ b->tm_sec)
Packit Service fdd496
	  | (a->tm_min ^ b->tm_min)
Packit Service fdd496
	  | (a->tm_hour ^ b->tm_hour)
Packit Service fdd496
	  | (a->tm_mday ^ b->tm_mday)
Packit Service fdd496
	  | (a->tm_mon ^ b->tm_mon)
Packit Service fdd496
	  | (a->tm_year ^ b->tm_year)
Packit Service fdd496
	  | (a->tm_yday ^ b->tm_yday)
Packit Service fdd496
	  | isdst_differ (a->tm_isdst, b->tm_isdst));
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
print_tm (const struct tm *tp)
Packit Service fdd496
{
Packit Service fdd496
  if (tp)
Packit Service fdd496
    printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
Packit Service fdd496
	    tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
Packit Service fdd496
	    tp->tm_hour, tp->tm_min, tp->tm_sec,
Packit Service fdd496
	    tp->tm_yday, tp->tm_wday, tp->tm_isdst);
Packit Service fdd496
  else
Packit Service fdd496
    printf ("0");
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static int
Packit Service fdd496
check_result (time_t tk, struct tm tmk, time_t tl, const struct tm *lt)
Packit Service fdd496
{
Packit Service fdd496
  if (tk != tl || !lt || not_equal_tm (&tmk, lt))
Packit Service fdd496
    {
Packit Service fdd496
      printf ("mktime (");
Packit Service fdd496
      print_tm (lt);
Packit Service fdd496
      printf (")\nyields (");
Packit Service fdd496
      print_tm (&tmk);
Packit Service fdd496
      printf (") == %ld, should be %ld\n", (long int) tk, (long int) tl);
Packit Service fdd496
      return 1;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return 0;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
int
Packit Service fdd496
main (int argc, char **argv)
Packit Service fdd496
{
Packit Service fdd496
  int status = 0;
Packit Service fdd496
  struct tm tm, tmk, tml;
Packit Service fdd496
  struct tm *lt;
Packit Service fdd496
  time_t tk, tl, tl1;
Packit Service fdd496
  char trailer;
Packit Service fdd496
Packit Service fdd496
  /* Sanity check, plus call tzset.  */
Packit Service fdd496
  tl = 0;
Packit Service fdd496
  if (! localtime (&tl))
Packit Service fdd496
    {
Packit Service fdd496
      printf ("localtime (0) fails\n");
Packit Service fdd496
      status = 1;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  if ((argc == 3 || argc == 4)
Packit Service fdd496
      && (sscanf (argv[1], "%d-%d-%d%c",
Packit Service fdd496
		  &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
Packit Service fdd496
	  == 3)
Packit Service fdd496
      && (sscanf (argv[2], "%d:%d:%d%c",
Packit Service fdd496
		  &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
Packit Service fdd496
	  == 3))
Packit Service fdd496
    {
Packit Service fdd496
      tm.tm_year -= TM_YEAR_BASE;
Packit Service fdd496
      tm.tm_mon--;
Packit Service fdd496
      tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
Packit Service fdd496
      tmk = tm;
Packit Service fdd496
      tl = mktime (&tmk);
Packit Service fdd496
      lt = localtime_r (&tl, &tml);
Packit Service fdd496
      printf ("mktime returns %ld == ", (long int) tl);
Packit Service fdd496
      print_tm (&tmk);
Packit Service fdd496
      printf ("\n");
Packit Service fdd496
      status = check_result (tl, tmk, tl, lt);
Packit Service fdd496
    }
Packit Service fdd496
  else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
Packit Service fdd496
    {
Packit Service fdd496
      time_t from = atol (argv[1]);
Packit Service fdd496
      time_t by = atol (argv[2]);
Packit Service fdd496
      time_t to = atol (argv[3]);
Packit Service fdd496
Packit Service fdd496
      if (argc == 4)
Packit Service fdd496
	for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
Packit Service fdd496
	  {
Packit Service fdd496
	    lt = localtime_r (&tl, &tml);
Packit Service fdd496
	    if (lt)
Packit Service fdd496
	      {
Packit Service fdd496
		tmk = tml;
Packit Service fdd496
		tk = mktime (&tmk);
Packit Service fdd496
		status |= check_result (tk, tmk, tl, &tml);
Packit Service fdd496
	      }
Packit Service fdd496
	    else
Packit Service fdd496
	      {
Packit Service fdd496
		printf ("localtime_r (%ld) yields 0\n", (long int) tl);
Packit Service fdd496
		status = 1;
Packit Service fdd496
	      }
Packit Service fdd496
	    tl1 = tl + by;
Packit Service fdd496
	    if ((tl1 < tl) != (by < 0))
Packit Service fdd496
	      break;
Packit Service fdd496
	  }
Packit Service fdd496
      else
Packit Service fdd496
	for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
Packit Service fdd496
	  {
Packit Service fdd496
	    /* Null benchmark.  */
Packit Service fdd496
	    lt = localtime_r (&tl, &tml);
Packit Service fdd496
	    if (lt)
Packit Service fdd496
	      {
Packit Service fdd496
		tmk = tml;
Packit Service fdd496
		tk = tl;
Packit Service fdd496
		status |= check_result (tk, tmk, tl, &tml);
Packit Service fdd496
	      }
Packit Service fdd496
	    else
Packit Service fdd496
	      {
Packit Service fdd496
		printf ("localtime_r (%ld) yields 0\n", (long int) tl);
Packit Service fdd496
		status = 1;
Packit Service fdd496
	      }
Packit Service fdd496
	    tl1 = tl + by;
Packit Service fdd496
	    if ((tl1 < tl) != (by < 0))
Packit Service fdd496
	      break;
Packit Service fdd496
	  }
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    printf ("Usage:\
Packit Service fdd496
\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
Packit Service fdd496
\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
Packit Service fdd496
\t%s FROM BY TO - # Do not test those values (for benchmark).\n",
Packit Service fdd496
	    argv[0], argv[0], argv[0]);
Packit Service fdd496
Packit Service fdd496
  return status;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
#endif /* DEBUG_MKTIME */
Packit Service fdd496

Packit Service fdd496
/*
Packit Service fdd496
Local Variables:
Packit Service fdd496
compile-command: "gcc -DDEBUG_MKTIME -I. -Wall -W -O2 -g mktime.c -o mktime"
Packit Service fdd496
End:
Packit Service fdd496
*/