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