Home | History | Annotate | Download | only in Time
      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 (at) 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
     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
    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 *
    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 *
    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 *
    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 *
    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 *
    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
    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
    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
    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
    764 gmtload(struct state * const sp)
    765 {
    766   if (tzload(gmt, sp) != 0)
    767     (void) tzparse(gmt, sp, TRUE);
    768 }
    769 
    770 static 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
    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