814 lines
25 KiB
C
814 lines
25 KiB
C
|
/*
|
||
|
Copyright (c) 1990-2001 Info-ZIP. All rights reserved.
|
||
|
|
||
|
See the accompanying file LICENSE, version 2000-Apr-09 or later
|
||
|
(the contents of which are also included in zip.h) for terms of use.
|
||
|
If, for some reason, all these files are missing, the Info-ZIP license
|
||
|
also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
|
||
|
*/
|
||
|
/* Replacement time library functions, based on platform independent public
|
||
|
* domain timezone code from ftp://elsie.nci.nih.gov/pub, with mktime and
|
||
|
* mkgmtime from our own mktime.c in Zip.
|
||
|
*
|
||
|
* Contains: tzset()
|
||
|
* __tzset()
|
||
|
* gmtime()
|
||
|
* localtime()
|
||
|
* mktime()
|
||
|
* mkgmtime()
|
||
|
* GetPlatformLocalTimezone() [different versions]
|
||
|
*/
|
||
|
|
||
|
/* HISTORY/CHANGES
|
||
|
* 17 Jun 00, Paul Kienitz, added the PD-based tzset(), localtime(), and so on
|
||
|
* to amiga/filedate.c, replacing GNU-based functions which had
|
||
|
* replaced time_lib.c, both having been rejected for licensing
|
||
|
* reasons. Support for timezone files and leap seconds was removed.
|
||
|
*
|
||
|
* 23 Aug 00, Paul Kienitz, split into separate timezone.c file, made platform
|
||
|
* independent, copied in mktime() and mkgmtime() from Zip, renamed
|
||
|
* locale_TZ as GetPlatformLocalTimezone(), for use as a generic
|
||
|
* hook by other platforms.
|
||
|
*/
|
||
|
|
||
|
#ifndef __timezone_c
|
||
|
#define __timezone_c
|
||
|
|
||
|
|
||
|
#include "zip.h"
|
||
|
#include "timezone.h"
|
||
|
#include <ctype.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
#ifdef IZTZ_DEFINESTDGLOBALS
|
||
|
long timezone = 0;
|
||
|
int daylight = 0;
|
||
|
char *tzname[2];
|
||
|
#endif
|
||
|
|
||
|
#ifndef IZTZ_GETLOCALETZINFO
|
||
|
# define IZTZ_GETLOCALETZINFO(ptzstruct, pgenrulefunct) (FALSE)
|
||
|
#endif
|
||
|
|
||
|
int real_timezone_is_set = FALSE; /* set by tzset() */
|
||
|
|
||
|
|
||
|
#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
|
||
|
#define TZDEFAULT "EST5EDT"
|
||
|
|
||
|
#define SECSPERMIN 60
|
||
|
#define MINSPERHOUR 60
|
||
|
#define HOURSPERDAY 24
|
||
|
#define DAYSPERWEEK 7
|
||
|
#define DAYSPERNYEAR 365
|
||
|
#define DAYSPERLYEAR 366
|
||
|
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
|
||
|
#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
|
||
|
#define MONSPERYEAR 12
|
||
|
|
||
|
#define EPOCH_WDAY 4 /* Jan 1, 1970 was thursday */
|
||
|
#define EPOCH_YEAR 1970
|
||
|
#define TM_YEAR_BASE 1900
|
||
|
#define FIRST_GOOD_YEAR ((time_t) -1 < (time_t) 1 ? EPOCH_YEAR-68 : EPOCH_YEAR)
|
||
|
#define LAST_GOOD_YEAR (EPOCH_YEAR + ((time_t) -1 < (time_t) 1 ? 67 : 135))
|
||
|
|
||
|
#define YDAYS(month, year) yr_days[leap(year)][month]
|
||
|
|
||
|
/* Nonzero if `y' is a leap year, else zero. */
|
||
|
#define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
|
||
|
|
||
|
/* Number of leap years from EPOCH_YEAR to `y' (not including `y' itself). */
|
||
|
#define _P4 ((EPOCH_YEAR / 4) * 4 + 1)
|
||
|
#define _P100 ((EPOCH_YEAR / 100) * 100 + 1)
|
||
|
#define _P400 ((EPOCH_YEAR / 400) * 400 + 1)
|
||
|
#define nleap(y) (((y) - _P4) / 4 - ((y) - _P100) / 100 + ((y) - _P400) / 400)
|
||
|
|
||
|
/* Length of month `m' (0 .. 11) */
|
||
|
#define monthlen(m, y) (yr_days[0][(m)+1] - yr_days[0][m] + \
|
||
|
((m) == 1 && leap(y)))
|
||
|
|
||
|
/* internal module-level constants */
|
||
|
#ifndef IZ_MKTIME_ONLY
|
||
|
static ZCONST char gmt[] = "GMT";
|
||
|
static ZCONST int mon_lengths[2][MONSPERYEAR] = {
|
||
|
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
|
||
|
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
||
|
};
|
||
|
#endif /* !IZ_MKTIME_ONLY */
|
||
|
static ZCONST int yr_days[2][MONSPERYEAR+1] = {
|
||
|
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
|
||
|
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
|
||
|
};
|
||
|
#ifndef IZ_MKTIME_ONLY
|
||
|
static ZCONST int year_lengths[2] = {
|
||
|
DAYSPERNYEAR, DAYSPERLYEAR
|
||
|
};
|
||
|
|
||
|
/* internal variables */
|
||
|
static struct state statism;
|
||
|
|
||
|
|
||
|
/* prototypes of static functions */
|
||
|
static time_t transtime OF((ZCONST time_t janfirst, ZCONST int year,
|
||
|
ZCONST struct rule * ZCONST rulep,
|
||
|
ZCONST long offset));
|
||
|
static void generate_transitions OF((register struct state * ZCONST sp,
|
||
|
ZCONST struct rule * ZCONST start,
|
||
|
ZCONST struct rule * ZCONST end));
|
||
|
static ZCONST char *getzname OF((ZCONST char *strp));
|
||
|
static ZCONST char *getnum OF((ZCONST char *strp, int * ZCONST nump,
|
||
|
ZCONST int min, ZCONST int max));
|
||
|
static ZCONST char *getsecs OF((ZCONST char *strp, long * ZCONST secsp));
|
||
|
static ZCONST char *getoffset OF((ZCONST char *strp, long * ZCONST offsetp));
|
||
|
static ZCONST char *getrule OF((ZCONST char *strp, struct rule * ZCONST rulep));
|
||
|
static int Parse_TZ OF((ZCONST char *name, register struct state * ZCONST sp));
|
||
|
|
||
|
|
||
|
static time_t transtime(janfirst, year, rulep, offset)
|
||
|
ZCONST time_t janfirst;
|
||
|
ZCONST int year;
|
||
|
ZCONST struct rule * ZCONST rulep;
|
||
|
ZCONST long offset;
|
||
|
{
|
||
|
register int leapyear;
|
||
|
register time_t value;
|
||
|
register int i;
|
||
|
int d, m1, yy0, yy1, yy2, dow;
|
||
|
|
||
|
value = 0;
|
||
|
leapyear = leap(year);
|
||
|
switch (rulep->r_type) {
|
||
|
|
||
|
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 = janfirst + (rulep->r_day - 1) * SECSPERDAY;
|
||
|
if (leapyear && rulep->r_day >= 60)
|
||
|
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.
|
||
|
*/
|
||
|
value = janfirst + rulep->r_day * SECSPERDAY;
|
||
|
break;
|
||
|
|
||
|
case MONTH_NTH_DAY_OF_WEEK:
|
||
|
/*
|
||
|
** Mm.n.d - nth "dth day" of month m.
|
||
|
*/
|
||
|
value = janfirst;
|
||
|
/*
|
||
|
for (i = 0; i < rulep->r_mon - 1; ++i)
|
||
|
value += mon_lengths[leapyear][i] * SECSPERDAY;
|
||
|
*/
|
||
|
value += yr_days[leapyear][rulep->r_mon - 1] * SECSPERDAY;
|
||
|
|
||
|
/*
|
||
|
** Use Zeller's Congruence to get day-of-week of first day of
|
||
|
** month.
|
||
|
*/
|
||
|
m1 = (rulep->r_mon + 9) % 12 + 1;
|
||
|
yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
|
||
|
yy1 = yy0 / 100;
|
||
|
yy2 = yy0 % 100;
|
||
|
dow = ((26 * m1 - 2) / 10 +
|
||
|
1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
|
||
|
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.
|
||
|
*/
|
||
|
d = rulep->r_day - dow;
|
||
|
if (d < 0)
|
||
|
d += DAYSPERWEEK;
|
||
|
for (i = 1; i < rulep->r_week; ++i) {
|
||
|
if (d + DAYSPERWEEK >= mon_lengths[leapyear][rulep->r_mon - 1])
|
||
|
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.
|
||
|
*/
|
||
|
return value + rulep->r_time + offset;
|
||
|
}
|
||
|
|
||
|
static void generate_transitions(sp, start, end)
|
||
|
register struct state * ZCONST sp;
|
||
|
ZCONST struct rule * ZCONST start;
|
||
|
ZCONST struct rule * ZCONST end;
|
||
|
{
|
||
|
register int year;
|
||
|
register time_t janfirst;
|
||
|
time_t starttime;
|
||
|
time_t endtime;
|
||
|
long stdoffset = -sp->ttis[0].tt_gmtoff;
|
||
|
long dstoffset = -sp->ttis[1].tt_gmtoff;
|
||
|
register time_t * atp;
|
||
|
register unsigned char * typep;
|
||
|
|
||
|
/*
|
||
|
** Two transitions per year, from EPOCH_YEAR to LAST_GOOD_YEAR.
|
||
|
*/
|
||
|
sp->timecnt = 2 * (LAST_GOOD_YEAR - EPOCH_YEAR + 1);
|
||
|
atp = sp->ats;
|
||
|
typep = sp->types;
|
||
|
janfirst = 0;
|
||
|
for (year = EPOCH_YEAR; year <= LAST_GOOD_YEAR; ++year) {
|
||
|
starttime = transtime(janfirst, year, start, stdoffset);
|
||
|
endtime = transtime(janfirst, year, end, dstoffset);
|
||
|
if (starttime > endtime) {
|
||
|
*atp++ = endtime;
|
||
|
*typep++ = 0; /* DST ends */
|
||
|
*atp++ = starttime;
|
||
|
*typep++ = 1; /* DST begins */
|
||
|
} else {
|
||
|
*atp++ = starttime;
|
||
|
*typep++ = 1; /* DST begins */
|
||
|
*atp++ = endtime;
|
||
|
*typep++ = 0; /* DST ends */
|
||
|
}
|
||
|
janfirst += year_lengths[leap(year)] * SECSPERDAY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static ZCONST char *getzname(strp)
|
||
|
ZCONST char *strp;
|
||
|
{
|
||
|
register char c;
|
||
|
|
||
|
while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' &&
|
||
|
c != '+')
|
||
|
++strp;
|
||
|
return strp;
|
||
|
}
|
||
|
|
||
|
static ZCONST char *getnum(strp, nump, min, max)
|
||
|
ZCONST char *strp;
|
||
|
int * ZCONST nump;
|
||
|
ZCONST int min;
|
||
|
ZCONST int max;
|
||
|
{
|
||
|
register char c;
|
||
|
register int num;
|
||
|
|
||
|
if (strp == NULL || !isdigit(c = *strp))
|
||
|
return NULL;
|
||
|
num = 0;
|
||
|
do {
|
||
|
num = num * 10 + (c - '0');
|
||
|
if (num > max)
|
||
|
return NULL; /* illegal value */
|
||
|
c = *++strp;
|
||
|
} while (isdigit(c));
|
||
|
if (num < min)
|
||
|
return NULL; /* illegal value */
|
||
|
*nump = num;
|
||
|
return strp;
|
||
|
}
|
||
|
|
||
|
static ZCONST char *getsecs(strp, secsp)
|
||
|
ZCONST char *strp;
|
||
|
long * ZCONST secsp;
|
||
|
{
|
||
|
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''.
|
||
|
*/
|
||
|
strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
|
||
|
if (strp == NULL)
|
||
|
return NULL;
|
||
|
*secsp = num * (long) SECSPERHOUR;
|
||
|
if (*strp == ':') {
|
||
|
++strp;
|
||
|
strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
|
||
|
if (strp == NULL)
|
||
|
return NULL;
|
||
|
*secsp += num * SECSPERMIN;
|
||
|
if (*strp == ':') {
|
||
|
++strp;
|
||
|
/* `SECSPERMIN' allows for leap seconds. */
|
||
|
strp = getnum(strp, &num, 0, SECSPERMIN);
|
||
|
if (strp == NULL)
|
||
|
return NULL;
|
||
|
*secsp += num;
|
||
|
}
|
||
|
}
|
||
|
return strp;
|
||
|
}
|
||
|
|
||
|
static ZCONST char *getoffset(strp, offsetp)
|
||
|
ZCONST char *strp;
|
||
|
long * ZCONST offsetp;
|
||
|
{
|
||
|
register int neg = 0;
|
||
|
|
||
|
if (*strp == '-') {
|
||
|
neg = 1;
|
||
|
++strp;
|
||
|
} else if (*strp == '+')
|
||
|
++strp;
|
||
|
strp = getsecs(strp, offsetp);
|
||
|
if (strp == NULL)
|
||
|
return NULL; /* illegal time */
|
||
|
if (neg)
|
||
|
*offsetp = -*offsetp;
|
||
|
return strp;
|
||
|
}
|
||
|
|
||
|
static ZCONST char *getrule(strp, rulep)
|
||
|
ZCONST char *strp;
|
||
|
struct rule * ZCONST rulep;
|
||
|
{
|
||
|
if (*strp == 'J') {
|
||
|
/*
|
||
|
** Julian day.
|
||
|
*/
|
||
|
rulep->r_type = JULIAN_DAY;
|
||
|
++strp;
|
||
|
strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
|
||
|
} else if (*strp == 'M') {
|
||
|
/*
|
||
|
** Month, week, day.
|
||
|
*/
|
||
|
rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
|
||
|
++strp;
|
||
|
strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
|
||
|
if (strp == NULL)
|
||
|
return NULL;
|
||
|
if (*strp++ != '.')
|
||
|
return NULL;
|
||
|
strp = getnum(strp, &rulep->r_week, 1, 5);
|
||
|
if (strp == NULL)
|
||
|
return NULL;
|
||
|
if (*strp++ != '.')
|
||
|
return NULL;
|
||
|
strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
|
||
|
} else if (isdigit(*strp)) {
|
||
|
/*
|
||
|
** Day of year.
|
||
|
*/
|
||
|
rulep->r_type = DAY_OF_YEAR;
|
||
|
strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
|
||
|
} else return NULL; /* invalid format */
|
||
|
if (strp == NULL)
|
||
|
return NULL;
|
||
|
if (*strp == '/') {
|
||
|
/*
|
||
|
** Time specified.
|
||
|
*/
|
||
|
++strp;
|
||
|
strp = getsecs(strp, &rulep->r_time);
|
||
|
} else
|
||
|
rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
|
||
|
return strp;
|
||
|
}
|
||
|
|
||
|
static int Parse_TZ(name, sp)
|
||
|
ZCONST char *name;
|
||
|
register struct state * ZCONST sp;
|
||
|
{
|
||
|
ZCONST char * stdname;
|
||
|
ZCONST char * dstname;
|
||
|
size_t stdlen;
|
||
|
size_t dstlen;
|
||
|
long stdoffset;
|
||
|
long dstoffset;
|
||
|
register char * cp;
|
||
|
|
||
|
dstname = NULL;
|
||
|
stdname = name;
|
||
|
name = getzname(name);
|
||
|
stdlen = name - stdname;
|
||
|
if (stdlen < 3)
|
||
|
return -1;
|
||
|
if (*name == '\0')
|
||
|
return -1;
|
||
|
name = getoffset(name, &stdoffset);
|
||
|
if (name == NULL)
|
||
|
return -1;
|
||
|
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 == '\0')
|
||
|
name = TZDEFRULESTRING;
|
||
|
if (*name == ',' || *name == ';') {
|
||
|
struct rule start;
|
||
|
struct rule end;
|
||
|
|
||
|
++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 */
|
||
|
sp->ttis[0].tt_gmtoff = -stdoffset;
|
||
|
sp->ttis[0].tt_isdst = 0;
|
||
|
sp->ttis[0].tt_abbrind = 0;
|
||
|
sp->ttis[1].tt_gmtoff = -dstoffset;
|
||
|
sp->ttis[1].tt_isdst = 1;
|
||
|
sp->ttis[1].tt_abbrind = stdlen + 1;
|
||
|
generate_transitions(sp, &start, &end);
|
||
|
}
|
||
|
} 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 ((size_t) 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;
|
||
|
}
|
||
|
|
||
|
void tzset()
|
||
|
{
|
||
|
char *TZstring;
|
||
|
int dstfirst;
|
||
|
static char *old_TZstring = NULL;
|
||
|
|
||
|
TZstring = getenv("TZ"); /* read TZ envvar */
|
||
|
if (old_TZstring && TZstring && !strcmp(old_TZstring, TZstring))
|
||
|
/* do not repeatedly parse an unchanged TZ specification */
|
||
|
return;
|
||
|
if ((TZstring && TZstring[0] && Parse_TZ(TZstring, &statism) == 0)
|
||
|
|| IZTZ_GETLOCALETZINFO(&statism, generate_transitions)
|
||
|
|| Parse_TZ(gmt, &statism) == 0) {
|
||
|
daylight = statism.typecnt > 1;
|
||
|
dstfirst = daylight && statism.ttis[0].tt_isdst && !statism.ttis[1].tt_isdst;
|
||
|
timezone = -statism.ttis[dstfirst].tt_gmtoff;
|
||
|
tzname[0] = statism.chars + statism.ttis[dstfirst].tt_abbrind;
|
||
|
tzname[1] = statism.chars + statism.ttis[!dstfirst].tt_abbrind;
|
||
|
real_timezone_is_set = TRUE;
|
||
|
if (TZstring) {
|
||
|
if (old_TZstring)
|
||
|
old_TZstring = realloc(old_TZstring, strlen(TZstring) + 1);
|
||
|
else
|
||
|
old_TZstring = malloc(strlen(TZstring) + 1);
|
||
|
if (old_TZstring)
|
||
|
strcpy(old_TZstring, TZstring);
|
||
|
}
|
||
|
} else {
|
||
|
timezone = 0; /* default is GMT0 which means no offsets */
|
||
|
daylight = 0; /* from local system time */
|
||
|
real_timezone_is_set = FALSE;
|
||
|
if (old_TZstring) {
|
||
|
free(old_TZstring);
|
||
|
old_TZstring = NULL;
|
||
|
}
|
||
|
}
|
||
|
#ifdef IZTZ_SETLOCALTZINFO
|
||
|
/* Some SAS/C library functions, e.g. stat(), call library */
|
||
|
/* __tzset() themselves. So envvar TZ *must* exist in order to */
|
||
|
/* to get the right offset from GMT. XXX TRY HARD to fix this! */
|
||
|
set_TZ(timezone, daylight);
|
||
|
#endif /* IZTZ_SETLOCALTZINFO */
|
||
|
}
|
||
|
|
||
|
/* XXX Does this also help SAS/C library work? */
|
||
|
void __tzset()
|
||
|
{
|
||
|
if (!real_timezone_is_set) tzset();
|
||
|
}
|
||
|
|
||
|
static struct tm _tmbuf;
|
||
|
|
||
|
struct tm *gmtime(when)
|
||
|
ZCONST time_t *when;
|
||
|
{
|
||
|
long days = *when / SECSPERDAY;
|
||
|
long secs = *when % SECSPERDAY;
|
||
|
int isleap;
|
||
|
|
||
|
memset(&_tmbuf, 0, sizeof(_tmbuf)); /* get any nonstandard fields */
|
||
|
_tmbuf.tm_wday = (days + EPOCH_WDAY) % 7;
|
||
|
_tmbuf.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
|
||
|
isleap = leap(_tmbuf.tm_year + TM_YEAR_BASE);
|
||
|
while (days >= year_lengths[isleap]) {
|
||
|
days -= year_lengths[isleap];
|
||
|
_tmbuf.tm_year++;
|
||
|
isleap = leap(_tmbuf.tm_year + TM_YEAR_BASE);
|
||
|
}
|
||
|
_tmbuf.tm_mon = 0;
|
||
|
_tmbuf.tm_yday = days;
|
||
|
while (days >= mon_lengths[isleap][_tmbuf.tm_mon])
|
||
|
days -= mon_lengths[isleap][_tmbuf.tm_mon++];
|
||
|
_tmbuf.tm_mday = days + 1;
|
||
|
_tmbuf.tm_isdst = 0;
|
||
|
_tmbuf.tm_sec = secs % SECSPERMIN;
|
||
|
_tmbuf.tm_min = (secs / SECSPERMIN) % SECSPERMIN;
|
||
|
_tmbuf.tm_hour = secs / SECSPERHOUR;
|
||
|
return &_tmbuf;
|
||
|
}
|
||
|
|
||
|
struct tm *localtime(when)
|
||
|
ZCONST time_t *when;
|
||
|
{
|
||
|
time_t localwhen = *when;
|
||
|
int timetype;
|
||
|
struct tm *ret;
|
||
|
|
||
|
__tzset();
|
||
|
if (statism.timecnt == 0 || localwhen < statism.ats[0])
|
||
|
timetype = statism.ttis[0].tt_isdst && statism.typecnt > 1 &&
|
||
|
!statism.ttis[1].tt_isdst;
|
||
|
else {
|
||
|
for (timetype = 1; timetype < statism.timecnt; ++timetype)
|
||
|
if (localwhen < statism.ats[timetype])
|
||
|
break;
|
||
|
timetype = statism.types[timetype - 1];
|
||
|
}
|
||
|
localwhen += statism.ttis[timetype].tt_gmtoff;
|
||
|
ret = gmtime(&localwhen);
|
||
|
ret->tm_isdst = statism.ttis[timetype].tt_isdst;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#ifdef NEED__ISINDST
|
||
|
int _isindst(tb)
|
||
|
struct tm *tb;
|
||
|
{
|
||
|
time_t localt; /* time_t equivalent of given tm struct */
|
||
|
time_t univt; /* assumed UTC value of given time */
|
||
|
long tzoffset_adj; /* timezone-adjustment `remainder' */
|
||
|
int bailout_cnt; /* counter of tries for tz correction */
|
||
|
int timetype;
|
||
|
|
||
|
__tzset();
|
||
|
|
||
|
/* when DST is unsupported in current timezone, DST is always off */
|
||
|
if (statism.typecnt <= 1) return FALSE;
|
||
|
|
||
|
localt = mkgmtime(tb);
|
||
|
if (localt == (time_t)-1)
|
||
|
/* specified time is out-of-range, default to FALSE */
|
||
|
return FALSE;
|
||
|
|
||
|
univt = localt - statism.ttis[0].tt_gmtoff;
|
||
|
bailout_cnt = 3;
|
||
|
do {
|
||
|
if (statism.timecnt == 0 || univt < statism.ats[0])
|
||
|
timetype = statism.ttis[0].tt_isdst && statism.typecnt > 1 &&
|
||
|
!statism.ttis[1].tt_isdst;
|
||
|
else {
|
||
|
for (timetype = 1; timetype < statism.timecnt; ++timetype)
|
||
|
if (univt < statism.ats[timetype])
|
||
|
break;
|
||
|
timetype = statism.types[timetype - 1];
|
||
|
}
|
||
|
if ((tzoffset_adj = localt - univt - statism.ttis[timetype].tt_gmtoff)
|
||
|
== 0L)
|
||
|
break;
|
||
|
univt += tzoffset_adj;
|
||
|
} while (--bailout_cnt > 0);
|
||
|
|
||
|
/* return TRUE when DST is active at given time */
|
||
|
return (statism.ttis[timetype].tt_isdst);
|
||
|
}
|
||
|
#endif /* NEED__ISINDST */
|
||
|
#endif /* !IZ_MKTIME_ONLY */
|
||
|
|
||
|
/* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT
|
||
|
of the local time and date in the exploded time structure `tm',
|
||
|
adjust out of range fields in `tm' and set `tm->tm_yday', `tm->tm_wday'.
|
||
|
If `tm->tm_isdst < 0' was passed to mktime(), the correct setting of
|
||
|
tm_isdst is determined and returned. Otherwise, mktime() assumes this
|
||
|
field as valid; its information is used when converting local time
|
||
|
to UTC.
|
||
|
Return -1 if time in `tm' cannot be represented as time_t value. */
|
||
|
|
||
|
time_t mktime(tm)
|
||
|
struct tm *tm;
|
||
|
{
|
||
|
struct tm *ltm; /* Local time. */
|
||
|
time_t loctime; /* The time_t value of local time. */
|
||
|
time_t then; /* The time to return. */
|
||
|
long tzoffset_adj; /* timezone-adjustment `remainder' */
|
||
|
int bailout_cnt; /* counter of tries for tz correction */
|
||
|
int save_isdst; /* Copy of the tm->isdst input value */
|
||
|
|
||
|
save_isdst = tm->tm_isdst;
|
||
|
loctime = mkgmtime(tm);
|
||
|
if (loctime == -1) {
|
||
|
tm->tm_isdst = save_isdst;
|
||
|
return (time_t)-1;
|
||
|
}
|
||
|
|
||
|
/* Correct for the timezone and any daylight savings time.
|
||
|
The correction is verified and repeated when not correct, to
|
||
|
take into account the rare case that a change to or from daylight
|
||
|
savings time occurs between when it is the time in `tm' locally
|
||
|
and when it is that time in Greenwich. After the second correction,
|
||
|
the "timezone & daylight" offset should be correct in all cases. To
|
||
|
be sure, we allow a third try, but then the loop is stopped. */
|
||
|
bailout_cnt = 3;
|
||
|
then = loctime;
|
||
|
do {
|
||
|
ltm = localtime(&then);
|
||
|
if (ltm == (struct tm *)NULL ||
|
||
|
(tzoffset_adj = loctime - mkgmtime(ltm)) == 0L)
|
||
|
break;
|
||
|
then += tzoffset_adj;
|
||
|
} while (--bailout_cnt > 0);
|
||
|
|
||
|
if (ltm == (struct tm *)NULL || tzoffset_adj != 0L) {
|
||
|
/* Signal failure if timezone adjustment did not converge. */
|
||
|
tm->tm_isdst = save_isdst;
|
||
|
return (time_t)-1;
|
||
|
}
|
||
|
|
||
|
if (save_isdst >= 0) {
|
||
|
if (ltm->tm_isdst && !save_isdst)
|
||
|
{
|
||
|
if (then + 3600 < then)
|
||
|
then = (time_t)-1;
|
||
|
else
|
||
|
then += 3600;
|
||
|
}
|
||
|
else if (!ltm->tm_isdst && save_isdst)
|
||
|
{
|
||
|
if (then - 3600 > then)
|
||
|
then = (time_t)-1;
|
||
|
else
|
||
|
then -= 3600;
|
||
|
}
|
||
|
ltm->tm_isdst = save_isdst;
|
||
|
}
|
||
|
|
||
|
if (tm != ltm) /* `tm' may already point to localtime's internal storage */
|
||
|
*tm = *ltm;
|
||
|
|
||
|
return then;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifndef NO_TIME_T_MAX
|
||
|
/* Provide default values for the upper limit of the time_t range.
|
||
|
These are the result of the decomposition into a `struct tm' for
|
||
|
the time value 0xFFFFFFFEL ( = (time_t)-2 ).
|
||
|
Note: `(time_t)-1' is reserved for "invalid time"! */
|
||
|
# ifndef TM_YEAR_MAX
|
||
|
# define TM_YEAR_MAX 2106
|
||
|
# endif
|
||
|
# ifndef TM_MON_MAX
|
||
|
# define TM_MON_MAX 1 /* February */
|
||
|
# endif
|
||
|
# ifndef TM_MDAY_MAX
|
||
|
# define TM_MDAY_MAX 7
|
||
|
# endif
|
||
|
# ifndef TM_HOUR_MAX
|
||
|
# define TM_HOUR_MAX 6
|
||
|
# endif
|
||
|
# ifndef TM_MIN_MAX
|
||
|
# define TM_MIN_MAX 28
|
||
|
# endif
|
||
|
# ifndef TM_SEC_MAX
|
||
|
# define TM_SEC_MAX 14
|
||
|
# endif
|
||
|
#endif /* NO_TIME_T_MAX */
|
||
|
|
||
|
/* Adjusts out-of-range values for `tm' field `tm_member'. */
|
||
|
#define ADJUST_TM(tm_member, tm_carry, modulus) \
|
||
|
if ((tm_member) < 0) { \
|
||
|
tm_carry -= (1 - ((tm_member)+1) / (modulus)); \
|
||
|
tm_member = (modulus-1) + (((tm_member)+1) % (modulus)); \
|
||
|
} else if ((tm_member) >= (modulus)) { \
|
||
|
tm_carry += (tm_member) / (modulus); \
|
||
|
tm_member = (tm_member) % (modulus); \
|
||
|
}
|
||
|
|
||
|
/* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT
|
||
|
of the Greenwich Mean time and date in the exploded time structure `tm'.
|
||
|
This function does always put back normalized values into the `tm' struct,
|
||
|
parameter, including the calculated numbers for `tm->tm_yday',
|
||
|
`tm->tm_wday', and `tm->tm_isdst'.
|
||
|
Returns -1 if the time in the `tm' parameter cannot be represented
|
||
|
as valid `time_t' number. */
|
||
|
|
||
|
time_t mkgmtime(tm)
|
||
|
struct tm *tm;
|
||
|
{
|
||
|
int years, months, days, hours, minutes, seconds;
|
||
|
|
||
|
years = tm->tm_year + TM_YEAR_BASE; /* year - 1900 -> year */
|
||
|
months = tm->tm_mon; /* 0..11 */
|
||
|
days = tm->tm_mday - 1; /* 1..31 -> 0..30 */
|
||
|
hours = tm->tm_hour; /* 0..23 */
|
||
|
minutes = tm->tm_min; /* 0..59 */
|
||
|
seconds = tm->tm_sec; /* 0..61 in ANSI C. */
|
||
|
|
||
|
ADJUST_TM(seconds, minutes, 60)
|
||
|
ADJUST_TM(minutes, hours, 60)
|
||
|
ADJUST_TM(hours, days, 24)
|
||
|
ADJUST_TM(months, years, 12)
|
||
|
if (days < 0)
|
||
|
do {
|
||
|
if (--months < 0) {
|
||
|
--years;
|
||
|
months = 11;
|
||
|
}
|
||
|
days += monthlen(months, years);
|
||
|
} while (days < 0);
|
||
|
else
|
||
|
while (days >= monthlen(months, years)) {
|
||
|
days -= monthlen(months, years);
|
||
|
if (++months >= 12) {
|
||
|
++years;
|
||
|
months = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Restore adjusted values in tm structure */
|
||
|
tm->tm_year = years - TM_YEAR_BASE;
|
||
|
tm->tm_mon = months;
|
||
|
tm->tm_mday = days + 1;
|
||
|
tm->tm_hour = hours;
|
||
|
tm->tm_min = minutes;
|
||
|
tm->tm_sec = seconds;
|
||
|
|
||
|
/* Set `days' to the number of days into the year. */
|
||
|
days += YDAYS(months, years);
|
||
|
tm->tm_yday = days;
|
||
|
|
||
|
/* Now calculate `days' to the number of days since Jan 1, 1970. */
|
||
|
days = (unsigned)days + 365 * (unsigned)(years - EPOCH_YEAR) +
|
||
|
(unsigned)(nleap (years));
|
||
|
tm->tm_wday = ((unsigned)days + EPOCH_WDAY) % 7;
|
||
|
tm->tm_isdst = 0;
|
||
|
|
||
|
if (years < EPOCH_YEAR)
|
||
|
return (time_t)-1;
|
||
|
|
||
|
#if (defined(TM_YEAR_MAX) && defined(TM_MON_MAX) && defined(TM_MDAY_MAX))
|
||
|
#if (defined(TM_HOUR_MAX) && defined(TM_MIN_MAX) && defined(TM_SEC_MAX))
|
||
|
if (years > TM_YEAR_MAX ||
|
||
|
(years == TM_YEAR_MAX &&
|
||
|
(tm->tm_yday > (YDAYS(TM_MON_MAX, TM_YEAR_MAX) + (TM_MDAY_MAX - 1)) ||
|
||
|
(tm->tm_yday == (YDAYS(TM_MON_MAX, TM_YEAR_MAX) + (TM_MDAY_MAX - 1)) &&
|
||
|
(hours > TM_HOUR_MAX ||
|
||
|
(hours == TM_HOUR_MAX &&
|
||
|
(minutes > TM_MIN_MAX ||
|
||
|
(minutes == TM_MIN_MAX && seconds > TM_SEC_MAX) )))))))
|
||
|
return (time_t)-1;
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
return (time_t)(SECSPERDAY * (unsigned long)(unsigned)days +
|
||
|
SECSPERHOUR * (unsigned long)hours +
|
||
|
(unsigned long)(SECSPERMIN * minutes + seconds));
|
||
|
}
|
||
|
|
||
|
#endif /* __timezone_c */
|