1 /** @file
2     Time Zone processing.
3 
4   Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
5   This program and the accompanying materials are licensed and made available under
6   the terms and conditions of the BSD License that accompanies this distribution.
7   The full text of the license may be found at
8   http://opensource.org/licenses/bsd-license.php.
9 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13   Portions derived from the NIH time zone package file, localtime.c,
14   which contains the following notice:
15 
16     This file is in the public domain, so clarified as of
17     1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
18 
19   NetBSD: localtime.c,v 1.39 2006/03/22 14:01:30 christos Exp
20 **/
21 #include  <LibConfig.h>
22 
23 #include  <ctype.h>
24 #include  <fcntl.h>
25 #include  <stdio.h>
26 #include  <stdlib.h>
27 #include  <string.h>
28 #include  <time.h>
29 #include  <unistd.h>
30 #include  "tzfile.h"
31 #include  "TimeVals.h"
32 
33 #ifndef WILDABBR
34 /*
35 ** Someone might make incorrect use of a time zone abbreviation:
36 **  1.  They might reference tzname[0] before calling tzset (explicitly
37 **    or implicitly).
38 **  2.  They might reference tzname[1] before calling tzset (explicitly
39 **    or implicitly).
40 **  3.  They might reference tzname[1] after setting to a time zone
41 **    in which Daylight Saving Time is never observed.
42 **  4.  They might reference tzname[0] after setting to a time zone
43 **    in which Standard Time is never observed.
44 **  5.  They might reference tm.TM_ZONE after calling offtime.
45 ** What's best to do in the above cases is open to debate;
46 ** for now, we just set things up so that in any of the five cases
47 ** WILDABBR is used.  Another possibility:  initialize tzname[0] to the
48 ** string "tzname[0] used before set", and similarly for the other cases.
49 ** And another:  initialize tzname[0] to "ERA", with an explanation in the
50 ** manual page of what this "time zone abbreviation" means (doing this so
51 ** that tzname[0] has the "normal" length of three characters).
52 */
53 #define WILDABBR  "   "
54 #endif /* !defined WILDABBR */
55 
56 const char wildabbr[9]  = "WILDABBR";
57 const char gmt[4]       = "GMT";
58 
59 struct state * lclptr = NULL;
60 struct state * gmtptr = NULL;
61 
62 #ifndef TZ_STRLEN_MAX
63 #define TZ_STRLEN_MAX 255
64 #endif /* !defined TZ_STRLEN_MAX */
65 
66 static char   lcl_TZname[TZ_STRLEN_MAX + 1];
67 static int    lcl_is_set = 0;
68 //static int    gmt_is_set = 0;
69 
70 char *   tzname[2] = {
71   (char *)__UNCONST(wildabbr),
72   (char *)__UNCONST(wildabbr)
73 };
74 
75 long int    timezone = 0;
76 int         daylight = 0;
77 
78 #ifndef NO_ZONEINFO_FILES
79 /** Get first 4 characters of codep as a 32-bit integer.
80 
81     The first character of codep becomes the MSB of the resultant integer.
82 **/
83 static INT32
detzcode(const char * const codep)84 detzcode(const char * const codep)
85 {
86   register INT32 result;
87 
88   /*
89   ** The first character must be sign extended on systems with >32bit
90   ** longs.  This was solved differently in the master tzcode sources
91   ** (the fix first appeared in tzcode95c.tar.gz).  But I believe
92   ** that this implementation is superior.
93   */
94 #define SIGN_EXTEND_CHAR(x) ((signed char) x)
95 
96   result = (SIGN_EXTEND_CHAR(codep[0]) << 24) \
97     | (codep[1] & 0xff) << 16 \
98     | (codep[2] & 0xff) << 8
99     | (codep[3] & 0xff);
100   return result;
101 }
102 #endif  /* NO_ZONEINFO_FILES */
103 
104 static void
settzname(void)105 settzname (void)
106 {
107   register struct state * const sp = lclptr;
108   register int      i;
109 
110   tzname[0] = (char *)__UNCONST(wildabbr);
111   tzname[1] = (char *)__UNCONST(wildabbr);
112   daylight = 0;
113   timezone = 0;
114   if (sp == NULL) {
115     tzname[0] = tzname[1] = (char *)__UNCONST(gmt);
116     return;
117   }
118   for (i = 0; i < sp->typecnt; ++i) {
119     register const struct ttinfo * const  ttisp = &sp->ttis[i];
120 
121     tzname[ttisp->tt_isdst] =
122       &sp->chars[ttisp->tt_abbrind];
123     if (ttisp->tt_isdst)
124       daylight = 1;
125     if (i == 0 || !ttisp->tt_isdst)
126       timezone = -(ttisp->tt_gmtoff);
127   }
128   /*
129   ** And to get the latest zone names into tzname. . .
130   */
131   for (i = 0; i < sp->timecnt; ++i) {
132     register const struct ttinfo * const  ttisp =
133       &sp->ttis[ sp->types[i] ];
134 
135     tzname[ttisp->tt_isdst] =
136       &sp->chars[ttisp->tt_abbrind];
137   }
138 }
139 
140 /*
141 ** Given a pointer into a time zone string, scan until a character that is not
142 ** a valid character in a zone name is found.  Return a pointer to that
143 ** character.
144 */
145 static const char *
getzname(register const char * strp)146 getzname(register const char *strp)
147 {
148   register char c;
149 
150   while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
151          c != '+')
152     ++strp;
153   return strp;
154 }
155 
156 /*
157 ** Given a pointer into a time zone string, extract a number from that string.
158 ** Check that the number is within a specified range; if it is not, return
159 ** NULL.
160 ** Otherwise, return a pointer to the first character not part of the number.
161 */
162 static const char *
getnum(register const char * strp,int * const nump,const int min,const int max)163 getnum(
164   register const char  *strp,
165   int           * const nump,
166   const int             min,
167   const int             max
168   )
169 {
170   register char c;
171   register int  num;
172 
173   if (strp == NULL || !is_digit(c = *strp))
174     return NULL;
175   num = 0;
176   do {
177     num = num * 10 + (c - '0');
178     if (num > max)
179       return NULL;  /* illegal value */
180     c = *++strp;
181   } while (is_digit(c));
182   if (num < min)
183     return NULL;    /* illegal value */
184   *nump = num;
185   return strp;
186 }
187 
188 /*
189 ** Given a pointer into a time zone string, extract a number of seconds,
190 ** in hh[:mm[:ss]] form, from the string.
191 ** If any error occurs, return NULL.
192 ** Otherwise, return a pointer to the first character not part of the number
193 ** of seconds.
194 */
195 static const char *
getsecs(register const char * strp,LONG32 * const secsp)196 getsecs(
197   register const char  *strp,
198   LONG32          * const secsp
199   )
200 {
201   int num;
202 
203   /*
204   ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
205   ** "M10.4.6/26", which does not conform to Posix,
206   ** but which specifies the equivalent of
207   ** ``02:00 on the first Sunday on or after 23 Oct''.
208   */
209   strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
210   if (strp == NULL)
211     return NULL;
212   *secsp = (long)(num * SECSPERHOUR);
213   if (*strp == ':') {
214     ++strp;
215     strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
216     if (strp == NULL)
217       return NULL;
218     *secsp += num * SECSPERMIN;
219     if (*strp == ':') {
220       ++strp;
221       /* `SECSPERMIN' allows for leap seconds.  */
222       strp = getnum(strp, &num, 0, SECSPERMIN);
223       if (strp == NULL)
224         return NULL;
225       *secsp += num;
226     }
227   }
228   return strp;
229 }
230 
231 /*
232 ** Given a pointer into a time zone string, extract an offset, in
233 ** [+-]hh[:mm[:ss]] form, from the string.
234 ** If any error occurs, return NULL.
235 ** Otherwise, return a pointer to the first character not part of the time.
236 */
237 static const char *
getoffset(register const char * strp,LONG32 * const offsetp)238 getoffset(
239   register const char  *strp,
240   LONG32          * const offsetp
241   )
242 {
243   register int  neg = 0;
244 
245   if (*strp == '-') {
246     neg = 1;
247     ++strp;
248   } else if (*strp == '+')
249     ++strp;
250   strp = getsecs(strp, offsetp);
251   if (strp == NULL)
252     return NULL;    /* illegal time */
253   if (neg)
254     *offsetp = -*offsetp;
255   return strp;
256 }
257 
258 /*
259 ** Given a pointer into a time zone string, extract a rule in the form
260 ** date[/time].  See POSIX section 8 for the format of "date" and "time".
261 ** If a valid rule is not found, return NULL.
262 ** Otherwise, return a pointer to the first character not part of the rule.
263 */
264 static const char *
getrule(const char * strp,register struct rule * const rulep)265 getrule(
266   const char *strp,
267   register struct rule * const rulep
268   )
269 {
270   if (*strp == 'J') {
271     /*
272     ** Julian day.
273     */
274     rulep->r_type = JULIAN_DAY;
275     ++strp;
276     strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
277   } else if (*strp == 'M') {
278     /*
279     ** Month, week, day.
280     */
281     rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
282     ++strp;
283     strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
284     if (strp == NULL)
285       return NULL;
286     if (*strp++ != '.')
287       return NULL;
288     strp = getnum(strp, &rulep->r_week, 1, 5);
289     if (strp == NULL)
290       return NULL;
291     if (*strp++ != '.')
292       return NULL;
293     strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
294   } else if (is_digit(*strp)) {
295     /*
296     ** Day of year.
297     */
298     rulep->r_type = DAY_OF_YEAR;
299     strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
300   } else  return NULL;    /* invalid format */
301   if (strp == NULL)
302     return NULL;
303   if (*strp == '/') {
304     /*
305     ** Time specified.
306     */
307     ++strp;
308     strp = getsecs(strp, &rulep->r_time);
309   } else  rulep->r_time = 2 * SECSPERHOUR;  /* default = 2:00:00 */
310   return strp;
311 }
312 
313 static int
tzload(register const char * name,register struct state * const sp)314 tzload(register const char *name, register struct state * const sp)
315 {
316 #ifndef NO_ZONEINFO_FILES
317   register const char * p;
318   register int    i;
319   register int    fid;
320 
321   if (name == NULL && (name = TZDEFAULT) == NULL)
322     return -1;
323 
324   {
325     register int  doaccess;
326     /*
327     ** Section 4.9.1 of the C standard says that
328     ** "FILENAME_MAX expands to an integral constant expression
329     ** that is the size needed for an array of char large enough
330     ** to hold the longest file name string that the implementation
331     ** guarantees can be opened."
332     */
333     char    fullname[FILENAME_MAX + 1];
334 
335     if (name[0] == ':')
336       ++name;
337     doaccess = name[0] == '/';
338     if (!doaccess) {
339       if ((p = TZDIR) == NULL)
340         return -1;
341       if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
342         return -1;
343       (void) strcpy(fullname, p); /* XXX strcpy is safe */
344       (void) strcat(fullname, "/"); /* XXX strcat is safe */
345       (void) strcat(fullname, name);  /* XXX strcat is safe */
346       /*
347       ** Set doaccess if '.' (as in "../") shows up in name.
348       */
349       if (strchr(name, '.') != NULL)
350         doaccess = TRUE;
351       name = fullname;
352     }
353     if (doaccess && access(name, R_OK) != 0)
354       return -1;
355     /*
356     * XXX potential security problem here if user of a set-id
357     * program has set TZ (which is passed in as name) here,
358     * and uses a race condition trick to defeat the access(2)
359     * above.
360     */
361     if ((fid = open(name, OPEN_MODE)) == -1)
362       return -1;
363   }
364   {
365     struct tzhead * tzhp;
366     union {
367       struct tzhead tzhead;
368       char    buf[sizeof *sp + sizeof *tzhp];
369     } u;
370     int   ttisstdcnt;
371     int   ttisgmtcnt;
372 
373     i = read(fid, u.buf, sizeof u.buf);
374     if (close(fid) != 0)
375       return -1;
376     ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
377     ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
378     sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
379     sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
380     sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
381     sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
382     p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
383     if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
384         sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
385         sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
386         sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
387         (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
388         (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
389       return -1;
390     if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */
391         sp->timecnt +     /* types */
392         sp->typecnt * (4 + 2) +   /* ttinfos */
393         sp->charcnt +     /* chars */
394         sp->leapcnt * (4 + 4) +   /* lsinfos */
395         ttisstdcnt +      /* ttisstds */
396         ttisgmtcnt)     /* ttisgmts */
397       return -1;
398     for (i = 0; i < sp->timecnt; ++i) {
399       sp->ats[i] = detzcode(p);
400       p += 4;
401     }
402     for (i = 0; i < sp->timecnt; ++i) {
403       sp->types[i] = (unsigned char) *p++;
404       if (sp->types[i] >= sp->typecnt)
405         return -1;
406     }
407     for (i = 0; i < sp->typecnt; ++i) {
408       register struct ttinfo *  ttisp;
409 
410       ttisp = &sp->ttis[i];
411       ttisp->tt_gmtoff = detzcode(p);
412       p += 4;
413       ttisp->tt_isdst = (unsigned char) *p++;
414       if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
415         return -1;
416       ttisp->tt_abbrind = (unsigned char) *p++;
417       if (ttisp->tt_abbrind < 0 ||
418           ttisp->tt_abbrind > sp->charcnt)
419         return -1;
420     }
421     for (i = 0; i < sp->charcnt; ++i)
422       sp->chars[i] = *p++;
423     sp->chars[i] = '\0';  /* ensure '\0' at end */
424     for (i = 0; i < sp->leapcnt; ++i) {
425       register struct lsinfo *  lsisp;
426 
427       lsisp = &sp->lsis[i];
428       lsisp->ls_trans = detzcode(p);
429       p += 4;
430       lsisp->ls_corr = detzcode(p);
431       p += 4;
432     }
433     for (i = 0; i < sp->typecnt; ++i) {
434       register struct ttinfo *  ttisp;
435 
436       ttisp = &sp->ttis[i];
437       if (ttisstdcnt == 0)
438         ttisp->tt_ttisstd = FALSE;
439       else {
440         ttisp->tt_ttisstd = *p++;
441         if (ttisp->tt_ttisstd != TRUE &&
442             ttisp->tt_ttisstd != FALSE)
443           return -1;
444       }
445     }
446     for (i = 0; i < sp->typecnt; ++i) {
447       register struct ttinfo *  ttisp;
448 
449       ttisp = &sp->ttis[i];
450       if (ttisgmtcnt == 0)
451         ttisp->tt_ttisgmt = FALSE;
452       else {
453         ttisp->tt_ttisgmt = *p++;
454         if (ttisp->tt_ttisgmt != TRUE &&
455             ttisp->tt_ttisgmt != FALSE)
456           return -1;
457       }
458     }
459   }
460   return 0;
461 #else   /* ! NO_ZONEINFO_FILES */
462   return -1;
463 #endif
464 }
465 
466 /*
467 ** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
468 ** year, a rule, and the offset from UTC at the time that rule takes effect,
469 ** calculate the Epoch-relative time that rule takes effect.
470 */
471 static
472 time_t
transtime(const time_t janfirst,const int year,const struct rule * const rulep,const LONG32 offset)473 transtime(
474   const time_t              janfirst,
475   const int                 year,
476   const struct rule * const rulep,
477   const LONG32                offset
478   )
479 {
480   register int  leapyear;
481   register time_t value;
482   register int  i;
483   int   d, m1, yy0, yy1, yy2, dow;
484 
485   INITIALIZE(value);
486   leapyear = isleap(year);
487   switch (rulep->r_type) {
488 
489     case JULIAN_DAY:
490       /*
491     ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
492     ** years.
493     ** In non-leap years, or if the day number is 59 or less, just
494     ** add SECSPERDAY times the day number-1 to the time of
495     ** January 1, midnight, to get the day.
496     */
497       value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
498       if (leapyear && rulep->r_day >= 60)
499         value += SECSPERDAY;
500       break;
501 
502     case DAY_OF_YEAR:
503       /*
504     ** n - day of year.
505     ** Just add SECSPERDAY times the day number to the time of
506     ** January 1, midnight, to get the day.
507     */
508       value = janfirst + rulep->r_day * SECSPERDAY;
509       break;
510 
511     case MONTH_NTH_DAY_OF_WEEK:
512       /*
513     ** Mm.n.d - nth "dth day" of month m.
514     */
515       value = janfirst;
516       for (i = 0; i < rulep->r_mon - 1; ++i)
517         value += mon_lengths[leapyear][i] * SECSPERDAY;
518 
519       /*
520     ** Use Zeller's Congruence to get day-of-week of first day of
521     ** month.
522     */
523       m1 = (rulep->r_mon + 9) % 12 + 1;
524       yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
525       yy1 = yy0 / 100;
526       yy2 = yy0 % 100;
527       dow = ((26 * m1 - 2) / 10 +
528              1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
529       if (dow < 0)
530         dow += DAYSPERWEEK;
531 
532       /*
533     ** "dow" is the day-of-week of the first day of the month.  Get
534     ** the day-of-month (zero-origin) of the first "dow" day of the
535     ** month.
536     */
537       d = rulep->r_day - dow;
538       if (d < 0)
539         d += DAYSPERWEEK;
540       for (i = 1; i < rulep->r_week; ++i) {
541         if (d + DAYSPERWEEK >=
542             mon_lengths[leapyear][rulep->r_mon - 1])
543           break;
544         d += DAYSPERWEEK;
545       }
546 
547       /*
548     ** "d" is the day-of-month (zero-origin) of the day we want.
549     */
550       value += d * SECSPERDAY;
551       break;
552   }
553 
554   /*
555   ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
556   ** question.  To get the Epoch-relative time of the specified local
557   ** time on that day, add the transition time and the current offset
558   ** from UTC.
559   */
560   return value + rulep->r_time + offset;
561 }
562 
563 /*
564 ** Given a POSIX section 8-style TZ string, fill in the rule tables as
565 ** appropriate.
566 */
567 static int
tzparse(const char * name,struct state * const sp,const int lastditch)568 tzparse(
569   const char    *       name,
570   struct state  * const sp,
571   const int             lastditch
572   )
573 {
574   const char     *stdname;
575   const char     *dstname;
576   size_t          stdlen;
577   size_t          dstlen;
578   LONG32            stdoffset;
579   LONG32            dstoffset;
580   time_t         *atp;
581   unsigned char  *typep;
582   char           *cp;
583   int             load_result;
584 
585   dstname = NULL;
586   stdname = name;
587   if (lastditch) {
588     stdlen = strlen(name);  /* length of standard zone name */
589     name += stdlen;
590     if (stdlen >= sizeof sp->chars)
591       stdlen = (sizeof sp->chars) - 1;
592     stdoffset = 0;
593   } else {
594     name = getzname(name);
595     stdlen = name - stdname;
596     if (stdlen < 3)
597       return -1;
598     if (*name == '\0')
599       return -1;
600     name = getoffset(name, &stdoffset);
601     if (name == NULL)
602       return -1;
603   }
604   load_result = tzload(TZDEFRULES, sp);
605   if (load_result != 0)
606     sp->leapcnt = 0;    /* so, we're off a little */
607   if (*name != '\0') {
608     dstname = name;
609     name = getzname(name);
610     dstlen = name - dstname;  /* length of DST zone name */
611     if (dstlen < 3)
612       return -1;
613     if (*name != '\0' && *name != ',' && *name != ';') {
614       name = getoffset(name, &dstoffset);
615       if (name == NULL)
616         return -1;
617     } else  dstoffset = stdoffset - SECSPERHOUR;
618     if (*name == '\0' && load_result != 0)
619       name = TZDEFRULESTRING;
620     if (*name == ',' || *name == ';') {
621       struct rule start;
622       struct rule end;
623       register int  year;
624       register time_t janfirst;
625       time_t    starttime;
626       time_t    endtime;
627 
628       ++name;
629       if ((name = getrule(name, &start)) == NULL)
630         return -1;
631       if (*name++ != ',')
632         return -1;
633       if ((name = getrule(name, &end)) == NULL)
634         return -1;
635       if (*name != '\0')
636         return -1;
637       sp->typecnt = 2;  /* standard time and DST */
638       /*
639       ** Two transitions per year, from EPOCH_YEAR to 2037.
640       */
641       sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
642       if (sp->timecnt > TZ_MAX_TIMES)
643         return -1;
644       sp->ttis[0].tt_gmtoff = -dstoffset;
645       sp->ttis[0].tt_isdst = 1;
646       sp->ttis[0].tt_abbrind = (int)stdlen + 1;
647       sp->ttis[1].tt_gmtoff = -stdoffset;
648       sp->ttis[1].tt_isdst = 0;
649       sp->ttis[1].tt_abbrind = 0;
650       atp = sp->ats;
651       typep = sp->types;
652       janfirst = 0;
653       for (year = EPOCH_YEAR; year <= 2037; ++year) {
654         starttime = transtime(janfirst, year, &start,
655                               stdoffset);
656         endtime = transtime(janfirst, year, &end,
657                             dstoffset);
658         if (starttime > endtime) {
659           *atp++ = endtime;
660           *typep++ = 1; /* DST ends */
661           *atp++ = starttime;
662           *typep++ = 0; /* DST begins */
663         } else {
664           *atp++ = starttime;
665           *typep++ = 0; /* DST begins */
666           *atp++ = endtime;
667           *typep++ = 1; /* DST ends */
668         }
669         janfirst += year_lengths[isleap(year)] *
670           SECSPERDAY;
671       }
672     } else {
673       register LONG32 theirstdoffset;
674       register LONG32 theiroffset;
675       register int  i;
676       register int  j;
677 
678       if (*name != '\0')
679         return -1;
680       /*
681       ** Initial values of theirstdoffset
682       */
683       theirstdoffset = 0;
684       for (i = 0; i < sp->timecnt; ++i) {
685         j = sp->types[i];
686         if (!sp->ttis[j].tt_isdst) {
687           theirstdoffset =
688             -sp->ttis[j].tt_gmtoff;
689           break;
690         }
691       }
692       /*
693       ** Initially we're assumed to be in standard time.
694       */
695       theiroffset = theirstdoffset;
696       /*
697       ** Now juggle transition times and types
698       ** tracking offsets as you do.
699       */
700       for (i = 0; i < sp->timecnt; ++i) {
701         j = sp->types[i];
702         sp->types[i] = (unsigned char)sp->ttis[j].tt_isdst;
703         if (sp->ttis[j].tt_ttisgmt) {
704           /* No adjustment to transition time */
705         } else {
706           /*
707           ** If summer time is in effect, and the
708           ** transition time was not specified as
709           ** standard time, add the summer time
710           ** offset to the transition time;
711           ** otherwise, add the standard time
712           ** offset to the transition time.
713           */
714           /*
715           ** Transitions from DST to DDST
716           ** will effectively disappear since
717           ** POSIX provides for only one DST
718           ** offset.
719           */
720           sp->ats[i] += stdoffset -
721             theirstdoffset;
722         }
723         theiroffset = -sp->ttis[j].tt_gmtoff;
724         if (!sp->ttis[j].tt_isdst)
725           theirstdoffset = theiroffset;
726       }
727       /*
728       ** Finally, fill in ttis.
729       ** ttisstd and ttisgmt need not be handled.
730       */
731       sp->ttis[0].tt_gmtoff = -stdoffset;
732       sp->ttis[0].tt_isdst = FALSE;
733       sp->ttis[0].tt_abbrind = 0;
734       sp->ttis[1].tt_gmtoff = -dstoffset;
735       sp->ttis[1].tt_isdst = TRUE;
736       sp->ttis[1].tt_abbrind = (int)stdlen + 1;
737       sp->typecnt = 2;
738     }
739   } else {
740     dstlen = 0;
741     sp->typecnt = 1;    /* only standard time */
742     sp->timecnt = 0;
743     sp->ttis[0].tt_gmtoff = -stdoffset;
744     sp->ttis[0].tt_isdst = 0;
745     sp->ttis[0].tt_abbrind = 0;
746   }
747   sp->charcnt = (int)stdlen + 1;
748   if (dstlen != 0)
749     sp->charcnt += (int)dstlen + 1;
750   if ((size_t) sp->charcnt > sizeof sp->chars)
751     return -1;
752   cp = sp->chars;
753   (void) strncpy(cp, stdname, stdlen);
754   cp += stdlen;
755   *cp++ = '\0';
756   if (dstlen != 0) {
757     (void) strncpy(cp, dstname, dstlen);
758     *(cp + dstlen) = '\0';
759   }
760   return 0;
761 }
762 
763 void
gmtload(struct state * const sp)764 gmtload(struct state * const sp)
765 {
766   if (tzload(gmt, sp) != 0)
767     (void) tzparse(gmt, sp, TRUE);
768 }
769 
770 static void
tzsetwall(void)771 tzsetwall(void)
772 {
773   if (lcl_is_set < 0)
774     return;
775   lcl_is_set = -1;
776 
777   if (lclptr == NULL) {
778     lclptr = (struct state *) malloc(sizeof *lclptr);
779     if (lclptr == NULL) {
780       settzname();  /* all we can do */
781       return;
782     }
783   }
784   if (tzload((char *) NULL, lclptr) != 0)
785     gmtload(lclptr);
786   settzname();
787 }
788 
789 void
tzset(void)790 tzset(void)
791 {
792   register const char * name;
793 
794   name = getenv("TZ");
795   if (name == NULL) {
796     tzsetwall();
797     return;
798   }
799 
800   if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0)
801     return;
802   lcl_is_set = strlen(name) < sizeof lcl_TZname;
803   if (lcl_is_set)
804     (void)strncpyX(lcl_TZname, name, sizeof(lcl_TZname));
805 
806   if (lclptr == NULL) {
807     lclptr = (struct state *) malloc(sizeof *lclptr);
808     if (lclptr == NULL) {
809       settzname();  /* all we can do */
810       return;
811     }
812   }
813   if (*name == '\0') {
814     /*
815     ** User wants it fast rather than right.
816     */
817     lclptr->leapcnt = 0;    /* so, we're off a little */
818     lclptr->timecnt = 0;
819     lclptr->typecnt = 0;
820     lclptr->ttis[0].tt_isdst = 0;
821     lclptr->ttis[0].tt_gmtoff = 0;
822     lclptr->ttis[0].tt_abbrind = 0;
823     (void)strncpyX(lclptr->chars, gmt, sizeof(lclptr->chars));
824   } else if (tzload(name, lclptr) != 0)
825     if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
826     (void) gmtload(lclptr);
827   settzname();
828 }
829