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