1/*
2  timezone.c - Zip 3
3
4  Copyright (c) 1990-2004 Info-ZIP.  All rights reserved.
5
6  See the accompanying file LICENSE, version 2003-May-08 or later
7  (the contents of which are also included in zip.h) for terms of use.
8  If, for some reason, all these files are missing, the Info-ZIP license
9  also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
10*/
11/* Replacement time library functions, based on platform independent public
12 * domain timezone code from ftp://elsie.nci.nih.gov/pub, with mktime and
13 * mkgmtime from our own mktime.c in Zip.
14 *
15 * Contains:  tzset()
16 *            __tzset()
17 *            gmtime()
18 *            localtime()
19 *            mktime()
20 *            mkgmtime()
21 *            GetPlatformLocalTimezone()  [different versions]
22 */
23
24/* HISTORY/CHANGES
25 * 17 Jun 00, Paul Kienitz, added the PD-based tzset(), localtime(), and so on
26 *            to amiga/filedate.c, replacing GNU-based functions which had
27 *            replaced time_lib.c, both having been rejected for licensing
28 *            reasons.  Support for timezone files and leap seconds was removed.
29 *
30 * 23 Aug 00, Paul Kienitz, split into separate timezone.c file, made platform
31 *            independent, copied in mktime() and mkgmtime() from Zip, renamed
32 *            locale_TZ as GetPlatformLocalTimezone(), for use as a generic
33 *            hook by other platforms.
34 */
35
36#ifndef __timezone_c
37#define __timezone_c
38
39
40#include "zip.h"
41#include "timezone.h"
42#include <ctype.h>
43#include <errno.h>
44
45#ifdef IZTZ_DEFINESTDGLOBALS
46long timezone = 0;
47int daylight = 0;
48char *tzname[2];
49#endif
50
51#ifndef IZTZ_GETLOCALETZINFO
52#  define IZTZ_GETLOCALETZINFO(ptzstruct, pgenrulefunct) (FALSE)
53#endif
54
55int real_timezone_is_set = FALSE;       /* set by tzset() */
56
57
58#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
59#define TZDEFAULT       "EST5EDT"
60
61#define SECSPERMIN      60
62#define MINSPERHOUR     60
63#define HOURSPERDAY     24
64#define DAYSPERWEEK     7
65#define DAYSPERNYEAR    365
66#define DAYSPERLYEAR    366
67#define SECSPERHOUR     (SECSPERMIN * MINSPERHOUR)
68#define SECSPERDAY      ((long) SECSPERHOUR * HOURSPERDAY)
69#define MONSPERYEAR 12
70
71#define EPOCH_WDAY      4     /* Jan 1, 1970 was thursday */
72#define EPOCH_YEAR      1970
73#define TM_YEAR_BASE    1900
74#define FIRST_GOOD_YEAR ((time_t) -1 < (time_t) 1 ? EPOCH_YEAR-68 : EPOCH_YEAR)
75#define LAST_GOOD_YEAR  (EPOCH_YEAR + ((time_t) -1 < (time_t) 1 ? 67 : 135))
76
77#define YDAYS(month, year) yr_days[leap(year)][month]
78
79/* Nonzero if `y' is a leap year, else zero. */
80#define leap(y)  (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
81
82/* Number of leap years from EPOCH_YEAR  to `y' (not including `y' itself). */
83#define _P4      ((EPOCH_YEAR / 4) * 4 + 1)
84#define _P100    ((EPOCH_YEAR / 100) * 100 + 1)
85#define _P400    ((EPOCH_YEAR / 400) * 400 + 1)
86#define nleap(y) (((y) - _P4) / 4 - ((y) - _P100) / 100 + ((y) - _P400) / 400)
87
88/* Length of month `m' (0 .. 11) */
89#define monthlen(m, y) (yr_days[0][(m)+1] - yr_days[0][m] + \
90                        ((m) == 1 && leap(y)))
91
92/* internal module-level constants */
93#ifndef IZ_MKTIME_ONLY
94static ZCONST char  gmt[] = "GMT";
95static ZCONST int    mon_lengths[2][MONSPERYEAR] = {
96    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
97    { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
98};
99#endif /* !IZ_MKTIME_ONLY */
100static ZCONST int    yr_days[2][MONSPERYEAR+1] = {
101    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
102    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
103};
104#ifndef IZ_MKTIME_ONLY
105static ZCONST int   year_lengths[2] = {
106    DAYSPERNYEAR, DAYSPERLYEAR
107};
108
109/* internal variables */
110static struct state statism;
111
112
113/* prototypes of static functions */
114static time_t transtime OF((ZCONST time_t janfirst, ZCONST int year,
115                            ZCONST struct rule * ZCONST rulep,
116                            ZCONST long offset));
117static void generate_transitions OF((register struct state * ZCONST sp,
118                                     ZCONST struct rule * ZCONST start,
119                                     ZCONST struct rule * ZCONST end));
120static ZCONST char *getzname OF((ZCONST char *strp));
121static ZCONST char *getnum OF((ZCONST char *strp, int * ZCONST nump,
122                               ZCONST int min, ZCONST int max));
123static ZCONST char *getsecs OF((ZCONST char *strp, long * ZCONST secsp));
124static ZCONST char *getoffset OF((ZCONST char *strp, long * ZCONST offsetp));
125static ZCONST char *getrule OF((ZCONST char *strp, struct rule * ZCONST rulep));
126static int Parse_TZ OF((ZCONST char *name, register struct state * ZCONST sp));
127
128
129static time_t transtime(janfirst, year, rulep, offset)
130     ZCONST time_t janfirst;
131     ZCONST int year;
132     ZCONST struct rule * ZCONST rulep;
133     ZCONST long offset;
134{
135    register int    leapyear;
136    register time_t value;
137    register int    i;
138    int             d, m1, yy0, yy1, yy2, dow;
139
140    value = 0;
141    leapyear = leap(year);
142    switch (rulep->r_type) {
143
144    case JULIAN_DAY:
145        /*
146        ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
147        ** years.
148        ** In non-leap years, or if the day number is 59 or less, just
149        ** add SECSPERDAY times the day number-1 to the time of
150        ** January 1, midnight, to get the day.
151        */
152        value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
153        if (leapyear && rulep->r_day >= 60)
154            value += SECSPERDAY;
155        break;
156
157    case DAY_OF_YEAR:
158        /*
159        ** n - day of year.
160        ** Just add SECSPERDAY times the day number to the time of
161        ** January 1, midnight, to get the day.
162        */
163        value = janfirst + rulep->r_day * SECSPERDAY;
164        break;
165
166    case MONTH_NTH_DAY_OF_WEEK:
167        /*
168        ** Mm.n.d - nth "dth day" of month m.
169        */
170        value = janfirst;
171/*
172        for (i = 0; i < rulep->r_mon - 1; ++i)
173            value += mon_lengths[leapyear][i] * SECSPERDAY;
174*/
175        value += yr_days[leapyear][rulep->r_mon - 1] * SECSPERDAY;
176
177        /*
178        ** Use Zeller's Congruence to get day-of-week of first day of
179        ** month.
180        */
181        m1 = (rulep->r_mon + 9) % 12 + 1;
182        yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
183        yy1 = yy0 / 100;
184        yy2 = yy0 % 100;
185        dow = ((26 * m1 - 2) / 10 +
186            1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
187        if (dow < 0)
188            dow += DAYSPERWEEK;
189
190        /*
191        ** "dow" is the day-of-week of the first day of the month.  Get
192        ** the day-of-month (zero-origin) of the first "dow" day of the
193        ** month.
194        */
195        d = rulep->r_day - dow;
196        if (d < 0)
197            d += DAYSPERWEEK;
198        for (i = 1; i < rulep->r_week; ++i) {
199            if (d + DAYSPERWEEK >= mon_lengths[leapyear][rulep->r_mon - 1])
200                break;
201            d += DAYSPERWEEK;
202        }
203
204        /*
205        ** "d" is the day-of-month (zero-origin) of the day we want.
206        */
207        value += d * SECSPERDAY;
208        break;
209    }
210
211    /*
212    ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
213    ** question.  To get the Epoch-relative time of the specified local
214    ** time on that day, add the transition time and the current offset
215    ** from UTC.
216    */
217    return value + rulep->r_time + offset;
218}
219
220static void generate_transitions(sp, start, end)
221     register struct state * ZCONST sp;
222     ZCONST struct rule * ZCONST start;
223     ZCONST struct rule * ZCONST end;
224{
225    register int             year;
226    register time_t          janfirst;
227    time_t                   starttime;
228    time_t                   endtime;
229    long                     stdoffset = -sp->ttis[0].tt_gmtoff;
230    long                     dstoffset = -sp->ttis[1].tt_gmtoff;
231    register time_t *        atp;
232    register unsigned char * typep;
233
234    /*
235    ** Two transitions per year, from EPOCH_YEAR to LAST_GOOD_YEAR.
236    */
237    sp->timecnt = 2 * (LAST_GOOD_YEAR - EPOCH_YEAR + 1);
238    atp = sp->ats;
239    typep = sp->types;
240    janfirst = 0;
241    for (year = EPOCH_YEAR; year <= LAST_GOOD_YEAR; ++year) {
242        starttime = transtime(janfirst, year, start, stdoffset);
243        endtime = transtime(janfirst, year, end, dstoffset);
244        if (starttime > endtime) {
245            *atp++ = endtime;
246            *typep++ = 0;   /* DST ends */
247            *atp++ = starttime;
248            *typep++ = 1;   /* DST begins */
249        } else {
250            *atp++ = starttime;
251            *typep++ = 1;   /* DST begins */
252            *atp++ = endtime;
253            *typep++ = 0;   /* DST ends */
254        }
255        janfirst += year_lengths[leap(year)] * SECSPERDAY;
256    }
257}
258
259static ZCONST char *getzname(strp)
260     ZCONST char *strp;
261{
262    register char   c;
263
264    while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' &&
265        c != '+')
266            ++strp;
267    return strp;
268}
269
270static ZCONST char *getnum(strp, nump, min, max)
271     ZCONST char *strp;
272     int * ZCONST nump;
273     ZCONST int min;
274     ZCONST int max;
275{
276    register char   c;
277    register int    num;
278
279    if (strp == NULL || !isdigit(c = *strp))
280        return NULL;
281    num = 0;
282    do {
283        num = num * 10 + (c - '0');
284        if (num > max)
285            return NULL;    /* illegal value */
286        c = *++strp;
287    } while (isdigit(c));
288    if (num < min)
289        return NULL;        /* illegal value */
290    *nump = num;
291    return strp;
292}
293
294static ZCONST char *getsecs(strp, secsp)
295     ZCONST char *strp;
296     long * ZCONST secsp;
297{
298    int num;
299
300    /*
301    ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
302    ** "M10.4.6/26", which does not conform to Posix,
303    ** but which specifies the equivalent of
304    ** ``02:00 on the first Sunday on or after 23 Oct''.
305    */
306    strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
307    if (strp == NULL)
308        return NULL;
309    *secsp = num * (long) SECSPERHOUR;
310    if (*strp == ':') {
311        ++strp;
312        strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
313        if (strp == NULL)
314            return NULL;
315        *secsp += num * SECSPERMIN;
316        if (*strp == ':') {
317            ++strp;
318            /* `SECSPERMIN' allows for leap seconds.  */
319            strp = getnum(strp, &num, 0, SECSPERMIN);
320            if (strp == NULL)
321                return NULL;
322            *secsp += num;
323        }
324    }
325    return strp;
326}
327
328static ZCONST char *getoffset(strp, offsetp)
329     ZCONST char *strp;
330     long * ZCONST offsetp;
331{
332    register int    neg = 0;
333
334    if (*strp == '-') {
335        neg = 1;
336        ++strp;
337    } else if (*strp == '+')
338        ++strp;
339    strp = getsecs(strp, offsetp);
340    if (strp == NULL)
341        return NULL;        /* illegal time */
342    if (neg)
343        *offsetp = -*offsetp;
344    return strp;
345}
346
347static ZCONST char *getrule(strp, rulep)
348     ZCONST char *strp;
349     struct rule * ZCONST rulep;
350{
351    if (*strp == 'J') {
352        /*
353        ** Julian day.
354        */
355        rulep->r_type = JULIAN_DAY;
356        ++strp;
357        strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
358    } else if (*strp == 'M') {
359        /*
360        ** Month, week, day.
361        */
362        rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
363        ++strp;
364        strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
365        if (strp == NULL)
366            return NULL;
367        if (*strp++ != '.')
368            return NULL;
369        strp = getnum(strp, &rulep->r_week, 1, 5);
370        if (strp == NULL)
371            return NULL;
372        if (*strp++ != '.')
373            return NULL;
374        strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
375    } else if (isdigit(*strp)) {
376        /*
377        ** Day of year.
378        */
379        rulep->r_type = DAY_OF_YEAR;
380        strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
381    } else  return NULL;        /* invalid format */
382    if (strp == NULL)
383        return NULL;
384    if (*strp == '/') {
385        /*
386        ** Time specified.
387        */
388        ++strp;
389        strp = getsecs(strp, &rulep->r_time);
390    } else
391        rulep->r_time = 2 * SECSPERHOUR;    /* default = 2:00:00 */
392    return strp;
393}
394
395static int Parse_TZ(name, sp)
396     ZCONST char *name;
397     register struct state * ZCONST sp;
398{
399    ZCONST char *            stdname;
400    ZCONST char *            dstname;
401    size_t                   stdlen;
402    size_t                   dstlen;
403    long                     stdoffset;
404    long                     dstoffset;
405    register char *          cp;
406
407    dstname = NULL;
408    stdname = name;
409    name = getzname(name);
410    stdlen = name - stdname;
411    if (stdlen < 3)
412        return -1;
413    if (*name == '\0')
414        return -1;
415    name = getoffset(name, &stdoffset);
416    if (name == NULL)
417        return -1;
418    if (*name != '\0') {
419        dstname = name;
420        name = getzname(name);
421        dstlen = name - dstname;    /* length of DST zone name */
422        if (dstlen < 3)
423            return -1;
424        if (*name != '\0' && *name != ',' && *name != ';') {
425            name = getoffset(name, &dstoffset);
426            if (name == NULL)
427                return -1;
428        } else
429            dstoffset = stdoffset - SECSPERHOUR;
430        if (*name == '\0')
431            name = TZDEFRULESTRING;
432        if (*name == ',' || *name == ';') {
433            struct rule     start;
434            struct rule     end;
435
436            ++name;
437            if ((name = getrule(name, &start)) == NULL)
438                return -1;
439            if (*name++ != ',')
440                return -1;
441            if ((name = getrule(name, &end)) == NULL)
442                return -1;
443            if (*name != '\0')
444                return -1;
445            sp->typecnt = 2;    /* standard time and DST */
446            sp->ttis[0].tt_gmtoff = -stdoffset;
447            sp->ttis[0].tt_isdst = 0;
448            sp->ttis[0].tt_abbrind = 0;
449            sp->ttis[1].tt_gmtoff = -dstoffset;
450            sp->ttis[1].tt_isdst = 1;
451            sp->ttis[1].tt_abbrind = stdlen + 1;
452            generate_transitions(sp, &start, &end);
453        }
454    } else {
455        dstlen = 0;
456        sp->typecnt = 1;        /* only standard time */
457        sp->timecnt = 0;
458        sp->ttis[0].tt_gmtoff = -stdoffset;
459        sp->ttis[0].tt_isdst = 0;
460        sp->ttis[0].tt_abbrind = 0;
461    }
462    sp->charcnt = stdlen + 1;
463    if (dstlen != 0)
464        sp->charcnt += dstlen + 1;
465    if ((size_t) sp->charcnt > sizeof(sp->chars))
466        return -1;
467    cp = sp->chars;
468    (void) strncpy(cp, stdname, stdlen);
469    cp += stdlen;
470    *cp++ = '\0';
471    if (dstlen != 0) {
472        (void) strncpy(cp, dstname, dstlen);
473        *(cp + dstlen) = '\0';
474    }
475    return 0;
476}
477
478void tzset()
479{
480    char *TZstring;
481    int dstfirst;
482    static char *old_TZstring = NULL;
483
484    TZstring = getenv("TZ");    /* read TZ envvar */
485    if (old_TZstring && TZstring && !strcmp(old_TZstring, TZstring))
486        /* do not repeatedly parse an unchanged TZ specification */
487        return;
488    if ((TZstring && TZstring[0] && Parse_TZ(TZstring, &statism) == 0)
489                || IZTZ_GETLOCALETZINFO(&statism, generate_transitions)
490                || Parse_TZ(gmt, &statism) == 0) {
491        daylight  = statism.typecnt > 1;
492        dstfirst  = daylight && statism.ttis[0].tt_isdst && !statism.ttis[1].tt_isdst;
493        timezone  = -statism.ttis[dstfirst].tt_gmtoff;
494        tzname[0] = statism.chars + statism.ttis[dstfirst].tt_abbrind;
495        tzname[1] = statism.chars + statism.ttis[!dstfirst].tt_abbrind;
496        real_timezone_is_set = TRUE;
497        if (TZstring) {
498            if (old_TZstring)
499                old_TZstring = realloc(old_TZstring, strlen(TZstring) + 1);
500            else
501                old_TZstring = malloc(strlen(TZstring) + 1);
502            if (old_TZstring)
503                strcpy(old_TZstring, TZstring);
504        }
505    } else {
506        timezone = 0;   /* default is GMT0 which means no offsets */
507        daylight = 0;   /* from local system time                 */
508        real_timezone_is_set = FALSE;
509        if (old_TZstring) {
510            free(old_TZstring);
511            old_TZstring = NULL;
512        }
513    }
514#ifdef IZTZ_SETLOCALTZINFO
515    /* Some SAS/C library functions, e.g. stat(), call library       */
516    /* __tzset() themselves. So envvar TZ *must* exist in order to   */
517    /* to get the right offset from GMT.  XXX  TRY HARD to fix this! */
518    set_TZ(timezone, daylight);
519#endif /* IZTZ_SETLOCALTZINFO */
520}
521
522/* XXX  Does this also help SAS/C library work? */
523void __tzset()
524{
525    if (!real_timezone_is_set) tzset();
526}
527
528static struct tm _tmbuf;
529
530struct tm *gmtime(when)
531     ZCONST time_t *when;
532{
533    long days = *when / SECSPERDAY;
534    long secs = *when % SECSPERDAY;
535    int isleap;
536
537    memset(&_tmbuf, 0, sizeof(_tmbuf));   /* get any nonstandard fields */
538    _tmbuf.tm_wday = (days + EPOCH_WDAY) % 7;
539    _tmbuf.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
540    isleap = leap(_tmbuf.tm_year + TM_YEAR_BASE);
541    while (days >= year_lengths[isleap]) {
542        days -= year_lengths[isleap];
543        _tmbuf.tm_year++;
544        isleap = leap(_tmbuf.tm_year + TM_YEAR_BASE);
545    }
546    _tmbuf.tm_mon = 0;
547    _tmbuf.tm_yday = days;
548    while (days >= mon_lengths[isleap][_tmbuf.tm_mon])
549        days -= mon_lengths[isleap][_tmbuf.tm_mon++];
550    _tmbuf.tm_mday = days + 1;
551    _tmbuf.tm_isdst = 0;
552    _tmbuf.tm_sec = secs % SECSPERMIN;
553    _tmbuf.tm_min = (secs / SECSPERMIN) % SECSPERMIN;
554    _tmbuf.tm_hour = secs / SECSPERHOUR;
555    return &_tmbuf;
556}
557
558struct tm *localtime(when)
559     ZCONST time_t *when;
560{
561    time_t     localwhen = *when;
562    int        timetype;
563    struct tm *ret;
564
565    __tzset();
566    if (statism.timecnt == 0 || localwhen < statism.ats[0])
567        timetype = statism.ttis[0].tt_isdst && statism.typecnt > 1 &&
568                   !statism.ttis[1].tt_isdst;
569    else {
570        for (timetype = 1; timetype < statism.timecnt; ++timetype)
571            if (localwhen < statism.ats[timetype])
572                break;
573        timetype = statism.types[timetype - 1];
574    }
575    localwhen += statism.ttis[timetype].tt_gmtoff;
576    ret = gmtime(&localwhen);
577    ret->tm_isdst = statism.ttis[timetype].tt_isdst;
578    return ret;
579}
580
581#ifdef NEED__ISINDST
582int _isindst(tb)
583    struct tm *tb;
584{
585    time_t     localt;          /* time_t equivalent of given tm struct */
586    time_t     univt;           /* assumed UTC value of given time */
587    long       tzoffset_adj;    /* timezone-adjustment `remainder' */
588    int        bailout_cnt;     /* counter of tries for tz correction */
589    int        timetype;
590
591    __tzset();
592
593    /* when DST is unsupported in current timezone, DST is always off */
594    if (statism.typecnt <= 1) return FALSE;
595
596    localt = mkgmtime(tb);
597    if (localt == (time_t)-1)
598        /* specified time is out-of-range, default to FALSE */
599        return FALSE;
600
601    univt = localt - statism.ttis[0].tt_gmtoff;
602    bailout_cnt = 3;
603    do {
604        if (statism.timecnt == 0 || univt < statism.ats[0])
605            timetype = statism.ttis[0].tt_isdst && statism.typecnt > 1 &&
606                       !statism.ttis[1].tt_isdst;
607        else {
608            for (timetype = 1; timetype < statism.timecnt; ++timetype)
609                if (univt < statism.ats[timetype])
610                    break;
611            timetype = statism.types[timetype - 1];
612        }
613        if ((tzoffset_adj = localt - univt - statism.ttis[timetype].tt_gmtoff)
614            == 0L)
615            break;
616        univt += tzoffset_adj;
617    } while (--bailout_cnt > 0);
618
619    /* return TRUE when DST is active at given time */
620    return (statism.ttis[timetype].tt_isdst);
621}
622#endif /* NEED__ISINDST */
623#endif /* !IZ_MKTIME_ONLY */
624
625/* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT
626   of the local time and date in the exploded time structure `tm',
627   adjust out of range fields in `tm' and set `tm->tm_yday', `tm->tm_wday'.
628   If `tm->tm_isdst < 0' was passed to mktime(), the correct setting of
629   tm_isdst is determined and returned. Otherwise, mktime() assumes this
630   field as valid; its information is used when converting local time
631   to UTC.
632   Return -1 if time in `tm' cannot be represented as time_t value. */
633
634time_t mktime(tm)
635     struct tm *tm;
636{
637  struct tm *ltm;               /* Local time. */
638  time_t loctime;               /* The time_t value of local time. */
639  time_t then;                  /* The time to return. */
640  long tzoffset_adj;            /* timezone-adjustment `remainder' */
641  int bailout_cnt;              /* counter of tries for tz correction */
642  int save_isdst;               /* Copy of the tm->isdst input value */
643
644  save_isdst = tm->tm_isdst;
645  loctime = mkgmtime(tm);
646  if (loctime == -1) {
647    tm->tm_isdst = save_isdst;
648    return (time_t)-1;
649  }
650
651  /* Correct for the timezone and any daylight savings time.
652     The correction is verified and repeated when not correct, to
653     take into account the rare case that a change to or from daylight
654     savings time occurs between when it is the time in `tm' locally
655     and when it is that time in Greenwich. After the second correction,
656     the "timezone & daylight" offset should be correct in all cases. To
657     be sure, we allow a third try, but then the loop is stopped. */
658  bailout_cnt = 3;
659  then = loctime;
660  do {
661    ltm = localtime(&then);
662    if (ltm == (struct tm *)NULL ||
663        (tzoffset_adj = loctime - mkgmtime(ltm)) == 0L)
664      break;
665    then += tzoffset_adj;
666  } while (--bailout_cnt > 0);
667
668  if (ltm == (struct tm *)NULL || tzoffset_adj != 0L) {
669    /* Signal failure if timezone adjustment did not converge. */
670    tm->tm_isdst = save_isdst;
671    return (time_t)-1;
672  }
673
674  if (save_isdst >= 0) {
675    if (ltm->tm_isdst  && !save_isdst)
676    {
677      if (then + 3600 < then)
678        then = (time_t)-1;
679      else
680        then += 3600;
681    }
682    else if (!ltm->tm_isdst && save_isdst)
683    {
684      if (then - 3600 > then)
685        then = (time_t)-1;
686      else
687        then -= 3600;
688    }
689    ltm->tm_isdst = save_isdst;
690  }
691
692  if (tm != ltm)  /* `tm' may already point to localtime's internal storage */
693    *tm = *ltm;
694
695  return then;
696}
697
698
699#ifndef NO_TIME_T_MAX
700   /* Provide default values for the upper limit of the time_t range.
701      These are the result of the decomposition into a `struct tm' for
702      the time value 0xFFFFFFFEL ( = (time_t)-2 ).
703      Note: `(time_t)-1' is reserved for "invalid time"!  */
704#  ifndef TM_YEAR_MAX
705#    define TM_YEAR_MAX         2106
706#  endif
707#  ifndef TM_MON_MAX
708#    define TM_MON_MAX          1       /* February */
709#  endif
710#  ifndef TM_MDAY_MAX
711#    define TM_MDAY_MAX         7
712#  endif
713#  ifndef TM_HOUR_MAX
714#    define TM_HOUR_MAX         6
715#  endif
716#  ifndef TM_MIN_MAX
717#    define TM_MIN_MAX          28
718#  endif
719#  ifndef TM_SEC_MAX
720#    define TM_SEC_MAX          14
721#  endif
722#endif /* NO_TIME_T_MAX */
723
724/* Adjusts out-of-range values for `tm' field `tm_member'. */
725#define ADJUST_TM(tm_member, tm_carry, modulus) \
726  if ((tm_member) < 0) { \
727    tm_carry -= (1 - ((tm_member)+1) / (modulus)); \
728    tm_member = (modulus-1) + (((tm_member)+1) % (modulus)); \
729  } else if ((tm_member) >= (modulus)) { \
730    tm_carry += (tm_member) / (modulus); \
731    tm_member = (tm_member) % (modulus); \
732  }
733
734/* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT
735   of the Greenwich Mean time and date in the exploded time structure `tm'.
736   This function does always put back normalized values into the `tm' struct,
737   parameter, including the calculated numbers for `tm->tm_yday',
738   `tm->tm_wday', and `tm->tm_isdst'.
739   Returns -1 if the time in the `tm' parameter cannot be represented
740   as valid `time_t' number. */
741
742time_t mkgmtime(tm)
743     struct tm *tm;
744{
745  int years, months, days, hours, minutes, seconds;
746
747  years = tm->tm_year + TM_YEAR_BASE;   /* year - 1900 -> year */
748  months = tm->tm_mon;                  /* 0..11 */
749  days = tm->tm_mday - 1;               /* 1..31 -> 0..30 */
750  hours = tm->tm_hour;                  /* 0..23 */
751  minutes = tm->tm_min;                 /* 0..59 */
752  seconds = tm->tm_sec;                 /* 0..61 in ANSI C. */
753
754  ADJUST_TM(seconds, minutes, 60)
755  ADJUST_TM(minutes, hours, 60)
756  ADJUST_TM(hours, days, 24)
757  ADJUST_TM(months, years, 12)
758  if (days < 0)
759    do {
760      if (--months < 0) {
761        --years;
762        months = 11;
763      }
764      days += monthlen(months, years);
765    } while (days < 0);
766  else
767    while (days >= monthlen(months, years)) {
768      days -= monthlen(months, years);
769      if (++months >= 12) {
770        ++years;
771        months = 0;
772      }
773    }
774
775  /* Restore adjusted values in tm structure */
776  tm->tm_year = years - TM_YEAR_BASE;
777  tm->tm_mon = months;
778  tm->tm_mday = days + 1;
779  tm->tm_hour = hours;
780  tm->tm_min = minutes;
781  tm->tm_sec = seconds;
782
783  /* Set `days' to the number of days into the year. */
784  days += YDAYS(months, years);
785  tm->tm_yday = days;
786
787  /* Now calculate `days' to the number of days since Jan 1, 1970. */
788  days = (unsigned)days + 365 * (unsigned)(years - EPOCH_YEAR) +
789         (unsigned)(nleap (years));
790  tm->tm_wday = ((unsigned)days + EPOCH_WDAY) % 7;
791  tm->tm_isdst = 0;
792
793  if (years < EPOCH_YEAR)
794    return (time_t)-1;
795
796#if (defined(TM_YEAR_MAX) && defined(TM_MON_MAX) && defined(TM_MDAY_MAX))
797#if (defined(TM_HOUR_MAX) && defined(TM_MIN_MAX) && defined(TM_SEC_MAX))
798  if (years > TM_YEAR_MAX ||
799      (years == TM_YEAR_MAX &&
800       (tm->tm_yday > (YDAYS(TM_MON_MAX, TM_YEAR_MAX) + (TM_MDAY_MAX - 1)) ||
801        (tm->tm_yday == (YDAYS(TM_MON_MAX, TM_YEAR_MAX) + (TM_MDAY_MAX - 1)) &&
802         (hours > TM_HOUR_MAX ||
803          (hours == TM_HOUR_MAX &&
804           (minutes > TM_MIN_MAX ||
805            (minutes == TM_MIN_MAX && seconds > TM_SEC_MAX) )))))))
806    return (time_t)-1;
807#endif
808#endif
809
810  return (time_t)(SECSPERDAY * (unsigned long)(unsigned)days +
811                  SECSPERHOUR * (unsigned long)hours +
812                  (unsigned long)(SECSPERMIN * minutes + seconds));
813}
814
815#endif /* __timezone_c */
816