+ ** "value" is the Epoch-relative time of 00:00:00 GMT on the day in
+ ** question. To get the Epoch-relative time of the specified local
+ ** time on that day, add the transition time and the current offset
+ ** from GMT.
+ */
+ return value + rulep->r_time + offset;
+}
+
+/*
+** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** appropriate.
+*/
+
+static int
+tzparse(name, sp, lastditch)
+const char * name;
+register struct state * const sp;
+const int lastditch;
+{
+ const char * stdname;
+ const char * dstname;
+ int stdlen;
+ int dstlen;
+ long stdoffset;
+ long dstoffset;
+ register time_t * atp;
+ register unsigned char * typep;
+ register char * cp;
+ register int load_result;
+
+ stdname = name;
+ if (lastditch) {
+ stdlen = strlen(name); /* length of standard zone name */
+ name += stdlen;
+ if (stdlen >= sizeof sp->chars)
+ stdlen = (sizeof sp->chars) - 1;
+ } else {
+ name = getzname(name);
+ stdlen = name - stdname;
+ if (stdlen < 3)
+ return -1;
+ }
+ if (*name == '\0')
+ return -1;
+ else {
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return -1;
+ }
+ load_result = tzload(TZDEFRULES, sp);
+ if (load_result != 0)
+ sp->leapcnt = 0; /* so, we're off a little */
+ if (*name != '\0') {
+ dstname = name;
+ name = getzname(name);
+ dstlen = name - dstname; /* length of DST zone name */
+ if (dstlen < 3)
+ return -1;
+ if (*name != '\0' && *name != ',' && *name != ';') {
+ name = getoffset(name, &dstoffset);
+ if (name == NULL)
+ return -1;
+ } else dstoffset = stdoffset - SECSPERHOUR;
+ if (*name == ',' || *name == ';') {
+ struct rule start;
+ struct rule end;
+ register int year;
+ register time_t janfirst;
+ time_t starttime;
+ time_t endtime;
+
+ ++name;
+ if ((name = getrule(name, &start)) == NULL)
+ return -1;
+ if (*name++ != ',')
+ return -1;
+ if ((name = getrule(name, &end)) == NULL)
+ return -1;
+ if (*name != '\0')
+ return -1;
+ sp->typecnt = 2; /* standard time and DST */
+ /*
+ ** Two transitions per year, from EPOCH_YEAR to 2037.
+ */
+ sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
+ if (sp->timecnt > TZ_MAX_TIMES)
+ return -1;
+ sp->ttis[0].tt_gmtoff = -dstoffset;
+ sp->ttis[0].tt_isdst = 1;
+ sp->ttis[0].tt_abbrind = stdlen + 1;
+ sp->ttis[1].tt_gmtoff = -stdoffset;
+ sp->ttis[1].tt_isdst = 0;
+ sp->ttis[1].tt_abbrind = 0;
+ atp = sp->ats;
+ typep = sp->types;
+ janfirst = 0;
+ for (year = EPOCH_YEAR; year <= 2037; ++year) {
+ starttime = transtime(janfirst, year, &start,
+ stdoffset);
+ endtime = transtime(janfirst, year, &end,
+ dstoffset);
+ if (starttime > endtime) {
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ } else {
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ }
+ janfirst +=
+ year_lengths[isleap(year)] * SECSPERDAY;
+ }
+ } else {
+ int sawstd;
+ int sawdst;
+ long stdfix;
+ long dstfix;
+ long oldfix;
+ int isdst;
+ register int i;
+
+ if (*name != '\0')
+ return -1;
+ if (load_result != 0)
+ return -1;
+ /*
+ ** Compute the difference between the real and
+ ** prototype standard and summer time offsets
+ ** from GMT, and put the real standard and summer
+ ** time offsets into the rules in place of the
+ ** prototype offsets.
+ */
+ sawstd = FALSE;
+ sawdst = FALSE;
+ stdfix = 0;
+ dstfix = 0;
+ for (i = 0; i < sp->typecnt; ++i) {
+ if (sp->ttis[i].tt_isdst) {
+ oldfix = dstfix;
+ dstfix =
+ sp->ttis[i].tt_gmtoff + dstoffset;
+ if (sawdst && (oldfix != dstfix))
+ return -1;
+ sp->ttis[i].tt_gmtoff = -dstoffset;
+ sp->ttis[i].tt_abbrind = stdlen + 1;
+ sawdst = TRUE;
+ } else {
+ oldfix = stdfix;
+ stdfix =
+ sp->ttis[i].tt_gmtoff + stdoffset;
+ if (sawstd && (oldfix != stdfix))
+ return -1;
+ sp->ttis[i].tt_gmtoff = -stdoffset;
+ sp->ttis[i].tt_abbrind = 0;
+ sawstd = TRUE;
+ }
+ }
+ /*
+ ** Make sure we have both standard and summer time.
+ */
+ if (!sawdst || !sawstd)
+ return -1;
+ /*
+ ** Now correct the transition times by shifting
+ ** them by the difference between the real and
+ ** prototype offsets. Note that this difference
+ ** can be different in standard and summer time;
+ ** the prototype probably has a 1-hour difference
+ ** between standard and summer time, but a different
+ ** difference can be specified in TZ.
+ */
+ isdst = FALSE; /* we start in standard time */
+ for (i = 0; i < sp->timecnt; ++i) {
+ register const struct ttinfo * ttisp;
+
+ /*
+ ** If summer time is in effect, and the
+ ** transition time was not specified as
+ ** standard time, add the summer time
+ ** offset to the transition time;
+ ** otherwise, add the standard time offset
+ ** to the transition time.
+ */
+ ttisp = &sp->ttis[sp->types[i]];
+ sp->ats[i] +=
+ (isdst && !ttisp->tt_ttisstd) ?
+ dstfix : stdfix;
+ isdst = ttisp->tt_isdst;
+ }
+ }
+ } else {
+ dstlen = 0;
+ sp->typecnt = 1; /* only standard time */
+ sp->timecnt = 0;
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = 0;
+ sp->ttis[0].tt_abbrind = 0;
+ }
+ sp->charcnt = stdlen + 1;
+ if (dstlen != 0)
+ sp->charcnt += dstlen + 1;
+ if (sp->charcnt > sizeof sp->chars)
+ return -1;
+ cp = sp->chars;
+ (void) strncpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0) {
+ (void) strncpy(cp, dstname, dstlen);
+ *(cp + dstlen) = '\0';
+ }
+ return 0;
+}
+
+static void
+gmtload(sp)
+struct state * const sp;
+{
+ if (tzload(GMT, sp) != 0)
+ (void) tzparse(GMT, sp, TRUE);
+}
+
+void
+tzset()
+{
+ register const char * name;
+ void tzsetwall();
+
+ name = getenv("TZ");
+ if (name == NULL) {
+ tzsetwall();
+ return;
+ }
+ lcl_is_set = TRUE;
+#ifdef ALL_STATE
+ if (lclptr == NULL) {
+ lclptr = (struct state *) malloc(sizeof *lclptr);
+ if (lclptr == NULL) {
+ settzname(); /* all we can do */
+ return;
+ }
+ }
+#endif /* defined ALL_STATE */
+ if (*name == '\0') {
+ /*
+ ** User wants it fast rather than right.
+ */
+ lclptr->leapcnt = 0; /* so, we're off a little */
+ lclptr->timecnt = 0;
+ lclptr->ttis[0].tt_gmtoff = 0;
+ lclptr->ttis[0].tt_abbrind = 0;
+ (void) strcpy(lclptr->chars, GMT);
+ } else if (tzload(name, lclptr) != 0)
+ if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
+ (void) gmtload(lclptr);
+ settzname();
+}
+
+void
+tzsetwall()
+{
+ lcl_is_set = TRUE;
+#ifdef ALL_STATE
+ if (lclptr == NULL) {
+ lclptr = (struct state *) malloc(sizeof *lclptr);
+ if (lclptr == NULL) {
+ settzname(); /* all we can do */
+ return;
+ }
+ }
+#endif /* defined ALL_STATE */
+ if (tzload((char *) NULL, lclptr) != 0)
+ gmtload(lclptr);
+ settzname();
+}
+
+/*
+** The easy way to behave "as if no library function calls" localtime
+** is to not call it--so we drop its guts into "localsub", which can be
+** freely called. (And no, the PANS doesn't require the above behavior--
+** but it *is* desirable.)
+**
+** The unused offset argument is for the benefit of mktime variants.
+*/