0,0 → 1,813 |
/* |
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 */ |