/* FUNCTION <>---set timezone characteristics from <[TZ]> environment variable INDEX tzset SYNOPSIS #include void tzset(void); DESCRIPTION <> examines the <[TZ]> environment variable and sets up the three external variables: <<_timezone>>, <<_daylight>>, and <>. The value of <<_timezone>> shall be the offset from the current time to Universal Time. The value of <<_daylight>> shall be 0 if there is no daylight savings time for the current time zone, otherwise it will be non-zero. The <> array has two entries: the first is the designation of the standard time period, the second is the designation of the alternate time period. The <[TZ]> environment variable is expected to be in the following POSIX format (spaces inserted for clarity): <[std]> <[offset1]> [<[dst]> [<[offset2]>] [,<[start]> [/<[time1]>], <[end]> [/<[time2]>]]] where: <[std]> is the designation for the standard time period (minimum 3, maximum <> bytes) in one of two forms: *- quoted within angle bracket '<' '>' characters: portable numeric sign or alphanumeric characters in the current locale; the quoting characters are not included in the designation *- unquoted: portable alphabetic characters in the current locale <[offset1]> is the value to add to local standard time to get Universal Time; it has the format: [<[S]>]<[hh]>[:<[mm]>[:<[ss]>]] where: <[S]> is an optional numeric sign character; if negative '-', the time zone is East of the International Reference Meridian; else it is positive and West, and '+' may be used <[hh]> is the required numeric hour between 0 and 24 <[mm]> is the optional numeric minute between 0 and 59 <[ss]> is the optional numeric second between 0 and 59 <[dst]> is the designation of the alternate (daylight saving or summer) time period, with the same limits and forms as the standard time period designation <[offset2]> is the value to add to local alternate time to get Universal Time; it has the same format as <[offset1]> <[start]> is the date in the year that alternate time starts; the form may be one of: (quotes "'" around characters below are used only to distinguish literals) <[n]> zero based Julian day (0-365), counting February 29 Leap days 'J'<[n]> one based Julian day (1-365), not counting February 29 Leap days; in all years day 59 is February 28 and day 60 is March 1 'M'<[m]>'.'<[w]>'.'<[d]> month <[m]> (1-12) week <[w]> (1-5) day <[d]> (0-6) where week 1 is the first week in month <[m]> with day <[d]>; week 5 is the last week in the month; day 0 is Sunday <[time1]> is the optional local time that alternate time starts, in the same format as <[offset1]> without any sign, and defaults to 02:00:00 <[end]> is the date in the year that alternate time ends, in the same forms as <[start]> <[time2]> is the optional local time that alternate time ends, in the same format as <[offset1]> without any sign, and defaults to 02:00:00 Note that there is no white-space padding between fields. Also note that if <[TZ]> is null, the default is Universal Time which has no daylight saving time. If <[TZ]> is empty, the default EST5EDT is used. RETURNS There is no return value. PORTABILITY <> is part of the POSIX standard. Supporting OS subroutine required: None */ #define _DEFAULT_SOURCE #include <_ansi.h> #include #include #include #include #include #include "local.h" #define TZNAME_MIN 3 /* POSIX min TZ abbr size local def */ #define TZNAME_MAX 10 /* POSIX max TZ abbr size local def */ static char __tzname_std[TZNAME_MAX + 2]; static char __tzname_dst[TZNAME_MAX + 2]; static char *prev_tzenv = NULL; void _tzset_unlocked (void) { char *tzenv; unsigned short hh, mm, ss, m, w, d; int sign, n; int i, ch; long offset0, offset1; __tzinfo_type *tz = __gettzinfo (); static const struct __tzrule_struct default_tzrule = {'J', 0, 0, 0, 0, (time_t)0, 0L }; if ((tzenv = getenv ("TZ")) == NULL) { _timezone = 0; _daylight = 0; _tzname[0] = "GMT"; _tzname[1] = "GMT"; tz->__tzrule[0] = default_tzrule; tz->__tzrule[1] = default_tzrule; free(prev_tzenv); prev_tzenv = NULL; return; } if (prev_tzenv != NULL && strcmp(tzenv, prev_tzenv) == 0) return; free(prev_tzenv); prev_tzenv = malloc (strlen(tzenv) + 1); if (prev_tzenv != NULL) strcpy (prev_tzenv, tzenv); /* default to unnamed UTC in case of error */ _timezone = 0; _daylight = 0; _tzname[0] = ""; _tzname[1] = ""; tz->__tzrule[0] = default_tzrule; tz->__tzrule[1] = default_tzrule; /* ignore implementation-specific format specifier */ if (*tzenv == ':') ++tzenv; /* allow POSIX angle bracket < > quoted signed alphanumeric tz abbr e.g. */ if (*tzenv == '<') { ++tzenv; /* quit if no items, too few or too many chars, or no close quote '>' */ if (sscanf (tzenv, "%11[-+0-9A-Za-z]%n", __tzname_std, &n) <= 0 || n < TZNAME_MIN || TZNAME_MAX < n || '>' != tzenv[n]) return; ++tzenv; /* bump for close quote '>' */ } else { /* allow POSIX unquoted alphabetic tz abbr e.g. MESZ */ if (sscanf (tzenv, "%11[A-Za-z]%n", __tzname_std, &n) <= 0 || n < TZNAME_MIN || TZNAME_MAX < n) return; } tzenv += n; sign = 1; if (*tzenv == '-') { sign = -1; ++tzenv; } else if (*tzenv == '+') ++tzenv; mm = 0; ss = 0; if (sscanf (tzenv, "%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n) < 1) return; offset0 = sign * (ss + SECSPERMIN * mm + SECSPERHOUR * hh); tzenv += n; /* allow POSIX angle bracket < > quoted signed alphanumeric tz abbr e.g. */ if (*tzenv == '<') { ++tzenv; /* quit if no items, too few or too many chars, or no close quote '>' */ if (sscanf (tzenv, "%11[-+0-9A-Za-z]%n", __tzname_dst, &n) <= 0 && tzenv[0] == '>') { /* No dst */ _tzname[0] = __tzname_std; _tzname[1] = _tzname[0]; tz->__tzrule[0].offset = offset0; _timezone = offset0; return; } else if (n < TZNAME_MIN || TZNAME_MAX < n || '>' != tzenv[n]) { /* error */ return; } ++tzenv; /* bump for close quote '>' */ } else { /* allow POSIX unquoted alphabetic tz abbr e.g. MESZ */ if (sscanf (tzenv, "%11[A-Za-z]%n", __tzname_dst, &n) <= 0) { /* No dst */ _tzname[0] = __tzname_std; _tzname[1] = _tzname[0]; tz->__tzrule[0].offset = offset0; _timezone = offset0; return; } else if (n < TZNAME_MIN || TZNAME_MAX < n) { /* error */ return; } } tzenv += n; /* otherwise we have a dst name, look for the offset */ sign = 1; if (*tzenv == '-') { sign = -1; ++tzenv; } else if (*tzenv == '+') ++tzenv; hh = 0; mm = 0; ss = 0; n = 0; if (sscanf (tzenv, "%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n) <= 0) offset1 = offset0 - 3600; else offset1 = sign * (ss + SECSPERMIN * mm + SECSPERHOUR * hh); tzenv += n; for (i = 0; i < 2; ++i) { if (*tzenv == ',') ++tzenv; if (*tzenv == 'M') { if (sscanf (tzenv, "M%hu%n.%hu%n.%hu%n", &m, &n, &w, &n, &d, &n) != 3 || m < 1 || m > 12 || w < 1 || w > 5 || d > 6) return; tz->__tzrule[i].ch = 'M'; tz->__tzrule[i].m = m; tz->__tzrule[i].n = w; tz->__tzrule[i].d = d; tzenv += n; } else { char *end; if (*tzenv == 'J') { ch = 'J'; ++tzenv; } else ch = 'D'; d = strtoul (tzenv, &end, 10); /* if unspecified, default to US settings */ /* From 1987-2006, US was M4.1.0,M10.5.0, but starting in 2007 is * M3.2.0,M11.1.0 (2nd Sunday March through 1st Sunday November) */ if (end == tzenv) { if (i == 0) { tz->__tzrule[0].ch = 'M'; tz->__tzrule[0].m = 3; tz->__tzrule[0].n = 2; tz->__tzrule[0].d = 0; } else { tz->__tzrule[1].ch = 'M'; tz->__tzrule[1].m = 11; tz->__tzrule[1].n = 1; tz->__tzrule[1].d = 0; } } else { tz->__tzrule[i].ch = ch; tz->__tzrule[i].d = d; } tzenv = end; } /* default time is 02:00:00 am */ hh = 2; mm = 0; ss = 0; n = 0; if (*tzenv == '/') if (sscanf (tzenv, "/%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n) <= 0) { /* error in time format, restore tz rules to default and return */ tz->__tzrule[0] = default_tzrule; tz->__tzrule[1] = default_tzrule; return; } tz->__tzrule[i].s = ss + SECSPERMIN * mm + SECSPERHOUR * hh; tzenv += n; } tz->__tzrule[0].offset = offset0; tz->__tzrule[1].offset = offset1; _tzname[0] = __tzname_std; _tzname[1] = __tzname_dst; __tzcalc_limits (tz->__tzyear); _timezone = tz->__tzrule[0].offset; _daylight = tz->__tzrule[0].offset != tz->__tzrule[1].offset; } void tzset (void) { TZ_LOCK; _tzset_unlocked (); TZ_UNLOCK; }