/** @file
Time Zone processing.
Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials are licensed and made available under
the terms and conditions of the BSD License that accompanies this distribution.
The full text of the license may be found at
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Portions derived from the NIH time zone package file, localtime.c,
which contains the following notice:
This file is in the public domain, so clarified as of
1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
NetBSD: localtime.c,v 1.39 2006/03/22 14:01:30 christos Exp
**/
#include <LibConfig.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "tzfile.h"
#include "TimeVals.h"
#ifndef WILDABBR
/*
** Someone might make incorrect use of a time zone abbreviation:
** 1. They might reference tzname[0] before calling tzset (explicitly
** or implicitly).
** 2. They might reference tzname[1] before calling tzset (explicitly
** or implicitly).
** 3. They might reference tzname[1] after setting to a time zone
** in which Daylight Saving Time is never observed.
** 4. They might reference tzname[0] after setting to a time zone
** in which Standard Time is never observed.
** 5. They might reference tm.TM_ZONE after calling offtime.
** What's best to do in the above cases is open to debate;
** for now, we just set things up so that in any of the five cases
** WILDABBR is used. Another possibility: initialize tzname[0] to the
** string "tzname[0] used before set", and similarly for the other cases.
** And another: initialize tzname[0] to "ERA", with an explanation in the
** manual page of what this "time zone abbreviation" means (doing this so
** that tzname[0] has the "normal" length of three characters).
*/
#endif /* !defined WILDABBR */
#ifndef TZ_STRLEN_MAX
#endif /* !defined TZ_STRLEN_MAX */
static int lcl_is_set = 0;
//static int gmt_is_set = 0;
};
long int timezone = 0;
int daylight = 0;
#ifndef NO_ZONEINFO_FILES
/** Get first 4 characters of codep as a 32-bit integer.
The first character of codep becomes the MSB of the resultant integer.
**/
static INT32
{
/*
** The first character must be sign extended on systems with >32bit
** longs. This was solved differently in the master tzcode sources
** (the fix first appeared in tzcode95c.tar.gz). But I believe
** that this implementation is superior.
*/
#define SIGN_EXTEND_CHAR(x) ((signed char) x)
return result;
}
#endif /* NO_ZONEINFO_FILES */
static void
settzname (void)
{
register int i;
daylight = 0;
timezone = 0;
return;
}
daylight = 1;
}
/*
** And to get the latest zone names into tzname. . .
*/
}
}
/*
** Given a pointer into a time zone string, scan until a character that is not
** a valid character in a zone name is found. Return a pointer to that
** character.
*/
static const char *
{
register char c;
c != '+')
++strp;
return strp;
}
/*
** Given a pointer into a time zone string, extract a number from that string.
** Check that the number is within a specified range; if it is not, return
** NULL.
** Otherwise, return a pointer to the first character not part of the number.
*/
static const char *
register const char *strp,
int * const nump,
const int min,
const int max
)
{
register char c;
register int num;
return NULL;
num = 0;
do {
return NULL; /* illegal value */
c = *++strp;
} while (is_digit(c));
return NULL; /* illegal value */
return strp;
}
/*
** Given a pointer into a time zone string, extract a number of seconds,
** in hh[:mm[:ss]] form, from the string.
** If any error occurs, return NULL.
** Otherwise, return a pointer to the first character not part of the number
** of seconds.
*/
static const char *
register const char *strp,
)
{
int num;
/*
** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
** "M10.4.6/26", which does not conform to Posix,
** but which specifies the equivalent of
** ``02:00 on the first Sunday on or after 23 Oct''.
*/
return NULL;
if (*strp == ':') {
++strp;
return NULL;
if (*strp == ':') {
++strp;
/* `SECSPERMIN' allows for leap seconds. */
return NULL;
}
}
return strp;
}
/*
** Given a pointer into a time zone string, extract an offset, in
** [+-]hh[:mm[:ss]] form, from the string.
** If any error occurs, return NULL.
** Otherwise, return a pointer to the first character not part of the time.
*/
static const char *
register const char *strp,
)
{
register int neg = 0;
if (*strp == '-') {
neg = 1;
++strp;
} else if (*strp == '+')
++strp;
return NULL; /* illegal time */
if (neg)
return strp;
}
/*
** Given a pointer into a time zone string, extract a rule in the form
** date[/time]. See POSIX section 8 for the format of "date" and "time".
** If a valid rule is not found, return NULL.
** Otherwise, return a pointer to the first character not part of the rule.
*/
static const char *
const char *strp,
)
{
if (*strp == 'J') {
/*
** Julian day.
*/
++strp;
} else if (*strp == 'M') {
/*
** Month, week, day.
*/
++strp;
return NULL;
if (*strp++ != '.')
return NULL;
return NULL;
if (*strp++ != '.')
return NULL;
/*
** Day of year.
*/
} else return NULL; /* invalid format */
return NULL;
if (*strp == '/') {
/*
** Time specified.
*/
++strp;
return strp;
}
static int
{
#ifndef NO_ZONEINFO_FILES
register const char * p;
register int i;
register int fid;
return -1;
{
register int doaccess;
/*
** Section 4.9.1 of the C standard says that
** "FILENAME_MAX expands to an integral constant expression
** that is the size needed for an array of char large enough
** to hold the longest file name string that the implementation
** guarantees can be opened."
*/
if (name[0] == ':')
++name;
if (!doaccess) {
return -1;
return -1;
/*
** Set doaccess if '.' (as in "../") shows up in name.
*/
}
return -1;
/*
* XXX potential security problem here if user of a set-id
* program has set TZ (which is passed in as name) here,
* and uses a race condition trick to defeat the access(2)
* above.
*/
return -1;
}
{
union {
} u;
int ttisstdcnt;
int ttisgmtcnt;
return -1;
return -1;
ttisstdcnt + /* ttisstds */
ttisgmtcnt) /* ttisgmts */
return -1;
p += 4;
}
return -1;
}
p += 4;
return -1;
ttisp->tt_abbrind = (unsigned char) *p++;
if (ttisp->tt_abbrind < 0 ||
return -1;
}
p += 4;
p += 4;
}
if (ttisstdcnt == 0)
else {
ttisp->tt_ttisstd = *p++;
return -1;
}
}
if (ttisgmtcnt == 0)
else {
ttisp->tt_ttisgmt = *p++;
return -1;
}
}
}
return 0;
#else /* ! NO_ZONEINFO_FILES */
return -1;
#endif
}
/*
** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
** year, a rule, and the offset from UTC at the time that rule takes effect,
** calculate the Epoch-relative time that rule takes effect.
*/
static
const int year,
)
{
register int leapyear;
register int i;
case JULIAN_DAY:
/*
** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
** years.
** In non-leap years, or if the day number is 59 or less, just
** add SECSPERDAY times the day number-1 to the time of
** January 1, midnight, to get the day.
*/
value += SECSPERDAY;
break;
case DAY_OF_YEAR:
/*
** n - day of year.
** Just add SECSPERDAY times the day number to the time of
** January 1, midnight, to get the day.
*/
break;
case MONTH_NTH_DAY_OF_WEEK:
/*
** Mm.n.d - nth "dth day" of month m.
*/
/*
** Use Zeller's Congruence to get day-of-week of first day of
** month.
*/
if (dow < 0)
dow += DAYSPERWEEK;
/*
** "dow" is the day-of-week of the first day of the month. Get
** the day-of-month (zero-origin) of the first "dow" day of the
** month.
*/
if (d < 0)
d += DAYSPERWEEK;
if (d + DAYSPERWEEK >=
break;
d += DAYSPERWEEK;
}
/*
** "d" is the day-of-month (zero-origin) of the day we want.
*/
value += d * SECSPERDAY;
break;
}
/*
** "value" is the Epoch-relative time of 00:00:00 UTC 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 UTC.
*/
}
/*
** Given a POSIX section 8-style TZ string, fill in the rule tables as
** appropriate.
*/
static int
const char * name,
const int lastditch
)
{
const char *stdname;
const char *dstname;
unsigned char *typep;
char *cp;
int load_result;
if (lastditch) {
stdoffset = 0;
} else {
if (stdlen < 3)
return -1;
if (*name == '\0')
return -1;
return -1;
}
if (load_result != 0)
if (*name != '\0') {
if (dstlen < 3)
return -1;
return -1;
register int year;
++name;
return -1;
if (*name++ != ',')
return -1;
return -1;
if (*name != '\0')
return -1;
/*
** Two transitions per year, from EPOCH_YEAR to 2037.
*/
return -1;
janfirst = 0;
*typep++ = 0; /* DST begins */
} else {
*typep++ = 0; /* DST begins */
}
}
} else {
register int i;
register int j;
if (*name != '\0')
return -1;
/*
** Initial values of theirstdoffset
*/
theirstdoffset = 0;
break;
}
}
/*
** Initially we're assumed to be in standard time.
*/
/*
** Now juggle transition times and types
** tracking offsets as you do.
*/
/* No adjustment to transition time */
} else {
/*
** 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.
*/
/*
** Transitions from DST to DDST
** will effectively disappear since
** POSIX provides for only one DST
** offset.
*/
}
}
/*
** Finally, fill in ttis.
** ttisstd and ttisgmt need not be handled.
*/
}
} else {
dstlen = 0;
}
if (dstlen != 0)
return -1;
*cp++ = '\0';
if (dstlen != 0) {
}
return 0;
}
void
{
}
static void
tzsetwall(void)
{
if (lcl_is_set < 0)
return;
lcl_is_set = -1;
settzname(); /* all we can do */
return;
}
}
settzname();
}
void
tzset(void)
{
register const char * name;
tzsetwall();
return;
}
return;
if (lcl_is_set)
settzname(); /* all we can do */
return;
}
}
if (*name == '\0') {
/*
** User wants it fast rather than right.
*/
settzname();
}