122084Smckusick /* 237140Sbostic * Copyright (c) 1987, 1989 Regents of the University of California. 332754Sbostic * All rights reserved. 432754Sbostic * 534952Sbostic * This code is derived from software contributed to Berkeley by 634952Sbostic * Arthur Olson. 734952Sbostic * 832754Sbostic * Redistribution and use in source and binary forms are permitted 934821Sbostic * provided that the above copyright notice and this paragraph are 1034821Sbostic * duplicated in all such forms and that any documentation, 1134821Sbostic * advertising materials, and other materials related to such 1234821Sbostic * distribution and use acknowledge that the software was developed 1334821Sbostic * by the University of California, Berkeley. The name of the 1434821Sbostic * University may not be used to endorse or promote products derived 1534821Sbostic * from this software without specific prior written permission. 1634821Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1734821Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1834821Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1922084Smckusick */ 2022084Smckusick 2130684Sbostic #if defined(LIBC_SCCS) && !defined(lint) 22*39720Sbostic static char sccsid[] = "@(#)ctime.c 5.19 (Berkeley) 12/14/89"; 2332754Sbostic #endif /* LIBC_SCCS and not lint */ 2422084Smckusick 2537140Sbostic /* 2637140Sbostic ** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu). 2737140Sbostic ** POSIX-style TZ environment variable handling from Guy Harris 2837140Sbostic ** (guy@auspex.com). 2937140Sbostic */ 301959Swnj 3137140Sbostic /*LINTLIBRARY*/ 321959Swnj 3337140Sbostic #include <sys/param.h> 3437140Sbostic #include <fcntl.h> 3537140Sbostic #include <time.h> 3637140Sbostic #include <tzfile.h> 3737140Sbostic #include <string.h> 3837140Sbostic #include <ctype.h> 3937140Sbostic #include <stdio.h> 4030608Sbostic 4137140Sbostic #ifdef __STDC__ 4237140Sbostic #include <stdlib.h> 4337140Sbostic 4437140Sbostic #define P(s) s 4537140Sbostic #define alloc_size_t size_t 4637140Sbostic #define qsort_size_t size_t 4737140Sbostic #define fread_size_t size_t 4837140Sbostic #define fwrite_size_t size_t 4937140Sbostic 5037140Sbostic #else /* !defined __STDC__ */ 5137140Sbostic 52*39720Sbostic #define P(s) () 5337140Sbostic #define const 5437140Sbostic #define volatile 5537140Sbostic 5637140Sbostic typedef char * genericptr_t; 5737140Sbostic typedef unsigned alloc_size_t; 5837140Sbostic typedef int qsort_size_t; 5937140Sbostic typedef int fread_size_t; 6037140Sbostic typedef int fwrite_size_t; 6137140Sbostic 6237140Sbostic extern char * calloc(); 6337140Sbostic extern char * malloc(); 6437140Sbostic extern char * realloc(); 6537140Sbostic extern char * getenv(); 6637140Sbostic 6737140Sbostic #endif /* !defined __STDC__ */ 6837140Sbostic 6937140Sbostic extern time_t time(); 7037140Sbostic 7137140Sbostic #define FILENAME_MAX MAXPATHLEN 7237140Sbostic #define ACCESS_MODE O_RDONLY 7337140Sbostic #define OPEN_MODE O_RDONLY 7437140Sbostic 7537140Sbostic #ifndef WILDABBR 761959Swnj /* 7737140Sbostic ** Someone might make incorrect use of a time zone abbreviation: 7837140Sbostic ** 1. They might reference tzname[0] before calling tzset (explicitly 7937140Sbostic ** or implicitly). 8037140Sbostic ** 2. They might reference tzname[1] before calling tzset (explicitly 8137140Sbostic ** or implicitly). 8237140Sbostic ** 3. They might reference tzname[1] after setting to a time zone 8337140Sbostic ** in which Daylight Saving Time is never observed. 8437140Sbostic ** 4. They might reference tzname[0] after setting to a time zone 8537140Sbostic ** in which Standard Time is never observed. 8637140Sbostic ** 5. They might reference tm.TM_ZONE after calling offtime. 8737140Sbostic ** What's best to do in the above cases is open to debate; 8837140Sbostic ** for now, we just set things up so that in any of the five cases 8937140Sbostic ** WILDABBR is used. Another possibility: initialize tzname[0] to the 9037140Sbostic ** string "tzname[0] used before set", and similarly for the other cases. 9137140Sbostic ** And another: initialize tzname[0] to "ERA", with an explanation in the 9237140Sbostic ** manual page of what this "time zone abbreviation" means (doing this so 9337140Sbostic ** that tzname[0] has the "normal" length of three characters). 9430608Sbostic */ 9537140Sbostic #define WILDABBR " " 9637140Sbostic #endif /* !defined WILDABBR */ 971959Swnj 9830608Sbostic #ifndef TRUE 9930608Sbostic #define TRUE 1 10030608Sbostic #define FALSE 0 10137140Sbostic #endif /* !defined TRUE */ 10230608Sbostic 10337140Sbostic static const char GMT[] = "GMT"; 10430608Sbostic 10530608Sbostic struct ttinfo { /* time type information */ 10630608Sbostic long tt_gmtoff; /* GMT offset in seconds */ 10730608Sbostic int tt_isdst; /* used to set tm_isdst */ 10830608Sbostic int tt_abbrind; /* abbreviation list index */ 10937140Sbostic int tt_ttisstd; /* TRUE if transition is std time */ 11013876Ssam }; 11113876Ssam 11237140Sbostic struct lsinfo { /* leap second information */ 11337140Sbostic time_t ls_trans; /* transition time */ 11437140Sbostic long ls_corr; /* correction to apply */ 11537140Sbostic }; 11637140Sbostic 11730608Sbostic struct state { 11837140Sbostic int leapcnt; 11930608Sbostic int timecnt; 12030608Sbostic int typecnt; 12130608Sbostic int charcnt; 12230608Sbostic time_t ats[TZ_MAX_TIMES]; 12330608Sbostic unsigned char types[TZ_MAX_TIMES]; 12430608Sbostic struct ttinfo ttis[TZ_MAX_TYPES]; 12537140Sbostic char chars[(TZ_MAX_CHARS + 1 > sizeof GMT) ? 12637140Sbostic TZ_MAX_CHARS + 1 : sizeof GMT]; 12737140Sbostic struct lsinfo lsis[TZ_MAX_LEAPS]; 12823720Skre }; 12923720Skre 13037140Sbostic struct rule { 13137140Sbostic int r_type; /* type of rule--see below */ 13237140Sbostic int r_day; /* day number of rule */ 13337140Sbostic int r_week; /* week number of rule */ 13437140Sbostic int r_mon; /* month number of rule */ 13537140Sbostic long r_time; /* transition time of rule */ 13637140Sbostic }; 13730608Sbostic 13837140Sbostic #define JULIAN_DAY 0 /* Jn - Julian day */ 13937140Sbostic #define DAY_OF_YEAR 1 /* n - day of year */ 14037140Sbostic #define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ 14130608Sbostic 14237140Sbostic /* 14337140Sbostic ** Prototypes for static functions. 14437140Sbostic */ 14537140Sbostic 14637140Sbostic static long detzcode P((const char * codep)); 14737140Sbostic static const char * getzname P((const char * strp)); 14837140Sbostic static const char * getnum P((const char * strp, int * nump, int min, 14937140Sbostic int max)); 15037140Sbostic static const char * getsecs P((const char * strp, long * secsp)); 15137140Sbostic static const char * getoffset P((const char * strp, long * offsetp)); 15237140Sbostic static const char * getrule P((const char * strp, struct rule * rulep)); 15337140Sbostic static void gmtload P((struct state * sp)); 15437140Sbostic static void gmtsub P((const time_t * timep, long offset, 15537140Sbostic struct tm * tmp)); 15637140Sbostic static void localsub P((const time_t * timep, long offset, 15737140Sbostic struct tm * tmp)); 15837140Sbostic static void normalize P((int * tensptr, int * unitsptr, int base)); 15937140Sbostic static void settzname P((void)); 16037140Sbostic static time_t time1 P((struct tm * tmp, void (* funcp)(), 16137140Sbostic long offset)); 16237140Sbostic static time_t time2 P((struct tm *tmp, void (* funcp)(), 16337140Sbostic long offset, int * okayp)); 16437140Sbostic static void timesub P((const time_t * timep, long offset, 16537140Sbostic const struct state * sp, struct tm * tmp)); 16637140Sbostic static int tmcomp P((const struct tm * atmp, 16737140Sbostic const struct tm * btmp)); 16837140Sbostic static time_t transtime P((time_t janfirst, int year, 16937140Sbostic const struct rule * rulep, long offset)); 17037140Sbostic static int tzload P((const char * name, struct state * sp)); 17137140Sbostic static int tzparse P((const char * name, struct state * sp, 17237140Sbostic int lastditch)); 17337140Sbostic 17437140Sbostic #ifdef ALL_STATE 17537140Sbostic static struct state * lclptr; 17637140Sbostic static struct state * gmtptr; 17737140Sbostic #endif /* defined ALL_STATE */ 17837140Sbostic 17937140Sbostic #ifndef ALL_STATE 18037140Sbostic static struct state lclmem; 18137140Sbostic static struct state gmtmem; 18237140Sbostic #define lclptr (&lclmem) 18337140Sbostic #define gmtptr (&gmtmem) 18437140Sbostic #endif /* State Farm */ 18537140Sbostic 18637140Sbostic static int lcl_is_set; 18737140Sbostic static int gmt_is_set; 18837140Sbostic 18930608Sbostic char * tzname[2] = { 19037140Sbostic WILDABBR, 19137140Sbostic WILDABBR 19212974Ssam }; 19312974Ssam 19430608Sbostic #ifdef USG_COMPAT 19530608Sbostic time_t timezone = 0; 19630608Sbostic int daylight = 0; 19737140Sbostic #endif /* defined USG_COMPAT */ 1981959Swnj 19937140Sbostic #ifdef ALTZONE 20037140Sbostic time_t altzone = 0; 20137140Sbostic #endif /* defined ALTZONE */ 20237140Sbostic 20330608Sbostic static long 20430608Sbostic detzcode(codep) 20537140Sbostic const char * const codep; 2061959Swnj { 20730608Sbostic register long result; 20830608Sbostic register int i; 20930608Sbostic 21030608Sbostic result = 0; 21130608Sbostic for (i = 0; i < 4; ++i) 21230608Sbostic result = (result << 8) | (codep[i] & 0xff); 21330608Sbostic return result; 2141959Swnj } 2151959Swnj 21637140Sbostic static void 21737140Sbostic settzname() 2181959Swnj { 21937140Sbostic register const struct state * const sp = lclptr; 22037140Sbostic register int i; 2211959Swnj 22237140Sbostic tzname[0] = WILDABBR; 22337140Sbostic tzname[1] = WILDABBR; 22437140Sbostic #ifdef USG_COMPAT 22537140Sbostic daylight = 0; 22637140Sbostic timezone = 0; 22737140Sbostic #endif /* defined USG_COMPAT */ 22837140Sbostic #ifdef ALTZONE 22937140Sbostic altzone = 0; 23037140Sbostic #endif /* defined ALTZONE */ 23137140Sbostic #ifdef ALL_STATE 23237140Sbostic if (sp == NULL) { 23337140Sbostic tzname[0] = tzname[1] = GMT; 23437140Sbostic return; 23537140Sbostic } 23637140Sbostic #endif /* defined ALL_STATE */ 23737140Sbostic for (i = 0; i < sp->typecnt; ++i) { 23837140Sbostic register const struct ttinfo * const ttisp = &sp->ttis[i]; 23937140Sbostic 24037140Sbostic tzname[ttisp->tt_isdst] = 24137140Sbostic (char *) &sp->chars[ttisp->tt_abbrind]; 24237140Sbostic #ifdef USG_COMPAT 24337140Sbostic if (ttisp->tt_isdst) 24437140Sbostic daylight = 1; 24537140Sbostic if (i == 0 || !ttisp->tt_isdst) 24637140Sbostic timezone = -(ttisp->tt_gmtoff); 24737140Sbostic #endif /* defined USG_COMPAT */ 24837140Sbostic #ifdef ALTZONE 24937140Sbostic if (i == 0 || ttisp->tt_isdst) 25037140Sbostic altzone = -(ttisp->tt_gmtoff); 25137140Sbostic #endif /* defined ALTZONE */ 25237140Sbostic } 25337140Sbostic /* 25437140Sbostic ** And to get the latest zone names into tzname. . . 25537140Sbostic */ 25637140Sbostic for (i = 0; i < sp->timecnt; ++i) { 25737140Sbostic register const struct ttinfo * const ttisp = 25837140Sbostic &sp->ttis[sp->types[i]]; 25937140Sbostic 26037140Sbostic tzname[ttisp->tt_isdst] = 26137140Sbostic (char *) &sp->chars[ttisp->tt_abbrind]; 26237140Sbostic } 26337140Sbostic } 26437140Sbostic 26537140Sbostic static int 26637140Sbostic tzload(name, sp) 26737140Sbostic register const char * name; 26837140Sbostic register struct state * const sp; 26937140Sbostic { 27037140Sbostic register const char * p; 27137140Sbostic register int i; 27237140Sbostic register int fid; 27337140Sbostic 27437140Sbostic if (name == NULL && (name = TZDEFAULT) == NULL) 27530608Sbostic return -1; 27630608Sbostic { 27737140Sbostic register int doaccess; 27837140Sbostic char fullname[FILENAME_MAX + 1]; 27930608Sbostic 28037140Sbostic if (name[0] == ':') 28137140Sbostic ++name; 28230608Sbostic doaccess = name[0] == '/'; 28330608Sbostic if (!doaccess) { 28437140Sbostic if ((p = TZDIR) == NULL) 28530608Sbostic return -1; 28630608Sbostic if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) 28730608Sbostic return -1; 28830608Sbostic (void) strcpy(fullname, p); 28930608Sbostic (void) strcat(fullname, "/"); 29030608Sbostic (void) strcat(fullname, name); 29130608Sbostic /* 29230608Sbostic ** Set doaccess if '.' (as in "../") shows up in name. 29330608Sbostic */ 29437140Sbostic if (strchr(name, '.') != NULL) 29537140Sbostic doaccess = TRUE; 29630608Sbostic name = fullname; 29730608Sbostic } 29837140Sbostic if (doaccess && access(name, ACCESS_MODE) != 0) 29930608Sbostic return -1; 30037140Sbostic if ((fid = open(name, OPEN_MODE)) == -1) 30130608Sbostic return -1; 30225662Sbloom } 30330608Sbostic { 30437140Sbostic register const struct tzhead * tzhp; 30537140Sbostic char buf[sizeof *sp + sizeof *tzhp]; 30637140Sbostic int ttisstdcnt; 30730608Sbostic 30830608Sbostic i = read(fid, buf, sizeof buf); 30930608Sbostic if (close(fid) != 0 || i < sizeof *tzhp) 31030608Sbostic return -1; 31130608Sbostic tzhp = (struct tzhead *) buf; 31237140Sbostic ttisstdcnt = (int) detzcode(tzhp->tzh_ttisstdcnt); 31337140Sbostic sp->leapcnt = (int) detzcode(tzhp->tzh_leapcnt); 31437140Sbostic sp->timecnt = (int) detzcode(tzhp->tzh_timecnt); 31537140Sbostic sp->typecnt = (int) detzcode(tzhp->tzh_typecnt); 31637140Sbostic sp->charcnt = (int) detzcode(tzhp->tzh_charcnt); 31737140Sbostic if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || 31837140Sbostic sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || 31937140Sbostic sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || 32037140Sbostic sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || 32137140Sbostic (ttisstdcnt != sp->typecnt && ttisstdcnt != 0)) 32230608Sbostic return -1; 32330608Sbostic if (i < sizeof *tzhp + 32437140Sbostic sp->timecnt * (4 + sizeof (char)) + 32537140Sbostic sp->typecnt * (4 + 2 * sizeof (char)) + 32637140Sbostic sp->charcnt * sizeof (char) + 32737140Sbostic sp->leapcnt * 2 * 4 + 32837140Sbostic ttisstdcnt * sizeof (char)) 32930608Sbostic return -1; 33030608Sbostic p = buf + sizeof *tzhp; 33137140Sbostic for (i = 0; i < sp->timecnt; ++i) { 33237140Sbostic sp->ats[i] = detzcode(p); 33330608Sbostic p += 4; 33412974Ssam } 33537140Sbostic for (i = 0; i < sp->timecnt; ++i) { 33637140Sbostic sp->types[i] = (unsigned char) *p++; 33737140Sbostic if (sp->types[i] >= sp->typecnt) 33837140Sbostic return -1; 33937140Sbostic } 34037140Sbostic for (i = 0; i < sp->typecnt; ++i) { 34130608Sbostic register struct ttinfo * ttisp; 34230608Sbostic 34337140Sbostic ttisp = &sp->ttis[i]; 34430608Sbostic ttisp->tt_gmtoff = detzcode(p); 34530608Sbostic p += 4; 34630608Sbostic ttisp->tt_isdst = (unsigned char) *p++; 34737140Sbostic if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) 34837140Sbostic return -1; 34930608Sbostic ttisp->tt_abbrind = (unsigned char) *p++; 35037140Sbostic if (ttisp->tt_abbrind < 0 || 35137140Sbostic ttisp->tt_abbrind > sp->charcnt) 35237140Sbostic return -1; 35330608Sbostic } 35437140Sbostic for (i = 0; i < sp->charcnt; ++i) 35537140Sbostic sp->chars[i] = *p++; 35637140Sbostic sp->chars[i] = '\0'; /* ensure '\0' at end */ 35737140Sbostic for (i = 0; i < sp->leapcnt; ++i) { 35837140Sbostic register struct lsinfo * lsisp; 35937140Sbostic 36037140Sbostic lsisp = &sp->lsis[i]; 36137140Sbostic lsisp->ls_trans = detzcode(p); 36237140Sbostic p += 4; 36337140Sbostic lsisp->ls_corr = detzcode(p); 36437140Sbostic p += 4; 36537140Sbostic } 36637140Sbostic for (i = 0; i < sp->typecnt; ++i) { 36737140Sbostic register struct ttinfo * ttisp; 36837140Sbostic 36937140Sbostic ttisp = &sp->ttis[i]; 37037140Sbostic if (ttisstdcnt == 0) 37137140Sbostic ttisp->tt_ttisstd = FALSE; 37237140Sbostic else { 37337140Sbostic ttisp->tt_ttisstd = *p++; 37437140Sbostic if (ttisp->tt_ttisstd != TRUE && 37537140Sbostic ttisp->tt_ttisstd != FALSE) 37637140Sbostic return -1; 37737140Sbostic } 37837140Sbostic } 3791959Swnj } 38037140Sbostic return 0; 38137140Sbostic } 38237140Sbostic 38337140Sbostic static const int mon_lengths[2][MONSPERYEAR] = { 38437140Sbostic 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 38537140Sbostic 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 38637140Sbostic }; 38737140Sbostic 38837140Sbostic static const int year_lengths[2] = { 38937140Sbostic DAYSPERNYEAR, DAYSPERLYEAR 39037140Sbostic }; 39137140Sbostic 39237140Sbostic /* 39337140Sbostic ** Given a pointer into a time zone string, scan until a character that is not 39437140Sbostic ** a valid character in a zone name is found. Return a pointer to that 39537140Sbostic ** character. 39637140Sbostic */ 39737140Sbostic 39837140Sbostic static const char * 39937140Sbostic getzname(strp) 40037140Sbostic register const char * strp; 40137140Sbostic { 40237140Sbostic register char c; 40337140Sbostic 40437140Sbostic while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' && 40537140Sbostic c != '+') 40637140Sbostic ++strp; 40737140Sbostic return strp; 40837140Sbostic } 40937140Sbostic 41037140Sbostic /* 41137140Sbostic ** Given a pointer into a time zone string, extract a number from that string. 41237140Sbostic ** Check that the number is within a specified range; if it is not, return 41337140Sbostic ** NULL. 41437140Sbostic ** Otherwise, return a pointer to the first character not part of the number. 41537140Sbostic */ 41637140Sbostic 41737140Sbostic static const char * 41837140Sbostic getnum(strp, nump, min, max) 41937140Sbostic register const char * strp; 42037140Sbostic int * const nump; 42137140Sbostic const int min; 42237140Sbostic const int max; 42337140Sbostic { 42437140Sbostic register char c; 42537140Sbostic register int num; 42637140Sbostic 42737140Sbostic if (strp == NULL || !isdigit(*strp)) 42837140Sbostic return NULL; 42937140Sbostic num = 0; 43037140Sbostic while ((c = *strp) != '\0' && isdigit(c)) { 43137140Sbostic num = num * 10 + (c - '0'); 43237140Sbostic if (num > max) 43337140Sbostic return NULL; /* illegal value */ 43437140Sbostic ++strp; 43537140Sbostic } 43637140Sbostic if (num < min) 43737140Sbostic return NULL; /* illegal value */ 43837140Sbostic *nump = num; 43937140Sbostic return strp; 44037140Sbostic } 44137140Sbostic 44237140Sbostic /* 44337140Sbostic ** Given a pointer into a time zone string, extract a number of seconds, 44437140Sbostic ** in hh[:mm[:ss]] form, from the string. 44537140Sbostic ** If any error occurs, return NULL. 44637140Sbostic ** Otherwise, return a pointer to the first character not part of the number 44737140Sbostic ** of seconds. 44837140Sbostic */ 44937140Sbostic 45037140Sbostic static const char * 45137140Sbostic getsecs(strp, secsp) 45237140Sbostic register const char * strp; 45337140Sbostic long * const secsp; 45437140Sbostic { 45537140Sbostic int num; 45637140Sbostic 45737140Sbostic strp = getnum(strp, &num, 0, HOURSPERDAY); 45837140Sbostic if (strp == NULL) 45937140Sbostic return NULL; 46037140Sbostic *secsp = num * SECSPERHOUR; 46137140Sbostic if (*strp == ':') { 46237140Sbostic ++strp; 46337140Sbostic strp = getnum(strp, &num, 0, MINSPERHOUR - 1); 46437140Sbostic if (strp == NULL) 46537140Sbostic return NULL; 46637140Sbostic *secsp += num * SECSPERMIN; 46737140Sbostic if (*strp == ':') { 46837140Sbostic ++strp; 46937140Sbostic strp = getnum(strp, &num, 0, SECSPERMIN - 1); 47037140Sbostic if (strp == NULL) 47137140Sbostic return NULL; 47237140Sbostic *secsp += num; 47337140Sbostic } 47437140Sbostic } 47537140Sbostic return strp; 47637140Sbostic } 47737140Sbostic 47837140Sbostic /* 47937140Sbostic ** Given a pointer into a time zone string, extract an offset, in 48037140Sbostic ** [+-]hh[:mm[:ss]] form, from the string. 48137140Sbostic ** If any error occurs, return NULL. 48237140Sbostic ** Otherwise, return a pointer to the first character not part of the time. 48337140Sbostic */ 48437140Sbostic 48537140Sbostic static const char * 48637140Sbostic getoffset(strp, offsetp) 48737140Sbostic register const char * strp; 48837140Sbostic long * const offsetp; 48937140Sbostic { 49037140Sbostic register int neg; 49137140Sbostic 49237140Sbostic if (*strp == '-') { 49337140Sbostic neg = 1; 49437140Sbostic ++strp; 49537140Sbostic } else if (isdigit(*strp) || *strp++ == '+') 49637140Sbostic neg = 0; 49737140Sbostic else return NULL; /* illegal offset */ 49837140Sbostic strp = getsecs(strp, offsetp); 49937140Sbostic if (strp == NULL) 50037140Sbostic return NULL; /* illegal time */ 50137140Sbostic if (neg) 50237140Sbostic *offsetp = -*offsetp; 50337140Sbostic return strp; 50437140Sbostic } 50537140Sbostic 50637140Sbostic /* 50737140Sbostic ** Given a pointer into a time zone string, extract a rule in the form 50837140Sbostic ** date[/time]. See POSIX section 8 for the format of "date" and "time". 50937140Sbostic ** If a valid rule is not found, return NULL. 51037140Sbostic ** Otherwise, return a pointer to the first character not part of the rule. 51137140Sbostic */ 51237140Sbostic 51337140Sbostic static const char * 51437140Sbostic getrule(strp, rulep) 51537140Sbostic const char * strp; 51637140Sbostic register struct rule * const rulep; 51737140Sbostic { 51837140Sbostic if (*strp == 'J') { 51937140Sbostic /* 52037140Sbostic ** Julian day. 52137140Sbostic */ 52237140Sbostic rulep->r_type = JULIAN_DAY; 52337140Sbostic ++strp; 52437140Sbostic strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); 52537140Sbostic } else if (*strp == 'M') { 52637140Sbostic /* 52737140Sbostic ** Month, week, day. 52837140Sbostic */ 52937140Sbostic rulep->r_type = MONTH_NTH_DAY_OF_WEEK; 53037140Sbostic ++strp; 53137140Sbostic strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); 53237140Sbostic if (strp == NULL) 53337140Sbostic return NULL; 53437140Sbostic if (*strp++ != '.') 53537140Sbostic return NULL; 53637140Sbostic strp = getnum(strp, &rulep->r_week, 1, 5); 53737140Sbostic if (strp == NULL) 53837140Sbostic return NULL; 53937140Sbostic if (*strp++ != '.') 54037140Sbostic return NULL; 54137140Sbostic strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); 54237140Sbostic } else if (isdigit(*strp)) { 54337140Sbostic /* 54437140Sbostic ** Day of year. 54537140Sbostic */ 54637140Sbostic rulep->r_type = DAY_OF_YEAR; 54737140Sbostic strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); 54837140Sbostic } else return NULL; /* invalid format */ 54937140Sbostic if (strp == NULL) 55037140Sbostic return NULL; 55137140Sbostic if (*strp == '/') { 55237140Sbostic /* 55337140Sbostic ** Time specified. 55437140Sbostic */ 55537140Sbostic ++strp; 55637140Sbostic strp = getsecs(strp, &rulep->r_time); 55737140Sbostic } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ 55837140Sbostic return strp; 55937140Sbostic } 56037140Sbostic 56137140Sbostic /* 56237140Sbostic ** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the 56337140Sbostic ** year, a rule, and the offset from GMT at the time that rule takes effect, 56437140Sbostic ** calculate the Epoch-relative time that rule takes effect. 56537140Sbostic */ 56637140Sbostic 56737140Sbostic static time_t 56837140Sbostic transtime(janfirst, year, rulep, offset) 56937140Sbostic const time_t janfirst; 57037140Sbostic const int year; 57137140Sbostic register const struct rule * const rulep; 57237140Sbostic const long offset; 57337140Sbostic { 57437140Sbostic register int leapyear; 57537140Sbostic register time_t value; 57637140Sbostic register int i; 57737140Sbostic int d, m1, yy0, yy1, yy2, dow; 57837140Sbostic 57937140Sbostic leapyear = isleap(year); 58037140Sbostic switch (rulep->r_type) { 58137140Sbostic 58237140Sbostic case JULIAN_DAY: 58337140Sbostic /* 58437140Sbostic ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap 58537140Sbostic ** years. 58637140Sbostic ** In non-leap years, or if the day number is 59 or less, just 58737140Sbostic ** add SECSPERDAY times the day number-1 to the time of 58837140Sbostic ** January 1, midnight, to get the day. 58937140Sbostic */ 59037140Sbostic value = janfirst + (rulep->r_day - 1) * SECSPERDAY; 59137140Sbostic if (leapyear && rulep->r_day >= 60) 59237140Sbostic value += SECSPERDAY; 59337140Sbostic break; 59437140Sbostic 59537140Sbostic case DAY_OF_YEAR: 59637140Sbostic /* 59737140Sbostic ** n - day of year. 59837140Sbostic ** Just add SECSPERDAY times the day number to the time of 59937140Sbostic ** January 1, midnight, to get the day. 60037140Sbostic */ 60137140Sbostic value = janfirst + rulep->r_day * SECSPERDAY; 60237140Sbostic break; 60337140Sbostic 60437140Sbostic case MONTH_NTH_DAY_OF_WEEK: 60537140Sbostic /* 60637140Sbostic ** Mm.n.d - nth "dth day" of month m. 60737140Sbostic */ 60837140Sbostic value = janfirst; 60937140Sbostic for (i = 0; i < rulep->r_mon - 1; ++i) 61037140Sbostic value += mon_lengths[leapyear][i] * SECSPERDAY; 61137140Sbostic 61237140Sbostic /* 61337140Sbostic ** Use Zeller's Congruence to get day-of-week of first day of 61437140Sbostic ** month. 61537140Sbostic */ 61637140Sbostic m1 = (rulep->r_mon + 9) % 12 + 1; 61737140Sbostic yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; 61837140Sbostic yy1 = yy0 / 100; 61937140Sbostic yy2 = yy0 % 100; 62037140Sbostic dow = ((26 * m1 - 2) / 10 + 62137140Sbostic 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; 62237140Sbostic if (dow < 0) 62337140Sbostic dow += DAYSPERWEEK; 62437140Sbostic 62537140Sbostic /* 62637140Sbostic ** "dow" is the day-of-week of the first day of the month. Get 62737140Sbostic ** the day-of-month (zero-origin) of the first "dow" day of the 62837140Sbostic ** month. 62937140Sbostic */ 63037140Sbostic d = rulep->r_day - dow; 63137140Sbostic if (d < 0) 63237140Sbostic d += DAYSPERWEEK; 63337140Sbostic for (i = 1; i < rulep->r_week; ++i) { 63437140Sbostic if (d + DAYSPERWEEK >= 63537140Sbostic mon_lengths[leapyear][rulep->r_mon - 1]) 63637140Sbostic break; 63737140Sbostic d += DAYSPERWEEK; 63837140Sbostic } 63937140Sbostic 64037140Sbostic /* 64137140Sbostic ** "d" is the day-of-month (zero-origin) of the day we want. 64237140Sbostic */ 64337140Sbostic value += d * SECSPERDAY; 64437140Sbostic break; 64537140Sbostic } 64637140Sbostic 64730608Sbostic /* 64837140Sbostic ** "value" is the Epoch-relative time of 00:00:00 GMT on the day in 64937140Sbostic ** question. To get the Epoch-relative time of the specified local 65037140Sbostic ** time on that day, add the transition time and the current offset 65137140Sbostic ** from GMT. 65230608Sbostic */ 65337140Sbostic return value + rulep->r_time + offset; 65437140Sbostic } 65537140Sbostic 65637140Sbostic /* 65737140Sbostic ** Given a POSIX section 8-style TZ string, fill in the rule tables as 65837140Sbostic ** appropriate. 65937140Sbostic */ 66037140Sbostic 66137140Sbostic static int 66237140Sbostic tzparse(name, sp, lastditch) 66337140Sbostic const char * name; 66437140Sbostic register struct state * const sp; 66537140Sbostic const int lastditch; 66637140Sbostic { 66737140Sbostic const char * stdname; 66837140Sbostic const char * dstname; 66937140Sbostic int stdlen; 67037140Sbostic int dstlen; 67137140Sbostic long stdoffset; 67237140Sbostic long dstoffset; 67337140Sbostic register time_t * atp; 67437140Sbostic register unsigned char * typep; 67537140Sbostic register char * cp; 67637140Sbostic register int load_result; 67737140Sbostic 67837140Sbostic stdname = name; 67937140Sbostic if (lastditch) { 68037140Sbostic stdlen = strlen(name); /* length of standard zone name */ 68137140Sbostic name += stdlen; 68237140Sbostic if (stdlen >= sizeof sp->chars) 68337140Sbostic stdlen = (sizeof sp->chars) - 1; 68437140Sbostic } else { 68537140Sbostic name = getzname(name); 68637140Sbostic stdlen = name - stdname; 68737140Sbostic if (stdlen < 3) 68830608Sbostic return -1; 68937140Sbostic } 69037140Sbostic if (*name == '\0') 69139105Sbostic return -1; 69237140Sbostic else { 69337140Sbostic name = getoffset(name, &stdoffset); 69437140Sbostic if (name == NULL) 69530608Sbostic return -1; 69637140Sbostic } 69737140Sbostic load_result = tzload(TZDEFRULES, sp); 69837140Sbostic if (load_result != 0) 69937140Sbostic sp->leapcnt = 0; /* so, we're off a little */ 70037140Sbostic if (*name != '\0') { 70137140Sbostic dstname = name; 70237140Sbostic name = getzname(name); 70337140Sbostic dstlen = name - dstname; /* length of DST zone name */ 70437140Sbostic if (dstlen < 3) 70537140Sbostic return -1; 70637140Sbostic if (*name != '\0' && *name != ',' && *name != ';') { 70737140Sbostic name = getoffset(name, &dstoffset); 70837140Sbostic if (name == NULL) 70937140Sbostic return -1; 71037140Sbostic } else dstoffset = stdoffset - SECSPERHOUR; 71137140Sbostic if (*name == ',' || *name == ';') { 71237140Sbostic struct rule start; 71337140Sbostic struct rule end; 71437140Sbostic register int year; 71537140Sbostic register time_t janfirst; 71637140Sbostic time_t starttime; 71737140Sbostic time_t endtime; 71830608Sbostic 71937140Sbostic ++name; 72037140Sbostic if ((name = getrule(name, &start)) == NULL) 72137140Sbostic return -1; 72237140Sbostic if (*name++ != ',') 72337140Sbostic return -1; 72437140Sbostic if ((name = getrule(name, &end)) == NULL) 72537140Sbostic return -1; 72637140Sbostic if (*name != '\0') 72737140Sbostic return -1; 72837140Sbostic sp->typecnt = 2; /* standard time and DST */ 72937140Sbostic /* 73037140Sbostic ** Two transitions per year, from EPOCH_YEAR to 2037. 73137140Sbostic */ 73237140Sbostic sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1); 73337140Sbostic if (sp->timecnt > TZ_MAX_TIMES) 73437140Sbostic return -1; 73537140Sbostic sp->ttis[0].tt_gmtoff = -dstoffset; 73637140Sbostic sp->ttis[0].tt_isdst = 1; 73737140Sbostic sp->ttis[0].tt_abbrind = stdlen + 1; 73837140Sbostic sp->ttis[1].tt_gmtoff = -stdoffset; 73937140Sbostic sp->ttis[1].tt_isdst = 0; 74037140Sbostic sp->ttis[1].tt_abbrind = 0; 74137140Sbostic atp = sp->ats; 74237140Sbostic typep = sp->types; 74337140Sbostic janfirst = 0; 74437140Sbostic for (year = EPOCH_YEAR; year <= 2037; ++year) { 74537140Sbostic starttime = transtime(janfirst, year, &start, 74637140Sbostic stdoffset); 74737140Sbostic endtime = transtime(janfirst, year, &end, 74837140Sbostic dstoffset); 74937140Sbostic if (starttime > endtime) { 75037140Sbostic *atp++ = endtime; 75137140Sbostic *typep++ = 1; /* DST ends */ 75237140Sbostic *atp++ = starttime; 75337140Sbostic *typep++ = 0; /* DST begins */ 75437140Sbostic } else { 75537140Sbostic *atp++ = starttime; 75637140Sbostic *typep++ = 0; /* DST begins */ 75737140Sbostic *atp++ = endtime; 75837140Sbostic *typep++ = 1; /* DST ends */ 75937140Sbostic } 76037140Sbostic janfirst += 76137140Sbostic year_lengths[isleap(year)] * SECSPERDAY; 76237140Sbostic } 76330608Sbostic } else { 76437140Sbostic int sawstd; 76537140Sbostic int sawdst; 76637140Sbostic long stdfix; 76737140Sbostic long dstfix; 76837140Sbostic long oldfix; 76937140Sbostic int isdst; 77037140Sbostic register int i; 77137140Sbostic 77237140Sbostic if (*name != '\0') 77337140Sbostic return -1; 77437140Sbostic if (load_result != 0) 77537140Sbostic return -1; 77637140Sbostic /* 77737140Sbostic ** Compute the difference between the real and 77837140Sbostic ** prototype standard and summer time offsets 77937140Sbostic ** from GMT, and put the real standard and summer 78037140Sbostic ** time offsets into the rules in place of the 78137140Sbostic ** prototype offsets. 78237140Sbostic */ 78337140Sbostic sawstd = FALSE; 78437140Sbostic sawdst = FALSE; 78537140Sbostic stdfix = 0; 78637140Sbostic dstfix = 0; 78737140Sbostic for (i = 0; i < sp->typecnt; ++i) { 78837140Sbostic if (sp->ttis[i].tt_isdst) { 78937140Sbostic oldfix = dstfix; 79037140Sbostic dstfix = 79137140Sbostic sp->ttis[i].tt_gmtoff + dstoffset; 79237140Sbostic if (sawdst && (oldfix != dstfix)) 79337140Sbostic return -1; 79437140Sbostic sp->ttis[i].tt_gmtoff = -dstoffset; 79537140Sbostic sp->ttis[i].tt_abbrind = stdlen + 1; 79637140Sbostic sawdst = TRUE; 79737140Sbostic } else { 79837140Sbostic oldfix = stdfix; 79937140Sbostic stdfix = 80037140Sbostic sp->ttis[i].tt_gmtoff + stdoffset; 80137140Sbostic if (sawstd && (oldfix != stdfix)) 80237140Sbostic return -1; 80337140Sbostic sp->ttis[i].tt_gmtoff = -stdoffset; 80437140Sbostic sp->ttis[i].tt_abbrind = 0; 80537140Sbostic sawstd = TRUE; 80637140Sbostic } 80737140Sbostic } 80837140Sbostic /* 80937140Sbostic ** Make sure we have both standard and summer time. 81037140Sbostic */ 81137140Sbostic if (!sawdst || !sawstd) 81237140Sbostic return -1; 81337140Sbostic /* 81437140Sbostic ** Now correct the transition times by shifting 81537140Sbostic ** them by the difference between the real and 81637140Sbostic ** prototype offsets. Note that this difference 81737140Sbostic ** can be different in standard and summer time; 81837140Sbostic ** the prototype probably has a 1-hour difference 81937140Sbostic ** between standard and summer time, but a different 82037140Sbostic ** difference can be specified in TZ. 82137140Sbostic */ 82237140Sbostic isdst = FALSE; /* we start in standard time */ 82337140Sbostic for (i = 0; i < sp->timecnt; ++i) { 82437140Sbostic register const struct ttinfo * ttisp; 82537140Sbostic 82637140Sbostic /* 82737140Sbostic ** If summer time is in effect, and the 82837140Sbostic ** transition time was not specified as 82937140Sbostic ** standard time, add the summer time 83037140Sbostic ** offset to the transition time; 83137140Sbostic ** otherwise, add the standard time offset 83237140Sbostic ** to the transition time. 83337140Sbostic */ 83437140Sbostic ttisp = &sp->ttis[sp->types[i]]; 83537140Sbostic sp->ats[i] += 83637140Sbostic (isdst && !ttisp->tt_ttisstd) ? 83737140Sbostic dstfix : stdfix; 83837140Sbostic isdst = ttisp->tt_isdst; 83937140Sbostic } 84030608Sbostic } 84137140Sbostic } else { 84237140Sbostic dstlen = 0; 84337140Sbostic sp->typecnt = 1; /* only standard time */ 84437140Sbostic sp->timecnt = 0; 84537140Sbostic sp->ttis[0].tt_gmtoff = -stdoffset; 84637140Sbostic sp->ttis[0].tt_isdst = 0; 84737140Sbostic sp->ttis[0].tt_abbrind = 0; 84830608Sbostic } 84937140Sbostic sp->charcnt = stdlen + 1; 85037140Sbostic if (dstlen != 0) 85137140Sbostic sp->charcnt += dstlen + 1; 85237140Sbostic if (sp->charcnt > sizeof sp->chars) 85330682Sbostic return -1; 85437140Sbostic cp = sp->chars; 85537140Sbostic (void) strncpy(cp, stdname, stdlen); 85637140Sbostic cp += stdlen; 85737140Sbostic *cp++ = '\0'; 85837140Sbostic if (dstlen != 0) { 85937140Sbostic (void) strncpy(cp, dstname, dstlen); 86037140Sbostic *(cp + dstlen) = '\0'; 86137140Sbostic } 86230682Sbostic return 0; 86330682Sbostic } 86430682Sbostic 86537140Sbostic static void 86637140Sbostic gmtload(sp) 86737140Sbostic struct state * const sp; 8681959Swnj { 86937140Sbostic if (tzload(GMT, sp) != 0) 87037140Sbostic (void) tzparse(GMT, sp, TRUE); 8711959Swnj } 8721959Swnj 87330608Sbostic void 87430608Sbostic tzset() 87530608Sbostic { 87637140Sbostic register const char * name; 87737140Sbostic void tzsetwall(); 87830608Sbostic 87930608Sbostic name = getenv("TZ"); 88037140Sbostic if (name == NULL) { 88137140Sbostic tzsetwall(); 88237140Sbostic return; 88337140Sbostic } 88437140Sbostic lcl_is_set = TRUE; 88537140Sbostic #ifdef ALL_STATE 88637140Sbostic if (lclptr == NULL) { 88737140Sbostic lclptr = (struct state *) malloc(sizeof *lclptr); 88837140Sbostic if (lclptr == NULL) { 88937140Sbostic settzname(); /* all we can do */ 89030682Sbostic return; 89137140Sbostic } 89237140Sbostic } 89337140Sbostic #endif /* defined ALL_STATE */ 89437140Sbostic if (*name == '\0') { 89537140Sbostic /* 89637140Sbostic ** User wants it fast rather than right. 89737140Sbostic */ 89837140Sbostic lclptr->leapcnt = 0; /* so, we're off a little */ 89937140Sbostic lclptr->timecnt = 0; 90037140Sbostic lclptr->ttis[0].tt_gmtoff = 0; 90137140Sbostic lclptr->ttis[0].tt_abbrind = 0; 90237140Sbostic (void) strcpy(lclptr->chars, GMT); 90337140Sbostic } else if (tzload(name, lclptr) != 0) 90437140Sbostic if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) 90539105Sbostic (void) gmtload(lclptr); 90637140Sbostic settzname(); 90737140Sbostic } 90837140Sbostic 90937140Sbostic void 91037140Sbostic tzsetwall() 91137140Sbostic { 91237140Sbostic lcl_is_set = TRUE; 91337140Sbostic #ifdef ALL_STATE 91437140Sbostic if (lclptr == NULL) { 91537140Sbostic lclptr = (struct state *) malloc(sizeof *lclptr); 91637140Sbostic if (lclptr == NULL) { 91737140Sbostic settzname(); /* all we can do */ 91830682Sbostic return; 91937140Sbostic } 92030682Sbostic } 92137140Sbostic #endif /* defined ALL_STATE */ 92237140Sbostic if (tzload((char *) NULL, lclptr) != 0) 92337140Sbostic gmtload(lclptr); 92437140Sbostic settzname(); 92530608Sbostic } 92630608Sbostic 92737140Sbostic /* 92837140Sbostic ** The easy way to behave "as if no library function calls" localtime 92937140Sbostic ** is to not call it--so we drop its guts into "localsub", which can be 93037140Sbostic ** freely called. (And no, the PANS doesn't require the above behavior-- 93137140Sbostic ** but it *is* desirable.) 93237140Sbostic ** 93337140Sbostic ** The unused offset argument is for the benefit of mktime variants. 93437140Sbostic */ 93537140Sbostic 93637140Sbostic /*ARGSUSED*/ 93737140Sbostic static void 93837140Sbostic localsub(timep, offset, tmp) 93937140Sbostic const time_t * const timep; 94037140Sbostic const long offset; 94137140Sbostic struct tm * const tmp; 9421959Swnj { 94337140Sbostic register const struct state * sp; 94437140Sbostic register const struct ttinfo * ttisp; 94530608Sbostic register int i; 94637140Sbostic const time_t t = *timep; 9471959Swnj 94837140Sbostic if (!lcl_is_set) 94937140Sbostic tzset(); 95037140Sbostic sp = lclptr; 95137140Sbostic #ifdef ALL_STATE 95237140Sbostic if (sp == NULL) { 95337140Sbostic gmtsub(timep, offset, tmp); 95437140Sbostic return; 95537140Sbostic } 95637140Sbostic #endif /* defined ALL_STATE */ 95737140Sbostic if (sp->timecnt == 0 || t < sp->ats[0]) { 95830608Sbostic i = 0; 95937140Sbostic while (sp->ttis[i].tt_isdst) 96037140Sbostic if (++i >= sp->typecnt) { 96130608Sbostic i = 0; 96230608Sbostic break; 96330608Sbostic } 96430608Sbostic } else { 96537140Sbostic for (i = 1; i < sp->timecnt; ++i) 96637140Sbostic if (t < sp->ats[i]) 96730608Sbostic break; 96837140Sbostic i = sp->types[i - 1]; 9691959Swnj } 97037140Sbostic ttisp = &sp->ttis[i]; 9711959Swnj /* 97230608Sbostic ** To get (wrong) behavior that's compatible with System V Release 2.0 97330608Sbostic ** you'd replace the statement below with 97437140Sbostic ** t += ttisp->tt_gmtoff; 97537140Sbostic ** timesub(&t, 0L, sp, tmp); 97630608Sbostic */ 97737140Sbostic timesub(&t, ttisp->tt_gmtoff, sp, tmp); 97830608Sbostic tmp->tm_isdst = ttisp->tt_isdst; 97937140Sbostic tzname[tmp->tm_isdst] = (char *) &sp->chars[ttisp->tt_abbrind]; 98037140Sbostic tmp->tm_zone = &sp->chars[ttisp->tt_abbrind]; 98130608Sbostic } 9821959Swnj 98330608Sbostic struct tm * 98437140Sbostic localtime(timep) 98537140Sbostic const time_t * const timep; 98630608Sbostic { 98737140Sbostic static struct tm tm; 9881959Swnj 98937140Sbostic localsub(timep, 0L, &tm); 99037140Sbostic return &tm; 99130608Sbostic } 9921959Swnj 99337140Sbostic /* 99437140Sbostic ** gmtsub is to gmtime as localsub is to localtime. 99537140Sbostic */ 9961959Swnj 99737140Sbostic static void 99837140Sbostic gmtsub(timep, offset, tmp) 99937140Sbostic const time_t * const timep; 100037140Sbostic const long offset; 100137140Sbostic struct tm * const tmp; 100237140Sbostic { 100337140Sbostic if (!gmt_is_set) { 100437140Sbostic gmt_is_set = TRUE; 100537140Sbostic #ifdef ALL_STATE 100637140Sbostic gmtptr = (struct state *) malloc(sizeof *gmtptr); 100737140Sbostic if (gmtptr != NULL) 100837140Sbostic #endif /* defined ALL_STATE */ 100937140Sbostic gmtload(gmtptr); 101037140Sbostic } 101137140Sbostic timesub(timep, offset, gmtptr, tmp); 101237140Sbostic /* 101337140Sbostic ** Could get fancy here and deliver something such as 101437140Sbostic ** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero, 101537140Sbostic ** but this is no time for a treasure hunt. 101637140Sbostic */ 101737140Sbostic if (offset != 0) 101837140Sbostic tmp->tm_zone = WILDABBR; 101937140Sbostic else { 102037140Sbostic #ifdef ALL_STATE 102137140Sbostic if (gmtptr == NULL) 102237140Sbostic tmp->TM_ZONE = GMT; 102337140Sbostic else tmp->TM_ZONE = gmtptr->chars; 102437140Sbostic #endif /* defined ALL_STATE */ 102537140Sbostic #ifndef ALL_STATE 102637140Sbostic tmp->tm_zone = gmtptr->chars; 102737140Sbostic #endif /* State Farm */ 102837140Sbostic } 102937140Sbostic } 10301959Swnj 103130608Sbostic struct tm * 103237140Sbostic gmtime(timep) 103337140Sbostic const time_t * const timep; 10341959Swnj { 103530608Sbostic static struct tm tm; 10361959Swnj 103737140Sbostic gmtsub(timep, 0L, &tm); 103837140Sbostic return &tm; 103937140Sbostic } 104037140Sbostic 104137140Sbostic static void 104237140Sbostic timesub(timep, offset, sp, tmp) 104337140Sbostic const time_t * const timep; 104437140Sbostic const long offset; 104537140Sbostic register const struct state * const sp; 104637140Sbostic register struct tm * const tmp; 104737140Sbostic { 104837140Sbostic register const struct lsinfo * lp; 104937140Sbostic register long days; 105037140Sbostic register long rem; 105137140Sbostic register int y; 105237140Sbostic register int yleap; 105337140Sbostic register const int * ip; 105437140Sbostic register long corr; 105537140Sbostic register int hit; 105637140Sbostic register int i; 105737140Sbostic 105837140Sbostic corr = 0; 105937140Sbostic hit = FALSE; 106037140Sbostic #ifdef ALL_STATE 106137140Sbostic i = (sp == NULL) ? 0 : sp->leapcnt; 106237140Sbostic #endif /* defined ALL_STATE */ 106337140Sbostic #ifndef ALL_STATE 106437140Sbostic i = sp->leapcnt; 106537140Sbostic #endif /* State Farm */ 106637140Sbostic while (--i >= 0) { 106737140Sbostic lp = &sp->lsis[i]; 106837140Sbostic if (*timep >= lp->ls_trans) { 106937140Sbostic if (*timep == lp->ls_trans) 107037140Sbostic hit = ((i == 0 && lp->ls_corr > 0) || 107137140Sbostic lp->ls_corr > sp->lsis[i - 1].ls_corr); 107237140Sbostic corr = lp->ls_corr; 107337140Sbostic break; 107437140Sbostic } 107537140Sbostic } 107637140Sbostic days = *timep / SECSPERDAY; 107737140Sbostic rem = *timep % SECSPERDAY; 107837140Sbostic #ifdef mc68k 107937140Sbostic if (*timep == 0x80000000) { 108037140Sbostic /* 108137140Sbostic ** A 3B1 muffs the division on the most negative number. 108237140Sbostic */ 108337140Sbostic days = -24855; 108437140Sbostic rem = -11648; 108537140Sbostic } 108637140Sbostic #endif /* mc68k */ 108737140Sbostic rem += (offset - corr); 108830608Sbostic while (rem < 0) { 108937140Sbostic rem += SECSPERDAY; 109030608Sbostic --days; 10911959Swnj } 109237140Sbostic while (rem >= SECSPERDAY) { 109337140Sbostic rem -= SECSPERDAY; 109430608Sbostic ++days; 109530608Sbostic } 109637140Sbostic tmp->tm_hour = (int) (rem / SECSPERHOUR); 109737140Sbostic rem = rem % SECSPERHOUR; 109837140Sbostic tmp->tm_min = (int) (rem / SECSPERMIN); 109937140Sbostic tmp->tm_sec = (int) (rem % SECSPERMIN); 110037140Sbostic if (hit) 110137140Sbostic /* 110237140Sbostic ** A positive leap second requires a special 110337140Sbostic ** representation. This uses "... ??:59:60". 110437140Sbostic */ 110537140Sbostic ++(tmp->tm_sec); 110637140Sbostic tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); 110730608Sbostic if (tmp->tm_wday < 0) 110837140Sbostic tmp->tm_wday += DAYSPERWEEK; 110930608Sbostic y = EPOCH_YEAR; 111030608Sbostic if (days >= 0) 111130608Sbostic for ( ; ; ) { 111230608Sbostic yleap = isleap(y); 111330608Sbostic if (days < (long) year_lengths[yleap]) 111430608Sbostic break; 111530608Sbostic ++y; 111630608Sbostic days = days - (long) year_lengths[yleap]; 111730608Sbostic } 111830608Sbostic else do { 111930608Sbostic --y; 112030608Sbostic yleap = isleap(y); 112130608Sbostic days = days + (long) year_lengths[yleap]; 112230608Sbostic } while (days < 0); 112330608Sbostic tmp->tm_year = y - TM_YEAR_BASE; 112430608Sbostic tmp->tm_yday = (int) days; 112530608Sbostic ip = mon_lengths[yleap]; 112630608Sbostic for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon)) 112730608Sbostic days = days - (long) ip[tmp->tm_mon]; 112830608Sbostic tmp->tm_mday = (int) (days + 1); 112930608Sbostic tmp->tm_isdst = 0; 113037140Sbostic #ifdef TM_GMTOFF 113137140Sbostic tmp->TM_GMTOFF = offset; 113237140Sbostic #endif /* defined TM_GMTOFF */ 11331959Swnj } 113437140Sbostic 113537140Sbostic /* 113637140Sbostic ** A la X3J11 113737140Sbostic */ 113837140Sbostic 113937140Sbostic char * 114037140Sbostic asctime(timeptr) 114137140Sbostic register const struct tm * timeptr; 114237140Sbostic { 114337140Sbostic static const char wday_name[DAYSPERWEEK][3] = { 114437140Sbostic "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 114537140Sbostic }; 114637140Sbostic static const char mon_name[MONSPERYEAR][3] = { 114737140Sbostic "Jan", "Feb", "Mar", "Apr", "May", "Jun", 114837140Sbostic "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 114937140Sbostic }; 115037140Sbostic static char result[26]; 115137140Sbostic 115237140Sbostic (void) sprintf(result, "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n", 115337140Sbostic wday_name[timeptr->tm_wday], 115437140Sbostic mon_name[timeptr->tm_mon], 115537140Sbostic timeptr->tm_mday, timeptr->tm_hour, 115637140Sbostic timeptr->tm_min, timeptr->tm_sec, 115737140Sbostic TM_YEAR_BASE + timeptr->tm_year); 115837140Sbostic return result; 115937140Sbostic } 116037140Sbostic 116137140Sbostic char * 116237140Sbostic ctime(timep) 116337140Sbostic const time_t * const timep; 116437140Sbostic { 116537140Sbostic return asctime(localtime(timep)); 116637140Sbostic } 116737140Sbostic 116837140Sbostic /* 116937140Sbostic ** Adapted from code provided by Robert Elz, who writes: 117037140Sbostic ** The "best" way to do mktime I think is based on an idea of Bob 117137140Sbostic ** Kridle's (so its said...) from a long time ago. (mtxinu!kridle now). 117237140Sbostic ** It does a binary search of the time_t space. Since time_t's are 117337140Sbostic ** just 32 bits, its a max of 32 iterations (even at 64 bits it 117437140Sbostic ** would still be very reasonable). 117537140Sbostic */ 117637140Sbostic 117737140Sbostic #ifndef WRONG 117837140Sbostic #define WRONG (-1) 117937140Sbostic #endif /* !defined WRONG */ 118037140Sbostic 118137140Sbostic static void 118237140Sbostic normalize(tensptr, unitsptr, base) 118337140Sbostic int * const tensptr; 118437140Sbostic int * const unitsptr; 118537140Sbostic const int base; 118637140Sbostic { 118737140Sbostic if (*unitsptr >= base) { 118837140Sbostic *tensptr += *unitsptr / base; 118937140Sbostic *unitsptr %= base; 119037140Sbostic } else if (*unitsptr < 0) { 119137140Sbostic --*tensptr; 119237140Sbostic *unitsptr += base; 119337140Sbostic if (*unitsptr < 0) { 119437140Sbostic *tensptr -= 1 + (-*unitsptr) / base; 119537140Sbostic *unitsptr = base - (-*unitsptr) % base; 119637140Sbostic } 119737140Sbostic } 119837140Sbostic } 119937140Sbostic 120037140Sbostic static int 120137140Sbostic tmcomp(atmp, btmp) 120237140Sbostic register const struct tm * const atmp; 120337140Sbostic register const struct tm * const btmp; 120437140Sbostic { 120537140Sbostic register int result; 120637140Sbostic 120737140Sbostic if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && 120837140Sbostic (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && 120937140Sbostic (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && 121037140Sbostic (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && 121137140Sbostic (result = (atmp->tm_min - btmp->tm_min)) == 0) 121237140Sbostic result = atmp->tm_sec - btmp->tm_sec; 121337140Sbostic return result; 121437140Sbostic } 121537140Sbostic 121637140Sbostic static time_t 121737140Sbostic time2(tmp, funcp, offset, okayp) 121837140Sbostic struct tm * const tmp; 121937140Sbostic void (* const funcp)(); 122037140Sbostic const long offset; 122137140Sbostic int * const okayp; 122237140Sbostic { 122337140Sbostic register const struct state * sp; 122437140Sbostic register int dir; 122537140Sbostic register int bits; 122637140Sbostic register int i, j ; 122737140Sbostic register int saved_seconds; 122837140Sbostic time_t newt; 122937140Sbostic time_t t; 123037140Sbostic struct tm yourtm, mytm; 123137140Sbostic 123237140Sbostic *okayp = FALSE; 123337140Sbostic yourtm = *tmp; 123437140Sbostic if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0) 123537140Sbostic normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN); 123637140Sbostic normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR); 123737140Sbostic normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY); 123837140Sbostic normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR); 123937140Sbostic while (yourtm.tm_mday <= 0) { 124037140Sbostic --yourtm.tm_year; 124137140Sbostic yourtm.tm_mday += 124237140Sbostic year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)]; 124337140Sbostic } 124437140Sbostic for ( ; ; ) { 124537140Sbostic i = mon_lengths[isleap(yourtm.tm_year + 124637140Sbostic TM_YEAR_BASE)][yourtm.tm_mon]; 124737140Sbostic if (yourtm.tm_mday <= i) 124837140Sbostic break; 124937140Sbostic yourtm.tm_mday -= i; 125037140Sbostic if (++yourtm.tm_mon >= MONSPERYEAR) { 125137140Sbostic yourtm.tm_mon = 0; 125237140Sbostic ++yourtm.tm_year; 125337140Sbostic } 125437140Sbostic } 125537140Sbostic saved_seconds = yourtm.tm_sec; 125637140Sbostic yourtm.tm_sec = 0; 125737140Sbostic /* 125837140Sbostic ** Calculate the number of magnitude bits in a time_t 125937140Sbostic ** (this works regardless of whether time_t is 126037140Sbostic ** signed or unsigned, though lint complains if unsigned). 126137140Sbostic */ 126237140Sbostic for (bits = 0, t = 1; t > 0; ++bits, t <<= 1) 126337140Sbostic ; 126437140Sbostic /* 126537140Sbostic ** If time_t is signed, then 0 is the median value, 126637140Sbostic ** if time_t is unsigned, then 1 << bits is median. 126737140Sbostic */ 126837140Sbostic t = (t < 0) ? 0 : ((time_t) 1 << bits); 126937140Sbostic for ( ; ; ) { 127037140Sbostic (*funcp)(&t, offset, &mytm); 127137140Sbostic dir = tmcomp(&mytm, &yourtm); 127237140Sbostic if (dir != 0) { 127337140Sbostic if (bits-- < 0) 127437140Sbostic return WRONG; 127537140Sbostic if (bits < 0) 127637140Sbostic --t; 127737140Sbostic else if (dir > 0) 127837140Sbostic t -= (time_t) 1 << bits; 127937140Sbostic else t += (time_t) 1 << bits; 128037140Sbostic continue; 128137140Sbostic } 128237140Sbostic if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) 128337140Sbostic break; 128437140Sbostic /* 128537140Sbostic ** Right time, wrong type. 128637140Sbostic ** Hunt for right time, right type. 128737140Sbostic ** It's okay to guess wrong since the guess 128837140Sbostic ** gets checked. 128937140Sbostic */ 129037140Sbostic sp = (const struct state *) 129137140Sbostic ((funcp == localsub) ? lclptr : gmtptr); 129237140Sbostic #ifdef ALL_STATE 129337140Sbostic if (sp == NULL) 129437140Sbostic return WRONG; 129537140Sbostic #endif /* defined ALL_STATE */ 129637140Sbostic for (i = 0; i < sp->typecnt; ++i) { 129737140Sbostic if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) 129837140Sbostic continue; 129937140Sbostic for (j = 0; j < sp->typecnt; ++j) { 130037140Sbostic if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) 130137140Sbostic continue; 130237140Sbostic newt = t + sp->ttis[j].tt_gmtoff - 130337140Sbostic sp->ttis[i].tt_gmtoff; 130437140Sbostic (*funcp)(&newt, offset, &mytm); 130537140Sbostic if (tmcomp(&mytm, &yourtm) != 0) 130637140Sbostic continue; 130737140Sbostic if (mytm.tm_isdst != yourtm.tm_isdst) 130837140Sbostic continue; 130937140Sbostic /* 131037140Sbostic ** We have a match. 131137140Sbostic */ 131237140Sbostic t = newt; 131337140Sbostic goto label; 131437140Sbostic } 131537140Sbostic } 131637140Sbostic return WRONG; 131737140Sbostic } 131837140Sbostic label: 131937140Sbostic t += saved_seconds; 132037140Sbostic (*funcp)(&t, offset, tmp); 132137140Sbostic *okayp = TRUE; 132237140Sbostic return t; 132337140Sbostic } 132437140Sbostic 132537140Sbostic static time_t 132637140Sbostic time1(tmp, funcp, offset) 132737140Sbostic struct tm * const tmp; 132837140Sbostic void (* const funcp)(); 132937140Sbostic const long offset; 133037140Sbostic { 133137140Sbostic register time_t t; 133237140Sbostic register const struct state * sp; 133337140Sbostic register int samei, otheri; 133437140Sbostic int okay; 133537140Sbostic 133637140Sbostic if (tmp->tm_isdst > 1) 133738558Sbostic tmp->tm_isdst = 1; 133837140Sbostic t = time2(tmp, funcp, offset, &okay); 133937140Sbostic if (okay || tmp->tm_isdst < 0) 134037140Sbostic return t; 134137140Sbostic /* 134237140Sbostic ** We're supposed to assume that somebody took a time of one type 134337140Sbostic ** and did some math on it that yielded a "struct tm" that's bad. 134437140Sbostic ** We try to divine the type they started from and adjust to the 134537140Sbostic ** type they need. 134637140Sbostic */ 134737140Sbostic sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr); 134837140Sbostic #ifdef ALL_STATE 134937140Sbostic if (sp == NULL) 135037140Sbostic return WRONG; 135137140Sbostic #endif /* defined ALL_STATE */ 135237140Sbostic for (samei = 0; samei < sp->typecnt; ++samei) { 135337140Sbostic if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) 135437140Sbostic continue; 135537140Sbostic for (otheri = 0; otheri < sp->typecnt; ++otheri) { 135637140Sbostic if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) 135737140Sbostic continue; 135837140Sbostic tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - 135937140Sbostic sp->ttis[samei].tt_gmtoff; 136037140Sbostic tmp->tm_isdst = !tmp->tm_isdst; 136137140Sbostic t = time2(tmp, funcp, offset, &okay); 136237140Sbostic if (okay) 136337140Sbostic return t; 136437140Sbostic tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - 136537140Sbostic sp->ttis[samei].tt_gmtoff; 136637140Sbostic tmp->tm_isdst = !tmp->tm_isdst; 136737140Sbostic } 136837140Sbostic } 136937140Sbostic return WRONG; 137037140Sbostic } 137137140Sbostic 137237140Sbostic time_t 137337140Sbostic mktime(tmp) 137437140Sbostic struct tm * const tmp; 137537140Sbostic { 137637140Sbostic return time1(tmp, localsub, 0L); 137737140Sbostic } 1378