localtime.c revision 9936
12708Swollman#ifndef lint 22708Swollman#ifndef NOID 39936Swollmanstatic char elsieid[] = "@(#)localtime.c 7.44"; 42708Swollman#endif /* !defined NOID */ 52708Swollman#endif /* !defined lint */ 62708Swollman 72708Swollman/* 82708Swollman** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu). 92708Swollman** POSIX-style TZ environment variable handling from Guy Harris 102708Swollman** (guy@auspex.com). 112708Swollman*/ 122708Swollman 132708Swollman/*LINTLIBRARY*/ 142708Swollman 152708Swollman#include "private.h" 162708Swollman#include "tzfile.h" 172708Swollman#include "fcntl.h" 182708Swollman 199936Swollman/* 209936Swollman** SunOS 4.1.1 headers lack O_BINARY. 219936Swollman*/ 222708Swollman 232708Swollman#ifdef O_BINARY 242708Swollman#define OPEN_MODE (O_RDONLY | O_BINARY) 252708Swollman#endif /* defined O_BINARY */ 262708Swollman#ifndef O_BINARY 272708Swollman#define OPEN_MODE O_RDONLY 282708Swollman#endif /* !defined O_BINARY */ 292708Swollman 302708Swollman#ifndef WILDABBR 312708Swollman/* 322708Swollman** Someone might make incorrect use of a time zone abbreviation: 332708Swollman** 1. They might reference tzname[0] before calling tzset (explicitly 349936Swollman** or implicitly). 352708Swollman** 2. They might reference tzname[1] before calling tzset (explicitly 369936Swollman** or implicitly). 372708Swollman** 3. They might reference tzname[1] after setting to a time zone 382708Swollman** in which Daylight Saving Time is never observed. 392708Swollman** 4. They might reference tzname[0] after setting to a time zone 402708Swollman** in which Standard Time is never observed. 412708Swollman** 5. They might reference tm.TM_ZONE after calling offtime. 422708Swollman** What's best to do in the above cases is open to debate; 432708Swollman** for now, we just set things up so that in any of the five cases 442708Swollman** WILDABBR is used. Another possibility: initialize tzname[0] to the 452708Swollman** string "tzname[0] used before set", and similarly for the other cases. 462708Swollman** And another: initialize tzname[0] to "ERA", with an explanation in the 472708Swollman** manual page of what this "time zone abbreviation" means (doing this so 482708Swollman** that tzname[0] has the "normal" length of three characters). 492708Swollman*/ 502708Swollman#define WILDABBR " " 512708Swollman#endif /* !defined WILDABBR */ 522708Swollman 539936Swollmanstatic char wildabbr[] = "WILDABBR"; 542708Swollman 559936Swollmanstatic const char gmt[] = "GMT"; 569936Swollman 572708Swollmanstruct ttinfo { /* time type information */ 582708Swollman long tt_gmtoff; /* GMT offset in seconds */ 592708Swollman int tt_isdst; /* used to set tm_isdst */ 602708Swollman int tt_abbrind; /* abbreviation list index */ 612708Swollman int tt_ttisstd; /* TRUE if transition is std time */ 629936Swollman int tt_ttisgmt; /* TRUE if transition is GMT */ 632708Swollman}; 642708Swollman 652708Swollmanstruct lsinfo { /* leap second information */ 662708Swollman time_t ls_trans; /* transition time */ 672708Swollman long ls_corr; /* correction to apply */ 682708Swollman}; 692708Swollman 702708Swollman#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) 712708Swollman 722708Swollman#ifdef TZNAME_MAX 732708Swollman#define MY_TZNAME_MAX TZNAME_MAX 742708Swollman#endif /* defined TZNAME_MAX */ 752708Swollman#ifndef TZNAME_MAX 762708Swollman#define MY_TZNAME_MAX 255 772708Swollman#endif /* !defined TZNAME_MAX */ 782708Swollman 792708Swollmanstruct state { 802708Swollman int leapcnt; 812708Swollman int timecnt; 822708Swollman int typecnt; 832708Swollman int charcnt; 842708Swollman time_t ats[TZ_MAX_TIMES]; 852708Swollman unsigned char types[TZ_MAX_TIMES]; 862708Swollman struct ttinfo ttis[TZ_MAX_TYPES]; 879936Swollman char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), 882708Swollman (2 * (MY_TZNAME_MAX + 1)))]; 892708Swollman struct lsinfo lsis[TZ_MAX_LEAPS]; 902708Swollman}; 912708Swollman 922708Swollmanstruct rule { 932708Swollman int r_type; /* type of rule--see below */ 942708Swollman int r_day; /* day number of rule */ 952708Swollman int r_week; /* week number of rule */ 962708Swollman int r_mon; /* month number of rule */ 972708Swollman long r_time; /* transition time of rule */ 982708Swollman}; 992708Swollman 1002708Swollman#define JULIAN_DAY 0 /* Jn - Julian day */ 1012708Swollman#define DAY_OF_YEAR 1 /* n - day of year */ 1022708Swollman#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ 1032708Swollman 1042708Swollman/* 1052708Swollman** Prototypes for static functions. 1062708Swollman*/ 1072708Swollman 1082708Swollmanstatic long detzcode P((const char * codep)); 1092708Swollmanstatic const char * getzname P((const char * strp)); 1102708Swollmanstatic const char * getnum P((const char * strp, int * nump, int min, 1112708Swollman int max)); 1122708Swollmanstatic const char * getsecs P((const char * strp, long * secsp)); 1132708Swollmanstatic const char * getoffset P((const char * strp, long * offsetp)); 1142708Swollmanstatic const char * getrule P((const char * strp, struct rule * rulep)); 1152708Swollmanstatic void gmtload P((struct state * sp)); 1162708Swollmanstatic void gmtsub P((const time_t * timep, long offset, 1172708Swollman struct tm * tmp)); 1182708Swollmanstatic void localsub P((const time_t * timep, long offset, 1192708Swollman struct tm * tmp)); 1202708Swollmanstatic int increment_overflow P((int * number, int delta)); 1212708Swollmanstatic int normalize_overflow P((int * tensptr, int * unitsptr, 1222708Swollman int base)); 1232708Swollmanstatic void settzname P((void)); 1249936Swollmanstatic time_t time1 P((struct tm * tmp, 1259936Swollman void(*funcp) P((const time_t *, 1269936Swollman long, struct tm *)), 1272708Swollman long offset)); 1289936Swollmanstatic time_t time2 P((struct tm *tmp, 1299936Swollman void(*funcp) P((const time_t *, 1309936Swollman long, struct tm*)), 1312708Swollman long offset, int * okayp)); 1322708Swollmanstatic void timesub P((const time_t * timep, long offset, 1332708Swollman const struct state * sp, struct tm * tmp)); 1342708Swollmanstatic int tmcomp P((const struct tm * atmp, 1352708Swollman const struct tm * btmp)); 1362708Swollmanstatic time_t transtime P((time_t janfirst, int year, 1372708Swollman const struct rule * rulep, long offset)); 1382708Swollmanstatic int tzload P((const char * name, struct state * sp)); 1392708Swollmanstatic int tzparse P((const char * name, struct state * sp, 1402708Swollman int lastditch)); 1412708Swollman 1422708Swollman#ifdef ALL_STATE 1432708Swollmanstatic struct state * lclptr; 1442708Swollmanstatic struct state * gmtptr; 1452708Swollman#endif /* defined ALL_STATE */ 1462708Swollman 1472708Swollman#ifndef ALL_STATE 1482708Swollmanstatic struct state lclmem; 1492708Swollmanstatic struct state gmtmem; 1502708Swollman#define lclptr (&lclmem) 1512708Swollman#define gmtptr (&gmtmem) 1522708Swollman#endif /* State Farm */ 1532708Swollman 1549936Swollman#ifndef TZ_STRLEN_MAX 1559936Swollman#define TZ_STRLEN_MAX 255 1569936Swollman#endif /* !defined TZ_STRLEN_MAX */ 1579936Swollman 1589936Swollmanstatic char lcl_TZname[TZ_STRLEN_MAX + 1]; 1592708Swollmanstatic int lcl_is_set; 1602708Swollmanstatic int gmt_is_set; 1612708Swollman 1622708Swollmanchar * tzname[2] = { 1639936Swollman wildabbr, 1649936Swollman wildabbr 1652708Swollman}; 1662708Swollman 1679936Swollman/* 1689936Swollman** Section 4.12.3 of X3.159-1989 requires that 1699936Swollman** Except for the strftime function, these functions [asctime, 1709936Swollman** ctime, gmtime, localtime] return values in one of two static 1719936Swollman** objects: a broken-down time structure and an array of char. 1729936Swollman** Thanks to Paul Eggert (eggert@twinsun.com) for noting this. 1739936Swollman*/ 1749936Swollman 1759936Swollmanstatic struct tm tm; 1769936Swollman 1772708Swollman#ifdef USG_COMPAT 1782708Swollmantime_t timezone = 0; 1792708Swollmanint daylight = 0; 1802708Swollman#endif /* defined USG_COMPAT */ 1812708Swollman 1822708Swollman#ifdef ALTZONE 1832708Swollmantime_t altzone = 0; 1842708Swollman#endif /* defined ALTZONE */ 1852708Swollman 1862708Swollmanstatic long 1872708Swollmandetzcode(codep) 1882708Swollmanconst char * const codep; 1892708Swollman{ 1902708Swollman register long result; 1912708Swollman register int i; 1922708Swollman 1939936Swollman result = (codep[0] & 0x80) ? ~0L : 0L; 1942708Swollman for (i = 0; i < 4; ++i) 1952708Swollman result = (result << 8) | (codep[i] & 0xff); 1962708Swollman return result; 1972708Swollman} 1982708Swollman 1992708Swollmanstatic void 2009936Swollmansettzname P((void)) 2012708Swollman{ 2029936Swollman register struct state * const sp = lclptr; 2032708Swollman register int i; 2042708Swollman 2059936Swollman tzname[0] = wildabbr; 2069936Swollman tzname[1] = wildabbr; 2072708Swollman#ifdef USG_COMPAT 2082708Swollman daylight = 0; 2092708Swollman timezone = 0; 2102708Swollman#endif /* defined USG_COMPAT */ 2112708Swollman#ifdef ALTZONE 2122708Swollman altzone = 0; 2132708Swollman#endif /* defined ALTZONE */ 2142708Swollman#ifdef ALL_STATE 2152708Swollman if (sp == NULL) { 2169936Swollman tzname[0] = tzname[1] = gmt; 2172708Swollman return; 2182708Swollman } 2192708Swollman#endif /* defined ALL_STATE */ 2202708Swollman for (i = 0; i < sp->typecnt; ++i) { 2212708Swollman register const struct ttinfo * const ttisp = &sp->ttis[i]; 2222708Swollman 2232708Swollman tzname[ttisp->tt_isdst] = 2249936Swollman &sp->chars[ttisp->tt_abbrind]; 2252708Swollman#ifdef USG_COMPAT 2262708Swollman if (ttisp->tt_isdst) 2272708Swollman daylight = 1; 2282708Swollman if (i == 0 || !ttisp->tt_isdst) 2292708Swollman timezone = -(ttisp->tt_gmtoff); 2302708Swollman#endif /* defined USG_COMPAT */ 2312708Swollman#ifdef ALTZONE 2322708Swollman if (i == 0 || ttisp->tt_isdst) 2332708Swollman altzone = -(ttisp->tt_gmtoff); 2342708Swollman#endif /* defined ALTZONE */ 2352708Swollman } 2362708Swollman /* 2372708Swollman ** And to get the latest zone names into tzname. . . 2382708Swollman */ 2392708Swollman for (i = 0; i < sp->timecnt; ++i) { 2402708Swollman register const struct ttinfo * const ttisp = 2412708Swollman &sp->ttis[ 2422708Swollman sp->types[i]]; 2432708Swollman 2442708Swollman tzname[ttisp->tt_isdst] = 2459936Swollman &sp->chars[ttisp->tt_abbrind]; 2462708Swollman } 2472708Swollman} 2482708Swollman 2492708Swollmanstatic int 2502708Swollmantzload(name, sp) 2512708Swollmanregister const char * name; 2522708Swollmanregister struct state * const sp; 2532708Swollman{ 2542708Swollman register const char * p; 2552708Swollman register int i; 2562708Swollman register int fid; 2572708Swollman 2582708Swollman if (name == NULL && (name = TZDEFAULT) == NULL) 2592708Swollman return -1; 2602708Swollman { 2619936Swollman register int doaccess; 2629936Swollman /* 2639936Swollman ** Section 4.9.1 of the C standard says that 2649936Swollman ** "FILENAME_MAX expands to an integral constant expression 2659936Swollman ** that is the sie needed for an array of char large enough 2669936Swollman ** to hold the longest file name string that the implementation 2679936Swollman ** guarantees can be opened." 2689936Swollman */ 2692708Swollman char fullname[FILENAME_MAX + 1]; 2702708Swollman 2712708Swollman if (name[0] == ':') 2722708Swollman ++name; 2732708Swollman doaccess = name[0] == '/'; 2742708Swollman if (!doaccess) { 2752708Swollman if ((p = TZDIR) == NULL) 2762708Swollman return -1; 2772708Swollman if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) 2782708Swollman return -1; 2792708Swollman (void) strcpy(fullname, p); 2802708Swollman (void) strcat(fullname, "/"); 2812708Swollman (void) strcat(fullname, name); 2822708Swollman /* 2832708Swollman ** Set doaccess if '.' (as in "../") shows up in name. 2842708Swollman */ 2852708Swollman if (strchr(name, '.') != NULL) 2862708Swollman doaccess = TRUE; 2872708Swollman name = fullname; 2882708Swollman } 2899936Swollman if (doaccess && access(name, R_OK) != 0) 2902708Swollman return -1; 2912708Swollman if ((fid = open(name, OPEN_MODE)) == -1) 2922708Swollman return -1; 2932708Swollman } 2942708Swollman { 2959936Swollman struct tzhead * tzhp; 2969936Swollman char buf[sizeof *sp + sizeof *tzhp]; 2979936Swollman int ttisstdcnt; 2989936Swollman int ttisgmtcnt; 2992708Swollman 3002708Swollman i = read(fid, buf, sizeof buf); 3019936Swollman if (close(fid) != 0) 3022708Swollman return -1; 3039936Swollman p = buf; 3049936Swollman p += sizeof tzhp->tzh_reserved; 3059936Swollman ttisstdcnt = (int) detzcode(p); 3069936Swollman p += 4; 3079936Swollman ttisgmtcnt = (int) detzcode(p); 3089936Swollman p += 4; 3099936Swollman sp->leapcnt = (int) detzcode(p); 3109936Swollman p += 4; 3119936Swollman sp->timecnt = (int) detzcode(p); 3129936Swollman p += 4; 3139936Swollman sp->typecnt = (int) detzcode(p); 3149936Swollman p += 4; 3159936Swollman sp->charcnt = (int) detzcode(p); 3169936Swollman p += 4; 3172708Swollman if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || 3182708Swollman sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || 3192708Swollman sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || 3202708Swollman sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || 3219936Swollman (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || 3229936Swollman (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) 3232708Swollman return -1; 3249936Swollman if (i - (p - buf) < sp->timecnt * 4 + /* ats */ 3259936Swollman sp->timecnt + /* types */ 3269936Swollman sp->typecnt * (4 + 2) + /* ttinfos */ 3279936Swollman sp->charcnt + /* chars */ 3289936Swollman sp->leapcnt * (4 + 4) + /* lsinfos */ 3299936Swollman ttisstdcnt + /* ttisstds */ 3309936Swollman ttisgmtcnt) /* ttisgmts */ 3312708Swollman return -1; 3322708Swollman for (i = 0; i < sp->timecnt; ++i) { 3332708Swollman sp->ats[i] = detzcode(p); 3342708Swollman p += 4; 3352708Swollman } 3362708Swollman for (i = 0; i < sp->timecnt; ++i) { 3372708Swollman sp->types[i] = (unsigned char) *p++; 3382708Swollman if (sp->types[i] >= sp->typecnt) 3392708Swollman return -1; 3402708Swollman } 3412708Swollman for (i = 0; i < sp->typecnt; ++i) { 3422708Swollman register struct ttinfo * ttisp; 3432708Swollman 3442708Swollman ttisp = &sp->ttis[i]; 3452708Swollman ttisp->tt_gmtoff = detzcode(p); 3462708Swollman p += 4; 3472708Swollman ttisp->tt_isdst = (unsigned char) *p++; 3482708Swollman if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) 3492708Swollman return -1; 3502708Swollman ttisp->tt_abbrind = (unsigned char) *p++; 3512708Swollman if (ttisp->tt_abbrind < 0 || 3522708Swollman ttisp->tt_abbrind > sp->charcnt) 3532708Swollman return -1; 3542708Swollman } 3552708Swollman for (i = 0; i < sp->charcnt; ++i) 3562708Swollman sp->chars[i] = *p++; 3572708Swollman sp->chars[i] = '\0'; /* ensure '\0' at end */ 3582708Swollman for (i = 0; i < sp->leapcnt; ++i) { 3592708Swollman register struct lsinfo * lsisp; 3602708Swollman 3612708Swollman lsisp = &sp->lsis[i]; 3622708Swollman lsisp->ls_trans = detzcode(p); 3632708Swollman p += 4; 3642708Swollman lsisp->ls_corr = detzcode(p); 3652708Swollman p += 4; 3662708Swollman } 3672708Swollman for (i = 0; i < sp->typecnt; ++i) { 3682708Swollman register struct ttinfo * ttisp; 3692708Swollman 3702708Swollman ttisp = &sp->ttis[i]; 3712708Swollman if (ttisstdcnt == 0) 3722708Swollman ttisp->tt_ttisstd = FALSE; 3732708Swollman else { 3742708Swollman ttisp->tt_ttisstd = *p++; 3752708Swollman if (ttisp->tt_ttisstd != TRUE && 3762708Swollman ttisp->tt_ttisstd != FALSE) 3772708Swollman return -1; 3782708Swollman } 3792708Swollman } 3809936Swollman for (i = 0; i < sp->typecnt; ++i) { 3819936Swollman register struct ttinfo * ttisp; 3829936Swollman 3839936Swollman ttisp = &sp->ttis[i]; 3849936Swollman if (ttisgmtcnt == 0) 3859936Swollman ttisp->tt_ttisgmt = FALSE; 3869936Swollman else { 3879936Swollman ttisp->tt_ttisgmt = *p++; 3889936Swollman if (ttisp->tt_ttisgmt != TRUE && 3899936Swollman ttisp->tt_ttisgmt != FALSE) 3909936Swollman return -1; 3919936Swollman } 3929936Swollman } 3932708Swollman } 3942708Swollman return 0; 3952708Swollman} 3962708Swollman 3972708Swollmanstatic const int mon_lengths[2][MONSPERYEAR] = { 3982708Swollman { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 3992708Swollman { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 4002708Swollman}; 4012708Swollman 4022708Swollmanstatic const int year_lengths[2] = { 4032708Swollman DAYSPERNYEAR, DAYSPERLYEAR 4042708Swollman}; 4052708Swollman 4062708Swollman/* 4072708Swollman** Given a pointer into a time zone string, scan until a character that is not 4082708Swollman** a valid character in a zone name is found. Return a pointer to that 4092708Swollman** character. 4102708Swollman*/ 4112708Swollman 4122708Swollmanstatic const char * 4132708Swollmangetzname(strp) 4142708Swollmanregister const char * strp; 4152708Swollman{ 4162708Swollman register char c; 4172708Swollman 4182708Swollman while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' && 4192708Swollman c != '+') 4202708Swollman ++strp; 4212708Swollman return strp; 4222708Swollman} 4232708Swollman 4242708Swollman/* 4252708Swollman** Given a pointer into a time zone string, extract a number from that string. 4262708Swollman** Check that the number is within a specified range; if it is not, return 4272708Swollman** NULL. 4282708Swollman** Otherwise, return a pointer to the first character not part of the number. 4292708Swollman*/ 4302708Swollman 4312708Swollmanstatic const char * 4322708Swollmangetnum(strp, nump, min, max) 4332708Swollmanregister const char * strp; 4342708Swollmanint * const nump; 4352708Swollmanconst int min; 4362708Swollmanconst int max; 4372708Swollman{ 4382708Swollman register char c; 4392708Swollman register int num; 4402708Swollman 4412708Swollman if (strp == NULL || !isdigit(*strp)) 4422708Swollman return NULL; 4432708Swollman num = 0; 4442708Swollman while ((c = *strp) != '\0' && isdigit(c)) { 4452708Swollman num = num * 10 + (c - '0'); 4462708Swollman if (num > max) 4472708Swollman return NULL; /* illegal value */ 4482708Swollman ++strp; 4492708Swollman } 4502708Swollman if (num < min) 4512708Swollman return NULL; /* illegal value */ 4522708Swollman *nump = num; 4532708Swollman return strp; 4542708Swollman} 4552708Swollman 4562708Swollman/* 4572708Swollman** Given a pointer into a time zone string, extract a number of seconds, 4582708Swollman** in hh[:mm[:ss]] form, from the string. 4592708Swollman** If any error occurs, return NULL. 4602708Swollman** Otherwise, return a pointer to the first character not part of the number 4612708Swollman** of seconds. 4622708Swollman*/ 4632708Swollman 4642708Swollmanstatic const char * 4652708Swollmangetsecs(strp, secsp) 4662708Swollmanregister const char * strp; 4672708Swollmanlong * const secsp; 4682708Swollman{ 4692708Swollman int num; 4702708Swollman 4719936Swollman /* 4729936Swollman ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like 4739936Swollman ** "M10.4.6/26", which does not conform to Posix, 4749936Swollman ** but which specifies the equivalent of 4759936Swollman ** ``02:00 on the first Sunday on or after 23 Oct''. 4769936Swollman */ 4779936Swollman strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); 4782708Swollman if (strp == NULL) 4792708Swollman return NULL; 4809936Swollman *secsp = num * (long) SECSPERHOUR; 4812708Swollman if (*strp == ':') { 4822708Swollman ++strp; 4832708Swollman strp = getnum(strp, &num, 0, MINSPERHOUR - 1); 4842708Swollman if (strp == NULL) 4852708Swollman return NULL; 4862708Swollman *secsp += num * SECSPERMIN; 4872708Swollman if (*strp == ':') { 4882708Swollman ++strp; 4899936Swollman /* `SECSPERMIN' allows for leap seconds. */ 4909936Swollman strp = getnum(strp, &num, 0, SECSPERMIN); 4912708Swollman if (strp == NULL) 4922708Swollman return NULL; 4932708Swollman *secsp += num; 4942708Swollman } 4952708Swollman } 4962708Swollman return strp; 4972708Swollman} 4982708Swollman 4992708Swollman/* 5002708Swollman** Given a pointer into a time zone string, extract an offset, in 5012708Swollman** [+-]hh[:mm[:ss]] form, from the string. 5022708Swollman** If any error occurs, return NULL. 5032708Swollman** Otherwise, return a pointer to the first character not part of the time. 5042708Swollman*/ 5052708Swollman 5062708Swollmanstatic const char * 5072708Swollmangetoffset(strp, offsetp) 5082708Swollmanregister const char * strp; 5092708Swollmanlong * const offsetp; 5102708Swollman{ 5112708Swollman register int neg; 5122708Swollman 5132708Swollman if (*strp == '-') { 5142708Swollman neg = 1; 5152708Swollman ++strp; 5162708Swollman } else if (isdigit(*strp) || *strp++ == '+') 5172708Swollman neg = 0; 5182708Swollman else return NULL; /* illegal offset */ 5192708Swollman strp = getsecs(strp, offsetp); 5202708Swollman if (strp == NULL) 5212708Swollman return NULL; /* illegal time */ 5222708Swollman if (neg) 5232708Swollman *offsetp = -*offsetp; 5242708Swollman return strp; 5252708Swollman} 5262708Swollman 5272708Swollman/* 5282708Swollman** Given a pointer into a time zone string, extract a rule in the form 5292708Swollman** date[/time]. See POSIX section 8 for the format of "date" and "time". 5302708Swollman** If a valid rule is not found, return NULL. 5312708Swollman** Otherwise, return a pointer to the first character not part of the rule. 5322708Swollman*/ 5332708Swollman 5342708Swollmanstatic const char * 5352708Swollmangetrule(strp, rulep) 5362708Swollmanconst char * strp; 5372708Swollmanregister struct rule * const rulep; 5382708Swollman{ 5392708Swollman if (*strp == 'J') { 5402708Swollman /* 5412708Swollman ** Julian day. 5422708Swollman */ 5432708Swollman rulep->r_type = JULIAN_DAY; 5442708Swollman ++strp; 5452708Swollman strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); 5462708Swollman } else if (*strp == 'M') { 5472708Swollman /* 5482708Swollman ** Month, week, day. 5492708Swollman */ 5502708Swollman rulep->r_type = MONTH_NTH_DAY_OF_WEEK; 5512708Swollman ++strp; 5522708Swollman strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); 5532708Swollman if (strp == NULL) 5542708Swollman return NULL; 5552708Swollman if (*strp++ != '.') 5562708Swollman return NULL; 5572708Swollman strp = getnum(strp, &rulep->r_week, 1, 5); 5582708Swollman if (strp == NULL) 5592708Swollman return NULL; 5602708Swollman if (*strp++ != '.') 5612708Swollman return NULL; 5622708Swollman strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); 5632708Swollman } else if (isdigit(*strp)) { 5642708Swollman /* 5652708Swollman ** Day of year. 5662708Swollman */ 5672708Swollman rulep->r_type = DAY_OF_YEAR; 5682708Swollman strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); 5692708Swollman } else return NULL; /* invalid format */ 5702708Swollman if (strp == NULL) 5712708Swollman return NULL; 5722708Swollman if (*strp == '/') { 5732708Swollman /* 5742708Swollman ** Time specified. 5752708Swollman */ 5762708Swollman ++strp; 5772708Swollman strp = getsecs(strp, &rulep->r_time); 5782708Swollman } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ 5792708Swollman return strp; 5802708Swollman} 5812708Swollman 5822708Swollman/* 5832708Swollman** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the 5842708Swollman** year, a rule, and the offset from GMT at the time that rule takes effect, 5852708Swollman** calculate the Epoch-relative time that rule takes effect. 5862708Swollman*/ 5872708Swollman 5882708Swollmanstatic time_t 5892708Swollmantranstime(janfirst, year, rulep, offset) 5902708Swollmanconst time_t janfirst; 5912708Swollmanconst int year; 5922708Swollmanregister const struct rule * const rulep; 5932708Swollmanconst long offset; 5942708Swollman{ 5952708Swollman register int leapyear; 5962708Swollman register time_t value; 5972708Swollman register int i; 5982708Swollman int d, m1, yy0, yy1, yy2, dow; 5992708Swollman 6009936Swollman INITIALIZE(value); 6012708Swollman leapyear = isleap(year); 6022708Swollman switch (rulep->r_type) { 6032708Swollman 6042708Swollman case JULIAN_DAY: 6052708Swollman /* 6062708Swollman ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap 6072708Swollman ** years. 6082708Swollman ** In non-leap years, or if the day number is 59 or less, just 6092708Swollman ** add SECSPERDAY times the day number-1 to the time of 6102708Swollman ** January 1, midnight, to get the day. 6112708Swollman */ 6122708Swollman value = janfirst + (rulep->r_day - 1) * SECSPERDAY; 6132708Swollman if (leapyear && rulep->r_day >= 60) 6142708Swollman value += SECSPERDAY; 6152708Swollman break; 6162708Swollman 6172708Swollman case DAY_OF_YEAR: 6182708Swollman /* 6192708Swollman ** n - day of year. 6202708Swollman ** Just add SECSPERDAY times the day number to the time of 6212708Swollman ** January 1, midnight, to get the day. 6222708Swollman */ 6232708Swollman value = janfirst + rulep->r_day * SECSPERDAY; 6242708Swollman break; 6252708Swollman 6262708Swollman case MONTH_NTH_DAY_OF_WEEK: 6272708Swollman /* 6282708Swollman ** Mm.n.d - nth "dth day" of month m. 6292708Swollman */ 6302708Swollman value = janfirst; 6312708Swollman for (i = 0; i < rulep->r_mon - 1; ++i) 6322708Swollman value += mon_lengths[leapyear][i] * SECSPERDAY; 6332708Swollman 6342708Swollman /* 6352708Swollman ** Use Zeller's Congruence to get day-of-week of first day of 6362708Swollman ** month. 6372708Swollman */ 6382708Swollman m1 = (rulep->r_mon + 9) % 12 + 1; 6392708Swollman yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; 6402708Swollman yy1 = yy0 / 100; 6412708Swollman yy2 = yy0 % 100; 6422708Swollman dow = ((26 * m1 - 2) / 10 + 6432708Swollman 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; 6442708Swollman if (dow < 0) 6452708Swollman dow += DAYSPERWEEK; 6462708Swollman 6472708Swollman /* 6482708Swollman ** "dow" is the day-of-week of the first day of the month. Get 6492708Swollman ** the day-of-month (zero-origin) of the first "dow" day of the 6502708Swollman ** month. 6512708Swollman */ 6522708Swollman d = rulep->r_day - dow; 6532708Swollman if (d < 0) 6542708Swollman d += DAYSPERWEEK; 6552708Swollman for (i = 1; i < rulep->r_week; ++i) { 6562708Swollman if (d + DAYSPERWEEK >= 6572708Swollman mon_lengths[leapyear][rulep->r_mon - 1]) 6582708Swollman break; 6592708Swollman d += DAYSPERWEEK; 6602708Swollman } 6612708Swollman 6622708Swollman /* 6632708Swollman ** "d" is the day-of-month (zero-origin) of the day we want. 6642708Swollman */ 6652708Swollman value += d * SECSPERDAY; 6662708Swollman break; 6672708Swollman } 6682708Swollman 6692708Swollman /* 6702708Swollman ** "value" is the Epoch-relative time of 00:00:00 GMT on the day in 6712708Swollman ** question. To get the Epoch-relative time of the specified local 6722708Swollman ** time on that day, add the transition time and the current offset 6732708Swollman ** from GMT. 6742708Swollman */ 6752708Swollman return value + rulep->r_time + offset; 6762708Swollman} 6772708Swollman 6782708Swollman/* 6792708Swollman** Given a POSIX section 8-style TZ string, fill in the rule tables as 6802708Swollman** appropriate. 6812708Swollman*/ 6822708Swollman 6832708Swollmanstatic int 6842708Swollmantzparse(name, sp, lastditch) 6852708Swollmanconst char * name; 6862708Swollmanregister struct state * const sp; 6872708Swollmanconst int lastditch; 6882708Swollman{ 6892708Swollman const char * stdname; 6902708Swollman const char * dstname; 6919936Swollman size_t stdlen; 6929936Swollman size_t dstlen; 6932708Swollman long stdoffset; 6942708Swollman long dstoffset; 6952708Swollman register time_t * atp; 6962708Swollman register unsigned char * typep; 6972708Swollman register char * cp; 6982708Swollman register int load_result; 6992708Swollman 7009936Swollman INITIALIZE(dstname); 7012708Swollman stdname = name; 7022708Swollman if (lastditch) { 7032708Swollman stdlen = strlen(name); /* length of standard zone name */ 7042708Swollman name += stdlen; 7052708Swollman if (stdlen >= sizeof sp->chars) 7062708Swollman stdlen = (sizeof sp->chars) - 1; 7072708Swollman } else { 7082708Swollman name = getzname(name); 7092708Swollman stdlen = name - stdname; 7102708Swollman if (stdlen < 3) 7112708Swollman return -1; 7122708Swollman } 7132708Swollman if (*name == '\0') 7142708Swollman return -1; /* was "stdoffset = 0;" */ 7152708Swollman else { 7162708Swollman name = getoffset(name, &stdoffset); 7172708Swollman if (name == NULL) 7182708Swollman return -1; 7192708Swollman } 7202708Swollman load_result = tzload(TZDEFRULES, sp); 7212708Swollman if (load_result != 0) 7222708Swollman sp->leapcnt = 0; /* so, we're off a little */ 7232708Swollman if (*name != '\0') { 7242708Swollman dstname = name; 7252708Swollman name = getzname(name); 7262708Swollman dstlen = name - dstname; /* length of DST zone name */ 7272708Swollman if (dstlen < 3) 7282708Swollman return -1; 7292708Swollman if (*name != '\0' && *name != ',' && *name != ';') { 7302708Swollman name = getoffset(name, &dstoffset); 7312708Swollman if (name == NULL) 7322708Swollman return -1; 7332708Swollman } else dstoffset = stdoffset - SECSPERHOUR; 7342708Swollman if (*name == ',' || *name == ';') { 7352708Swollman struct rule start; 7362708Swollman struct rule end; 7372708Swollman register int year; 7382708Swollman register time_t janfirst; 7392708Swollman time_t starttime; 7402708Swollman time_t endtime; 7412708Swollman 7422708Swollman ++name; 7432708Swollman if ((name = getrule(name, &start)) == NULL) 7442708Swollman return -1; 7452708Swollman if (*name++ != ',') 7462708Swollman return -1; 7472708Swollman if ((name = getrule(name, &end)) == NULL) 7482708Swollman return -1; 7492708Swollman if (*name != '\0') 7502708Swollman return -1; 7512708Swollman sp->typecnt = 2; /* standard time and DST */ 7522708Swollman /* 7532708Swollman ** Two transitions per year, from EPOCH_YEAR to 2037. 7542708Swollman */ 7552708Swollman sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1); 7562708Swollman if (sp->timecnt > TZ_MAX_TIMES) 7572708Swollman return -1; 7582708Swollman sp->ttis[0].tt_gmtoff = -dstoffset; 7592708Swollman sp->ttis[0].tt_isdst = 1; 7602708Swollman sp->ttis[0].tt_abbrind = stdlen + 1; 7612708Swollman sp->ttis[1].tt_gmtoff = -stdoffset; 7622708Swollman sp->ttis[1].tt_isdst = 0; 7632708Swollman sp->ttis[1].tt_abbrind = 0; 7642708Swollman atp = sp->ats; 7652708Swollman typep = sp->types; 7662708Swollman janfirst = 0; 7672708Swollman for (year = EPOCH_YEAR; year <= 2037; ++year) { 7682708Swollman starttime = transtime(janfirst, year, &start, 7692708Swollman stdoffset); 7702708Swollman endtime = transtime(janfirst, year, &end, 7712708Swollman dstoffset); 7722708Swollman if (starttime > endtime) { 7732708Swollman *atp++ = endtime; 7742708Swollman *typep++ = 1; /* DST ends */ 7752708Swollman *atp++ = starttime; 7762708Swollman *typep++ = 0; /* DST begins */ 7772708Swollman } else { 7782708Swollman *atp++ = starttime; 7792708Swollman *typep++ = 0; /* DST begins */ 7802708Swollman *atp++ = endtime; 7812708Swollman *typep++ = 1; /* DST ends */ 7822708Swollman } 7832708Swollman janfirst += year_lengths[isleap(year)] * 7842708Swollman SECSPERDAY; 7852708Swollman } 7862708Swollman } else { 7879936Swollman register long theirstdoffset; 7889936Swollman register long theirdstoffset; 7899936Swollman register long theiroffset; 7909936Swollman register int isdst; 7912708Swollman register int i; 7929936Swollman register int j; 7932708Swollman 7942708Swollman if (*name != '\0') 7952708Swollman return -1; 7962708Swollman if (load_result != 0) 7972708Swollman return -1; 7982708Swollman /* 7999936Swollman ** Initial values of theirstdoffset and theirdstoffset. 8002708Swollman */ 8019936Swollman theirstdoffset = 0; 8029936Swollman for (i = 0; i < sp->timecnt; ++i) { 8039936Swollman j = sp->types[i]; 8049936Swollman if (!sp->ttis[j].tt_isdst) { 8059936Swollman theirstdoffset = -sp->ttis[j].tt_gmtoff; 8069936Swollman break; 8072708Swollman } 8082708Swollman } 8099936Swollman theirdstoffset = 0; 8109936Swollman for (i = 0; i < sp->timecnt; ++i) { 8119936Swollman j = sp->types[i]; 8129936Swollman if (sp->ttis[j].tt_isdst) { 8139936Swollman theirdstoffset = -sp->ttis[j].tt_gmtoff; 8149936Swollman break; 8159936Swollman } 8169936Swollman } 8172708Swollman /* 8189936Swollman ** Initially we're assumed to be in standard time. 8192708Swollman */ 8209936Swollman isdst = FALSE; 8219936Swollman theiroffset = theirstdoffset; 8222708Swollman /* 8239936Swollman ** Now juggle transition times and types 8249936Swollman ** tracking offsets as you do. 8252708Swollman */ 8262708Swollman for (i = 0; i < sp->timecnt; ++i) { 8279936Swollman j = sp->types[i]; 8289936Swollman sp->types[i] = sp->ttis[j].tt_isdst; 8299936Swollman if (sp->ttis[j].tt_ttisgmt) { 8309936Swollman /* No adjustment to transition time */ 8319936Swollman } else { 8329936Swollman /* 8339936Swollman ** If summer time is in effect, and the 8349936Swollman ** transition time was not specified as 8359936Swollman ** standard time, add the summer time 8369936Swollman ** offset to the transition time; 8379936Swollman ** otherwise, add the standard time 8389936Swollman ** offset to the transition time. 8399936Swollman */ 8409936Swollman /* 8419936Swollman ** Transitions from DST to DDST 8429936Swollman ** will effectively disappear since 8439936Swollman ** POSIX provides for only one DST 8449936Swollman ** offset. 8459936Swollman */ 8469936Swollman if (isdst && !sp->ttis[j].tt_ttisstd) { 8479936Swollman sp->ats[i] += dstoffset - 8489936Swollman theirdstoffset; 8499936Swollman } else { 8509936Swollman sp->ats[i] += stdoffset - 8519936Swollman theirstdoffset; 8529936Swollman } 8539936Swollman } 8549936Swollman theiroffset = -sp->ttis[j].tt_gmtoff; 8559936Swollman if (sp->ttis[j].tt_isdst) 8569936Swollman theirdstoffset = theiroffset; 8579936Swollman else theirstdoffset = theiroffset; 8582708Swollman } 8599936Swollman /* 8609936Swollman ** Finally, fill in ttis. 8619936Swollman ** ttisstd and ttisgmt need not be handled. 8629936Swollman */ 8639936Swollman sp->ttis[0].tt_gmtoff = -stdoffset; 8649936Swollman sp->ttis[0].tt_isdst = FALSE; 8659936Swollman sp->ttis[0].tt_abbrind = 0; 8669936Swollman sp->ttis[1].tt_gmtoff = -dstoffset; 8679936Swollman sp->ttis[1].tt_isdst = TRUE; 8689936Swollman sp->ttis[1].tt_abbrind = stdlen + 1; 8692708Swollman } 8702708Swollman } else { 8712708Swollman dstlen = 0; 8722708Swollman sp->typecnt = 1; /* only standard time */ 8732708Swollman sp->timecnt = 0; 8742708Swollman sp->ttis[0].tt_gmtoff = -stdoffset; 8752708Swollman sp->ttis[0].tt_isdst = 0; 8762708Swollman sp->ttis[0].tt_abbrind = 0; 8772708Swollman } 8782708Swollman sp->charcnt = stdlen + 1; 8792708Swollman if (dstlen != 0) 8802708Swollman sp->charcnt += dstlen + 1; 8812708Swollman if (sp->charcnt > sizeof sp->chars) 8822708Swollman return -1; 8832708Swollman cp = sp->chars; 8842708Swollman (void) strncpy(cp, stdname, stdlen); 8852708Swollman cp += stdlen; 8862708Swollman *cp++ = '\0'; 8872708Swollman if (dstlen != 0) { 8882708Swollman (void) strncpy(cp, dstname, dstlen); 8892708Swollman *(cp + dstlen) = '\0'; 8902708Swollman } 8912708Swollman return 0; 8922708Swollman} 8932708Swollman 8942708Swollmanstatic void 8952708Swollmangmtload(sp) 8962708Swollmanstruct state * const sp; 8972708Swollman{ 8989936Swollman if (tzload(gmt, sp) != 0) 8999936Swollman (void) tzparse(gmt, sp, TRUE); 9002708Swollman} 9012708Swollman 9022708Swollman#ifndef STD_INSPIRED 9039936Swollman/* 9049936Swollman** A non-static declaration of tzsetwall in a system header file 9059936Swollman** may cause a warning about this upcoming static declaration... 9069936Swollman*/ 9072708Swollmanstatic 9082708Swollman#endif /* !defined STD_INSPIRED */ 9092708Swollmanvoid 9109936Swollmantzsetwall P((void)) 9112708Swollman{ 9129936Swollman if (lcl_is_set < 0) 9139936Swollman return; 9149936Swollman lcl_is_set = -1; 9159936Swollman 9162708Swollman#ifdef ALL_STATE 9172708Swollman if (lclptr == NULL) { 9182708Swollman lclptr = (struct state *) malloc(sizeof *lclptr); 9192708Swollman if (lclptr == NULL) { 9202708Swollman settzname(); /* all we can do */ 9212708Swollman return; 9222708Swollman } 9232708Swollman } 9242708Swollman#endif /* defined ALL_STATE */ 9252708Swollman if (tzload((char *) NULL, lclptr) != 0) 9262708Swollman gmtload(lclptr); 9272708Swollman settzname(); 9282708Swollman} 9292708Swollman 9302708Swollmanvoid 9319936Swollmantzset P((void)) 9322708Swollman{ 9332708Swollman register const char * name; 9342708Swollman 9352708Swollman name = getenv("TZ"); 9362708Swollman if (name == NULL) { 9372708Swollman tzsetwall(); 9382708Swollman return; 9392708Swollman } 9409936Swollman 9419936Swollman if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) 9429936Swollman return; 9439936Swollman lcl_is_set = (strlen(name) < sizeof(lcl_TZname)); 9449936Swollman if (lcl_is_set) 9459936Swollman (void) strcpy(lcl_TZname, name); 9469936Swollman 9472708Swollman#ifdef ALL_STATE 9482708Swollman if (lclptr == NULL) { 9492708Swollman lclptr = (struct state *) malloc(sizeof *lclptr); 9502708Swollman if (lclptr == NULL) { 9512708Swollman settzname(); /* all we can do */ 9522708Swollman return; 9532708Swollman } 9542708Swollman } 9552708Swollman#endif /* defined ALL_STATE */ 9562708Swollman if (*name == '\0') { 9572708Swollman /* 9582708Swollman ** User wants it fast rather than right. 9592708Swollman */ 9602708Swollman lclptr->leapcnt = 0; /* so, we're off a little */ 9612708Swollman lclptr->timecnt = 0; 9622708Swollman lclptr->ttis[0].tt_gmtoff = 0; 9632708Swollman lclptr->ttis[0].tt_abbrind = 0; 9649936Swollman (void) strcpy(lclptr->chars, gmt); 9652708Swollman } else if (tzload(name, lclptr) != 0) 9662708Swollman if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) 9672708Swollman (void) gmtload(lclptr); 9682708Swollman settzname(); 9692708Swollman} 9702708Swollman 9712708Swollman/* 9722708Swollman** The easy way to behave "as if no library function calls" localtime 9732708Swollman** is to not call it--so we drop its guts into "localsub", which can be 9742708Swollman** freely called. (And no, the PANS doesn't require the above behavior-- 9752708Swollman** but it *is* desirable.) 9762708Swollman** 9772708Swollman** The unused offset argument is for the benefit of mktime variants. 9782708Swollman*/ 9792708Swollman 9802708Swollman/*ARGSUSED*/ 9812708Swollmanstatic void 9822708Swollmanlocalsub(timep, offset, tmp) 9832708Swollmanconst time_t * const timep; 9842708Swollmanconst long offset; 9852708Swollmanstruct tm * const tmp; 9862708Swollman{ 9879936Swollman register struct state * sp; 9882708Swollman register const struct ttinfo * ttisp; 9892708Swollman register int i; 9902708Swollman const time_t t = *timep; 9912708Swollman 9922708Swollman sp = lclptr; 9932708Swollman#ifdef ALL_STATE 9942708Swollman if (sp == NULL) { 9952708Swollman gmtsub(timep, offset, tmp); 9962708Swollman return; 9972708Swollman } 9982708Swollman#endif /* defined ALL_STATE */ 9992708Swollman if (sp->timecnt == 0 || t < sp->ats[0]) { 10002708Swollman i = 0; 10012708Swollman while (sp->ttis[i].tt_isdst) 10022708Swollman if (++i >= sp->typecnt) { 10032708Swollman i = 0; 10042708Swollman break; 10052708Swollman } 10062708Swollman } else { 10072708Swollman for (i = 1; i < sp->timecnt; ++i) 10082708Swollman if (t < sp->ats[i]) 10092708Swollman break; 10102708Swollman i = sp->types[i - 1]; 10112708Swollman } 10122708Swollman ttisp = &sp->ttis[i]; 10132708Swollman /* 10142708Swollman ** To get (wrong) behavior that's compatible with System V Release 2.0 10152708Swollman ** you'd replace the statement below with 10162708Swollman ** t += ttisp->tt_gmtoff; 10172708Swollman ** timesub(&t, 0L, sp, tmp); 10182708Swollman */ 10192708Swollman timesub(&t, ttisp->tt_gmtoff, sp, tmp); 10202708Swollman tmp->tm_isdst = ttisp->tt_isdst; 10219936Swollman tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; 10222708Swollman#ifdef TM_ZONE 10239936Swollman tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; 10242708Swollman#endif /* defined TM_ZONE */ 10252708Swollman} 10262708Swollman 10272708Swollmanstruct tm * 10282708Swollmanlocaltime(timep) 10292708Swollmanconst time_t * const timep; 10302708Swollman{ 10319936Swollman tzset(); 10322708Swollman localsub(timep, 0L, &tm); 10332708Swollman return &tm; 10342708Swollman} 10352708Swollman 10362708Swollman/* 10372708Swollman** gmtsub is to gmtime as localsub is to localtime. 10382708Swollman*/ 10392708Swollman 10402708Swollmanstatic void 10412708Swollmangmtsub(timep, offset, tmp) 10422708Swollmanconst time_t * const timep; 10432708Swollmanconst long offset; 10442708Swollmanstruct tm * const tmp; 10452708Swollman{ 10462708Swollman if (!gmt_is_set) { 10472708Swollman gmt_is_set = TRUE; 10482708Swollman#ifdef ALL_STATE 10492708Swollman gmtptr = (struct state *) malloc(sizeof *gmtptr); 10502708Swollman if (gmtptr != NULL) 10512708Swollman#endif /* defined ALL_STATE */ 10522708Swollman gmtload(gmtptr); 10532708Swollman } 10542708Swollman timesub(timep, offset, gmtptr, tmp); 10552708Swollman#ifdef TM_ZONE 10562708Swollman /* 10572708Swollman ** Could get fancy here and deliver something such as 10582708Swollman ** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero, 10592708Swollman ** but this is no time for a treasure hunt. 10602708Swollman */ 10612708Swollman if (offset != 0) 10629936Swollman tmp->TM_ZONE = wildabbr; 10632708Swollman else { 10642708Swollman#ifdef ALL_STATE 10652708Swollman if (gmtptr == NULL) 10669936Swollman tmp->TM_ZONE = gmt; 10672708Swollman else tmp->TM_ZONE = gmtptr->chars; 10682708Swollman#endif /* defined ALL_STATE */ 10692708Swollman#ifndef ALL_STATE 10702708Swollman tmp->TM_ZONE = gmtptr->chars; 10712708Swollman#endif /* State Farm */ 10722708Swollman } 10732708Swollman#endif /* defined TM_ZONE */ 10742708Swollman} 10752708Swollman 10762708Swollmanstruct tm * 10772708Swollmangmtime(timep) 10782708Swollmanconst time_t * const timep; 10792708Swollman{ 10802708Swollman gmtsub(timep, 0L, &tm); 10812708Swollman return &tm; 10822708Swollman} 10832708Swollman 10842708Swollman#ifdef STD_INSPIRED 10852708Swollman 10862708Swollmanstruct tm * 10872708Swollmanofftime(timep, offset) 10882708Swollmanconst time_t * const timep; 10892708Swollmanconst long offset; 10902708Swollman{ 10912708Swollman gmtsub(timep, offset, &tm); 10922708Swollman return &tm; 10932708Swollman} 10942708Swollman 10952708Swollman#endif /* defined STD_INSPIRED */ 10962708Swollman 10972708Swollmanstatic void 10982708Swollmantimesub(timep, offset, sp, tmp) 10992708Swollmanconst time_t * const timep; 11002708Swollmanconst long offset; 11012708Swollmanregister const struct state * const sp; 11022708Swollmanregister struct tm * const tmp; 11032708Swollman{ 11042708Swollman register const struct lsinfo * lp; 11052708Swollman register long days; 11062708Swollman register long rem; 11072708Swollman register int y; 11082708Swollman register int yleap; 11092708Swollman register const int * ip; 11102708Swollman register long corr; 11112708Swollman register int hit; 11122708Swollman register int i; 11132708Swollman 11142708Swollman corr = 0; 11152708Swollman hit = 0; 11162708Swollman#ifdef ALL_STATE 11172708Swollman i = (sp == NULL) ? 0 : sp->leapcnt; 11182708Swollman#endif /* defined ALL_STATE */ 11192708Swollman#ifndef ALL_STATE 11202708Swollman i = sp->leapcnt; 11212708Swollman#endif /* State Farm */ 11222708Swollman while (--i >= 0) { 11232708Swollman lp = &sp->lsis[i]; 11242708Swollman if (*timep >= lp->ls_trans) { 11252708Swollman if (*timep == lp->ls_trans) { 11262708Swollman hit = ((i == 0 && lp->ls_corr > 0) || 11272708Swollman lp->ls_corr > sp->lsis[i - 1].ls_corr); 11282708Swollman if (hit) 11292708Swollman while (i > 0 && 11302708Swollman sp->lsis[i].ls_trans == 11312708Swollman sp->lsis[i - 1].ls_trans + 1 && 11322708Swollman sp->lsis[i].ls_corr == 11332708Swollman sp->lsis[i - 1].ls_corr + 1) { 11342708Swollman ++hit; 11352708Swollman --i; 11362708Swollman } 11372708Swollman } 11382708Swollman corr = lp->ls_corr; 11392708Swollman break; 11402708Swollman } 11412708Swollman } 11422708Swollman days = *timep / SECSPERDAY; 11432708Swollman rem = *timep % SECSPERDAY; 11442708Swollman#ifdef mc68k 11452708Swollman if (*timep == 0x80000000) { 11462708Swollman /* 11472708Swollman ** A 3B1 muffs the division on the most negative number. 11482708Swollman */ 11492708Swollman days = -24855; 11502708Swollman rem = -11648; 11512708Swollman } 11529936Swollman#endif /* defined mc68k */ 11532708Swollman rem += (offset - corr); 11542708Swollman while (rem < 0) { 11552708Swollman rem += SECSPERDAY; 11562708Swollman --days; 11572708Swollman } 11582708Swollman while (rem >= SECSPERDAY) { 11592708Swollman rem -= SECSPERDAY; 11602708Swollman ++days; 11612708Swollman } 11622708Swollman tmp->tm_hour = (int) (rem / SECSPERHOUR); 11632708Swollman rem = rem % SECSPERHOUR; 11642708Swollman tmp->tm_min = (int) (rem / SECSPERMIN); 11652708Swollman tmp->tm_sec = (int) (rem % SECSPERMIN); 11662708Swollman if (hit) 11672708Swollman /* 11682708Swollman ** A positive leap second requires a special 11692708Swollman ** representation. This uses "... ??:59:60" et seq. 11702708Swollman */ 11712708Swollman tmp->tm_sec += hit; 11722708Swollman tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); 11732708Swollman if (tmp->tm_wday < 0) 11742708Swollman tmp->tm_wday += DAYSPERWEEK; 11752708Swollman y = EPOCH_YEAR; 11762708Swollman if (days >= 0) 11772708Swollman for ( ; ; ) { 11782708Swollman yleap = isleap(y); 11792708Swollman if (days < (long) year_lengths[yleap]) 11802708Swollman break; 11812708Swollman ++y; 11822708Swollman days = days - (long) year_lengths[yleap]; 11832708Swollman } 11842708Swollman else do { 11852708Swollman --y; 11862708Swollman yleap = isleap(y); 11872708Swollman days = days + (long) year_lengths[yleap]; 11882708Swollman } while (days < 0); 11892708Swollman tmp->tm_year = y - TM_YEAR_BASE; 11902708Swollman tmp->tm_yday = (int) days; 11912708Swollman ip = mon_lengths[yleap]; 11922708Swollman for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon)) 11932708Swollman days = days - (long) ip[tmp->tm_mon]; 11942708Swollman tmp->tm_mday = (int) (days + 1); 11952708Swollman tmp->tm_isdst = 0; 11962708Swollman#ifdef TM_GMTOFF 11972708Swollman tmp->TM_GMTOFF = offset; 11982708Swollman#endif /* defined TM_GMTOFF */ 11992708Swollman} 12002708Swollman 12012708Swollmanchar * 12022708Swollmanctime(timep) 12032708Swollmanconst time_t * const timep; 12042708Swollman{ 12059936Swollman/* 12069936Swollman** Section 4.12.3.2 of X3.159-1989 requires that 12079936Swollman** The ctime funciton converts the calendar time pointed to by timer 12089936Swollman** to local time in the form of a string. It is equivalent to 12099936Swollman** asctime(localtime(timer)) 12109936Swollman*/ 12112708Swollman return asctime(localtime(timep)); 12122708Swollman} 12132708Swollman 12142708Swollman/* 12152708Swollman** Adapted from code provided by Robert Elz, who writes: 12162708Swollman** The "best" way to do mktime I think is based on an idea of Bob 12172708Swollman** Kridle's (so its said...) from a long time ago. (mtxinu!kridle now). 12182708Swollman** It does a binary search of the time_t space. Since time_t's are 12192708Swollman** just 32 bits, its a max of 32 iterations (even at 64 bits it 12202708Swollman** would still be very reasonable). 12212708Swollman*/ 12222708Swollman 12232708Swollman#ifndef WRONG 12242708Swollman#define WRONG (-1) 12252708Swollman#endif /* !defined WRONG */ 12262708Swollman 12272708Swollman/* 12282708Swollman** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com). 12292708Swollman*/ 12302708Swollman 12312708Swollmanstatic int 12322708Swollmanincrement_overflow(number, delta) 12332708Swollmanint * number; 12342708Swollmanint delta; 12352708Swollman{ 12369936Swollman int number0; 12378870Srgrimes 12382708Swollman number0 = *number; 12392708Swollman *number += delta; 12402708Swollman return (*number < number0) != (delta < 0); 12412708Swollman} 12422708Swollman 12432708Swollmanstatic int 12442708Swollmannormalize_overflow(tensptr, unitsptr, base) 12452708Swollmanint * const tensptr; 12462708Swollmanint * const unitsptr; 12472708Swollmanconst int base; 12482708Swollman{ 12492708Swollman register int tensdelta; 12502708Swollman 12512708Swollman tensdelta = (*unitsptr >= 0) ? 12522708Swollman (*unitsptr / base) : 12532708Swollman (-1 - (-1 - *unitsptr) / base); 12542708Swollman *unitsptr -= tensdelta * base; 12552708Swollman return increment_overflow(tensptr, tensdelta); 12562708Swollman} 12572708Swollman 12582708Swollmanstatic int 12592708Swollmantmcomp(atmp, btmp) 12602708Swollmanregister const struct tm * const atmp; 12612708Swollmanregister const struct tm * const btmp; 12622708Swollman{ 12632708Swollman register int result; 12642708Swollman 12652708Swollman if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && 12662708Swollman (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && 12672708Swollman (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && 12682708Swollman (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && 12692708Swollman (result = (atmp->tm_min - btmp->tm_min)) == 0) 12702708Swollman result = atmp->tm_sec - btmp->tm_sec; 12712708Swollman return result; 12722708Swollman} 12732708Swollman 12742708Swollmanstatic time_t 12752708Swollmantime2(tmp, funcp, offset, okayp) 12762708Swollmanstruct tm * const tmp; 12779936Swollmanvoid (* const funcp) P((const time_t*, long, struct tm*)); 12782708Swollmanconst long offset; 12792708Swollmanint * const okayp; 12802708Swollman{ 12812708Swollman register const struct state * sp; 12822708Swollman register int dir; 12832708Swollman register int bits; 12842708Swollman register int i, j ; 12852708Swollman register int saved_seconds; 12862708Swollman time_t newt; 12872708Swollman time_t t; 12882708Swollman struct tm yourtm, mytm; 12892708Swollman 12902708Swollman *okayp = FALSE; 12912708Swollman yourtm = *tmp; 12922708Swollman if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) 12932708Swollman return WRONG; 12942708Swollman if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) 12952708Swollman return WRONG; 12962708Swollman if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR)) 12972708Swollman return WRONG; 12982708Swollman /* 12992708Swollman ** Turn yourtm.tm_year into an actual year number for now. 13002708Swollman ** It is converted back to an offset from TM_YEAR_BASE later. 13012708Swollman */ 13022708Swollman if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE)) 13032708Swollman return WRONG; 13042708Swollman while (yourtm.tm_mday <= 0) { 13052708Swollman if (increment_overflow(&yourtm.tm_year, -1)) 13062708Swollman return WRONG; 13072708Swollman yourtm.tm_mday += year_lengths[isleap(yourtm.tm_year)]; 13082708Swollman } 13092708Swollman while (yourtm.tm_mday > DAYSPERLYEAR) { 13102708Swollman yourtm.tm_mday -= year_lengths[isleap(yourtm.tm_year)]; 13112708Swollman if (increment_overflow(&yourtm.tm_year, 1)) 13122708Swollman return WRONG; 13132708Swollman } 13142708Swollman for ( ; ; ) { 13152708Swollman i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon]; 13162708Swollman if (yourtm.tm_mday <= i) 13172708Swollman break; 13182708Swollman yourtm.tm_mday -= i; 13192708Swollman if (++yourtm.tm_mon >= MONSPERYEAR) { 13202708Swollman yourtm.tm_mon = 0; 13212708Swollman if (increment_overflow(&yourtm.tm_year, 1)) 13222708Swollman return WRONG; 13232708Swollman } 13242708Swollman } 13252708Swollman if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) 13262708Swollman return WRONG; 13272708Swollman if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { 13282708Swollman /* 13292708Swollman ** We can't set tm_sec to 0, because that might push the 13302708Swollman ** time below the minimum representable time. 13312708Swollman ** Set tm_sec to 59 instead. 13322708Swollman ** This assumes that the minimum representable time is 13332708Swollman ** not in the same minute that a leap second was deleted from, 13342708Swollman ** which is a safer assumption than using 58 would be. 13352708Swollman */ 13362708Swollman if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) 13372708Swollman return WRONG; 13382708Swollman saved_seconds = yourtm.tm_sec; 13392708Swollman yourtm.tm_sec = SECSPERMIN - 1; 13402708Swollman } else { 13412708Swollman saved_seconds = yourtm.tm_sec; 13422708Swollman yourtm.tm_sec = 0; 13432708Swollman } 13442708Swollman /* 13452708Swollman ** Calculate the number of magnitude bits in a time_t 13462708Swollman ** (this works regardless of whether time_t is 13472708Swollman ** signed or unsigned, though lint complains if unsigned). 13482708Swollman */ 13492708Swollman for (bits = 0, t = 1; t > 0; ++bits, t <<= 1) 13502708Swollman continue; 13512708Swollman /* 13522708Swollman ** If time_t is signed, then 0 is the median value, 13532708Swollman ** if time_t is unsigned, then 1 << bits is median. 13542708Swollman */ 13552708Swollman t = (t < 0) ? 0 : ((time_t) 1 << bits); 13562708Swollman for ( ; ; ) { 13572708Swollman (*funcp)(&t, offset, &mytm); 13582708Swollman dir = tmcomp(&mytm, &yourtm); 13592708Swollman if (dir != 0) { 13602708Swollman if (bits-- < 0) 13612708Swollman return WRONG; 13622708Swollman if (bits < 0) 13632708Swollman --t; 13642708Swollman else if (dir > 0) 13652708Swollman t -= (time_t) 1 << bits; 13662708Swollman else t += (time_t) 1 << bits; 13672708Swollman continue; 13682708Swollman } 13692708Swollman if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) 13702708Swollman break; 13712708Swollman /* 13722708Swollman ** Right time, wrong type. 13732708Swollman ** Hunt for right time, right type. 13742708Swollman ** It's okay to guess wrong since the guess 13752708Swollman ** gets checked. 13762708Swollman */ 13772708Swollman /* 13782708Swollman ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. 13792708Swollman */ 13802708Swollman sp = (const struct state *) 13812708Swollman (((void *) funcp == (void *) localsub) ? 13822708Swollman lclptr : gmtptr); 13832708Swollman#ifdef ALL_STATE 13842708Swollman if (sp == NULL) 13852708Swollman return WRONG; 13862708Swollman#endif /* defined ALL_STATE */ 13872708Swollman for (i = 0; i < sp->typecnt; ++i) { 13882708Swollman if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) 13892708Swollman continue; 13902708Swollman for (j = 0; j < sp->typecnt; ++j) { 13912708Swollman if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) 13922708Swollman continue; 13932708Swollman newt = t + sp->ttis[j].tt_gmtoff - 13942708Swollman sp->ttis[i].tt_gmtoff; 13952708Swollman (*funcp)(&newt, offset, &mytm); 13962708Swollman if (tmcomp(&mytm, &yourtm) != 0) 13972708Swollman continue; 13982708Swollman if (mytm.tm_isdst != yourtm.tm_isdst) 13992708Swollman continue; 14002708Swollman /* 14012708Swollman ** We have a match. 14022708Swollman */ 14032708Swollman t = newt; 14042708Swollman goto label; 14052708Swollman } 14062708Swollman } 14072708Swollman return WRONG; 14082708Swollman } 14092708Swollmanlabel: 14102708Swollman newt = t + saved_seconds; 14112708Swollman if ((newt < t) != (saved_seconds < 0)) 14122708Swollman return WRONG; 14132708Swollman t = newt; 14142708Swollman (*funcp)(&t, offset, tmp); 14152708Swollman *okayp = TRUE; 14162708Swollman return t; 14172708Swollman} 14182708Swollman 14192708Swollmanstatic time_t 14202708Swollmantime1(tmp, funcp, offset) 14212708Swollmanstruct tm * const tmp; 14229936Swollmanvoid (* const funcp) P((const time_t*, long, struct tm*)); 14232708Swollmanconst long offset; 14242708Swollman{ 14252708Swollman register time_t t; 14262708Swollman register const struct state * sp; 14272708Swollman register int samei, otheri; 14282708Swollman int okay; 14292708Swollman 14302708Swollman if (tmp->tm_isdst > 1) 14312708Swollman tmp->tm_isdst = 1; 14322708Swollman t = time2(tmp, funcp, offset, &okay); 14332708Swollman#ifdef PCTS 14342708Swollman /* 14352708Swollman ** PCTS code courtesy Grant Sullivan (grant@osf.org). 14362708Swollman */ 14372708Swollman if (okay) 14382708Swollman return t; 14392708Swollman if (tmp->tm_isdst < 0) 14402708Swollman tmp->tm_isdst = 0; /* reset to std and try again */ 14412708Swollman#endif /* defined PCTS */ 14422708Swollman#ifndef PCTS 14432708Swollman if (okay || tmp->tm_isdst < 0) 14442708Swollman return t; 14452708Swollman#endif /* !defined PCTS */ 14462708Swollman /* 14472708Swollman ** We're supposed to assume that somebody took a time of one type 14482708Swollman ** and did some math on it that yielded a "struct tm" that's bad. 14492708Swollman ** We try to divine the type they started from and adjust to the 14502708Swollman ** type they need. 14512708Swollman */ 14522708Swollman /* 14532708Swollman ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. 14542708Swollman */ 14552708Swollman sp = (const struct state *) (((void *) funcp == (void *) localsub) ? 14562708Swollman lclptr : gmtptr); 14572708Swollman#ifdef ALL_STATE 14582708Swollman if (sp == NULL) 14592708Swollman return WRONG; 14602708Swollman#endif /* defined ALL_STATE */ 14612708Swollman for (samei = 0; samei < sp->typecnt; ++samei) { 14622708Swollman if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) 14632708Swollman continue; 14642708Swollman for (otheri = 0; otheri < sp->typecnt; ++otheri) { 14652708Swollman if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) 14662708Swollman continue; 14672708Swollman tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - 14682708Swollman sp->ttis[samei].tt_gmtoff; 14692708Swollman tmp->tm_isdst = !tmp->tm_isdst; 14702708Swollman t = time2(tmp, funcp, offset, &okay); 14712708Swollman if (okay) 14722708Swollman return t; 14732708Swollman tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - 14742708Swollman sp->ttis[samei].tt_gmtoff; 14752708Swollman tmp->tm_isdst = !tmp->tm_isdst; 14762708Swollman } 14772708Swollman } 14782708Swollman return WRONG; 14792708Swollman} 14802708Swollman 14812708Swollmantime_t 14822708Swollmanmktime(tmp) 14832708Swollmanstruct tm * const tmp; 14842708Swollman{ 14859936Swollman tzset(); 14862708Swollman return time1(tmp, localsub, 0L); 14872708Swollman} 14882708Swollman 14892708Swollman#ifdef STD_INSPIRED 14902708Swollman 14912708Swollmantime_t 14922708Swollmantimelocal(tmp) 14932708Swollmanstruct tm * const tmp; 14942708Swollman{ 14952708Swollman tmp->tm_isdst = -1; /* in case it wasn't initialized */ 14962708Swollman return mktime(tmp); 14972708Swollman} 14982708Swollman 14992708Swollmantime_t 15002708Swollmantimegm(tmp) 15012708Swollmanstruct tm * const tmp; 15022708Swollman{ 15032708Swollman tmp->tm_isdst = 0; 15042708Swollman return time1(tmp, gmtsub, 0L); 15052708Swollman} 15062708Swollman 15072708Swollmantime_t 15082708Swollmantimeoff(tmp, offset) 15092708Swollmanstruct tm * const tmp; 15102708Swollmanconst long offset; 15112708Swollman{ 15122708Swollman tmp->tm_isdst = 0; 15132708Swollman return time1(tmp, gmtsub, offset); 15142708Swollman} 15152708Swollman 15162708Swollman#endif /* defined STD_INSPIRED */ 15172708Swollman 15182708Swollman#ifdef CMUCS 15192708Swollman 15202708Swollman/* 15212708Swollman** The following is supplied for compatibility with 15222708Swollman** previous versions of the CMUCS runtime library. 15232708Swollman*/ 15242708Swollman 15252708Swollmanlong 15262708Swollmangtime(tmp) 15272708Swollmanstruct tm * const tmp; 15282708Swollman{ 15292708Swollman const time_t t = mktime(tmp); 15302708Swollman 15312708Swollman if (t == WRONG) 15322708Swollman return -1; 15332708Swollman return t; 15342708Swollman} 15352708Swollman 15362708Swollman#endif /* defined CMUCS */ 15372708Swollman 15382708Swollman/* 15392708Swollman** XXX--is the below the right way to conditionalize?? 15402708Swollman*/ 15412708Swollman 15422708Swollman#ifdef STD_INSPIRED 15432708Swollman 15442708Swollman/* 15452708Swollman** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599 15462708Swollman** shall correspond to "Wed Dec 31 23:59:59 GMT 1986", which 15472708Swollman** is not the case if we are accounting for leap seconds. 15482708Swollman** So, we provide the following conversion routines for use 15492708Swollman** when exchanging timestamps with POSIX conforming systems. 15502708Swollman*/ 15512708Swollman 15522708Swollmanstatic long 15532708Swollmanleapcorr(timep) 15542708Swollmantime_t * timep; 15552708Swollman{ 15562708Swollman register struct state * sp; 15572708Swollman register struct lsinfo * lp; 15582708Swollman register int i; 15592708Swollman 15602708Swollman sp = lclptr; 15612708Swollman i = sp->leapcnt; 15622708Swollman while (--i >= 0) { 15632708Swollman lp = &sp->lsis[i]; 15642708Swollman if (*timep >= lp->ls_trans) 15652708Swollman return lp->ls_corr; 15662708Swollman } 15672708Swollman return 0; 15682708Swollman} 15692708Swollman 15702708Swollmantime_t 15712708Swollmantime2posix(t) 15722708Swollmantime_t t; 15732708Swollman{ 15749936Swollman tzset(); 15752708Swollman return t - leapcorr(&t); 15762708Swollman} 15772708Swollman 15782708Swollmantime_t 15792708Swollmanposix2time(t) 15802708Swollmantime_t t; 15812708Swollman{ 15822708Swollman time_t x; 15832708Swollman time_t y; 15842708Swollman 15859936Swollman tzset(); 15862708Swollman /* 15872708Swollman ** For a positive leap second hit, the result 15882708Swollman ** is not unique. For a negative leap second 15892708Swollman ** hit, the corresponding time doesn't exist, 15902708Swollman ** so we return an adjacent second. 15912708Swollman */ 15922708Swollman x = t + leapcorr(&t); 15932708Swollman y = x - leapcorr(&x); 15942708Swollman if (y < t) { 15952708Swollman do { 15962708Swollman x++; 15972708Swollman y = x - leapcorr(&x); 15982708Swollman } while (y < t); 15992708Swollman if (t != y) 16002708Swollman return x - 1; 16012708Swollman } else if (y > t) { 16022708Swollman do { 16032708Swollman --x; 16042708Swollman y = x - leapcorr(&x); 16052708Swollman } while (y > t); 16062708Swollman if (t != y) 16072708Swollman return x + 1; 16082708Swollman } 16092708Swollman return x; 16102708Swollman} 16112708Swollman 16122708Swollman#endif /* defined STD_INSPIRED */ 1613