1/* free mktime function
2   Copyright 1988, 1989 by David MacKenzie <djm@ai.mit.edu>
3   and Michael Haertel <mike@ai.mit.edu>
4   Unlimited distribution permitted provided this copyright notice is
5   retained and any functional modifications are prominently identified.  */
6
7/* Revised 1997 by Christian Spieler:
8   The code was changed to get more conformance with ANSI's (resp. modern
9   UNIX releases) definition for mktime():
10   - Added adjustment for out-of-range values in the fields of struct tm.
11   - Added iterations to get the correct UTC result for input values at
12     the gaps when daylight saving time is switched on or off.
13   - Allow forcing of DST "on" or DST "off" by setting `tm_isdst' field in
14     the tm struct to positive number resp. zero. The `tm_isdst' field must
15     be negative on entrance of mktime() to enable automatic determination
16     if DST is in effect for the requested local time.
17   - Added optional check for overflowing the time_t range.  */
18
19/* Note: This version of mktime is ignorant of the tzfile.
20   When the tm structure passed to mktime represents a local time that
21   is valid both as DST time and as standard time (= time value in the
22   gap when switching from DST back to standard time), the behaviour
23   for `tm_isdst < 0' depends on the current timezone: TZ east of GMT
24   assumes winter time, TZ west of GMT assumes summer time.
25   Although mktime() (resp. mkgmtime()) tries to adjust for invalid values
26   of struct tm members, this may fail for input values that are far away
27   from the valid ranges. The adjustment process does not check for overflows
28   or wrap arounds in the struct tm components.  */
29
30#ifndef OF
31#  ifdef __STDC__
32#    define OF(a) a
33#  else
34#    define OF(a) ()
35#  endif
36#endif
37
38#ifndef ZCONST
39#  define ZCONST const
40#endif
41
42#include <time.h>
43
44time_t mkgmtime OF((struct tm *));
45time_t mktime OF((struct tm *));
46
47/* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT
48   of the local time and date in the exploded time structure `tm',
49   adjust out of range fields in `tm' and set `tm->tm_yday', `tm->tm_wday'.
50   If `tm->tm_isdst < 0' was passed to mktime(), the correct setting of
51   tm_isdst is determined and returned. Otherwise, mktime() assumes this
52   field as valid; its information is used when converting local time
53   to UTC.
54   Return -1 if time in `tm' cannot be represented as time_t value. */
55
56time_t
57mktime(tm)
58     struct tm *tm;
59{
60  struct tm *ltm;               /* Local time. */
61  time_t loctime;               /* The time_t value of local time. */
62  time_t then;                  /* The time to return. */
63  long tzoffset_adj;            /* timezone-adjustment `remainder' */
64  int bailout_cnt;              /* counter of tries for tz correction */
65  int save_isdst;               /* Copy of the tm->isdst input value */
66
67  save_isdst = tm->tm_isdst;
68  loctime = mkgmtime(tm);
69  if (loctime == -1) {
70    tm->tm_isdst = save_isdst;
71    return (time_t)-1;
72  }
73
74  /* Correct for the timezone and any daylight savings time.
75     The correction is verified and repeated when not correct, to
76     take into account the rare case that a change to or from daylight
77     savings time occurs between when it is the time in `tm' locally
78     and when it is that time in Greenwich. After the second correction,
79     the "timezone & daylight" offset should be correct in all cases. To
80     be sure, we allow a third try, but then the loop is stopped. */
81  bailout_cnt = 3;
82  then = loctime;
83  do {
84    ltm = localtime(&then);
85    if (ltm == (struct tm *)NULL ||
86        (tzoffset_adj = loctime - mkgmtime(ltm)) == 0L)
87      break;
88    then += tzoffset_adj;
89  } while (--bailout_cnt > 0);
90
91  if (ltm == (struct tm *)NULL || tzoffset_adj != 0L) {
92    /* Signal failure if timezone adjustment did not converge. */
93    tm->tm_isdst = save_isdst;
94    return (time_t)-1;
95  }
96
97  if (save_isdst >= 0) {
98    if (ltm->tm_isdst  && !save_isdst)
99    {
100      if (then + 3600 < then)
101        then = (time_t)-1;
102      else
103        then += 3600;
104    }
105    else if (!ltm->tm_isdst && save_isdst)
106    {
107      if (then - 3600 > then)
108        then = (time_t)-1;
109      else
110        then -= 3600;
111    }
112    ltm->tm_isdst = save_isdst;
113  }
114
115  if (tm != ltm)  /* `tm' may already point to localtime's internal storage */
116    *tm = *ltm;
117
118  return then;
119}
120
121
122#ifndef NO_TIME_T_MAX
123   /* Provide default values for the upper limit of the time_t range.
124      These are the result of the decomposition into a `struct tm' for
125      the time value 0xFFFFFFFEL ( = (time_t)-2 ).
126      Note: `(time_t)-1' is reserved for "invalid time"!  */
127#  ifndef TM_YEAR_MAX
128#    define TM_YEAR_MAX         2106
129#  endif
130#  ifndef TM_MON_MAX
131#    define TM_MON_MAX          1       /* February */
132#  endif
133#  ifndef TM_MDAY_MAX
134#    define TM_MDAY_MAX         7
135#  endif
136#  ifndef TM_HOUR_MAX
137#    define TM_HOUR_MAX         6
138#  endif
139#  ifndef TM_MIN_MAX
140#    define TM_MIN_MAX          28
141#  endif
142#  ifndef TM_SEC_MAX
143#    define TM_SEC_MAX          14
144#  endif
145#endif /* NO_TIME_T_MAX */
146
147/* Adjusts out-of-range values for `tm' field `tm_member'. */
148#define ADJUST_TM(tm_member, tm_carry, modulus) \
149  if ((tm_member) < 0) { \
150    tm_carry -= (1 - ((tm_member)+1) / (modulus)); \
151    tm_member = (modulus-1) + (((tm_member)+1) % (modulus)); \
152  } else if ((tm_member) >= (modulus)) { \
153    tm_carry += (tm_member) / (modulus); \
154    tm_member = (tm_member) % (modulus); \
155  }
156
157/* Nonzero if `y' is a leap year, else zero. */
158#define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
159
160/* Number of leap years from 1970 to `y' (not including `y' itself). */
161#define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
162
163/* Additional leapday in February of leap years. */
164#define leapday(m, y) ((m) == 1 && leap (y))
165
166/* Length of month `m' (0 .. 11) */
167#define monthlen(m, y) (ydays[(m)+1] - ydays[m] + leapday (m, y))
168
169/* Accumulated number of days from 01-Jan up to start of current month. */
170static ZCONST short ydays[] =
171{
172  0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
173};
174
175/* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT
176   of the Greenwich Mean time and date in the exploded time structure `tm'.
177   This function does always put back normalized values into the `tm' struct,
178   parameter, including the calculated numbers for `tm->tm_yday',
179   `tm->tm_wday', and `tm->tm_isdst'.
180   Returns -1 if the time in the `tm' parameter cannot be represented
181   as valid `time_t' number. */
182
183time_t
184mkgmtime(tm)
185     struct tm *tm;
186{
187  int years, months, days, hours, minutes, seconds;
188
189  years = tm->tm_year + 1900;   /* year - 1900 -> year */
190  months = tm->tm_mon;          /* 0..11 */
191  days = tm->tm_mday - 1;       /* 1..31 -> 0..30 */
192  hours = tm->tm_hour;          /* 0..23 */
193  minutes = tm->tm_min;         /* 0..59 */
194  seconds = tm->tm_sec;         /* 0..61 in ANSI C. */
195
196  ADJUST_TM(seconds, minutes, 60)
197  ADJUST_TM(minutes, hours, 60)
198  ADJUST_TM(hours, days, 24)
199  ADJUST_TM(months, years, 12)
200  if (days < 0)
201    do {
202      if (--months < 0) {
203        --years;
204        months = 11;
205      }
206      days += monthlen(months, years);
207    } while (days < 0);
208  else
209    while (days >= monthlen(months, years)) {
210      days -= monthlen(months, years);
211      if (++months >= 12) {
212        ++years;
213        months = 0;
214      }
215    }
216
217  /* Restore adjusted values in tm structure */
218  tm->tm_year = years - 1900;
219  tm->tm_mon = months;
220  tm->tm_mday = days + 1;
221  tm->tm_hour = hours;
222  tm->tm_min = minutes;
223  tm->tm_sec = seconds;
224
225  /* Set `days' to the number of days into the year. */
226  days += ydays[months] + (months > 1 && leap (years));
227  tm->tm_yday = days;
228
229  /* Now calculate `days' to the number of days since Jan 1, 1970. */
230  days = (unsigned)days + 365 * (unsigned)(years - 1970) +
231         (unsigned)(nleap (years));
232  tm->tm_wday = ((unsigned)days + 4) % 7; /* Jan 1, 1970 was Thursday. */
233  tm->tm_isdst = 0;
234
235  if (years < 1970)
236    return (time_t)-1;
237
238#if (defined(TM_YEAR_MAX) && defined(TM_MON_MAX) && defined(TM_MDAY_MAX))
239#if (defined(TM_HOUR_MAX) && defined(TM_MIN_MAX) && defined(TM_SEC_MAX))
240  if (years > TM_YEAR_MAX ||
241      (years == TM_YEAR_MAX &&
242       (tm->tm_yday > ydays[TM_MON_MAX] + (TM_MDAY_MAX - 1) +
243                      (TM_MON_MAX > 1 && leap (TM_YEAR_MAX)) ||
244        (tm->tm_yday == ydays[TM_MON_MAX] + (TM_MDAY_MAX - 1) +
245                        (TM_MON_MAX > 1 && leap (TM_YEAR_MAX)) &&
246         (hours > TM_HOUR_MAX ||
247          (hours == TM_HOUR_MAX &&
248           (minutes > TM_MIN_MAX ||
249            (minutes == TM_MIN_MAX && seconds > TM_SEC_MAX) )))))))
250    return (time_t)-1;
251#endif
252#endif
253
254  return (time_t)(86400L * (unsigned long)(unsigned)days +
255                  3600L * (unsigned long)hours +
256                  (unsigned long)(60 * minutes + seconds));
257}
258