xref: /haiku/src/bin/unzip/timezone.c (revision 17049c451a91f427aec94b944b75876b611103e7)
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
44 long timezone = 0;
45 int daylight = 0;
46 char *tzname[2];
47 #endif
48 
49 #ifndef IZTZ_GETLOCALETZINFO
50 #  define IZTZ_GETLOCALETZINFO(ptzstruct, pgenrulefunct) (FALSE)
51 #endif
52 
53 int 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
92 static ZCONST char  gmt[] = "GMT";
93 static 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 */
98 static 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
103 static ZCONST int   year_lengths[2] = {
104     DAYSPERNYEAR, DAYSPERLYEAR
105 };
106 
107 /* internal variables */
108 static struct state statism;
109 
110 
111 /* prototypes of static functions */
112 static time_t transtime OF((ZCONST time_t janfirst, ZCONST int year,
113                             ZCONST struct rule * ZCONST rulep,
114                             ZCONST long offset));
115 static void generate_transitions OF((register struct state * ZCONST sp,
116                                      ZCONST struct rule * ZCONST start,
117                                      ZCONST struct rule * ZCONST end));
118 static ZCONST char *getzname OF((ZCONST char *strp));
119 static ZCONST char *getnum OF((ZCONST char *strp, int * ZCONST nump,
120                                ZCONST int min, ZCONST int max));
121 static ZCONST char *getsecs OF((ZCONST char *strp, long * ZCONST secsp));
122 static ZCONST char *getoffset OF((ZCONST char *strp, long * ZCONST offsetp));
123 static ZCONST char *getrule OF((ZCONST char *strp, struct rule * ZCONST rulep));
124 static int Parse_TZ OF((ZCONST char *name, register struct state * ZCONST sp));
125 
126 
transtime(janfirst,year,rulep,offset)127 static 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 
generate_transitions(sp,start,end)218 static 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 
getzname(strp)257 static 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 
getnum(strp,nump,min,max)268 static 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 
getsecs(strp,secsp)292 static 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 
getoffset(strp,offsetp)326 static 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 
getrule(strp,rulep)345 static 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 
Parse_TZ(name,sp)393 static 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 
tzset()476 void 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? */
__tzset()521 void __tzset()
522 {
523     if (!real_timezone_is_set) tzset();
524 }
525 
526 static struct tm _tmbuf;
527 
gmtime(when)528 struct 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 
localtime(when)556 struct 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
_isindst(tb)580 int _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 
mktime(tm)632 time_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 
mkgmtime(tm)740 time_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