1/* 2 timezone.c - Zip 3 3 4 Copyright (c) 1990-2004 Info-ZIP. All rights reserved. 5 6 See the accompanying file LICENSE, version 2003-May-08 or later 7 (the contents of which are also included in zip.h) for terms of use. 8 If, for some reason, all these files are missing, the Info-ZIP license 9 also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html 10*/ 11/* Replacement time library functions, based on platform independent public 12 * domain timezone code from ftp://elsie.nci.nih.gov/pub, with mktime and 13 * mkgmtime from our own mktime.c in Zip. 14 * 15 * Contains: tzset() 16 * __tzset() 17 * gmtime() 18 * localtime() 19 * mktime() 20 * mkgmtime() 21 * GetPlatformLocalTimezone() [different versions] 22 */ 23 24/* HISTORY/CHANGES 25 * 17 Jun 00, Paul Kienitz, added the PD-based tzset(), localtime(), and so on 26 * to amiga/filedate.c, replacing GNU-based functions which had 27 * replaced time_lib.c, both having been rejected for licensing 28 * reasons. Support for timezone files and leap seconds was removed. 29 * 30 * 23 Aug 00, Paul Kienitz, split into separate timezone.c file, made platform 31 * independent, copied in mktime() and mkgmtime() from Zip, renamed 32 * locale_TZ as GetPlatformLocalTimezone(), for use as a generic 33 * hook by other platforms. 34 */ 35 36#ifndef __timezone_c 37#define __timezone_c 38 39 40#include "zip.h" 41#include "timezone.h" 42#include <ctype.h> 43#include <errno.h> 44 45#ifdef IZTZ_DEFINESTDGLOBALS 46long timezone = 0; 47int daylight = 0; 48char *tzname[2]; 49#endif 50 51#ifndef IZTZ_GETLOCALETZINFO 52# define IZTZ_GETLOCALETZINFO(ptzstruct, pgenrulefunct) (FALSE) 53#endif 54 55int real_timezone_is_set = FALSE; /* set by tzset() */ 56 57 58#define TZDEFRULESTRING ",M4.1.0,M10.5.0" 59#define TZDEFAULT "EST5EDT" 60 61#define SECSPERMIN 60 62#define MINSPERHOUR 60 63#define HOURSPERDAY 24 64#define DAYSPERWEEK 7 65#define DAYSPERNYEAR 365 66#define DAYSPERLYEAR 366 67#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) 68#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) 69#define MONSPERYEAR 12 70 71#define EPOCH_WDAY 4 /* Jan 1, 1970 was thursday */ 72#define EPOCH_YEAR 1970 73#define TM_YEAR_BASE 1900 74#define FIRST_GOOD_YEAR ((time_t) -1 < (time_t) 1 ? EPOCH_YEAR-68 : EPOCH_YEAR) 75#define LAST_GOOD_YEAR (EPOCH_YEAR + ((time_t) -1 < (time_t) 1 ? 67 : 135)) 76 77#define YDAYS(month, year) yr_days[leap(year)][month] 78 79/* Nonzero if `y' is a leap year, else zero. */ 80#define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0) 81 82/* Number of leap years from EPOCH_YEAR to `y' (not including `y' itself). */ 83#define _P4 ((EPOCH_YEAR / 4) * 4 + 1) 84#define _P100 ((EPOCH_YEAR / 100) * 100 + 1) 85#define _P400 ((EPOCH_YEAR / 400) * 400 + 1) 86#define nleap(y) (((y) - _P4) / 4 - ((y) - _P100) / 100 + ((y) - _P400) / 400) 87 88/* Length of month `m' (0 .. 11) */ 89#define monthlen(m, y) (yr_days[0][(m)+1] - yr_days[0][m] + \ 90 ((m) == 1 && leap(y))) 91 92/* internal module-level constants */ 93#ifndef IZ_MKTIME_ONLY 94static ZCONST char gmt[] = "GMT"; 95static ZCONST int mon_lengths[2][MONSPERYEAR] = { 96 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 97 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 98}; 99#endif /* !IZ_MKTIME_ONLY */ 100static ZCONST int yr_days[2][MONSPERYEAR+1] = { 101 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 102 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 103}; 104#ifndef IZ_MKTIME_ONLY 105static ZCONST int year_lengths[2] = { 106 DAYSPERNYEAR, DAYSPERLYEAR 107}; 108 109/* internal variables */ 110static struct state statism; 111 112 113/* prototypes of static functions */ 114static time_t transtime OF((ZCONST time_t janfirst, ZCONST int year, 115 ZCONST struct rule * ZCONST rulep, 116 ZCONST long offset)); 117static void generate_transitions OF((register struct state * ZCONST sp, 118 ZCONST struct rule * ZCONST start, 119 ZCONST struct rule * ZCONST end)); 120static ZCONST char *getzname OF((ZCONST char *strp)); 121static ZCONST char *getnum OF((ZCONST char *strp, int * ZCONST nump, 122 ZCONST int min, ZCONST int max)); 123static ZCONST char *getsecs OF((ZCONST char *strp, long * ZCONST secsp)); 124static ZCONST char *getoffset OF((ZCONST char *strp, long * ZCONST offsetp)); 125static ZCONST char *getrule OF((ZCONST char *strp, struct rule * ZCONST rulep)); 126static int Parse_TZ OF((ZCONST char *name, register struct state * ZCONST sp)); 127 128 129static time_t transtime(janfirst, year, rulep, offset) 130 ZCONST time_t janfirst; 131 ZCONST int year; 132 ZCONST struct rule * ZCONST rulep; 133 ZCONST long offset; 134{ 135 register int leapyear; 136 register time_t value; 137 register int i; 138 int d, m1, yy0, yy1, yy2, dow; 139 140 value = 0; 141 leapyear = leap(year); 142 switch (rulep->r_type) { 143 144 case JULIAN_DAY: 145 /* 146 ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap 147 ** years. 148 ** In non-leap years, or if the day number is 59 or less, just 149 ** add SECSPERDAY times the day number-1 to the time of 150 ** January 1, midnight, to get the day. 151 */ 152 value = janfirst + (rulep->r_day - 1) * SECSPERDAY; 153 if (leapyear && rulep->r_day >= 60) 154 value += SECSPERDAY; 155 break; 156 157 case DAY_OF_YEAR: 158 /* 159 ** n - day of year. 160 ** Just add SECSPERDAY times the day number to the time of 161 ** January 1, midnight, to get the day. 162 */ 163 value = janfirst + rulep->r_day * SECSPERDAY; 164 break; 165 166 case MONTH_NTH_DAY_OF_WEEK: 167 /* 168 ** Mm.n.d - nth "dth day" of month m. 169 */ 170 value = janfirst; 171/* 172 for (i = 0; i < rulep->r_mon - 1; ++i) 173 value += mon_lengths[leapyear][i] * SECSPERDAY; 174*/ 175 value += yr_days[leapyear][rulep->r_mon - 1] * SECSPERDAY; 176 177 /* 178 ** Use Zeller's Congruence to get day-of-week of first day of 179 ** month. 180 */ 181 m1 = (rulep->r_mon + 9) % 12 + 1; 182 yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; 183 yy1 = yy0 / 100; 184 yy2 = yy0 % 100; 185 dow = ((26 * m1 - 2) / 10 + 186 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; 187 if (dow < 0) 188 dow += DAYSPERWEEK; 189 190 /* 191 ** "dow" is the day-of-week of the first day of the month. Get 192 ** the day-of-month (zero-origin) of the first "dow" day of the 193 ** month. 194 */ 195 d = rulep->r_day - dow; 196 if (d < 0) 197 d += DAYSPERWEEK; 198 for (i = 1; i < rulep->r_week; ++i) { 199 if (d + DAYSPERWEEK >= mon_lengths[leapyear][rulep->r_mon - 1]) 200 break; 201 d += DAYSPERWEEK; 202 } 203 204 /* 205 ** "d" is the day-of-month (zero-origin) of the day we want. 206 */ 207 value += d * SECSPERDAY; 208 break; 209 } 210 211 /* 212 ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in 213 ** question. To get the Epoch-relative time of the specified local 214 ** time on that day, add the transition time and the current offset 215 ** from UTC. 216 */ 217 return value + rulep->r_time + offset; 218} 219 220static void generate_transitions(sp, start, end) 221 register struct state * ZCONST sp; 222 ZCONST struct rule * ZCONST start; 223 ZCONST struct rule * ZCONST end; 224{ 225 register int year; 226 register time_t janfirst; 227 time_t starttime; 228 time_t endtime; 229 long stdoffset = -sp->ttis[0].tt_gmtoff; 230 long dstoffset = -sp->ttis[1].tt_gmtoff; 231 register time_t * atp; 232 register unsigned char * typep; 233 234 /* 235 ** Two transitions per year, from EPOCH_YEAR to LAST_GOOD_YEAR. 236 */ 237 sp->timecnt = 2 * (LAST_GOOD_YEAR - EPOCH_YEAR + 1); 238 atp = sp->ats; 239 typep = sp->types; 240 janfirst = 0; 241 for (year = EPOCH_YEAR; year <= LAST_GOOD_YEAR; ++year) { 242 starttime = transtime(janfirst, year, start, stdoffset); 243 endtime = transtime(janfirst, year, end, dstoffset); 244 if (starttime > endtime) { 245 *atp++ = endtime; 246 *typep++ = 0; /* DST ends */ 247 *atp++ = starttime; 248 *typep++ = 1; /* DST begins */ 249 } else { 250 *atp++ = starttime; 251 *typep++ = 1; /* DST begins */ 252 *atp++ = endtime; 253 *typep++ = 0; /* DST ends */ 254 } 255 janfirst += year_lengths[leap(year)] * SECSPERDAY; 256 } 257} 258 259static ZCONST char *getzname(strp) 260 ZCONST char *strp; 261{ 262 register char c; 263 264 while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' && 265 c != '+') 266 ++strp; 267 return strp; 268} 269 270static ZCONST char *getnum(strp, nump, min, max) 271 ZCONST char *strp; 272 int * ZCONST nump; 273 ZCONST int min; 274 ZCONST int max; 275{ 276 register char c; 277 register int num; 278 279 if (strp == NULL || !isdigit(c = *strp)) 280 return NULL; 281 num = 0; 282 do { 283 num = num * 10 + (c - '0'); 284 if (num > max) 285 return NULL; /* illegal value */ 286 c = *++strp; 287 } while (isdigit(c)); 288 if (num < min) 289 return NULL; /* illegal value */ 290 *nump = num; 291 return strp; 292} 293 294static ZCONST char *getsecs(strp, secsp) 295 ZCONST char *strp; 296 long * ZCONST secsp; 297{ 298 int num; 299 300 /* 301 ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like 302 ** "M10.4.6/26", which does not conform to Posix, 303 ** but which specifies the equivalent of 304 ** ``02:00 on the first Sunday on or after 23 Oct''. 305 */ 306 strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); 307 if (strp == NULL) 308 return NULL; 309 *secsp = num * (long) SECSPERHOUR; 310 if (*strp == ':') { 311 ++strp; 312 strp = getnum(strp, &num, 0, MINSPERHOUR - 1); 313 if (strp == NULL) 314 return NULL; 315 *secsp += num * SECSPERMIN; 316 if (*strp == ':') { 317 ++strp; 318 /* `SECSPERMIN' allows for leap seconds. */ 319 strp = getnum(strp, &num, 0, SECSPERMIN); 320 if (strp == NULL) 321 return NULL; 322 *secsp += num; 323 } 324 } 325 return strp; 326} 327 328static ZCONST char *getoffset(strp, offsetp) 329 ZCONST char *strp; 330 long * ZCONST offsetp; 331{ 332 register int neg = 0; 333 334 if (*strp == '-') { 335 neg = 1; 336 ++strp; 337 } else if (*strp == '+') 338 ++strp; 339 strp = getsecs(strp, offsetp); 340 if (strp == NULL) 341 return NULL; /* illegal time */ 342 if (neg) 343 *offsetp = -*offsetp; 344 return strp; 345} 346 347static ZCONST char *getrule(strp, rulep) 348 ZCONST char *strp; 349 struct rule * ZCONST rulep; 350{ 351 if (*strp == 'J') { 352 /* 353 ** Julian day. 354 */ 355 rulep->r_type = JULIAN_DAY; 356 ++strp; 357 strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); 358 } else if (*strp == 'M') { 359 /* 360 ** Month, week, day. 361 */ 362 rulep->r_type = MONTH_NTH_DAY_OF_WEEK; 363 ++strp; 364 strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); 365 if (strp == NULL) 366 return NULL; 367 if (*strp++ != '.') 368 return NULL; 369 strp = getnum(strp, &rulep->r_week, 1, 5); 370 if (strp == NULL) 371 return NULL; 372 if (*strp++ != '.') 373 return NULL; 374 strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); 375 } else if (isdigit(*strp)) { 376 /* 377 ** Day of year. 378 */ 379 rulep->r_type = DAY_OF_YEAR; 380 strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); 381 } else return NULL; /* invalid format */ 382 if (strp == NULL) 383 return NULL; 384 if (*strp == '/') { 385 /* 386 ** Time specified. 387 */ 388 ++strp; 389 strp = getsecs(strp, &rulep->r_time); 390 } else 391 rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ 392 return strp; 393} 394 395static int Parse_TZ(name, sp) 396 ZCONST char *name; 397 register struct state * ZCONST sp; 398{ 399 ZCONST char * stdname; 400 ZCONST char * dstname; 401 size_t stdlen; 402 size_t dstlen; 403 long stdoffset; 404 long dstoffset; 405 register char * cp; 406 407 dstname = NULL; 408 stdname = name; 409 name = getzname(name); 410 stdlen = name - stdname; 411 if (stdlen < 3) 412 return -1; 413 if (*name == '\0') 414 return -1; 415 name = getoffset(name, &stdoffset); 416 if (name == NULL) 417 return -1; 418 if (*name != '\0') { 419 dstname = name; 420 name = getzname(name); 421 dstlen = name - dstname; /* length of DST zone name */ 422 if (dstlen < 3) 423 return -1; 424 if (*name != '\0' && *name != ',' && *name != ';') { 425 name = getoffset(name, &dstoffset); 426 if (name == NULL) 427 return -1; 428 } else 429 dstoffset = stdoffset - SECSPERHOUR; 430 if (*name == '\0') 431 name = TZDEFRULESTRING; 432 if (*name == ',' || *name == ';') { 433 struct rule start; 434 struct rule end; 435 436 ++name; 437 if ((name = getrule(name, &start)) == NULL) 438 return -1; 439 if (*name++ != ',') 440 return -1; 441 if ((name = getrule(name, &end)) == NULL) 442 return -1; 443 if (*name != '\0') 444 return -1; 445 sp->typecnt = 2; /* standard time and DST */ 446 sp->ttis[0].tt_gmtoff = -stdoffset; 447 sp->ttis[0].tt_isdst = 0; 448 sp->ttis[0].tt_abbrind = 0; 449 sp->ttis[1].tt_gmtoff = -dstoffset; 450 sp->ttis[1].tt_isdst = 1; 451 sp->ttis[1].tt_abbrind = stdlen + 1; 452 generate_transitions(sp, &start, &end); 453 } 454 } else { 455 dstlen = 0; 456 sp->typecnt = 1; /* only standard time */ 457 sp->timecnt = 0; 458 sp->ttis[0].tt_gmtoff = -stdoffset; 459 sp->ttis[0].tt_isdst = 0; 460 sp->ttis[0].tt_abbrind = 0; 461 } 462 sp->charcnt = stdlen + 1; 463 if (dstlen != 0) 464 sp->charcnt += dstlen + 1; 465 if ((size_t) sp->charcnt > sizeof(sp->chars)) 466 return -1; 467 cp = sp->chars; 468 (void) strncpy(cp, stdname, stdlen); 469 cp += stdlen; 470 *cp++ = '\0'; 471 if (dstlen != 0) { 472 (void) strncpy(cp, dstname, dstlen); 473 *(cp + dstlen) = '\0'; 474 } 475 return 0; 476} 477 478void tzset() 479{ 480 char *TZstring; 481 int dstfirst; 482 static char *old_TZstring = NULL; 483 484 TZstring = getenv("TZ"); /* read TZ envvar */ 485 if (old_TZstring && TZstring && !strcmp(old_TZstring, TZstring)) 486 /* do not repeatedly parse an unchanged TZ specification */ 487 return; 488 if ((TZstring && TZstring[0] && Parse_TZ(TZstring, &statism) == 0) 489 || IZTZ_GETLOCALETZINFO(&statism, generate_transitions) 490 || Parse_TZ(gmt, &statism) == 0) { 491 daylight = statism.typecnt > 1; 492 dstfirst = daylight && statism.ttis[0].tt_isdst && !statism.ttis[1].tt_isdst; 493 timezone = -statism.ttis[dstfirst].tt_gmtoff; 494 tzname[0] = statism.chars + statism.ttis[dstfirst].tt_abbrind; 495 tzname[1] = statism.chars + statism.ttis[!dstfirst].tt_abbrind; 496 real_timezone_is_set = TRUE; 497 if (TZstring) { 498 if (old_TZstring) 499 old_TZstring = realloc(old_TZstring, strlen(TZstring) + 1); 500 else 501 old_TZstring = malloc(strlen(TZstring) + 1); 502 if (old_TZstring) 503 strcpy(old_TZstring, TZstring); 504 } 505 } else { 506 timezone = 0; /* default is GMT0 which means no offsets */ 507 daylight = 0; /* from local system time */ 508 real_timezone_is_set = FALSE; 509 if (old_TZstring) { 510 free(old_TZstring); 511 old_TZstring = NULL; 512 } 513 } 514#ifdef IZTZ_SETLOCALTZINFO 515 /* Some SAS/C library functions, e.g. stat(), call library */ 516 /* __tzset() themselves. So envvar TZ *must* exist in order to */ 517 /* to get the right offset from GMT. XXX TRY HARD to fix this! */ 518 set_TZ(timezone, daylight); 519#endif /* IZTZ_SETLOCALTZINFO */ 520} 521 522/* XXX Does this also help SAS/C library work? */ 523void __tzset() 524{ 525 if (!real_timezone_is_set) tzset(); 526} 527 528static struct tm _tmbuf; 529 530struct tm *gmtime(when) 531 ZCONST time_t *when; 532{ 533 long days = *when / SECSPERDAY; 534 long secs = *when % SECSPERDAY; 535 int isleap; 536 537 memset(&_tmbuf, 0, sizeof(_tmbuf)); /* get any nonstandard fields */ 538 _tmbuf.tm_wday = (days + EPOCH_WDAY) % 7; 539 _tmbuf.tm_year = EPOCH_YEAR - TM_YEAR_BASE; 540 isleap = leap(_tmbuf.tm_year + TM_YEAR_BASE); 541 while (days >= year_lengths[isleap]) { 542 days -= year_lengths[isleap]; 543 _tmbuf.tm_year++; 544 isleap = leap(_tmbuf.tm_year + TM_YEAR_BASE); 545 } 546 _tmbuf.tm_mon = 0; 547 _tmbuf.tm_yday = days; 548 while (days >= mon_lengths[isleap][_tmbuf.tm_mon]) 549 days -= mon_lengths[isleap][_tmbuf.tm_mon++]; 550 _tmbuf.tm_mday = days + 1; 551 _tmbuf.tm_isdst = 0; 552 _tmbuf.tm_sec = secs % SECSPERMIN; 553 _tmbuf.tm_min = (secs / SECSPERMIN) % SECSPERMIN; 554 _tmbuf.tm_hour = secs / SECSPERHOUR; 555 return &_tmbuf; 556} 557 558struct tm *localtime(when) 559 ZCONST time_t *when; 560{ 561 time_t localwhen = *when; 562 int timetype; 563 struct tm *ret; 564 565 __tzset(); 566 if (statism.timecnt == 0 || localwhen < statism.ats[0]) 567 timetype = statism.ttis[0].tt_isdst && statism.typecnt > 1 && 568 !statism.ttis[1].tt_isdst; 569 else { 570 for (timetype = 1; timetype < statism.timecnt; ++timetype) 571 if (localwhen < statism.ats[timetype]) 572 break; 573 timetype = statism.types[timetype - 1]; 574 } 575 localwhen += statism.ttis[timetype].tt_gmtoff; 576 ret = gmtime(&localwhen); 577 ret->tm_isdst = statism.ttis[timetype].tt_isdst; 578 return ret; 579} 580 581#ifdef NEED__ISINDST 582int _isindst(tb) 583 struct tm *tb; 584{ 585 time_t localt; /* time_t equivalent of given tm struct */ 586 time_t univt; /* assumed UTC value of given time */ 587 long tzoffset_adj; /* timezone-adjustment `remainder' */ 588 int bailout_cnt; /* counter of tries for tz correction */ 589 int timetype; 590 591 __tzset(); 592 593 /* when DST is unsupported in current timezone, DST is always off */ 594 if (statism.typecnt <= 1) return FALSE; 595 596 localt = mkgmtime(tb); 597 if (localt == (time_t)-1) 598 /* specified time is out-of-range, default to FALSE */ 599 return FALSE; 600 601 univt = localt - statism.ttis[0].tt_gmtoff; 602 bailout_cnt = 3; 603 do { 604 if (statism.timecnt == 0 || univt < statism.ats[0]) 605 timetype = statism.ttis[0].tt_isdst && statism.typecnt > 1 && 606 !statism.ttis[1].tt_isdst; 607 else { 608 for (timetype = 1; timetype < statism.timecnt; ++timetype) 609 if (univt < statism.ats[timetype]) 610 break; 611 timetype = statism.types[timetype - 1]; 612 } 613 if ((tzoffset_adj = localt - univt - statism.ttis[timetype].tt_gmtoff) 614 == 0L) 615 break; 616 univt += tzoffset_adj; 617 } while (--bailout_cnt > 0); 618 619 /* return TRUE when DST is active at given time */ 620 return (statism.ttis[timetype].tt_isdst); 621} 622#endif /* NEED__ISINDST */ 623#endif /* !IZ_MKTIME_ONLY */ 624 625/* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT 626 of the local time and date in the exploded time structure `tm', 627 adjust out of range fields in `tm' and set `tm->tm_yday', `tm->tm_wday'. 628 If `tm->tm_isdst < 0' was passed to mktime(), the correct setting of 629 tm_isdst is determined and returned. Otherwise, mktime() assumes this 630 field as valid; its information is used when converting local time 631 to UTC. 632 Return -1 if time in `tm' cannot be represented as time_t value. */ 633 634time_t mktime(tm) 635 struct tm *tm; 636{ 637 struct tm *ltm; /* Local time. */ 638 time_t loctime; /* The time_t value of local time. */ 639 time_t then; /* The time to return. */ 640 long tzoffset_adj; /* timezone-adjustment `remainder' */ 641 int bailout_cnt; /* counter of tries for tz correction */ 642 int save_isdst; /* Copy of the tm->isdst input value */ 643 644 save_isdst = tm->tm_isdst; 645 loctime = mkgmtime(tm); 646 if (loctime == -1) { 647 tm->tm_isdst = save_isdst; 648 return (time_t)-1; 649 } 650 651 /* Correct for the timezone and any daylight savings time. 652 The correction is verified and repeated when not correct, to 653 take into account the rare case that a change to or from daylight 654 savings time occurs between when it is the time in `tm' locally 655 and when it is that time in Greenwich. After the second correction, 656 the "timezone & daylight" offset should be correct in all cases. To 657 be sure, we allow a third try, but then the loop is stopped. */ 658 bailout_cnt = 3; 659 then = loctime; 660 do { 661 ltm = localtime(&then); 662 if (ltm == (struct tm *)NULL || 663 (tzoffset_adj = loctime - mkgmtime(ltm)) == 0L) 664 break; 665 then += tzoffset_adj; 666 } while (--bailout_cnt > 0); 667 668 if (ltm == (struct tm *)NULL || tzoffset_adj != 0L) { 669 /* Signal failure if timezone adjustment did not converge. */ 670 tm->tm_isdst = save_isdst; 671 return (time_t)-1; 672 } 673 674 if (save_isdst >= 0) { 675 if (ltm->tm_isdst && !save_isdst) 676 { 677 if (then + 3600 < then) 678 then = (time_t)-1; 679 else 680 then += 3600; 681 } 682 else if (!ltm->tm_isdst && save_isdst) 683 { 684 if (then - 3600 > then) 685 then = (time_t)-1; 686 else 687 then -= 3600; 688 } 689 ltm->tm_isdst = save_isdst; 690 } 691 692 if (tm != ltm) /* `tm' may already point to localtime's internal storage */ 693 *tm = *ltm; 694 695 return then; 696} 697 698 699#ifndef NO_TIME_T_MAX 700 /* Provide default values for the upper limit of the time_t range. 701 These are the result of the decomposition into a `struct tm' for 702 the time value 0xFFFFFFFEL ( = (time_t)-2 ). 703 Note: `(time_t)-1' is reserved for "invalid time"! */ 704# ifndef TM_YEAR_MAX 705# define TM_YEAR_MAX 2106 706# endif 707# ifndef TM_MON_MAX 708# define TM_MON_MAX 1 /* February */ 709# endif 710# ifndef TM_MDAY_MAX 711# define TM_MDAY_MAX 7 712# endif 713# ifndef TM_HOUR_MAX 714# define TM_HOUR_MAX 6 715# endif 716# ifndef TM_MIN_MAX 717# define TM_MIN_MAX 28 718# endif 719# ifndef TM_SEC_MAX 720# define TM_SEC_MAX 14 721# endif 722#endif /* NO_TIME_T_MAX */ 723 724/* Adjusts out-of-range values for `tm' field `tm_member'. */ 725#define ADJUST_TM(tm_member, tm_carry, modulus) \ 726 if ((tm_member) < 0) { \ 727 tm_carry -= (1 - ((tm_member)+1) / (modulus)); \ 728 tm_member = (modulus-1) + (((tm_member)+1) % (modulus)); \ 729 } else if ((tm_member) >= (modulus)) { \ 730 tm_carry += (tm_member) / (modulus); \ 731 tm_member = (tm_member) % (modulus); \ 732 } 733 734/* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT 735 of the Greenwich Mean time and date in the exploded time structure `tm'. 736 This function does always put back normalized values into the `tm' struct, 737 parameter, including the calculated numbers for `tm->tm_yday', 738 `tm->tm_wday', and `tm->tm_isdst'. 739 Returns -1 if the time in the `tm' parameter cannot be represented 740 as valid `time_t' number. */ 741 742time_t mkgmtime(tm) 743 struct tm *tm; 744{ 745 int years, months, days, hours, minutes, seconds; 746 747 years = tm->tm_year + TM_YEAR_BASE; /* year - 1900 -> year */ 748 months = tm->tm_mon; /* 0..11 */ 749 days = tm->tm_mday - 1; /* 1..31 -> 0..30 */ 750 hours = tm->tm_hour; /* 0..23 */ 751 minutes = tm->tm_min; /* 0..59 */ 752 seconds = tm->tm_sec; /* 0..61 in ANSI C. */ 753 754 ADJUST_TM(seconds, minutes, 60) 755 ADJUST_TM(minutes, hours, 60) 756 ADJUST_TM(hours, days, 24) 757 ADJUST_TM(months, years, 12) 758 if (days < 0) 759 do { 760 if (--months < 0) { 761 --years; 762 months = 11; 763 } 764 days += monthlen(months, years); 765 } while (days < 0); 766 else 767 while (days >= monthlen(months, years)) { 768 days -= monthlen(months, years); 769 if (++months >= 12) { 770 ++years; 771 months = 0; 772 } 773 } 774 775 /* Restore adjusted values in tm structure */ 776 tm->tm_year = years - TM_YEAR_BASE; 777 tm->tm_mon = months; 778 tm->tm_mday = days + 1; 779 tm->tm_hour = hours; 780 tm->tm_min = minutes; 781 tm->tm_sec = seconds; 782 783 /* Set `days' to the number of days into the year. */ 784 days += YDAYS(months, years); 785 tm->tm_yday = days; 786 787 /* Now calculate `days' to the number of days since Jan 1, 1970. */ 788 days = (unsigned)days + 365 * (unsigned)(years - EPOCH_YEAR) + 789 (unsigned)(nleap (years)); 790 tm->tm_wday = ((unsigned)days + EPOCH_WDAY) % 7; 791 tm->tm_isdst = 0; 792 793 if (years < EPOCH_YEAR) 794 return (time_t)-1; 795 796#if (defined(TM_YEAR_MAX) && defined(TM_MON_MAX) && defined(TM_MDAY_MAX)) 797#if (defined(TM_HOUR_MAX) && defined(TM_MIN_MAX) && defined(TM_SEC_MAX)) 798 if (years > TM_YEAR_MAX || 799 (years == TM_YEAR_MAX && 800 (tm->tm_yday > (YDAYS(TM_MON_MAX, TM_YEAR_MAX) + (TM_MDAY_MAX - 1)) || 801 (tm->tm_yday == (YDAYS(TM_MON_MAX, TM_YEAR_MAX) + (TM_MDAY_MAX - 1)) && 802 (hours > TM_HOUR_MAX || 803 (hours == TM_HOUR_MAX && 804 (minutes > TM_MIN_MAX || 805 (minutes == TM_MIN_MAX && seconds > TM_SEC_MAX) ))))))) 806 return (time_t)-1; 807#endif 808#endif 809 810 return (time_t)(SECSPERDAY * (unsigned long)(unsigned)days + 811 SECSPERHOUR * (unsigned long)hours + 812 (unsigned long)(SECSPERMIN * minutes + seconds)); 813} 814 815#endif /* __timezone_c */ 816