localtime.c revision 8870
1224135Sdim#ifndef lint 2224135Sdim#ifndef NOID 3224135Sdimstatic char elsieid[] = "@(#)localtime.c 7.19"; 4224135Sdim#endif /* !defined NOID */ 5224135Sdim#endif /* !defined lint */ 6224135Sdim 7224135Sdim/* 8224135Sdim** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu). 9224135Sdim** POSIX-style TZ environment variable handling from Guy Harris 10224135Sdim** (guy@auspex.com). 11224135Sdim*/ 12224135Sdim 13224135Sdim/*LINTLIBRARY*/ 14263509Sdim 15224135Sdim#include "private.h" 16224135Sdim#include "tzfile.h" 17224135Sdim#include "fcntl.h" 18224135Sdim 19224135Sdim#define ACCESS_MODE O_RDONLY 20224135Sdim 21252723Sdim#ifdef O_BINARY 22252723Sdim#define OPEN_MODE (O_RDONLY | O_BINARY) 23252723Sdim#endif /* defined O_BINARY */ 24224135Sdim#ifndef O_BINARY 25224135Sdim#define OPEN_MODE O_RDONLY 26224135Sdim#endif /* !defined O_BINARY */ 27224135Sdim 28252723Sdim#ifndef WILDABBR 29252723Sdim/* 30252723Sdim** Someone might make incorrect use of a time zone abbreviation: 31252723Sdim** 1. They might reference tzname[0] before calling tzset (explicitly 32252723Sdim** or implicitly). 33252723Sdim** 2. They might reference tzname[1] before calling tzset (explicitly 34224135Sdim** or implicitly). 35224135Sdim** 3. They might reference tzname[1] after setting to a time zone 36252723Sdim** in which Daylight Saving Time is never observed. 37252723Sdim** 4. They might reference tzname[0] after setting to a time zone 38252723Sdim** in which Standard Time is never observed. 39252723Sdim** 5. They might reference tm.TM_ZONE after calling offtime. 40252723Sdim** What's best to do in the above cases is open to debate; 41224135Sdim** for now, we just set things up so that in any of the five cases 42224135Sdim** WILDABBR is used. Another possibility: initialize tzname[0] to the 43224135Sdim** string "tzname[0] used before set", and similarly for the other cases. 44224135Sdim** And another: initialize tzname[0] to "ERA", with an explanation in the 45224135Sdim** manual page of what this "time zone abbreviation" means (doing this so 46224135Sdim** that tzname[0] has the "normal" length of three characters). 47224135Sdim*/ 48224135Sdim#define WILDABBR " " 49224135Sdim#endif /* !defined WILDABBR */ 50235633Sdim 51224135Sdimstatic const char GMT[] = "GMT"; 52224135Sdim 53224135Sdimstruct ttinfo { /* time type information */ 54224135Sdim long tt_gmtoff; /* GMT offset in seconds */ 55224135Sdim int tt_isdst; /* used to set tm_isdst */ 56224135Sdim int tt_abbrind; /* abbreviation list index */ 57224135Sdim int tt_ttisstd; /* TRUE if transition is std time */ 58224135Sdim}; 59224135Sdim 60224135Sdimstruct lsinfo { /* leap second information */ 61224135Sdim time_t ls_trans; /* transition time */ 62224135Sdim long ls_corr; /* correction to apply */ 63224135Sdim}; 64252723Sdim 65252723Sdim#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) 66252723Sdim 67224135Sdim#ifdef TZNAME_MAX 68224135Sdim#define MY_TZNAME_MAX TZNAME_MAX 69224135Sdim#endif /* defined TZNAME_MAX */ 70224135Sdim#ifndef TZNAME_MAX 71235633Sdim#define MY_TZNAME_MAX 255 72235633Sdim#endif /* !defined TZNAME_MAX */ 73235633Sdim 74235633Sdimstruct state { 75235633Sdim int leapcnt; 76224135Sdim int timecnt; 77224135Sdim int typecnt; 78224135Sdim int charcnt; 79224135Sdim time_t ats[TZ_MAX_TIMES]; 80224135Sdim unsigned char types[TZ_MAX_TIMES]; 81224135Sdim struct ttinfo ttis[TZ_MAX_TYPES]; 82224135Sdim char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof GMT), 83224135Sdim (2 * (MY_TZNAME_MAX + 1)))]; 84224135Sdim struct lsinfo lsis[TZ_MAX_LEAPS]; 85224135Sdim}; 86224135Sdim 87224135Sdimstruct rule { 88235633Sdim int r_type; /* type of rule--see below */ 89224135Sdim int r_day; /* day number of rule */ 90224135Sdim int r_week; /* week number of rule */ 91224135Sdim int r_mon; /* month number of rule */ 92224135Sdim long r_time; /* transition time of rule */ 93224135Sdim}; 94224135Sdim 95224135Sdim#define JULIAN_DAY 0 /* Jn - Julian day */ 96224135Sdim#define DAY_OF_YEAR 1 /* n - day of year */ 97224135Sdim#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ 98235633Sdim 99235633Sdim/* 100224135Sdim** Prototypes for static functions. 101224135Sdim*/ 102224135Sdim 103224135Sdimstatic long detzcode P((const char * codep)); 104224135Sdimstatic const char * getzname P((const char * strp)); 105224135Sdimstatic const char * getnum P((const char * strp, int * nump, int min, 106235633Sdim int max)); 107252723Sdimstatic const char * getsecs P((const char * strp, long * secsp)); 108224135Sdimstatic const char * getoffset P((const char * strp, long * offsetp)); 109224135Sdimstatic const char * getrule P((const char * strp, struct rule * rulep)); 110224135Sdimstatic void gmtload P((struct state * sp)); 111224135Sdimstatic void gmtsub P((const time_t * timep, long offset, 112252723Sdim struct tm * tmp)); 113252723Sdimstatic void localsub P((const time_t * timep, long offset, 114235633Sdim struct tm * tmp)); 115224135Sdimstatic int increment_overflow P((int * number, int delta)); 116224135Sdimstatic int normalize_overflow P((int * tensptr, int * unitsptr, 117224135Sdim int base)); 118224135Sdimstatic void settzname P((void)); 119224135Sdimstatic time_t time1 P((struct tm * tmp, void (* funcp)(), 120224135Sdim long offset)); 121224135Sdimstatic time_t time2 P((struct tm *tmp, void (* funcp)(), 122224135Sdim long offset, int * okayp)); 123224135Sdimstatic void timesub P((const time_t * timep, long offset, 124224135Sdim const struct state * sp, struct tm * tmp)); 125224135Sdimstatic int tmcomp P((const struct tm * atmp, 126224135Sdim const struct tm * btmp)); 127252723Sdimstatic time_t transtime P((time_t janfirst, int year, 128252723Sdim const struct rule * rulep, long offset)); 129252723Sdimstatic int tzload P((const char * name, struct state * sp)); 130252723Sdimstatic int tzparse P((const char * name, struct state * sp, 131252723Sdim int lastditch)); 132252723Sdim 133252723Sdim#ifdef ALL_STATE 134252723Sdimstatic struct state * lclptr; 135252723Sdimstatic struct state * gmtptr; 136252723Sdim#endif /* defined ALL_STATE */ 137252723Sdim 138252723Sdim#ifndef ALL_STATE 139252723Sdimstatic struct state lclmem; 140252723Sdimstatic struct state gmtmem; 141252723Sdim#define lclptr (&lclmem) 142252723Sdim#define gmtptr (&gmtmem) 143252723Sdim#endif /* State Farm */ 144224135Sdim 145252723Sdimstatic int lcl_is_set; 146252723Sdimstatic int gmt_is_set; 147252723Sdim 148252723Sdimchar * tzname[2] = { 149252723Sdim WILDABBR, 150252723Sdim WILDABBR 151252723Sdim}; 152252723Sdim 153235633Sdim#ifdef USG_COMPAT 154235633Sdimtime_t timezone = 0; 155224135Sdimint daylight = 0; 156224135Sdim#endif /* defined USG_COMPAT */ 157224135Sdim 158224135Sdim#ifdef ALTZONE 159235633Sdimtime_t altzone = 0; 160235633Sdim#endif /* defined ALTZONE */ 161252723Sdim 162252723Sdimstatic long 163224135Sdimdetzcode(codep) 164224135Sdimconst char * const codep; 165224135Sdim{ 166224135Sdim register long result; 167224135Sdim register int i; 168224135Sdim 169224135Sdim result = 0; 170224135Sdim for (i = 0; i < 4; ++i) 171224135Sdim result = (result << 8) | (codep[i] & 0xff); 172224135Sdim return result; 173224135Sdim} 174224135Sdim 175224135Sdimstatic void 176224135Sdimsettzname() 177224135Sdim{ 178235633Sdim register const struct state * const sp = lclptr; 179252723Sdim register int i; 180252723Sdim 181252723Sdim tzname[0] = WILDABBR; 182252723Sdim tzname[1] = WILDABBR; 183252723Sdim#ifdef USG_COMPAT 184252723Sdim daylight = 0; 185252723Sdim timezone = 0; 186224135Sdim#endif /* defined USG_COMPAT */ 187224135Sdim#ifdef ALTZONE 188224135Sdim altzone = 0; 189224135Sdim#endif /* defined ALTZONE */ 190252723Sdim#ifdef ALL_STATE 191235633Sdim if (sp == NULL) { 192252723Sdim tzname[0] = tzname[1] = GMT; 193252723Sdim return; 194224135Sdim } 195224135Sdim#endif /* defined ALL_STATE */ 196224135Sdim for (i = 0; i < sp->typecnt; ++i) { 197224135Sdim register const struct ttinfo * const ttisp = &sp->ttis[i]; 198224135Sdim 199235633Sdim tzname[ttisp->tt_isdst] = 200224135Sdim (char *) &sp->chars[ttisp->tt_abbrind]; 201235633Sdim#ifdef USG_COMPAT 202224135Sdim if (ttisp->tt_isdst) 203224135Sdim daylight = 1; 204224135Sdim if (i == 0 || !ttisp->tt_isdst) 205224135Sdim timezone = -(ttisp->tt_gmtoff); 206235633Sdim#endif /* defined USG_COMPAT */ 207235633Sdim#ifdef ALTZONE 208235633Sdim if (i == 0 || ttisp->tt_isdst) 209235633Sdim altzone = -(ttisp->tt_gmtoff); 210235633Sdim#endif /* defined ALTZONE */ 211235633Sdim } 212235633Sdim /* 213235633Sdim ** And to get the latest zone names into tzname. . . 214235633Sdim */ 215235633Sdim for (i = 0; i < sp->timecnt; ++i) { 216235633Sdim register const struct ttinfo * const ttisp = 217235633Sdim &sp->ttis[ 218224135Sdim sp->types[i]]; 219235633Sdim 220235633Sdim tzname[ttisp->tt_isdst] = 221235633Sdim (char *) &sp->chars[ttisp->tt_abbrind]; 222235633Sdim } 223235633Sdim} 224235633Sdim 225235633Sdimstatic int 226224135Sdimtzload(name, sp) 227235633Sdimregister const char * name; 228235633Sdimregister struct state * const sp; 229235633Sdim{ 230235633Sdim register const char * p; 231224135Sdim register int i; 232224135Sdim register int fid; 233224135Sdim 234224135Sdim if (name == NULL && (name = TZDEFAULT) == NULL) 235224135Sdim return -1; 236235633Sdim { 237224135Sdim register int doaccess; 238224135Sdim char fullname[FILENAME_MAX + 1]; 239224135Sdim 240224135Sdim if (name[0] == ':') 241235633Sdim ++name; 242224135Sdim doaccess = name[0] == '/'; 243224135Sdim if (!doaccess) { 244224135Sdim if ((p = TZDIR) == NULL) 245224135Sdim return -1; 246235633Sdim if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) 247224135Sdim return -1; 248235633Sdim (void) strcpy(fullname, p); 249235633Sdim (void) strcat(fullname, "/"); 250235633Sdim (void) strcat(fullname, name); 251235633Sdim /* 252224135Sdim ** Set doaccess if '.' (as in "../") shows up in name. 253252723Sdim */ 254224135Sdim if (strchr(name, '.') != NULL) 255224135Sdim doaccess = TRUE; 256235633Sdim name = fullname; 257235633Sdim } 258224135Sdim if (doaccess && access(name, ACCESS_MODE) != 0) 259224135Sdim return -1; 260224135Sdim if ((fid = open(name, OPEN_MODE)) == -1) 261224135Sdim return -1; 262235633Sdim } 263224135Sdim { 264224135Sdim register const struct tzhead * tzhp; 265224135Sdim char buf[sizeof *sp + sizeof *tzhp]; 266224135Sdim int ttisstdcnt; 267224135Sdim 268224135Sdim i = read(fid, buf, sizeof buf); 269224135Sdim if (close(fid) != 0 || i < sizeof *tzhp) 270224135Sdim return -1; 271224135Sdim tzhp = (struct tzhead *) buf; 272224135Sdim ttisstdcnt = (int) detzcode(tzhp->tzh_ttisstdcnt); 273224135Sdim sp->leapcnt = (int) detzcode(tzhp->tzh_leapcnt); 274224135Sdim sp->timecnt = (int) detzcode(tzhp->tzh_timecnt); 275224135Sdim sp->typecnt = (int) detzcode(tzhp->tzh_typecnt); 276224135Sdim sp->charcnt = (int) detzcode(tzhp->tzh_charcnt); 277224135Sdim if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || 278224135Sdim sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || 279224135Sdim sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || 280224135Sdim sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || 281224135Sdim (ttisstdcnt != sp->typecnt && ttisstdcnt != 0)) 282224135Sdim return -1; 283224135Sdim if (i < sizeof *tzhp + 284224135Sdim sp->timecnt * (4 + sizeof (char)) + 285224135Sdim sp->typecnt * (4 + 2 * sizeof (char)) + 286224135Sdim sp->charcnt * sizeof (char) + 287224135Sdim sp->leapcnt * 2 * 4 + 288224135Sdim ttisstdcnt * sizeof (char)) 289224135Sdim return -1; 290224135Sdim p = buf + sizeof *tzhp; 291224135Sdim for (i = 0; i < sp->timecnt; ++i) { 292224135Sdim sp->ats[i] = detzcode(p); 293224135Sdim p += 4; 294224135Sdim } 295224135Sdim for (i = 0; i < sp->timecnt; ++i) { 296224135Sdim sp->types[i] = (unsigned char) *p++; 297224135Sdim if (sp->types[i] >= sp->typecnt) 298224135Sdim return -1; 299224135Sdim } 300224135Sdim for (i = 0; i < sp->typecnt; ++i) { 301224135Sdim register struct ttinfo * ttisp; 302224135Sdim 303224135Sdim ttisp = &sp->ttis[i]; 304224135Sdim ttisp->tt_gmtoff = detzcode(p); 305224135Sdim p += 4; 306224135Sdim ttisp->tt_isdst = (unsigned char) *p++; 307224135Sdim if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) 308224135Sdim return -1; 309224135Sdim ttisp->tt_abbrind = (unsigned char) *p++; 310224135Sdim if (ttisp->tt_abbrind < 0 || 311224135Sdim ttisp->tt_abbrind > sp->charcnt) 312224135Sdim return -1; 313224135Sdim } 314224135Sdim for (i = 0; i < sp->charcnt; ++i) 315224135Sdim sp->chars[i] = *p++; 316224135Sdim sp->chars[i] = '\0'; /* ensure '\0' at end */ 317224135Sdim for (i = 0; i < sp->leapcnt; ++i) { 318224135Sdim register struct lsinfo * lsisp; 319224135Sdim 320224135Sdim lsisp = &sp->lsis[i]; 321224135Sdim lsisp->ls_trans = detzcode(p); 322224135Sdim p += 4; 323224135Sdim lsisp->ls_corr = detzcode(p); 324224135Sdim p += 4; 325224135Sdim } 326224135Sdim for (i = 0; i < sp->typecnt; ++i) { 327226890Sdim register struct ttinfo * ttisp; 328224135Sdim 329224135Sdim ttisp = &sp->ttis[i]; 330224135Sdim if (ttisstdcnt == 0) 331224135Sdim ttisp->tt_ttisstd = FALSE; 332224135Sdim else { 333224135Sdim ttisp->tt_ttisstd = *p++; 334224135Sdim if (ttisp->tt_ttisstd != TRUE && 335224135Sdim ttisp->tt_ttisstd != FALSE) 336224135Sdim return -1; 337224135Sdim } 338224135Sdim } 339224135Sdim } 340224135Sdim return 0; 341224135Sdim} 342224135Sdim 343224135Sdimstatic const int mon_lengths[2][MONSPERYEAR] = { 344224135Sdim { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 345224135Sdim { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 346224135Sdim}; 347224135Sdim 348224135Sdimstatic const int year_lengths[2] = { 349224135Sdim DAYSPERNYEAR, DAYSPERLYEAR 350224135Sdim}; 351224135Sdim 352224135Sdim/* 353224135Sdim** Given a pointer into a time zone string, scan until a character that is not 354224135Sdim** a valid character in a zone name is found. Return a pointer to that 355224135Sdim** character. 356224135Sdim*/ 357224135Sdim 358224135Sdimstatic const char * 359224135Sdimgetzname(strp) 360224135Sdimregister const char * strp; 361224135Sdim{ 362224135Sdim register char c; 363224135Sdim 364224135Sdim while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' && 365224135Sdim c != '+') 366226890Sdim ++strp; 367245431Sdim return strp; 368226890Sdim} 369226890Sdim 370226890Sdim/* 371226890Sdim** Given a pointer into a time zone string, extract a number from that string. 372224135Sdim** Check that the number is within a specified range; if it is not, return 373226890Sdim** NULL. 374224135Sdim** Otherwise, return a pointer to the first character not part of the number. 375224135Sdim*/ 376224135Sdim 377224135Sdimstatic const char * 378224135Sdimgetnum(strp, nump, min, max) 379224135Sdimregister const char * strp; 380224135Sdimint * const nump; 381224135Sdimconst int min; 382224135Sdimconst int max; 383224135Sdim{ 384224135Sdim register char c; 385224135Sdim register int num; 386224135Sdim 387224135Sdim if (strp == NULL || !isdigit(*strp)) 388224135Sdim return NULL; 389224135Sdim num = 0; 390224135Sdim while ((c = *strp) != '\0' && isdigit(c)) { 391224135Sdim num = num * 10 + (c - '0'); 392224135Sdim if (num > max) 393224135Sdim return NULL; /* illegal value */ 394224135Sdim ++strp; 395224135Sdim } 396224135Sdim if (num < min) 397224135Sdim return NULL; /* illegal value */ 398224135Sdim *nump = num; 399224135Sdim return strp; 400224135Sdim} 401224135Sdim 402224135Sdim/* 403224135Sdim** Given a pointer into a time zone string, extract a number of seconds, 404224135Sdim** in hh[:mm[:ss]] form, from the string. 405224135Sdim** If any error occurs, return NULL. 406224135Sdim** Otherwise, return a pointer to the first character not part of the number 407224135Sdim** of seconds. 408224135Sdim*/ 409224135Sdim 410224135Sdimstatic const char * 411224135Sdimgetsecs(strp, secsp) 412224135Sdimregister const char * strp; 413224135Sdimlong * const secsp; 414224135Sdim{ 415224135Sdim int num; 416224135Sdim 417224135Sdim strp = getnum(strp, &num, 0, HOURSPERDAY); 418224135Sdim if (strp == NULL) 419224135Sdim return NULL; 420224135Sdim *secsp = num * SECSPERHOUR; 421224135Sdim if (*strp == ':') { 422224135Sdim ++strp; 423224135Sdim strp = getnum(strp, &num, 0, MINSPERHOUR - 1); 424224135Sdim if (strp == NULL) 425224135Sdim return NULL; 426224135Sdim *secsp += num * SECSPERMIN; 427224135Sdim if (*strp == ':') { 428224135Sdim ++strp; 429224135Sdim strp = getnum(strp, &num, 0, SECSPERMIN - 1); 430224135Sdim if (strp == NULL) 431224135Sdim return NULL; 432235633Sdim *secsp += num; 433224135Sdim } 434224135Sdim } 435224135Sdim return strp; 436224135Sdim} 437224135Sdim 438224135Sdim/* 439224135Sdim** Given a pointer into a time zone string, extract an offset, in 440224135Sdim** [+-]hh[:mm[:ss]] form, from the string. 441224135Sdim** If any error occurs, return NULL. 442224135Sdim** Otherwise, return a pointer to the first character not part of the time. 443224135Sdim*/ 444224135Sdim 445224135Sdimstatic const char * 446224135Sdimgetoffset(strp, offsetp) 447235633Sdimregister const char * strp; 448224135Sdimlong * const offsetp; 449224135Sdim{ 450245431Sdim register int neg; 451245431Sdim 452224135Sdim if (*strp == '-') { 453224135Sdim neg = 1; 454224135Sdim ++strp; 455224135Sdim } else if (isdigit(*strp) || *strp++ == '+') 456224135Sdim neg = 0; 457224135Sdim else return NULL; /* illegal offset */ 458224135Sdim strp = getsecs(strp, offsetp); 459224135Sdim if (strp == NULL) 460224135Sdim return NULL; /* illegal time */ 461224135Sdim if (neg) 462224135Sdim *offsetp = -*offsetp; 463224135Sdim return strp; 464224135Sdim} 465235633Sdim 466224135Sdim/* 467224135Sdim** Given a pointer into a time zone string, extract a rule in the form 468224135Sdim** date[/time]. See POSIX section 8 for the format of "date" and "time". 469224135Sdim** If a valid rule is not found, return NULL. 470224135Sdim** Otherwise, return a pointer to the first character not part of the rule. 471224135Sdim*/ 472224135Sdim 473224135Sdimstatic const char * 474224135Sdimgetrule(strp, rulep) 475224135Sdimconst char * strp; 476224135Sdimregister struct rule * const rulep; 477224135Sdim{ 478224135Sdim if (*strp == 'J') { 479224135Sdim /* 480224135Sdim ** Julian day. 481224135Sdim */ 482235633Sdim rulep->r_type = JULIAN_DAY; 483224135Sdim ++strp; 484224135Sdim strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); 485224135Sdim } else if (*strp == 'M') { 486224135Sdim /* 487224135Sdim ** Month, week, day. 488224135Sdim */ 489224135Sdim rulep->r_type = MONTH_NTH_DAY_OF_WEEK; 490224135Sdim ++strp; 491224135Sdim strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); 492224135Sdim if (strp == NULL) 493224135Sdim return NULL; 494224135Sdim if (*strp++ != '.') 495224135Sdim return NULL; 496224135Sdim strp = getnum(strp, &rulep->r_week, 1, 5); 497224135Sdim if (strp == NULL) 498224135Sdim return NULL; 499224135Sdim if (*strp++ != '.') 500224135Sdim return NULL; 501224135Sdim strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); 502224135Sdim } else if (isdigit(*strp)) { 503224135Sdim /* 504252723Sdim ** Day of year. 505252723Sdim */ 506252723Sdim rulep->r_type = DAY_OF_YEAR; 507224135Sdim strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); 508224135Sdim } else return NULL; /* invalid format */ 509224135Sdim if (strp == NULL) 510224135Sdim return NULL; 511224135Sdim if (*strp == '/') { 512224135Sdim /* 513224135Sdim ** Time specified. 514224135Sdim */ 515224135Sdim ++strp; 516224135Sdim strp = getsecs(strp, &rulep->r_time); 517224135Sdim } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ 518224135Sdim return strp; 519224135Sdim} 520224135Sdim 521224135Sdim/* 522224135Sdim** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the 523224135Sdim** year, a rule, and the offset from GMT at the time that rule takes effect, 524224135Sdim** calculate the Epoch-relative time that rule takes effect. 525224135Sdim*/ 526224135Sdim 527252723Sdimstatic time_t 528252723Sdimtranstime(janfirst, year, rulep, offset) 529252723Sdimconst time_t janfirst; 530224135Sdimconst int year; 531224135Sdimregister const struct rule * const rulep; 532224135Sdimconst long offset; 533224135Sdim{ 534224135Sdim register int leapyear; 535224135Sdim register time_t value; 536224135Sdim register int i; 537224135Sdim int d, m1, yy0, yy1, yy2, dow; 538224135Sdim 539235633Sdim leapyear = isleap(year); 540235633Sdim switch (rulep->r_type) { 541235633Sdim 542263509Sdim case JULIAN_DAY: 543263509Sdim /* 544235633Sdim ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap 545235633Sdim ** years. 546263509Sdim ** In non-leap years, or if the day number is 59 or less, just 547263509Sdim ** add SECSPERDAY times the day number-1 to the time of 548263509Sdim ** January 1, midnight, to get the day. 549235633Sdim */ 550235633Sdim value = janfirst + (rulep->r_day - 1) * SECSPERDAY; 551263509Sdim if (leapyear && rulep->r_day >= 60) 552263509Sdim value += SECSPERDAY; 553263509Sdim break; 554263509Sdim 555263509Sdim case DAY_OF_YEAR: 556263509Sdim /* 557263509Sdim ** n - day of year. 558263509Sdim ** Just add SECSPERDAY times the day number to the time of 559263509Sdim ** January 1, midnight, to get the day. 560263509Sdim */ 561263509Sdim value = janfirst + rulep->r_day * SECSPERDAY; 562263509Sdim break; 563263509Sdim 564263509Sdim case MONTH_NTH_DAY_OF_WEEK: 565263509Sdim /* 566263509Sdim ** Mm.n.d - nth "dth day" of month m. 567263509Sdim */ 568263509Sdim value = janfirst; 569263509Sdim for (i = 0; i < rulep->r_mon - 1; ++i) 570263509Sdim value += mon_lengths[leapyear][i] * SECSPERDAY; 571263509Sdim 572263509Sdim /* 573263509Sdim ** Use Zeller's Congruence to get day-of-week of first day of 574235633Sdim ** month. 575263509Sdim */ 576263509Sdim m1 = (rulep->r_mon + 9) % 12 + 1; 577263509Sdim yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; 578235633Sdim yy1 = yy0 / 100; 579235633Sdim yy2 = yy0 % 100; 580235633Sdim dow = ((26 * m1 - 2) / 10 + 581235633Sdim 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; 582224135Sdim if (dow < 0) 583235633Sdim dow += DAYSPERWEEK; 584224135Sdim 585224135Sdim /* 586224135Sdim ** "dow" is the day-of-week of the first day of the month. Get 587224135Sdim ** the day-of-month (zero-origin) of the first "dow" day of the 588235633Sdim ** month. 589235633Sdim */ 590245431Sdim d = rulep->r_day - dow; 591245431Sdim if (d < 0) 592224135Sdim d += DAYSPERWEEK; 593224135Sdim for (i = 1; i < rulep->r_week; ++i) { 594224135Sdim if (d + DAYSPERWEEK >= 595224135Sdim mon_lengths[leapyear][rulep->r_mon - 1]) 596224135Sdim break; 597224135Sdim d += DAYSPERWEEK; 598224135Sdim } 599224135Sdim 600224135Sdim /* 601224135Sdim ** "d" is the day-of-month (zero-origin) of the day we want. 602224135Sdim */ 603224135Sdim value += d * SECSPERDAY; 604224135Sdim break; 605224135Sdim } 606224135Sdim 607224135Sdim /* 608224135Sdim ** "value" is the Epoch-relative time of 00:00:00 GMT on the day in 609224135Sdim ** question. To get the Epoch-relative time of the specified local 610224135Sdim ** time on that day, add the transition time and the current offset 611224135Sdim ** from GMT. 612224135Sdim */ 613224135Sdim return value + rulep->r_time + offset; 614224135Sdim} 615224135Sdim 616224135Sdim/* 617224135Sdim** Given a POSIX section 8-style TZ string, fill in the rule tables as 618224135Sdim** appropriate. 619224135Sdim*/ 620224135Sdim 621224135Sdimstatic int 622224135Sdimtzparse(name, sp, lastditch) 623224135Sdimconst char * name; 624224135Sdimregister struct state * const sp; 625224135Sdimconst int lastditch; 626224135Sdim{ 627224135Sdim const char * stdname; 628224135Sdim const char * dstname; 629224135Sdim int stdlen; 630224135Sdim int dstlen; 631224135Sdim long stdoffset; 632224135Sdim long dstoffset; 633263509Sdim register time_t * atp; 634224135Sdim register unsigned char * typep; 635224135Sdim register char * cp; 636235633Sdim register int load_result; 637224135Sdim 638263509Sdim stdname = name; 639224135Sdim if (lastditch) { 640263509Sdim stdlen = strlen(name); /* length of standard zone name */ 641263509Sdim name += stdlen; 642263509Sdim if (stdlen >= sizeof sp->chars) 643263509Sdim stdlen = (sizeof sp->chars) - 1; 644263509Sdim } else { 645263509Sdim name = getzname(name); 646263509Sdim stdlen = name - stdname; 647263509Sdim if (stdlen < 3) 648263509Sdim return -1; 649263509Sdim } 650263509Sdim if (*name == '\0') 651263509Sdim return -1; /* was "stdoffset = 0;" */ 652263509Sdim else { 653263509Sdim name = getoffset(name, &stdoffset); 654263509Sdim if (name == NULL) 655263509Sdim return -1; 656263509Sdim } 657263509Sdim load_result = tzload(TZDEFRULES, sp); 658224135Sdim if (load_result != 0) 659224135Sdim sp->leapcnt = 0; /* so, we're off a little */ 660224135Sdim if (*name != '\0') { 661224135Sdim dstname = name; 662224135Sdim name = getzname(name); 663224135Sdim dstlen = name - dstname; /* length of DST zone name */ 664224135Sdim if (dstlen < 3) 665224135Sdim return -1; 666224135Sdim if (*name != '\0' && *name != ',' && *name != ';') { 667235633Sdim name = getoffset(name, &dstoffset); 668224135Sdim if (name == NULL) 669224135Sdim return -1; 670224135Sdim } else dstoffset = stdoffset - SECSPERHOUR; 671224135Sdim if (*name == ',' || *name == ';') { 672224135Sdim struct rule start; 673224135Sdim struct rule end; 674224135Sdim register int year; 675235633Sdim register time_t janfirst; 676224135Sdim time_t starttime; 677224135Sdim time_t endtime; 678224135Sdim 679224135Sdim ++name; 680224135Sdim if ((name = getrule(name, &start)) == NULL) 681224135Sdim return -1; 682224135Sdim if (*name++ != ',') 683224135Sdim return -1; 684224135Sdim if ((name = getrule(name, &end)) == NULL) 685224135Sdim return -1; 686224135Sdim if (*name != '\0') 687224135Sdim return -1; 688224135Sdim sp->typecnt = 2; /* standard time and DST */ 689224135Sdim /* 690224135Sdim ** Two transitions per year, from EPOCH_YEAR to 2037. 691224135Sdim */ 692224135Sdim sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1); 693224135Sdim if (sp->timecnt > TZ_MAX_TIMES) 694224135Sdim return -1; 695224135Sdim sp->ttis[0].tt_gmtoff = -dstoffset; 696224135Sdim sp->ttis[0].tt_isdst = 1; 697224135Sdim sp->ttis[0].tt_abbrind = stdlen + 1; 698224135Sdim sp->ttis[1].tt_gmtoff = -stdoffset; 699224135Sdim sp->ttis[1].tt_isdst = 0; 700224135Sdim sp->ttis[1].tt_abbrind = 0; 701224135Sdim atp = sp->ats; 702224135Sdim typep = sp->types; 703224135Sdim janfirst = 0; 704224135Sdim for (year = EPOCH_YEAR; year <= 2037; ++year) { 705224135Sdim starttime = transtime(janfirst, year, &start, 706245431Sdim stdoffset); 707224135Sdim endtime = transtime(janfirst, year, &end, 708224135Sdim dstoffset); 709224135Sdim if (starttime > endtime) { 710224135Sdim *atp++ = endtime; 711224135Sdim *typep++ = 1; /* DST ends */ 712224135Sdim *atp++ = starttime; 713235633Sdim *typep++ = 0; /* DST begins */ 714235633Sdim } else { 715224135Sdim *atp++ = starttime; 716224135Sdim *typep++ = 0; /* DST begins */ 717224135Sdim *atp++ = endtime; 718224135Sdim *typep++ = 1; /* DST ends */ 719224135Sdim } 720224135Sdim janfirst += year_lengths[isleap(year)] * 721224135Sdim SECSPERDAY; 722224135Sdim } 723224135Sdim } else { 724224135Sdim int sawstd; 725224135Sdim int sawdst; 726224135Sdim long stdfix; 727263509Sdim long dstfix; 728224135Sdim long oldfix; 729224135Sdim int isdst; 730224135Sdim register int i; 731224135Sdim 732224135Sdim if (*name != '\0') 733224135Sdim return -1; 734224135Sdim if (load_result != 0) 735224135Sdim return -1; 736224135Sdim /* 737224135Sdim ** Compute the difference between the real and 738224135Sdim ** prototype standard and summer time offsets 739224135Sdim ** from GMT, and put the real standard and summer 740224135Sdim ** time offsets into the rules in place of the 741224135Sdim ** prototype offsets. 742224135Sdim */ 743224135Sdim sawstd = FALSE; 744224135Sdim sawdst = FALSE; 745224135Sdim stdfix = 0; 746224135Sdim dstfix = 0; 747224135Sdim for (i = 0; i < sp->typecnt; ++i) { 748224135Sdim if (sp->ttis[i].tt_isdst) { 749224135Sdim oldfix = dstfix; 750224135Sdim dstfix = sp->ttis[i].tt_gmtoff + 751224135Sdim dstoffset; 752224135Sdim if (sawdst && (oldfix != dstfix)) 753224135Sdim return -1; 754224135Sdim sp->ttis[i].tt_gmtoff = -dstoffset; 755224135Sdim sp->ttis[i].tt_abbrind = stdlen + 1; 756224135Sdim sawdst = TRUE; 757224135Sdim } else { 758224135Sdim oldfix = stdfix; 759224135Sdim stdfix = sp->ttis[i].tt_gmtoff + 760224135Sdim stdoffset; 761224135Sdim if (sawstd && (oldfix != stdfix)) 762224135Sdim return -1; 763224135Sdim sp->ttis[i].tt_gmtoff = -stdoffset; 764224135Sdim sp->ttis[i].tt_abbrind = 0; 765224135Sdim sawstd = TRUE; 766224135Sdim } 767224135Sdim } 768224135Sdim /* 769224135Sdim ** Make sure we have both standard and summer time. 770224135Sdim */ 771224135Sdim if (!sawdst || !sawstd) 772224135Sdim return -1; 773224135Sdim /* 774224135Sdim ** Now correct the transition times by shifting 775224135Sdim ** them by the difference between the real and 776224135Sdim ** prototype offsets. Note that this difference 777224135Sdim ** can be different in standard and summer time; 778224135Sdim ** the prototype probably has a 1-hour difference 779224135Sdim ** between standard and summer time, but a different 780235633Sdim ** difference can be specified in TZ. 781224135Sdim */ 782224135Sdim isdst = FALSE; /* we start in standard time */ 783224135Sdim for (i = 0; i < sp->timecnt; ++i) { 784224135Sdim register const struct ttinfo * ttisp; 785224135Sdim 786224135Sdim /* 787224135Sdim ** If summer time is in effect, and the 788235633Sdim ** transition time was not specified as 789224135Sdim ** standard time, add the summer time 790224135Sdim ** offset to the transition time; 791224135Sdim ** otherwise, add the standard time offset 792224135Sdim ** to the transition time. 793224135Sdim */ 794224135Sdim ttisp = &sp->ttis[sp->types[i]]; 795224135Sdim sp->ats[i] += 796224135Sdim (isdst && !ttisp->tt_ttisstd) ? 797224135Sdim dstfix : stdfix; 798224135Sdim isdst = ttisp->tt_isdst; 799224135Sdim } 800224135Sdim } 801224135Sdim } else { 802224135Sdim dstlen = 0; 803224135Sdim sp->typecnt = 1; /* only standard time */ 804224135Sdim sp->timecnt = 0; 805224135Sdim sp->ttis[0].tt_gmtoff = -stdoffset; 806224135Sdim sp->ttis[0].tt_isdst = 0; 807224135Sdim sp->ttis[0].tt_abbrind = 0; 808224135Sdim } 809224135Sdim sp->charcnt = stdlen + 1; 810224135Sdim if (dstlen != 0) 811263509Sdim sp->charcnt += dstlen + 1; 812263509Sdim if (sp->charcnt > sizeof sp->chars) 813263509Sdim return -1; 814224135Sdim cp = sp->chars; 815224135Sdim (void) strncpy(cp, stdname, stdlen); 816224135Sdim cp += stdlen; 817224135Sdim *cp++ = '\0'; 818224135Sdim if (dstlen != 0) { 819224135Sdim (void) strncpy(cp, dstname, dstlen); 820224135Sdim *(cp + dstlen) = '\0'; 821224135Sdim } 822224135Sdim return 0; 823224135Sdim} 824263509Sdim 825224135Sdimstatic void 826224135Sdimgmtload(sp) 827263509Sdimstruct state * const sp; 828263509Sdim{ 829263509Sdim if (tzload(GMT, sp) != 0) 830224135Sdim (void) tzparse(GMT, sp, TRUE); 831263509Sdim} 832263509Sdim 833224135Sdim#ifndef STD_INSPIRED 834224135Sdimstatic 835224135Sdim#endif /* !defined STD_INSPIRED */ 836224135Sdimvoid 837224135Sdimtzsetwall() 838252723Sdim{ 839252723Sdim lcl_is_set = TRUE; 840252723Sdim#ifdef ALL_STATE 841252723Sdim if (lclptr == NULL) { 842252723Sdim lclptr = (struct state *) malloc(sizeof *lclptr); 843252723Sdim if (lclptr == NULL) { 844252723Sdim settzname(); /* all we can do */ 845252723Sdim return; 846252723Sdim } 847252723Sdim } 848252723Sdim#endif /* defined ALL_STATE */ 849252723Sdim if (tzload((char *) NULL, lclptr) != 0) 850252723Sdim gmtload(lclptr); 851224135Sdim settzname(); 852235633Sdim} 853235633Sdim 854235633Sdimvoid 855235633Sdimtzset() 856235633Sdim{ 857224135Sdim register const char * name; 858224135Sdim 859224135Sdim name = getenv("TZ"); 860224135Sdim if (name == NULL) { 861224135Sdim tzsetwall(); 862235633Sdim return; 863235633Sdim } 864235633Sdim lcl_is_set = TRUE; 865235633Sdim#ifdef ALL_STATE 866235633Sdim if (lclptr == NULL) { 867235633Sdim lclptr = (struct state *) malloc(sizeof *lclptr); 868235633Sdim if (lclptr == NULL) { 869224135Sdim settzname(); /* all we can do */ 870224135Sdim return; 871224135Sdim } 872224135Sdim } 873224135Sdim#endif /* defined ALL_STATE */ 874224135Sdim if (*name == '\0') { 875235633Sdim /* 876224135Sdim ** User wants it fast rather than right. 877224135Sdim */ 878245431Sdim lclptr->leapcnt = 0; /* so, we're off a little */ 879245431Sdim lclptr->timecnt = 0; 880245431Sdim lclptr->ttis[0].tt_gmtoff = 0; 881224135Sdim lclptr->ttis[0].tt_abbrind = 0; 882224135Sdim (void) strcpy(lclptr->chars, GMT); 883224135Sdim } else if (tzload(name, lclptr) != 0) 884235633Sdim if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) 885224135Sdim (void) gmtload(lclptr); 886224135Sdim settzname(); 887263509Sdim} 888263509Sdim 889263509Sdim/* 890263509Sdim** The easy way to behave "as if no library function calls" localtime 891263509Sdim** is to not call it--so we drop its guts into "localsub", which can be 892263509Sdim** freely called. (And no, the PANS doesn't require the above behavior-- 893263509Sdim** but it *is* desirable.) 894263509Sdim** 895263509Sdim** The unused offset argument is for the benefit of mktime variants. 896263509Sdim*/ 897263509Sdim 898263509Sdim/*ARGSUSED*/ 899263509Sdimstatic void 900263509Sdimlocalsub(timep, offset, tmp) 901263509Sdimconst time_t * const timep; 902263509Sdimconst long offset; 903263509Sdimstruct tm * const tmp; 904263509Sdim{ 905263509Sdim register const struct state * sp; 906263509Sdim register const struct ttinfo * ttisp; 907263509Sdim register int i; 908263509Sdim const time_t t = *timep; 909263509Sdim 910263509Sdim if (!lcl_is_set) 911263509Sdim tzset(); 912263509Sdim sp = lclptr; 913263509Sdim#ifdef ALL_STATE 914263509Sdim if (sp == NULL) { 915263509Sdim gmtsub(timep, offset, tmp); 916263509Sdim return; 917263509Sdim } 918263509Sdim#endif /* defined ALL_STATE */ 919263509Sdim if (sp->timecnt == 0 || t < sp->ats[0]) { 920263509Sdim i = 0; 921263509Sdim while (sp->ttis[i].tt_isdst) 922263509Sdim if (++i >= sp->typecnt) { 923263509Sdim i = 0; 924263509Sdim break; 925263509Sdim } 926263509Sdim } else { 927263509Sdim for (i = 1; i < sp->timecnt; ++i) 928263509Sdim if (t < sp->ats[i]) 929263509Sdim break; 930263509Sdim i = sp->types[i - 1]; 931263509Sdim } 932263509Sdim ttisp = &sp->ttis[i]; 933263509Sdim /* 934263509Sdim ** To get (wrong) behavior that's compatible with System V Release 2.0 935224135Sdim ** you'd replace the statement below with 936224135Sdim ** t += ttisp->tt_gmtoff; 937224135Sdim ** timesub(&t, 0L, sp, tmp); 938224135Sdim */ 939224135Sdim timesub(&t, ttisp->tt_gmtoff, sp, tmp); 940224135Sdim tmp->tm_isdst = ttisp->tt_isdst; 941224135Sdim tzname[tmp->tm_isdst] = (char *) &sp->chars[ttisp->tt_abbrind]; 942224135Sdim#ifdef TM_ZONE 943224135Sdim tmp->TM_ZONE = (char *)&sp->chars[ttisp->tt_abbrind]; 944224135Sdim#endif /* defined TM_ZONE */ 945224135Sdim} 946224135Sdim 947224135Sdimstruct tm * 948245431Sdimlocaltime(timep) 949245431Sdimconst time_t * const timep; 950245431Sdim{ 951245431Sdim static struct tm tm; 952245431Sdim 953245431Sdim localsub(timep, 0L, &tm); 954245431Sdim return &tm; 955245431Sdim} 956245431Sdim 957245431Sdim/* 958245431Sdim** gmtsub is to gmtime as localsub is to localtime. 959245431Sdim*/ 960245431Sdim 961245431Sdimstatic void 962245431Sdimgmtsub(timep, offset, tmp) 963245431Sdimconst time_t * const timep; 964245431Sdimconst long offset; 965245431Sdimstruct tm * const tmp; 966245431Sdim{ 967245431Sdim if (!gmt_is_set) { 968245431Sdim gmt_is_set = TRUE; 969245431Sdim#ifdef ALL_STATE 970245431Sdim gmtptr = (struct state *) malloc(sizeof *gmtptr); 971245431Sdim if (gmtptr != NULL) 972245431Sdim#endif /* defined ALL_STATE */ 973245431Sdim gmtload(gmtptr); 974224135Sdim } 975224135Sdim timesub(timep, offset, gmtptr, tmp); 976224135Sdim#ifdef TM_ZONE 977224135Sdim /* 978224135Sdim ** Could get fancy here and deliver something such as 979224135Sdim ** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero, 980224135Sdim ** but this is no time for a treasure hunt. 981224135Sdim */ 982224135Sdim if (offset != 0) 983224135Sdim tmp->TM_ZONE = WILDABBR; 984224135Sdim else { 985224135Sdim#ifdef ALL_STATE 986224135Sdim if (gmtptr == NULL) 987224135Sdim tmp->TM_ZONE = GMT; 988224135Sdim else tmp->TM_ZONE = gmtptr->chars; 989224135Sdim#endif /* defined ALL_STATE */ 990224135Sdim#ifndef ALL_STATE 991224135Sdim tmp->TM_ZONE = gmtptr->chars; 992224135Sdim#endif /* State Farm */ 993235633Sdim } 994224135Sdim#endif /* defined TM_ZONE */ 995224135Sdim} 996224135Sdim 997224135Sdimstruct tm * 998224135Sdimgmtime(timep) 999224135Sdimconst time_t * const timep; 1000224135Sdim{ 1001224135Sdim static struct tm tm; 1002224135Sdim 1003224135Sdim gmtsub(timep, 0L, &tm); 1004224135Sdim return &tm; 1005224135Sdim} 1006235633Sdim 1007224135Sdim#ifdef STD_INSPIRED 1008224135Sdim 1009224135Sdimstruct tm * 1010224135Sdimofftime(timep, offset) 1011224135Sdimconst time_t * const timep; 1012224135Sdimconst long offset; 1013224135Sdim{ 1014224135Sdim static struct tm tm; 1015224135Sdim 1016224135Sdim gmtsub(timep, offset, &tm); 1017224135Sdim return &tm; 1018224135Sdim} 1019224135Sdim 1020224135Sdim#endif /* defined STD_INSPIRED */ 1021224135Sdim 1022224135Sdimstatic void 1023224135Sdimtimesub(timep, offset, sp, tmp) 1024224135Sdimconst time_t * const timep; 1025224135Sdimconst long offset; 1026224135Sdimregister const struct state * const sp; 1027224135Sdimregister struct tm * const tmp; 1028224135Sdim{ 1029224135Sdim register const struct lsinfo * lp; 1030224135Sdim register long days; 1031224135Sdim register long rem; 1032224135Sdim register int y; 1033235633Sdim register int yleap; 1034224135Sdim register const int * ip; 1035224135Sdim register long corr; 1036224135Sdim register int hit; 1037224135Sdim register int i; 1038224135Sdim 1039224135Sdim corr = 0; 1040224135Sdim hit = 0; 1041224135Sdim#ifdef ALL_STATE 1042224135Sdim i = (sp == NULL) ? 0 : sp->leapcnt; 1043224135Sdim#endif /* defined ALL_STATE */ 1044224135Sdim#ifndef ALL_STATE 1045224135Sdim i = sp->leapcnt; 1046224135Sdim#endif /* State Farm */ 1047224135Sdim while (--i >= 0) { 1048224135Sdim lp = &sp->lsis[i]; 1049224135Sdim if (*timep >= lp->ls_trans) { 1050224135Sdim if (*timep == lp->ls_trans) { 1051224135Sdim hit = ((i == 0 && lp->ls_corr > 0) || 1052224135Sdim lp->ls_corr > sp->lsis[i - 1].ls_corr); 1053224135Sdim if (hit) 1054224135Sdim while (i > 0 && 1055252723Sdim sp->lsis[i].ls_trans == 1056252723Sdim sp->lsis[i - 1].ls_trans + 1 && 1057252723Sdim sp->lsis[i].ls_corr == 1058252723Sdim sp->lsis[i - 1].ls_corr + 1) { 1059224135Sdim ++hit; 1060224135Sdim --i; 1061224135Sdim } 1062224135Sdim } 1063263509Sdim corr = lp->ls_corr; 1064263509Sdim break; 1065224135Sdim } 1066224135Sdim } 1067235633Sdim days = *timep / SECSPERDAY; 1068235633Sdim rem = *timep % SECSPERDAY; 1069224135Sdim#ifdef mc68k 1070224135Sdim if (*timep == 0x80000000) { 1071224135Sdim /* 1072224135Sdim ** A 3B1 muffs the division on the most negative number. 1073224135Sdim */ 1074224135Sdim days = -24855; 1075224135Sdim rem = -11648; 1076224135Sdim } 1077224135Sdim#endif /* mc68k */ 1078224135Sdim rem += (offset - corr); 1079224135Sdim while (rem < 0) { 1080224135Sdim rem += SECSPERDAY; 1081224135Sdim --days; 1082224135Sdim } 1083224135Sdim while (rem >= SECSPERDAY) { 1084235633Sdim rem -= SECSPERDAY; 1085235633Sdim ++days; 1086235633Sdim } 1087235633Sdim tmp->tm_hour = (int) (rem / SECSPERHOUR); 1088224135Sdim rem = rem % SECSPERHOUR; 1089224135Sdim tmp->tm_min = (int) (rem / SECSPERMIN); 1090224135Sdim tmp->tm_sec = (int) (rem % SECSPERMIN); 1091224135Sdim if (hit) 1092235633Sdim /* 1093235633Sdim ** A positive leap second requires a special 1094224135Sdim ** representation. This uses "... ??:59:60" et seq. 1095224135Sdim */ 1096224135Sdim tmp->tm_sec += hit; 1097224135Sdim tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); 1098224135Sdim if (tmp->tm_wday < 0) 1099224135Sdim tmp->tm_wday += DAYSPERWEEK; 1100224135Sdim y = EPOCH_YEAR; 1101224135Sdim if (days >= 0) 1102224135Sdim for ( ; ; ) { 1103224135Sdim yleap = isleap(y); 1104224135Sdim if (days < (long) year_lengths[yleap]) 1105224135Sdim break; 1106224135Sdim ++y; 1107224135Sdim days = days - (long) year_lengths[yleap]; 1108224135Sdim } 1109224135Sdim else do { 1110224135Sdim --y; 1111224135Sdim yleap = isleap(y); 1112224135Sdim days = days + (long) year_lengths[yleap]; 1113224135Sdim } while (days < 0); 1114224135Sdim tmp->tm_year = y - TM_YEAR_BASE; 1115224135Sdim tmp->tm_yday = (int) days; 1116224135Sdim ip = mon_lengths[yleap]; 1117224135Sdim for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon)) 1118224135Sdim days = days - (long) ip[tmp->tm_mon]; 1119224135Sdim tmp->tm_mday = (int) (days + 1); 1120224135Sdim tmp->tm_isdst = 0; 1121224135Sdim#ifdef TM_GMTOFF 1122224135Sdim tmp->TM_GMTOFF = offset; 1123224135Sdim#endif /* defined TM_GMTOFF */ 1124224135Sdim} 1125224135Sdim 1126224135Sdimchar * 1127224135Sdimctime(timep) 1128226890Sdimconst time_t * const timep; 1129224135Sdim{ 1130226890Sdim return asctime(localtime(timep)); 1131224135Sdim} 1132224135Sdim 1133224135Sdim/* 1134224135Sdim** Adapted from code provided by Robert Elz, who writes: 1135224135Sdim** The "best" way to do mktime I think is based on an idea of Bob 1136224135Sdim** Kridle's (so its said...) from a long time ago. (mtxinu!kridle now). 1137224135Sdim** It does a binary search of the time_t space. Since time_t's are 1138245431Sdim** just 32 bits, its a max of 32 iterations (even at 64 bits it 1139224135Sdim** would still be very reasonable). 1140224135Sdim*/ 1141224135Sdim 1142224135Sdim#ifndef WRONG 1143224135Sdim#define WRONG (-1) 1144224135Sdim#endif /* !defined WRONG */ 1145226890Sdim 1146226890Sdim/* 1147226890Sdim** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com). 1148226890Sdim*/ 1149226890Sdim 1150226890Sdimstatic int 1151226890Sdimincrement_overflow(number, delta) 1152226890Sdimint * number; 1153226890Sdimint delta; 1154235633Sdim{ 1155235633Sdim int number0; 1156235633Sdim 1157235633Sdim number0 = *number; 1158235633Sdim *number += delta; 1159235633Sdim return (*number < number0) != (delta < 0); 1160235633Sdim} 1161235633Sdim 1162235633Sdimstatic int 1163224135Sdimnormalize_overflow(tensptr, unitsptr, base) 1164224135Sdimint * const tensptr; 1165224135Sdimint * const unitsptr; 1166224135Sdimconst int base; 1167224135Sdim{ 1168224135Sdim register int tensdelta; 1169224135Sdim 1170224135Sdim tensdelta = (*unitsptr >= 0) ? 1171224135Sdim (*unitsptr / base) : 1172224135Sdim (-1 - (-1 - *unitsptr) / base); 1173224135Sdim *unitsptr -= tensdelta * base; 1174224135Sdim return increment_overflow(tensptr, tensdelta); 1175224135Sdim} 1176224135Sdim 1177224135Sdimstatic int 1178224135Sdimtmcomp(atmp, btmp) 1179224135Sdimregister const struct tm * const atmp; 1180224135Sdimregister const struct tm * const btmp; 1181235633Sdim{ 1182226890Sdim register int result; 1183226890Sdim 1184224135Sdim if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && 1185224135Sdim (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && 1186224135Sdim (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && 1187224135Sdim (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && 1188224135Sdim (result = (atmp->tm_min - btmp->tm_min)) == 0) 1189224135Sdim result = atmp->tm_sec - btmp->tm_sec; 1190224135Sdim return result; 1191224135Sdim} 1192224135Sdim 1193224135Sdimstatic time_t 1194224135Sdimtime2(tmp, funcp, offset, okayp) 1195224135Sdimstruct tm * const tmp; 1196224135Sdimvoid (* const funcp)(); 1197224135Sdimconst long offset; 1198224135Sdimint * const okayp; 1199224135Sdim{ 1200224135Sdim register const struct state * sp; 1201224135Sdim register int dir; 1202224135Sdim register int bits; 1203224135Sdim register int i, j ; 1204224135Sdim register int saved_seconds; 1205224135Sdim time_t newt; 1206224135Sdim time_t t; 1207263509Sdim struct tm yourtm, mytm; 1208263509Sdim 1209263509Sdim *okayp = FALSE; 1210263509Sdim yourtm = *tmp; 1211263509Sdim if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) 1212263509Sdim return WRONG; 1213263509Sdim if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) 1214224135Sdim return WRONG; 1215235633Sdim if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR)) 1216224135Sdim return WRONG; 1217224135Sdim /* 1218224135Sdim ** Turn yourtm.tm_year into an actual year number for now. 1219224135Sdim ** It is converted back to an offset from TM_YEAR_BASE later. 1220224135Sdim */ 1221224135Sdim if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE)) 1222224135Sdim return WRONG; 1223224135Sdim while (yourtm.tm_mday <= 0) { 1224224135Sdim if (increment_overflow(&yourtm.tm_year, -1)) 1225224135Sdim return WRONG; 1226224135Sdim yourtm.tm_mday += year_lengths[isleap(yourtm.tm_year)]; 1227224135Sdim } 1228224135Sdim while (yourtm.tm_mday > DAYSPERLYEAR) { 1229224135Sdim yourtm.tm_mday -= year_lengths[isleap(yourtm.tm_year)]; 1230224135Sdim if (increment_overflow(&yourtm.tm_year, 1)) 1231224135Sdim return WRONG; 1232224135Sdim } 1233224135Sdim for ( ; ; ) { 1234224135Sdim i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon]; 1235224135Sdim if (yourtm.tm_mday <= i) 1236224135Sdim break; 1237226890Sdim yourtm.tm_mday -= i; 1238226890Sdim if (++yourtm.tm_mon >= MONSPERYEAR) { 1239226890Sdim yourtm.tm_mon = 0; 1240226890Sdim if (increment_overflow(&yourtm.tm_year, 1)) 1241226890Sdim return WRONG; 1242226890Sdim } 1243226890Sdim } 1244224135Sdim if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) 1245224135Sdim return WRONG; 1246224135Sdim if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { 1247224135Sdim /* 1248224135Sdim ** We can't set tm_sec to 0, because that might push the 1249224135Sdim ** time below the minimum representable time. 1250224135Sdim ** Set tm_sec to 59 instead. 1251224135Sdim ** This assumes that the minimum representable time is 1252235633Sdim ** not in the same minute that a leap second was deleted from, 1253224135Sdim ** which is a safer assumption than using 58 would be. 1254224135Sdim */ 1255224135Sdim if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) 1256224135Sdim return WRONG; 1257245431Sdim saved_seconds = yourtm.tm_sec; 1258252723Sdim yourtm.tm_sec = SECSPERMIN - 1; 1259224135Sdim } else { 1260252723Sdim saved_seconds = yourtm.tm_sec; 1261224135Sdim yourtm.tm_sec = 0; 1262224135Sdim } 1263224135Sdim /* 1264224135Sdim ** Calculate the number of magnitude bits in a time_t 1265224135Sdim ** (this works regardless of whether time_t is 1266224135Sdim ** signed or unsigned, though lint complains if unsigned). 1267245431Sdim */ 1268245431Sdim for (bits = 0, t = 1; t > 0; ++bits, t <<= 1) 1269245431Sdim continue; 1270235633Sdim /* 1271235633Sdim ** If time_t is signed, then 0 is the median value, 1272235633Sdim ** if time_t is unsigned, then 1 << bits is median. 1273224135Sdim */ 1274224135Sdim t = (t < 0) ? 0 : ((time_t) 1 << bits); 1275224135Sdim for ( ; ; ) { 1276224135Sdim (*funcp)(&t, offset, &mytm); 1277235633Sdim dir = tmcomp(&mytm, &yourtm); 1278235633Sdim if (dir != 0) { 1279235633Sdim if (bits-- < 0) 1280235633Sdim return WRONG; 1281235633Sdim if (bits < 0) 1282235633Sdim --t; 1283263509Sdim else if (dir > 0) 1284263509Sdim t -= (time_t) 1 << bits; 1285263509Sdim else t += (time_t) 1 << bits; 1286263509Sdim continue; 1287235633Sdim } 1288235633Sdim if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) 1289235633Sdim break; 1290235633Sdim /* 1291235633Sdim ** Right time, wrong type. 1292235633Sdim ** Hunt for right time, right type. 1293224135Sdim ** It's okay to guess wrong since the guess 1294224135Sdim ** gets checked. 1295224135Sdim */ 1296224135Sdim /* 1297224135Sdim ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. 1298224135Sdim */ 1299224135Sdim sp = (const struct state *) 1300224135Sdim (((void *) funcp == (void *) localsub) ? 1301224135Sdim lclptr : gmtptr); 1302224135Sdim#ifdef ALL_STATE 1303224135Sdim if (sp == NULL) 1304224135Sdim return WRONG; 1305224135Sdim#endif /* defined ALL_STATE */ 1306224135Sdim for (i = 0; i < sp->typecnt; ++i) { 1307224135Sdim if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) 1308235633Sdim continue; 1309235633Sdim for (j = 0; j < sp->typecnt; ++j) { 1310224135Sdim if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) 1311224135Sdim continue; 1312224135Sdim newt = t + sp->ttis[j].tt_gmtoff - 1313224135Sdim sp->ttis[i].tt_gmtoff; 1314224135Sdim (*funcp)(&newt, offset, &mytm); 1315224135Sdim if (tmcomp(&mytm, &yourtm) != 0) 1316224135Sdim continue; 1317224135Sdim if (mytm.tm_isdst != yourtm.tm_isdst) 1318224135Sdim continue; 1319224135Sdim /* 1320224135Sdim ** We have a match. 1321224135Sdim */ 1322224135Sdim t = newt; 1323224135Sdim goto label; 1324224135Sdim } 1325224135Sdim } 1326224135Sdim return WRONG; 1327224135Sdim } 1328224135Sdimlabel: 1329224135Sdim newt = t + saved_seconds; 1330224135Sdim if ((newt < t) != (saved_seconds < 0)) 1331224135Sdim return WRONG; 1332224135Sdim t = newt; 1333224135Sdim (*funcp)(&t, offset, tmp); 1334224135Sdim *okayp = TRUE; 1335224135Sdim return t; 1336224135Sdim} 1337224135Sdim 1338224135Sdimstatic time_t 1339235633Sdimtime1(tmp, funcp, offset) 1340235633Sdimstruct tm * const tmp; 1341235633Sdimvoid (* const funcp)(); 1342235633Sdimconst long offset; 1343235633Sdim{ 1344235633Sdim register time_t t; 1345235633Sdim register const struct state * sp; 1346235633Sdim register int samei, otheri; 1347235633Sdim int okay; 1348224135Sdim 1349235633Sdim if (tmp->tm_isdst > 1) 1350224135Sdim tmp->tm_isdst = 1; 1351245431Sdim t = time2(tmp, funcp, offset, &okay); 1352235633Sdim#ifdef PCTS 1353224135Sdim /* 1354224135Sdim ** PCTS code courtesy Grant Sullivan (grant@osf.org). 1355224135Sdim */ 1356224135Sdim if (okay) 1357224135Sdim return t; 1358224135Sdim if (tmp->tm_isdst < 0) 1359245431Sdim tmp->tm_isdst = 0; /* reset to std and try again */ 1360224135Sdim#endif /* defined PCTS */ 1361245431Sdim#ifndef PCTS 1362245431Sdim if (okay || tmp->tm_isdst < 0) 1363224135Sdim return t; 1364245431Sdim#endif /* !defined PCTS */ 1365245431Sdim /* 1366245431Sdim ** We're supposed to assume that somebody took a time of one type 1367245431Sdim ** and did some math on it that yielded a "struct tm" that's bad. 1368252723Sdim ** We try to divine the type they started from and adjust to the 1369252723Sdim ** type they need. 1370245431Sdim */ 1371245431Sdim /* 1372245431Sdim ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. 1373245431Sdim */ 1374224135Sdim sp = (const struct state *) (((void *) funcp == (void *) localsub) ? 1375245431Sdim lclptr : gmtptr); 1376252723Sdim#ifdef ALL_STATE 1377245431Sdim if (sp == NULL) 1378245431Sdim return WRONG; 1379245431Sdim#endif /* defined ALL_STATE */ 1380245431Sdim for (samei = 0; samei < sp->typecnt; ++samei) { 1381245431Sdim if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) 1382245431Sdim continue; 1383245431Sdim for (otheri = 0; otheri < sp->typecnt; ++otheri) { 1384245431Sdim if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) 1385245431Sdim continue; 1386263509Sdim tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - 1387245431Sdim sp->ttis[samei].tt_gmtoff; 1388245431Sdim tmp->tm_isdst = !tmp->tm_isdst; 1389245431Sdim t = time2(tmp, funcp, offset, &okay); 1390245431Sdim if (okay) 1391224135Sdim return t; 1392224135Sdim tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - 1393224135Sdim sp->ttis[samei].tt_gmtoff; 1394224135Sdim tmp->tm_isdst = !tmp->tm_isdst; 1395224135Sdim } 1396226890Sdim } 1397226890Sdim return WRONG; 1398226890Sdim} 1399226890Sdim 1400226890Sdimtime_t 1401226890Sdimmktime(tmp) 1402226890Sdimstruct tm * const tmp; 1403224135Sdim{ 1404224135Sdim return time1(tmp, localsub, 0L); 1405224135Sdim} 1406224135Sdim 1407224135Sdim#ifdef STD_INSPIRED 1408224135Sdim 1409224135Sdimtime_t 1410224135Sdimtimelocal(tmp) 1411224135Sdimstruct tm * const tmp; 1412224135Sdim{ 1413224135Sdim tmp->tm_isdst = -1; /* in case it wasn't initialized */ 1414224135Sdim return mktime(tmp); 1415224135Sdim} 1416224135Sdim 1417224135Sdimtime_t 1418224135Sdimtimegm(tmp) 1419224135Sdimstruct tm * const tmp; 1420224135Sdim{ 1421224135Sdim tmp->tm_isdst = 0; 1422224135Sdim return time1(tmp, gmtsub, 0L); 1423224135Sdim} 1424235633Sdim 1425235633Sdimtime_t 1426224135Sdimtimeoff(tmp, offset) 1427224135Sdimstruct tm * const tmp; 1428224135Sdimconst long offset; 1429224135Sdim{ 1430224135Sdim tmp->tm_isdst = 0; 1431224135Sdim return time1(tmp, gmtsub, offset); 1432224135Sdim} 1433224135Sdim 1434224135Sdim#endif /* defined STD_INSPIRED */ 1435224135Sdim 1436224135Sdim#ifdef CMUCS 1437263509Sdim 1438263509Sdim/* 1439263509Sdim** The following is supplied for compatibility with 1440224135Sdim** previous versions of the CMUCS runtime library. 1441224135Sdim*/ 1442224135Sdim 1443224135Sdimlong 1444224135Sdimgtime(tmp) 1445235633Sdimstruct tm * const tmp; 1446235633Sdim{ 1447235633Sdim const time_t t = mktime(tmp); 1448224135Sdim 1449224135Sdim if (t == WRONG) 1450224135Sdim return -1; 1451224135Sdim return t; 1452224135Sdim} 1453224135Sdim 1454224135Sdim#endif /* defined CMUCS */ 1455224135Sdim 1456224135Sdim/* 1457224135Sdim** XXX--is the below the right way to conditionalize?? 1458224135Sdim*/ 1459224135Sdim 1460224135Sdim#ifdef STD_INSPIRED 1461224135Sdim 1462224135Sdim/* 1463224135Sdim** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599 1464224135Sdim** shall correspond to "Wed Dec 31 23:59:59 GMT 1986", which 1465224135Sdim** is not the case if we are accounting for leap seconds. 1466224135Sdim** So, we provide the following conversion routines for use 1467224135Sdim** when exchanging timestamps with POSIX conforming systems. 1468224135Sdim*/ 1469224135Sdim 1470224135Sdimstatic long 1471224135Sdimleapcorr(timep) 1472224135Sdimtime_t * timep; 1473224135Sdim{ 1474224135Sdim register struct state * sp; 1475224135Sdim register struct lsinfo * lp; 1476224135Sdim register int i; 1477224135Sdim 1478224135Sdim if (!lcl_is_set) 1479224135Sdim (void) tzset(); 1480224135Sdim sp = lclptr; 1481224135Sdim i = sp->leapcnt; 1482224135Sdim while (--i >= 0) { 1483224135Sdim lp = &sp->lsis[i]; 1484224135Sdim if (*timep >= lp->ls_trans) 1485224135Sdim return lp->ls_corr; 1486263509Sdim } 1487263509Sdim return 0; 1488263509Sdim} 1489224135Sdim 1490224135Sdimtime_t 1491224135Sdimtime2posix(t) 1492224135Sdimtime_t t; 1493224135Sdim{ 1494224135Sdim return t - leapcorr(&t); 1495224135Sdim} 1496224135Sdim 1497224135Sdimtime_t 1498224135Sdimposix2time(t) 1499224135Sdimtime_t t; 1500224135Sdim{ 1501235633Sdim time_t x; 1502235633Sdim time_t y; 1503224135Sdim 1504224135Sdim /* 1505224135Sdim ** For a positive leap second hit, the result 1506224135Sdim ** is not unique. For a negative leap second 1507224135Sdim ** hit, the corresponding time doesn't exist, 1508224135Sdim ** so we return an adjacent second. 1509224135Sdim */ 1510224135Sdim x = t + leapcorr(&t); 1511224135Sdim y = x - leapcorr(&x); 1512224135Sdim if (y < t) { 1513224135Sdim do { 1514224135Sdim x++; 1515224135Sdim y = x - leapcorr(&x); 1516224135Sdim } while (y < t); 1517224135Sdim if (t != y) 1518224135Sdim return x - 1; 1519224135Sdim } else if (y > t) { 1520224135Sdim do { 1521224135Sdim --x; 1522224135Sdim y = x - leapcorr(&x); 1523224135Sdim } while (y > t); 1524224135Sdim if (t != y) 1525224135Sdim return x + 1; 1526224135Sdim } 1527224135Sdim return x; 1528224135Sdim} 1529224135Sdim 1530224135Sdim#endif /* defined STD_INSPIRED */ 1531224135Sdim