localtime.c revision 17209
1139825Simp/* 236270Swpaul** This file is in the public domain, so clarified as of 336270Swpaul** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov). 436270Swpaul*/ 536270Swpaul 636270Swpaul#ifndef lint 736270Swpaul#ifndef NOID 836270Swpaulstatic char elsieid[] = "@(#)localtime.c 7.57"; 936270Swpaul#endif /* !defined NOID */ 1036270Swpaul#endif /* !defined lint */ 1136270Swpaul 1236270Swpaul/* 1336270Swpaul** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu). 1436270Swpaul** POSIX-style TZ environment variable handling from Guy Harris 1536270Swpaul** (guy@auspex.com). 1636270Swpaul*/ 1736270Swpaul 1836270Swpaul/*LINTLIBRARY*/ 1936270Swpaul 2036270Swpaul#include "private.h" 2136270Swpaul#include "tzfile.h" 2236270Swpaul#include "fcntl.h" 2336270Swpaul#ifdef _THREAD_SAFE 2436270Swpaul#include <pthread.h> 2536270Swpaul#include "pthread_private.h" 2636270Swpaul#endif 2736270Swpaul 2836270Swpaul/* 2936270Swpaul** SunOS 4.1.1 headers lack O_BINARY. 3036270Swpaul*/ 3136270Swpaul 3236270Swpaul#ifdef O_BINARY 33122678Sobrien#define OPEN_MODE (O_RDONLY | O_BINARY) 34122678Sobrien#endif /* defined O_BINARY */ 35122678Sobrien#ifndef O_BINARY 3636270Swpaul#define OPEN_MODE O_RDONLY 3736270Swpaul#endif /* !defined O_BINARY */ 3836270Swpaul 3936270Swpaul#ifndef WILDABBR 4036270Swpaul/* 4136270Swpaul** Someone might make incorrect use of a time zone abbreviation: 4239583Swpaul** 1. They might reference tzname[0] before calling tzset (explicitly 4336270Swpaul** or implicitly). 4436270Swpaul** 2. They might reference tzname[1] before calling tzset (explicitly 4536270Swpaul** or implicitly). 4636270Swpaul** 3. They might reference tzname[1] after setting to a time zone 4739583Swpaul** in which Daylight Saving Time is never observed. 4836270Swpaul** 4. They might reference tzname[0] after setting to a time zone 4936270Swpaul** in which Standard Time is never observed. 5036270Swpaul** 5. They might reference tm.TM_ZONE after calling offtime. 5136270Swpaul** What's best to do in the above cases is open to debate; 5236270Swpaul** for now, we just set things up so that in any of the five cases 5336270Swpaul** WILDABBR is used. Another possibility: initialize tzname[0] to the 5436270Swpaul** string "tzname[0] used before set", and similarly for the other cases. 5536270Swpaul** And another: initialize tzname[0] to "ERA", with an explanation in the 5636270Swpaul** manual page of what this "time zone abbreviation" means (doing this so 5736270Swpaul** that tzname[0] has the "normal" length of three characters). 5839583Swpaul*/ 5936270Swpaul#define WILDABBR " " 6036270Swpaul#endif /* !defined WILDABBR */ 6136270Swpaul 6236270Swpaulstatic char wildabbr[] = "WILDABBR"; 6336270Swpaul 6436270Swpaulstatic const char gmt[] = "GMT"; 6536270Swpaul 6639583Swpaulstruct ttinfo { /* time type information */ 6739583Swpaul long tt_gmtoff; /* GMT offset in seconds */ 6839583Swpaul int tt_isdst; /* used to set tm_isdst */ 6939583Swpaul int tt_abbrind; /* abbreviation list index */ 7039583Swpaul int tt_ttisstd; /* TRUE if transition is std time */ 7139583Swpaul int tt_ttisgmt; /* TRUE if transition is GMT */ 7239583Swpaul}; 7339583Swpaul 7439583Swpaulstruct lsinfo { /* leap second information */ 7536270Swpaul time_t ls_trans; /* transition time */ 7636270Swpaul long ls_corr; /* correction to apply */ 7739583Swpaul}; 7836270Swpaul 7936270Swpaul#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) 8036270Swpaul 8136270Swpaul#ifdef TZNAME_MAX 8236270Swpaul#define MY_TZNAME_MAX TZNAME_MAX 8336270Swpaul#endif /* defined TZNAME_MAX */ 8436270Swpaul#ifndef TZNAME_MAX 8536270Swpaul#define MY_TZNAME_MAX 255 8636270Swpaul#endif /* !defined TZNAME_MAX */ 8736270Swpaul 8836270Swpaulstruct state { 8936270Swpaul int leapcnt; 9036270Swpaul int timecnt; 9136270Swpaul int typecnt; 9236270Swpaul int charcnt; 9336270Swpaul time_t ats[TZ_MAX_TIMES]; 9436270Swpaul unsigned char types[TZ_MAX_TIMES]; 9536270Swpaul struct ttinfo ttis[TZ_MAX_TYPES]; 9636270Swpaul char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), 9736270Swpaul (2 * (MY_TZNAME_MAX + 1)))]; 9836270Swpaul struct lsinfo lsis[TZ_MAX_LEAPS]; 9936270Swpaul}; 10036270Swpaul 10136270Swpaulstruct rule { 10236270Swpaul int r_type; /* type of rule--see below */ 10336270Swpaul int r_day; /* day number of rule */ 10436270Swpaul int r_week; /* week number of rule */ 10536270Swpaul int r_mon; /* month number of rule */ 10636270Swpaul long r_time; /* transition time of rule */ 10736270Swpaul}; 10836270Swpaul 10936270Swpaul#define JULIAN_DAY 0 /* Jn - Julian day */ 11036270Swpaul#define DAY_OF_YEAR 1 /* n - day of year */ 11136270Swpaul#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ 11236270Swpaul 11336270Swpaul/* 11436270Swpaul** Prototypes for static functions. 11536270Swpaul*/ 11636270Swpaul 11736270Swpaulstatic long detzcode P((const char * codep)); 11836270Swpaulstatic const char * getzname P((const char * strp)); 11936270Swpaulstatic const char * getnum P((const char * strp, int * nump, int min, 12036270Swpaul int max)); 12136270Swpaulstatic const char * getsecs P((const char * strp, long * secsp)); 12236270Swpaulstatic const char * getoffset P((const char * strp, long * offsetp)); 12336270Swpaulstatic const char * getrule P((const char * strp, struct rule * rulep)); 12436270Swpaulstatic void gmtload P((struct state * sp)); 12536270Swpaulstatic void gmtsub P((const time_t * timep, long offset, 12636270Swpaul struct tm * tmp)); 12736270Swpaulstatic void localsub P((const time_t * timep, long offset, 12836270Swpaul struct tm * tmp)); 12936270Swpaulstatic int increment_overflow P((int * number, int delta)); 13036270Swpaulstatic int normalize_overflow P((int * tensptr, int * unitsptr, 13136270Swpaul int base)); 13236270Swpaulstatic void settzname P((void)); 13336270Swpaulstatic time_t time1 P((struct tm * tmp, 13436270Swpaul void(*funcp) P((const time_t *, 13536270Swpaul long, struct tm *)), 13636270Swpaul long offset)); 13736270Swpaulstatic time_t time2 P((struct tm *tmp, 13836270Swpaul void(*funcp) P((const time_t *, 13936270Swpaul long, struct tm*)), 14036270Swpaul long offset, int * okayp)); 14136270Swpaulstatic void timesub P((const time_t * timep, long offset, 14236270Swpaul const struct state * sp, struct tm * tmp)); 14336270Swpaulstatic int tmcomp P((const struct tm * atmp, 14436270Swpaul const struct tm * btmp)); 14536270Swpaulstatic time_t transtime P((time_t janfirst, int year, 14636270Swpaul const struct rule * rulep, long offset)); 14736270Swpaulstatic int tzload P((const char * name, struct state * sp)); 14836270Swpaulstatic int tzparse P((const char * name, struct state * sp, 14936270Swpaul int lastditch)); 15036270Swpaul 15136270Swpaul#ifdef ALL_STATE 15236270Swpaulstatic struct state * lclptr; 15336270Swpaulstatic struct state * gmtptr; 15436270Swpaul#endif /* defined ALL_STATE */ 15536270Swpaul 15636270Swpaul#ifndef ALL_STATE 15736270Swpaulstatic struct state lclmem; 15836270Swpaulstatic struct state gmtmem; 15936270Swpaul#define lclptr (&lclmem) 16036270Swpaul#define gmtptr (&gmtmem) 16136270Swpaul#endif /* State Farm */ 16236270Swpaul 16336270Swpaul#ifndef TZ_STRLEN_MAX 16436270Swpaul#define TZ_STRLEN_MAX 255 16536270Swpaul#endif /* !defined TZ_STRLEN_MAX */ 16636270Swpaul 16736270Swpaulstatic char lcl_TZname[TZ_STRLEN_MAX + 1]; 16836270Swpaulstatic int lcl_is_set; 16936270Swpaulstatic int gmt_is_set; 17036270Swpaul#ifdef _THREAD_SAFE 17136270Swpaulstatic pthread_mutex_t lcl_mutex = PTHREAD_MUTEX_INITIALIZER; 17236270Swpaulstatic pthread_mutex_t gmt_mutex = PTHREAD_MUTEX_INITIALIZER; 17336270Swpaul#endif 17436270Swpaul 17536270Swpaulchar * tzname[2] = { 17636270Swpaul wildabbr, 17736270Swpaul wildabbr 17836270Swpaul}; 17936270Swpaul 18036270Swpaul/* 18136270Swpaul** Section 4.12.3 of X3.159-1989 requires that 18236270Swpaul** Except for the strftime function, these functions [asctime, 18336270Swpaul** ctime, gmtime, localtime] return values in one of two static 18436270Swpaul** objects: a broken-down time structure and an array of char. 18536270Swpaul** Thanks to Paul Eggert (eggert@twinsun.com) for noting this. 186129878Sphk*/ 18736270Swpaul 18836270Swpaulstatic struct tm tm; 18936270Swpaul 19036270Swpaul#ifdef USG_COMPAT 19136270Swpaultime_t timezone = 0; 19236270Swpaulint daylight = 0; 19336270Swpaul#endif /* defined USG_COMPAT */ 194147256Sbrooks 19536270Swpaul#ifdef ALTZONE 19636270Swpaultime_t altzone = 0; 19736270Swpaul#endif /* defined ALTZONE */ 19836270Swpaul 19936270Swpaulstatic long 20045155Swpauldetzcode(codep) 20148992Swpaulconst char * const codep; 20248992Swpaul{ 20348992Swpaul register long result; 20436270Swpaul register int i; 20550462Swpaul 20650462Swpaul result = (codep[0] & 0x80) ? ~0L : 0L; 20750462Swpaul for (i = 0; i < 4; ++i) 208119288Simp result = (result << 8) | (codep[i] & 0xff); 209119288Simp return result; 21036270Swpaul} 21139957Swpaul 21239957Swpaulstatic void 21339957Swpaulsettzname P((void)) 21439957Swpaul{ 21539957Swpaul register struct state * const sp = lclptr; 21639957Swpaul register int i; 21739957Swpaul 218181738Simp tzname[0] = wildabbr; 21936270Swpaul tzname[1] = wildabbr; 220113506Smdodd#ifdef USG_COMPAT 221113506Smdodd daylight = 0; 22259758Speter timezone = 0; 22359758Speter#endif /* defined USG_COMPAT */ 224151545Simp#ifdef ALTZONE 22550462Swpaul altzone = 0; 22650462Swpaul#endif /* defined ALTZONE */ 22736270Swpaul#ifdef ALL_STATE 22836270Swpaul if (sp == NULL) { 22936270Swpaul tzname[0] = tzname[1] = gmt; 23036270Swpaul return; 23136270Swpaul } 23236270Swpaul#endif /* defined ALL_STATE */ 23336270Swpaul for (i = 0; i < sp->typecnt; ++i) { 23436270Swpaul register const struct ttinfo * const ttisp = &sp->ttis[i]; 23536270Swpaul 23636270Swpaul tzname[ttisp->tt_isdst] = 23736270Swpaul &sp->chars[ttisp->tt_abbrind]; 23836270Swpaul#ifdef USG_COMPAT 23936270Swpaul if (ttisp->tt_isdst) 24036270Swpaul daylight = 1; 24136270Swpaul if (i == 0 || !ttisp->tt_isdst) 24236270Swpaul timezone = -(ttisp->tt_gmtoff); 24336270Swpaul#endif /* defined USG_COMPAT */ 24436270Swpaul#ifdef ALTZONE 24536270Swpaul if (i == 0 || ttisp->tt_isdst) 24636270Swpaul altzone = -(ttisp->tt_gmtoff); 24736270Swpaul#endif /* defined ALTZONE */ 24837626Swpaul } 24937626Swpaul /* 25037626Swpaul ** And to get the latest zone names into tzname. . . 25137626Swpaul */ 25237626Swpaul for (i = 0; i < sp->timecnt; ++i) { 25337626Swpaul register const struct ttinfo * const ttisp = 25437626Swpaul &sp->ttis[ 25537626Swpaul sp->types[i]]; 25637626Swpaul 25737626Swpaul tzname[ttisp->tt_isdst] = 25837626Swpaul &sp->chars[ttisp->tt_abbrind]; 25937626Swpaul } 26036270Swpaul} 26136270Swpaul 26236270Swpaulstatic int 263142407Simptzload(name, sp) 264142407Simpregister const char * name; 265142407Simpregister struct state * const sp; 266142407Simp{ 267142407Simp register const char * p; 268142407Simp register int i; 269142407Simp register int fid; 270142407Simp 271142407Simp if (name == NULL && (name = TZDEFAULT) == NULL) 27236270Swpaul return -1; 273142407Simp { 274142407Simp register int doaccess; 275142407Simp /* 27636270Swpaul ** Section 4.9.1 of the C standard says that 277142407Simp ** "FILENAME_MAX expands to an integral constant expression 278142407Simp ** that is the sie needed for an array of char large enough 279150171Sjhb ** to hold the longest file name string that the implementation 280142407Simp ** guarantees can be opened." 281142407Simp */ 282150171Sjhb char fullname[FILENAME_MAX + 1]; 283142407Simp 284199560Sjhb if (name[0] == ':') 285188463Simp ++name; 286142407Simp doaccess = name[0] == '/'; 287142407Simp if (!doaccess) { 28836270Swpaul if ((p = TZDIR) == NULL) 289142407Simp return -1; 290142407Simp if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) 291142407Simp return -1; 29236270Swpaul (void) strcpy(fullname, p); 293142407Simp (void) strcat(fullname, "/"); 294142407Simp (void) strcat(fullname, name); 295142407Simp /* 296142407Simp ** Set doaccess if '.' (as in "../") shows up in name. 297142407Simp */ 298142407Simp if (strchr(name, '.') != NULL) 299142407Simp doaccess = TRUE; 30036270Swpaul name = fullname; 301142407Simp } 302142407Simp if (doaccess && access(name, R_OK) != 0) 303142407Simp return -1; 304142407Simp if ((fid = open(name, OPEN_MODE)) == -1) 305142407Simp return -1; 306142407Simp } 307142407Simp { 308142407Simp struct tzhead * tzhp; 30936270Swpaul char buf[sizeof *sp + sizeof *tzhp]; 310142407Simp int ttisstdcnt; 311142407Simp int ttisgmtcnt; 312142407Simp 313142407Simp i = read(fid, buf, sizeof buf); 314142407Simp if (close(fid) != 0) 315142407Simp return -1; 316142407Simp p = buf; 317142407Simp p += sizeof tzhp->tzh_reserved; 318142407Simp ttisstdcnt = (int) detzcode(p); 319142407Simp p += 4; 32039583Swpaul ttisgmtcnt = (int) detzcode(p); 32149010Swpaul p += 4; 32249010Swpaul sp->leapcnt = (int) detzcode(p); 32349010Swpaul p += 4; 32449010Swpaul sp->timecnt = (int) detzcode(p); 32549010Swpaul p += 4; 32649010Swpaul sp->typecnt = (int) detzcode(p); 32749010Swpaul p += 4; 32849010Swpaul sp->charcnt = (int) detzcode(p); 32948992Swpaul p += 4; 33048992Swpaul if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || 33148992Swpaul sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || 33248992Swpaul sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || 33348992Swpaul sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || 33448992Swpaul (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || 33550462Swpaul (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) 33650462Swpaul return -1; 33750462Swpaul if (i - (p - buf) < sp->timecnt * 4 + /* ats */ 33850462Swpaul sp->timecnt + /* types */ 33950462Swpaul sp->typecnt * (4 + 2) + /* ttinfos */ 34050462Swpaul sp->charcnt + /* chars */ 34150462Swpaul sp->leapcnt * (4 + 4) + /* lsinfos */ 34250462Swpaul ttisstdcnt + /* ttisstds */ 34350462Swpaul ttisgmtcnt) /* ttisgmts */ 34450462Swpaul return -1; 34548992Swpaul for (i = 0; i < sp->timecnt; ++i) { 34648992Swpaul sp->ats[i] = detzcode(p); 34748992Swpaul p += 4; 34848992Swpaul } 34951455Swpaul for (i = 0; i < sp->timecnt; ++i) { 35048992Swpaul sp->types[i] = (unsigned char) *p++; 35148992Swpaul if (sp->types[i] >= sp->typecnt) 35248992Swpaul return -1; 35348992Swpaul } 35448992Swpaul for (i = 0; i < sp->typecnt; ++i) { 35548992Swpaul register struct ttinfo * ttisp; 356113506Smdodd 35751473Swpaul ttisp = &sp->ttis[i]; 35848992Swpaul ttisp->tt_gmtoff = detzcode(p); 35939583Swpaul p += 4; 36041656Swpaul ttisp->tt_isdst = (unsigned char) *p++; 36141656Swpaul if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) 36239583Swpaul return -1; 36339583Swpaul ttisp->tt_abbrind = (unsigned char) *p++; 36439583Swpaul if (ttisp->tt_abbrind < 0 || 36539583Swpaul ttisp->tt_abbrind > sp->charcnt) 36639583Swpaul return -1; 36739583Swpaul } 36841656Swpaul for (i = 0; i < sp->charcnt; ++i) 36941656Swpaul sp->chars[i] = *p++; 37039583Swpaul sp->chars[i] = '\0'; /* ensure '\0' at end */ 37139583Swpaul for (i = 0; i < sp->leapcnt; ++i) { 37239583Swpaul register struct lsinfo * lsisp; 37339583Swpaul 37439583Swpaul lsisp = &sp->lsis[i]; 37539583Swpaul lsisp->ls_trans = detzcode(p); 37641656Swpaul p += 4; 37741656Swpaul lsisp->ls_corr = detzcode(p); 37839583Swpaul p += 4; 37939583Swpaul } 38039583Swpaul for (i = 0; i < sp->typecnt; ++i) { 38139583Swpaul register struct ttinfo * ttisp; 38239583Swpaul 38339583Swpaul ttisp = &sp->ttis[i]; 38441656Swpaul if (ttisstdcnt == 0) 38541656Swpaul ttisp->tt_ttisstd = FALSE; 38641656Swpaul else { 38739583Swpaul ttisp->tt_ttisstd = *p++; 38839583Swpaul if (ttisp->tt_ttisstd != TRUE && 38939583Swpaul ttisp->tt_ttisstd != FALSE) 39039583Swpaul return -1; 39139583Swpaul } 39239583Swpaul } 39339583Swpaul for (i = 0; i < sp->typecnt; ++i) { 39441656Swpaul register struct ttinfo * ttisp; 39541656Swpaul 39641656Swpaul ttisp = &sp->ttis[i]; 39739583Swpaul if (ttisgmtcnt == 0) 39839583Swpaul ttisp->tt_ttisgmt = FALSE; 39939583Swpaul else { 40039583Swpaul ttisp->tt_ttisgmt = *p++; 40139583Swpaul if (ttisp->tt_ttisgmt != TRUE && 40239583Swpaul ttisp->tt_ttisgmt != FALSE) 40339583Swpaul return -1; 40441656Swpaul } 40541656Swpaul } 40641656Swpaul } 40739583Swpaul return 0; 40839583Swpaul} 40939583Swpaul 41039583Swpaulstatic const int mon_lengths[2][MONSPERYEAR] = { 41139583Swpaul { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 41239583Swpaul { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 413102336Salfred}; 414102336Salfred 41541656Swpaulstatic const int year_lengths[2] = { 41641656Swpaul DAYSPERNYEAR, DAYSPERLYEAR 41741656Swpaul}; 41839583Swpaul 41939583Swpaul/* 42039583Swpaul** Given a pointer into a time zone string, scan until a character that is not 42139583Swpaul** a valid character in a zone name is found. Return a pointer to that 42239583Swpaul** character. 42339583Swpaul*/ 42439583Swpaul 42539583Swpaulstatic const char * 42639583Swpaulgetzname(strp) 42739583Swpaulregister const char * strp; 42839583Swpaul{ 429102336Salfred register char c; 430102336Salfred 43141656Swpaul while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && 43241656Swpaul c != '+') 43341656Swpaul ++strp; 43439583Swpaul return strp; 43539583Swpaul} 43639583Swpaul 43739583Swpaul/* 43839583Swpaul** Given a pointer into a time zone string, extract a number from that string. 43939583Swpaul** Check that the number is within a specified range; if it is not, return 44039583Swpaul** NULL. 44139583Swpaul** Otherwise, return a pointer to the first character not part of the number. 44239583Swpaul*/ 44339583Swpaul 44439583Swpaulstatic const char * 44539583Swpaulgetnum(strp, nump, min, max) 44641656Swpaulregister const char * strp; 44741656Swpaulint * const nump; 44841656Swpaulconst int min; 44939583Swpaulconst int max; 45039583Swpaul{ 45139583Swpaul register char c; 45239583Swpaul register int num; 45339583Swpaul 45439583Swpaul if (strp == NULL || !is_digit(c = *strp)) 45539583Swpaul return NULL; 45639583Swpaul num = 0; 45739583Swpaul do { 45839583Swpaul num = num * 10 + (c - '0'); 45939583Swpaul if (num > max) 46039583Swpaul return NULL; /* illegal value */ 46141656Swpaul c = *++strp; 46241656Swpaul } while (is_digit(c)); 46341656Swpaul if (num < min) 46439583Swpaul return NULL; /* illegal value */ 46539583Swpaul *nump = num; 46639583Swpaul return strp; 46739583Swpaul} 46839583Swpaul 46939583Swpaul/* 47039583Swpaul** Given a pointer into a time zone string, extract a number of seconds, 47139583Swpaul** in hh[:mm[:ss]] form, from the string. 47239583Swpaul** If any error occurs, return NULL. 47339583Swpaul** Otherwise, return a pointer to the first character not part of the number 47439583Swpaul** of seconds. 47536270Swpaul*/ 47636270Swpaul 47736270Swpaulstatic const char * 47839583Swpaulgetsecs(strp, secsp) 47939583Swpaulregister const char * strp; 48041656Swpaullong * const secsp; 48136270Swpaul{ 48236270Swpaul int num; 48336270Swpaul 48436270Swpaul /* 48536270Swpaul ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like 48636270Swpaul ** "M10.4.6/26", which does not conform to Posix, 48739583Swpaul ** but which specifies the equivalent of 48836270Swpaul ** ``02:00 on the first Sunday on or after 23 Oct''. 48936270Swpaul */ 49036270Swpaul strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); 49136270Swpaul if (strp == NULL) 49236270Swpaul return NULL; 49336270Swpaul *secsp = num * (long) SECSPERHOUR; 49439583Swpaul if (*strp == ':') { 49536270Swpaul ++strp; 49639583Swpaul strp = getnum(strp, &num, 0, MINSPERHOUR - 1); 49736270Swpaul if (strp == NULL) 49839583Swpaul return NULL; 49939583Swpaul *secsp += num * SECSPERMIN; 50039583Swpaul if (*strp == ':') { 50139583Swpaul ++strp; 50236270Swpaul /* `SECSPERMIN' allows for leap seconds. */ 50336270Swpaul strp = getnum(strp, &num, 0, SECSPERMIN); 50436270Swpaul if (strp == NULL) 50536270Swpaul return NULL; 50636270Swpaul *secsp += num; 50739583Swpaul } 50836270Swpaul } 50936270Swpaul return strp; 51036270Swpaul} 51136270Swpaul 51239583Swpaul/* 51339583Swpaul** Given a pointer into a time zone string, extract an offset, in 51439583Swpaul** [+-]hh[:mm[:ss]] form, from the string. 51536270Swpaul** If any error occurs, return NULL. 51636270Swpaul** Otherwise, return a pointer to the first character not part of the time. 51736270Swpaul*/ 51836270Swpaul 51936270Swpaulstatic const char * 52036270Swpaulgetoffset(strp, offsetp) 52136270Swpaulregister const char * strp; 52239583Swpaullong * const offsetp; 52339583Swpaul{ 52441656Swpaul register int neg = 0; 52536270Swpaul 52636270Swpaul if (*strp == '-') { 52736270Swpaul neg = 1; 52836270Swpaul ++strp; 529162315Sglebius } else if (*strp == '+') 53036270Swpaul ++strp; 53139583Swpaul strp = getsecs(strp, offsetp); 53239583Swpaul if (strp == NULL) 53336270Swpaul return NULL; /* illegal time */ 53439583Swpaul if (neg) 53536270Swpaul *offsetp = -*offsetp; 53636270Swpaul return strp; 53736270Swpaul} 53839583Swpaul 539162315Sglebius/* 540105599Sbrooks** Given a pointer into a time zone string, extract a rule in the form 54136270Swpaul** date[/time]. See POSIX section 8 for the format of "date" and "time". 54239583Swpaul** If a valid rule is not found, return NULL. 54336270Swpaul** Otherwise, return a pointer to the first character not part of the rule. 54436270Swpaul*/ 54536270Swpaul 54636270Swpaulstatic const char * 54739583Swpaulgetrule(strp, rulep) 548162315Sglebiusconst char * strp; 549105599Sbrooksregister struct rule * const rulep; 55036270Swpaul{ 55139583Swpaul if (*strp == 'J') { 55236270Swpaul /* 55336270Swpaul ** Julian day. 55436270Swpaul */ 55536270Swpaul rulep->r_type = JULIAN_DAY; 55636270Swpaul ++strp; 55736270Swpaul strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); 55839583Swpaul } else if (*strp == 'M') { 559162315Sglebius /* 560105599Sbrooks ** Month, week, day. 56136270Swpaul */ 56239583Swpaul rulep->r_type = MONTH_NTH_DAY_OF_WEEK; 56336270Swpaul ++strp; 56436270Swpaul strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); 56536270Swpaul if (strp == NULL) 56636270Swpaul return NULL; 56739583Swpaul if (*strp++ != '.') 56836270Swpaul return NULL; 56939583Swpaul strp = getnum(strp, &rulep->r_week, 1, 5); 57039583Swpaul if (strp == NULL) 57139583Swpaul return NULL; 57236270Swpaul if (*strp++ != '.') 57339583Swpaul return NULL; 57436501Swpaul strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); 57536270Swpaul } else if (is_digit(*strp)) { 57636270Swpaul /* 57736270Swpaul ** Day of year. 57836270Swpaul */ 57936270Swpaul rulep->r_type = DAY_OF_YEAR; 58036270Swpaul strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); 58136270Swpaul } else return NULL; /* invalid format */ 58236270Swpaul if (strp == NULL) 58336270Swpaul return NULL; 58436270Swpaul if (*strp == '/') { 58536270Swpaul /* 58636270Swpaul ** Time specified. 58736270Swpaul */ 58839583Swpaul ++strp; 58939583Swpaul strp = getsecs(strp, &rulep->r_time); 59039583Swpaul } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ 591102336Salfred return strp; 592102336Salfred} 59339583Swpaul 59439583Swpaul/* 59539583Swpaul** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the 59639583Swpaul** year, a rule, and the offset from GMT at the time that rule takes effect, 59736270Swpaul** calculate the Epoch-relative time that rule takes effect. 59839583Swpaul*/ 59939583Swpaul 60039583Swpaulstatic time_t 60139583Swpaultranstime(janfirst, year, rulep, offset) 60239583Swpaulconst time_t janfirst; 60339583Swpaulconst int year; 60439583Swpaulregister const struct rule * const rulep; 60539583Swpaulconst long offset; 60639583Swpaul{ 60739583Swpaul register int leapyear; 60839583Swpaul register time_t value; 60939583Swpaul register int i; 61039583Swpaul int d, m1, yy0, yy1, yy2, dow; 611102336Salfred 612102336Salfred INITIALIZE(value); 61339583Swpaul leapyear = isleap(year); 61439583Swpaul switch (rulep->r_type) { 61536270Swpaul 61636270Swpaul case JULIAN_DAY: 61739583Swpaul /* 61836270Swpaul ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap 61936270Swpaul ** years. 62039583Swpaul ** In non-leap years, or if the day number is 59 or less, just 62139583Swpaul ** add SECSPERDAY times the day number-1 to the time of 62236270Swpaul ** January 1, midnight, to get the day. 62336270Swpaul */ 62436270Swpaul value = janfirst + (rulep->r_day - 1) * SECSPERDAY; 62536270Swpaul if (leapyear && rulep->r_day >= 60) 62636270Swpaul value += SECSPERDAY; 627102336Salfred break; 628102336Salfred 62939583Swpaul case DAY_OF_YEAR: 63036270Swpaul /* 63136270Swpaul ** n - day of year. 63236270Swpaul ** Just add SECSPERDAY times the day number to the time of 63336270Swpaul ** January 1, midnight, to get the day. 63436270Swpaul */ 63536270Swpaul value = janfirst + rulep->r_day * SECSPERDAY; 63639583Swpaul break; 63736270Swpaul 63839583Swpaul case MONTH_NTH_DAY_OF_WEEK: 63936270Swpaul /* 64039583Swpaul ** Mm.n.d - nth "dth day" of month m. 64136270Swpaul */ 64239583Swpaul value = janfirst; 64336270Swpaul for (i = 0; i < rulep->r_mon - 1; ++i) 64436270Swpaul value += mon_lengths[leapyear][i] * SECSPERDAY; 64536270Swpaul 646102336Salfred /* 647102336Salfred ** Use Zeller's Congruence to get day-of-week of first day of 64839583Swpaul ** month. 64936270Swpaul */ 65036270Swpaul m1 = (rulep->r_mon + 9) % 12 + 1; 65136270Swpaul yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; 65267087Swpaul yy1 = yy0 / 100; 65336270Swpaul yy2 = yy0 % 100; 65436270Swpaul dow = ((26 * m1 - 2) / 10 + 65539583Swpaul 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; 65636270Swpaul if (dow < 0) 65736270Swpaul dow += DAYSPERWEEK; 65836270Swpaul 65936270Swpaul /* 66036270Swpaul ** "dow" is the day-of-week of the first day of the month. Get 66136270Swpaul ** the day-of-month (zero-origin) of the first "dow" day of the 66236270Swpaul ** month. 66336270Swpaul */ 66436270Swpaul d = rulep->r_day - dow; 66536270Swpaul if (d < 0) 66636270Swpaul d += DAYSPERWEEK; 66736270Swpaul for (i = 1; i < rulep->r_week; ++i) { 66839583Swpaul if (d + DAYSPERWEEK >= 66936270Swpaul mon_lengths[leapyear][rulep->r_mon - 1]) 67039583Swpaul break; 67136270Swpaul d += DAYSPERWEEK; 67236270Swpaul } 67336270Swpaul 67436270Swpaul /* 67536270Swpaul ** "d" is the day-of-month (zero-origin) of the day we want. 67639583Swpaul */ 67736270Swpaul value += d * SECSPERDAY; 67836270Swpaul break; 67936270Swpaul } 68036270Swpaul 68139583Swpaul /* 68239583Swpaul ** "value" is the Epoch-relative time of 00:00:00 GMT on the day in 68339583Swpaul ** question. To get the Epoch-relative time of the specified local 68439583Swpaul ** time on that day, add the transition time and the current offset 68536270Swpaul ** from GMT. 68636270Swpaul */ 68736270Swpaul return value + rulep->r_time + offset; 68836270Swpaul} 68939583Swpaul 69036270Swpaul/* 69136270Swpaul** Given a POSIX section 8-style TZ string, fill in the rule tables as 69239583Swpaul** appropriate. 69339583Swpaul*/ 69436270Swpaul 69536270Swpaulstatic int 69639583Swpaultzparse(name, sp, lastditch) 69739583Swpaulconst char * name; 69836270Swpaulregister struct state * const sp; 69936270Swpaulconst int lastditch; 70039583Swpaul{ 70136270Swpaul const char * stdname; 70236270Swpaul const char * dstname; 70336270Swpaul size_t stdlen; 70436270Swpaul size_t dstlen; 70536270Swpaul long stdoffset; 70636270Swpaul long dstoffset; 70736270Swpaul register time_t * atp; 70839583Swpaul register unsigned char * typep; 70939583Swpaul register char * cp; 71036270Swpaul register int load_result; 71136270Swpaul 71236270Swpaul INITIALIZE(dstname); 71336270Swpaul stdname = name; 71436270Swpaul if (lastditch) { 71539583Swpaul stdlen = strlen(name); /* length of standard zone name */ 71636270Swpaul name += stdlen; 71739583Swpaul if (stdlen >= sizeof sp->chars) 71836270Swpaul stdlen = (sizeof sp->chars) - 1; 71936270Swpaul } else { 72039583Swpaul name = getzname(name); 72136270Swpaul stdlen = name - stdname; 72236270Swpaul if (stdlen < 3) 72336270Swpaul return -1; 72436270Swpaul } 72539583Swpaul if (*name == '\0') 72639583Swpaul return -1; /* was "stdoffset = 0;" */ 72736270Swpaul else { 72836270Swpaul name = getoffset(name, &stdoffset); 72936270Swpaul if (name == NULL) 73039583Swpaul return -1; 73136270Swpaul } 73236270Swpaul load_result = tzload(TZDEFRULES, sp); 73336270Swpaul if (load_result != 0) 73436270Swpaul sp->leapcnt = 0; /* so, we're off a little */ 73536270Swpaul if (*name != '\0') { 73636270Swpaul dstname = name; 73736270Swpaul name = getzname(name); 738102336Salfred dstlen = name - dstname; /* length of DST zone name */ 739102336Salfred if (dstlen < 3) 74039583Swpaul return -1; 74136270Swpaul if (*name != '\0' && *name != ',' && *name != ';') { 74236270Swpaul name = getoffset(name, &dstoffset); 74336270Swpaul if (name == NULL) 74436270Swpaul return -1; 74536270Swpaul } else dstoffset = stdoffset - SECSPERHOUR; 74639583Swpaul if (*name == ',' || *name == ';') { 74736270Swpaul struct rule start; 74836270Swpaul struct rule end; 74936270Swpaul register int year; 75036270Swpaul register time_t janfirst; 75136270Swpaul time_t starttime; 75236270Swpaul time_t endtime; 75336270Swpaul 75436270Swpaul ++name; 75536270Swpaul if ((name = getrule(name, &start)) == NULL) 75636270Swpaul return -1; 75736270Swpaul if (*name++ != ',') 75836270Swpaul return -1; 75939583Swpaul if ((name = getrule(name, &end)) == NULL) 76036270Swpaul return -1; 76139583Swpaul if (*name != '\0') 76236270Swpaul return -1; 76336270Swpaul sp->typecnt = 2; /* standard time and DST */ 76436270Swpaul /* 76536270Swpaul ** Two transitions per year, from EPOCH_YEAR to 2037. 76636270Swpaul */ 76739583Swpaul sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1); 76836270Swpaul if (sp->timecnt > TZ_MAX_TIMES) 76939583Swpaul return -1; 77039583Swpaul sp->ttis[0].tt_gmtoff = -dstoffset; 77139583Swpaul sp->ttis[0].tt_isdst = 1; 77239583Swpaul sp->ttis[0].tt_abbrind = stdlen + 1; 77339583Swpaul sp->ttis[1].tt_gmtoff = -stdoffset; 77439583Swpaul sp->ttis[1].tt_isdst = 0; 77536270Swpaul sp->ttis[1].tt_abbrind = 0; 77639583Swpaul atp = sp->ats; 77739583Swpaul typep = sp->types; 77836270Swpaul janfirst = 0; 77936270Swpaul for (year = EPOCH_YEAR; year <= 2037; ++year) { 78036270Swpaul starttime = transtime(janfirst, year, &start, 78136270Swpaul stdoffset); 78239583Swpaul endtime = transtime(janfirst, year, &end, 78336270Swpaul dstoffset); 78436270Swpaul if (starttime > endtime) { 78536270Swpaul *atp++ = endtime; 78639583Swpaul *typep++ = 1; /* DST ends */ 78736270Swpaul *atp++ = starttime; 78836270Swpaul *typep++ = 0; /* DST begins */ 78936270Swpaul } else { 79036270Swpaul *atp++ = starttime; 791102336Salfred *typep++ = 0; /* DST begins */ 792102336Salfred *atp++ = endtime; 79350462Swpaul *typep++ = 1; /* DST ends */ 79450462Swpaul } 79550462Swpaul janfirst += year_lengths[isleap(year)] * 79636270Swpaul SECSPERDAY; 79736270Swpaul } 79836270Swpaul } else { 79950462Swpaul register long theirstdoffset; 80036270Swpaul register long theirdstoffset; 80136270Swpaul register long theiroffset; 80250462Swpaul register int isdst; 80336270Swpaul register int i; 80439583Swpaul register int j; 80536270Swpaul 80636270Swpaul if (*name != '\0') 80736270Swpaul return -1; 80836270Swpaul if (load_result != 0) 809102336Salfred return -1; 810102336Salfred /* 81150462Swpaul ** Initial values of theirstdoffset and theirdstoffset. 81250462Swpaul */ 81350462Swpaul theirstdoffset = 0; 81436270Swpaul for (i = 0; i < sp->timecnt; ++i) { 81536270Swpaul j = sp->types[i]; 81636270Swpaul if (!sp->ttis[j].tt_isdst) { 81750462Swpaul theirstdoffset = 81836270Swpaul -sp->ttis[j].tt_gmtoff; 81936270Swpaul break; 82050462Swpaul } 82136270Swpaul } 82236270Swpaul theirdstoffset = 0; 82336270Swpaul for (i = 0; i < sp->timecnt; ++i) { 82439583Swpaul j = sp->types[i]; 82536270Swpaul if (sp->ttis[j].tt_isdst) { 82650462Swpaul theirdstoffset = 82736270Swpaul -sp->ttis[j].tt_gmtoff; 82836270Swpaul break; 829102336Salfred } 830102336Salfred } 83150462Swpaul /* 83250462Swpaul ** Initially we're assumed to be in standard time. 83336270Swpaul */ 83450462Swpaul isdst = FALSE; 83536270Swpaul theiroffset = theirstdoffset; 83650462Swpaul /* 83750462Swpaul ** Now juggle transition times and types 83836270Swpaul ** tracking offsets as you do. 83950462Swpaul */ 84050462Swpaul for (i = 0; i < sp->timecnt; ++i) { 84136270Swpaul j = sp->types[i]; 84250462Swpaul sp->types[i] = sp->ttis[j].tt_isdst; 84336270Swpaul if (sp->ttis[j].tt_ttisgmt) { 84436270Swpaul /* No adjustment to transition time */ 84536270Swpaul } else { 84636270Swpaul /* 84736270Swpaul ** If summer time is in effect, and the 84836270Swpaul ** transition time was not specified as 84950462Swpaul ** standard time, add the summer time 85036270Swpaul ** offset to the transition time; 851102336Salfred ** otherwise, add the standard time 852102336Salfred ** offset to the transition time. 85336270Swpaul */ 85436270Swpaul /* 85536270Swpaul ** Transitions from DST to DDST 85650462Swpaul ** will effectively disappear since 85750462Swpaul ** POSIX provides for only one DST 85836270Swpaul ** offset. 85950462Swpaul */ 86036270Swpaul if (isdst && !sp->ttis[j].tt_ttisstd) { 86150462Swpaul sp->ats[i] += dstoffset - 86239583Swpaul theirdstoffset; 86336270Swpaul } else { 86450462Swpaul sp->ats[i] += stdoffset - 86539583Swpaul theirstdoffset; 86636270Swpaul } 86736270Swpaul } 86836270Swpaul theiroffset = -sp->ttis[j].tt_gmtoff; 86936270Swpaul if (sp->ttis[j].tt_isdst) 87036270Swpaul theirdstoffset = theiroffset; 87136270Swpaul else theirstdoffset = theiroffset; 87236464Swpaul } 87336464Swpaul /* 87436464Swpaul ** Finally, fill in ttis. 87536464Swpaul ** ttisstd and ttisgmt need not be handled. 87636464Swpaul */ 87736464Swpaul sp->ttis[0].tt_gmtoff = -stdoffset; 87836464Swpaul sp->ttis[0].tt_isdst = FALSE; 87936464Swpaul sp->ttis[0].tt_abbrind = 0; 88036464Swpaul sp->ttis[1].tt_gmtoff = -dstoffset; 881123289Sobrien sp->ttis[1].tt_isdst = TRUE; 882122625Sobrien sp->ttis[1].tt_abbrind = stdlen + 1; 883123289Sobrien } 88436270Swpaul } else { 885123289Sobrien dstlen = 0; 88636270Swpaul sp->typecnt = 1; /* only standard time */ 88736464Swpaul sp->timecnt = 0; 88836464Swpaul sp->ttis[0].tt_gmtoff = -stdoffset; 88936464Swpaul sp->ttis[0].tt_isdst = 0; 89036270Swpaul sp->ttis[0].tt_abbrind = 0; 89136270Swpaul } 89239583Swpaul sp->charcnt = stdlen + 1; 89339583Swpaul if (dstlen != 0) 89439583Swpaul sp->charcnt += dstlen + 1; 89539583Swpaul if (sp->charcnt > sizeof sp->chars) 89639583Swpaul return -1; 89739583Swpaul cp = sp->chars; 89839583Swpaul (void) strncpy(cp, stdname, stdlen); 899102336Salfred cp += stdlen; 900102336Salfred *cp++ = '\0'; 90139583Swpaul if (dstlen != 0) { 90241656Swpaul (void) strncpy(cp, dstname, dstlen); 90339583Swpaul *(cp + dstlen) = '\0'; 90439583Swpaul } 90539583Swpaul return 0; 90639583Swpaul} 90739583Swpaul 90839583Swpaulstatic void 90939583Swpaulgmtload(sp) 91039583Swpaulstruct state * const sp; 91139583Swpaul{ 91239583Swpaul if (tzload(gmt, sp) != 0) 91339583Swpaul (void) tzparse(gmt, sp, TRUE); 91439583Swpaul} 91539583Swpaul 91639583Swpaul#ifndef STD_INSPIRED 91739583Swpaul/* 91839583Swpaul** A non-static declaration of tzsetwall in a system header file 91939583Swpaul** may cause a warning about this upcoming static declaration... 92039583Swpaul*/ 92139583Swpaulstatic 92239583Swpaul#endif /* !defined STD_INSPIRED */ 92339583Swpaul#ifdef _THREAD_SAFE 92439583Swpaulvoid 92539583Swpaultzsetwall_basic P((void)) 92639583Swpaul#else 92739583Swpaulvoid 92839583Swpaultzsetwall P((void)) 92939583Swpaul#endif 93039583Swpaul{ 93139583Swpaul if (lcl_is_set < 0) 932102336Salfred return; 933102336Salfred lcl_is_set = -1; 93436270Swpaul 93536270Swpaul#ifdef ALL_STATE 93636270Swpaul if (lclptr == NULL) { 93736270Swpaul lclptr = (struct state *) malloc(sizeof *lclptr); 93839583Swpaul if (lclptr == NULL) { 93936270Swpaul settzname(); /* all we can do */ 94039583Swpaul return; 941147256Sbrooks } 94236270Swpaul } 94339583Swpaul#endif /* defined ALL_STATE */ 94439583Swpaul if (tzload((char *) NULL, lclptr) != 0) 94541656Swpaul gmtload(lclptr); 94639583Swpaul settzname(); 94739583Swpaul} 94839583Swpaul 94939583Swpaul#ifdef _THREAD_SAFE 95039583Swpaulvoid 95136270Swpaultzsetwall P((void)) 95236270Swpaul{ 95336270Swpaul pthread_mutex_lock(&lcl_mutex); 95439583Swpaul tzsetwall_basic(); 955195049Srwatson pthread_mutex_unlock(&lcl_mutex); 95672084Sphk} 95736270Swpaul#endif 95836270Swpaul 95939583Swpaul#ifdef _THREAD_SAFE 96039583Swpaulstatic void 96139583Swpaultzset_basic P((void)) 96239583Swpaul#else 96339583Swpaulvoid 96439583Swpaultzset P((void)) 96539583Swpaul#endif 96639583Swpaul{ 96739583Swpaul register const char * name; 96839583Swpaul 96939583Swpaul name = getenv("TZ"); 97039583Swpaul if (name == NULL) { 971122625Sobrien tzsetwall(); 97236270Swpaul return; 97336270Swpaul } 97436270Swpaul 97536270Swpaul if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) 97636317Swpaul return; 97736270Swpaul lcl_is_set = (strlen(name) < sizeof(lcl_TZname)); 978195049Srwatson if (lcl_is_set) 97936270Swpaul (void) strcpy(lcl_TZname, name); 98036270Swpaul 98139583Swpaul#ifdef ALL_STATE 98239583Swpaul if (lclptr == NULL) { 98336270Swpaul lclptr = (struct state *) malloc(sizeof *lclptr); 98436270Swpaul if (lclptr == NULL) { 98536270Swpaul settzname(); /* all we can do */ 98636270Swpaul return; 98739583Swpaul } 98839583Swpaul } 98939583Swpaul#endif /* defined ALL_STATE */ 99039583Swpaul if (*name == '\0') { 99139583Swpaul /* 99239583Swpaul ** User wants it fast rather than right. 993102336Salfred */ 994102336Salfred lclptr->leapcnt = 0; /* so, we're off a little */ 99550468Swpaul lclptr->timecnt = 0; 99650468Swpaul lclptr->ttis[0].tt_gmtoff = 0; 99739583Swpaul lclptr->ttis[0].tt_abbrind = 0; 99839583Swpaul (void) strcpy(lclptr->chars, gmt); 99950468Swpaul } else if (tzload(name, lclptr) != 0) 100039583Swpaul if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) 100150468Swpaul (void) gmtload(lclptr); 100239583Swpaul settzname(); 100350468Swpaul} 100439583Swpaul 100550468Swpaul#ifdef _THREAD_SAFE 100639583Swpaulvoid 100750468Swpaultzset P((void)) 100850468Swpaul{ 100939583Swpaul pthread_mutex_lock(&lcl_mutex); 101050468Swpaul tzset_basic(); 101139583Swpaul pthread_mutex_unlock(&lcl_mutex); 101250468Swpaul} 101339583Swpaul#endif 101450468Swpaul 101539583Swpaul/* 101650468Swpaul** The easy way to behave "as if no library function calls" localtime 101739583Swpaul** is to not call it--so we drop its guts into "localsub", which can be 101839583Swpaul** freely called. (And no, the PANS doesn't require the above behavior-- 101939583Swpaul** but it *is* desirable.) 1020102336Salfred** 1021102336Salfred** The unused offset argument is for the benefit of mktime variants. 102239583Swpaul*/ 102336270Swpaul 102436270Swpaul/*ARGSUSED*/ 102539583Swpaulstatic void 102636270Swpaullocalsub(timep, offset, tmp) 102736270Swpaulconst time_t * const timep; 102839583Swpaulconst long offset; 102950468Swpaulstruct tm * const tmp; 103036270Swpaul{ 103139583Swpaul register struct state * sp; 103236270Swpaul register const struct ttinfo * ttisp; 103336270Swpaul register int i; 103439583Swpaul const time_t t = *timep; 103539583Swpaul 103636270Swpaul sp = lclptr; 103736270Swpaul#ifdef ALL_STATE 103839583Swpaul if (sp == NULL) { 103939583Swpaul gmtsub(timep, offset, tmp); 104036270Swpaul return; 104136270Swpaul } 104236270Swpaul#endif /* defined ALL_STATE */ 104336270Swpaul if (sp->timecnt == 0 || t < sp->ats[0]) { 104436270Swpaul i = 0; 104539583Swpaul while (sp->ttis[i].tt_isdst) 104645155Swpaul if (++i >= sp->typecnt) { 104739583Swpaul i = 0; 104836270Swpaul break; 104939583Swpaul } 105036270Swpaul } else { 105136270Swpaul for (i = 1; i < sp->timecnt; ++i) 105245155Swpaul if (t < sp->ats[i]) 105345155Swpaul break; 105445155Swpaul i = sp->types[i - 1]; 105545155Swpaul } 105636270Swpaul ttisp = &sp->ttis[i]; 105736270Swpaul /* 105836270Swpaul ** To get (wrong) behavior that's compatible with System V Release 2.0 105936270Swpaul ** you'd replace the statement below with 106036270Swpaul ** t += ttisp->tt_gmtoff; 106139583Swpaul ** timesub(&t, 0L, sp, tmp); 106236270Swpaul */ 106336270Swpaul timesub(&t, ttisp->tt_gmtoff, sp, tmp); 106439583Swpaul tmp->tm_isdst = ttisp->tt_isdst; 106539583Swpaul tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; 106636270Swpaul#ifdef TM_ZONE 106736270Swpaul tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; 106839583Swpaul#endif /* defined TM_ZONE */ 106936270Swpaul} 107036270Swpaul 107139583Swpaul#ifdef _THREAD_SAFE 107236270Swpaulint 107336270Swpaullocaltime_r(timep, p_tm) 107436270Swpaulconst time_t * const timep; 107536270Swpaulstruct tm *p_tm; 107636270Swpaul{ 107736270Swpaul pthread_mutex_lock(&lcl_mutex); 107836270Swpaul tzset(); 107936270Swpaul localsub(timep, 0L, p_tm); 108036270Swpaul pthread_mutex_unlock(&lcl_mutex); 108139583Swpaul return(0); 108236270Swpaul} 1083102336Salfred#endif 1084102336Salfred 108548992Swpaulstruct tm * 108636270Swpaullocaltime(timep) 108736270Swpaulconst time_t * const timep; 108836270Swpaul{ 108936270Swpaul#ifdef _THREAD_SAFE 109036270Swpaul static pthread_mutex_t localtime_mutex = PTHREAD_MUTEX_INITIALIZER; 109136270Swpaul static pthread_key_t localtime_key = -1; 109248992Swpaul struct tm *p_tm; 109348992Swpaul 109448992Swpaul pthread_mutex_lock(&localtime_mutex); 1095142398Simp if (localtime_key < 0) { 109648992Swpaul if (pthread_keycreate(&localtime_key, free) < 0) { 109736270Swpaul pthread_mutex_unlock(&localtime_mutex); 109836270Swpaul return(NULL); 109936270Swpaul } 110048992Swpaul } 110136270Swpaul pthread_mutex_unlock(&localtime_mutex); 110236270Swpaul if (pthread_getspecific(localtime_key,(void **) &p_tm) != 0) { 1103102336Salfred return(NULL); 1104102336Salfred } else if (p_tm == NULL) { 110548992Swpaul if ((p_tm = (struct tm *)malloc(sizeof(struct tm))) == NULL) { 110636270Swpaul return(NULL); 110767087Swpaul } 110839583Swpaul pthread_setspecific(localtime_key, p_tm); 110939583Swpaul } 111039583Swpaul pthread_mutex_lock(&lcl_mutex); 111139583Swpaul tzset(); 111248992Swpaul localsub(timep, 0L, p_tm); 1113147256Sbrooks pthread_mutex_unlock(&lcl_mutex); 111436270Swpaul return p_tm; 111548992Swpaul#else 111648992Swpaul tzset(); 111748992Swpaul localsub(timep, 0L, &tm); 1118162315Sglebius return &tm; 111948992Swpaul#endif 112039583Swpaul} 112139583Swpaul 112239583Swpaul/* 112339583Swpaul** gmtsub is to gmtime as localsub is to localtime. 112436270Swpaul*/ 112539583Swpaul 112639583Swpaulstatic void 112736270Swpaulgmtsub(timep, offset, tmp) 112839583Swpaulconst time_t * const timep; 1129105599Sbrooksconst long offset; 1130112878Sjhbstruct tm * const tmp; 113136270Swpaul{ 113236270Swpaul#ifdef _THREAD_SAFE 113393818Sjhb pthread_mutex_lock(&gmt_mutex); 1134150171Sjhb#endif 113569583Swpaul if (!gmt_is_set) { 113636270Swpaul gmt_is_set = TRUE; 113736270Swpaul#ifdef ALL_STATE 113836270Swpaul gmtptr = (struct state *) malloc(sizeof *gmtptr); 113972813Swpaul if (gmtptr != NULL) 114036270Swpaul#endif /* defined ALL_STATE */ 114139583Swpaul gmtload(gmtptr); 114239583Swpaul } 114348992Swpaul#ifdef _THREAD_SAFE 1144127135Snjl pthread_mutex_unlock(&gmt_mutex); 1145127135Snjl#endif 114648992Swpaul timesub(timep, offset, gmtptr, tmp); 114748992Swpaul#ifdef TM_ZONE 114848992Swpaul /* 114948992Swpaul ** Could get fancy here and deliver something such as 115048992Swpaul ** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero, 115148992Swpaul ** but this is no time for a treasure hunt. 115248992Swpaul */ 1153127135Snjl if (offset != 0) 1154127135Snjl tmp->TM_ZONE = wildabbr; 115545155Swpaul else { 115639583Swpaul#ifdef ALL_STATE 115748992Swpaul if (gmtptr == NULL) 1158127135Snjl tmp->TM_ZONE = gmt; 1159127135Snjl else tmp->TM_ZONE = gmtptr->chars; 116048992Swpaul#endif /* defined ALL_STATE */ 116148992Swpaul#ifndef ALL_STATE 1162127135Snjl tmp->TM_ZONE = gmtptr->chars; 1163127135Snjl#endif /* State Farm */ 116436270Swpaul } 116539583Swpaul#endif /* defined TM_ZONE */ 116636270Swpaul} 116748992Swpaul 1168105599Sbrooksstruct tm * 116948992Swpaulgmtime(timep) 117048992Swpaulconst time_t * const timep; 117148992Swpaul{ 117248992Swpaul#ifdef _THREAD_SAFE 117339583Swpaul static pthread_mutex_t gmtime_mutex = PTHREAD_MUTEX_INITIALIZER; 117439583Swpaul static pthread_key_t gmtime_key = -1; 117539583Swpaul struct tm *p_tm; 117639583Swpaul 117739583Swpaul pthread_mutex_lock(&gmtime_mutex); 117839583Swpaul if (gmtime_key < 0) { 117939583Swpaul if (pthread_keycreate(&gmtime_key, free) < 0) { 118048992Swpaul pthread_mutex_unlock(&gmtime_mutex); 118139583Swpaul return(NULL); 118248992Swpaul } 118339583Swpaul } 118436270Swpaul pthread_mutex_unlock(&gmtime_mutex); 118536270Swpaul if (pthread_getspecific(gmtime_key,(void **) &p_tm) != 0) { 118648992Swpaul return(NULL); 1187127135Snjl } else if (p_tm == NULL) { 118848992Swpaul if ((p_tm = (struct tm *)malloc(sizeof(struct tm))) == NULL) { 118948992Swpaul return(NULL); 119048992Swpaul } 1191105599Sbrooks pthread_setspecific(gmtime_key, p_tm); 119248992Swpaul } 119336270Swpaul gmtsub(timep, 0L, p_tm); 119436270Swpaul return(p_tm); 119536270Swpaul#else 119636270Swpaul gmtsub(timep, 0L, &tm); 119751439Swpaul return &tm; 119836270Swpaul#endif 119951439Swpaul} 120051657Swpaul 120139583Swpaul#ifdef _THREAD_SAFE 120251439Swpaulint 1203105599Sbrooksgmtime_r(const time_t * timep, struct tm * tm) 120448992Swpaul{ 120536270Swpaul gmtsub(timep, 0L, tm); 120636270Swpaul return(0); 120736270Swpaul} 120839583Swpaul#endif 120939583Swpaul 121039583Swpaul#ifdef STD_INSPIRED 121143235Swpaul 121239583Swpaulstruct tm * 121339583Swpaulofftime(timep, offset) 121439583Swpaulconst time_t * const timep; 121539583Swpaulconst long offset; 121639583Swpaul{ 121739583Swpaul gmtsub(timep, offset, &tm); 121850468Swpaul return &tm; 121939583Swpaul} 122039583Swpaul 122138030Swpaul#endif /* defined STD_INSPIRED */ 122239583Swpaul 122339583Swpaulstatic void 1224147256Sbrookstimesub(timep, offset, sp, tmp) 1225105599Sbrooksconst time_t * const timep; 122648992Swpaulconst long offset; 122739583Swpaulregister const struct state * const sp; 122839583Swpaulregister struct tm * const tmp; 122939583Swpaul{ 123039583Swpaul register const struct lsinfo * lp; 123139583Swpaul register long days; 123239583Swpaul register long rem; 123339583Swpaul register int y; 123439583Swpaul register int yleap; 123539583Swpaul register const int * ip; 123639583Swpaul register long corr; 123739583Swpaul register int hit; 123839583Swpaul register int i; 123939583Swpaul 124039583Swpaul corr = 0; 124139583Swpaul hit = 0; 124239583Swpaul#ifdef ALL_STATE 124339583Swpaul i = (sp == NULL) ? 0 : sp->leapcnt; 124439583Swpaul#endif /* defined ALL_STATE */ 124539583Swpaul#ifndef ALL_STATE 124639583Swpaul i = sp->leapcnt; 1247147256Sbrooks#endif /* State Farm */ 124839583Swpaul while (--i >= 0) { 124939583Swpaul lp = &sp->lsis[i]; 125039583Swpaul if (*timep >= lp->ls_trans) { 125139583Swpaul if (*timep == lp->ls_trans) { 1252147256Sbrooks hit = ((i == 0 && lp->ls_corr > 0) || 1253147256Sbrooks lp->ls_corr > sp->lsis[i - 1].ls_corr); 1254147256Sbrooks if (hit) 1255147256Sbrooks while (i > 0 && 1256147256Sbrooks sp->lsis[i].ls_trans == 1257147256Sbrooks sp->lsis[i - 1].ls_trans + 1 && 125839583Swpaul sp->lsis[i].ls_corr == 1259121816Sbrooks sp->lsis[i - 1].ls_corr + 1) { 1260150171Sjhb ++hit; 126139583Swpaul --i; 126239583Swpaul } 126339583Swpaul } 126439583Swpaul corr = lp->ls_corr; 126551439Swpaul break; 1266169414Syar } 1267169414Syar } 1268150171Sjhb days = *timep / SECSPERDAY; 126939583Swpaul rem = *timep % SECSPERDAY; 127039583Swpaul#ifdef mc68k 127139583Swpaul if (*timep == 0x80000000) { 127250468Swpaul /* 127339583Swpaul ** A 3B1 muffs the division on the most negative number. 127439583Swpaul */ 127536270Swpaul days = -24855; 127650462Swpaul rem = -11648; 127750462Swpaul } 127850462Swpaul#endif /* defined mc68k */ 1279213894Smarius rem += (offset - corr); 1280213894Smarius while (rem < 0) { 128136270Swpaul rem += SECSPERDAY; 1282213894Smarius --days; 1283213894Smarius } 128445155Swpaul while (rem >= SECSPERDAY) { 128545155Swpaul rem -= SECSPERDAY; 128645155Swpaul ++days; 128745155Swpaul } 128845155Swpaul tmp->tm_hour = (int) (rem / SECSPERHOUR); 128945155Swpaul rem = rem % SECSPERHOUR; 129045155Swpaul tmp->tm_min = (int) (rem / SECSPERMIN); 129145166Swpaul /* 129245155Swpaul ** A positive leap second requires a special 129345155Swpaul ** representation. This uses "... ??:59:60" et seq. 129445155Swpaul */ 129545155Swpaul tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; 129645155Swpaul tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); 129736270Swpaul if (tmp->tm_wday < 0) 129836270Swpaul tmp->tm_wday += DAYSPERWEEK; 129939583Swpaul y = EPOCH_YEAR; 130063090Sarchie#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) 130139583Swpaul while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) { 1302147256Sbrooks register int newy; 130338030Swpaul 1304113609Snjl newy = y + days / DAYSPERNYEAR; 1305150171Sjhb if (days < 0) 1306166901Spiso --newy; 1307112872Snjl days -= (newy - y) * DAYSPERNYEAR + 1308112872Snjl LEAPS_THRU_END_OF(newy - 1) - 1309112872Snjl LEAPS_THRU_END_OF(y - 1); 1310113609Snjl y = newy; 1311112872Snjl } 1312112872Snjl tmp->tm_year = y - TM_YEAR_BASE; 1313112872Snjl tmp->tm_yday = (int) days; 131436270Swpaul ip = mon_lengths[yleap]; 1315112872Snjl for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon)) 1316112872Snjl days = days - (long) ip[tmp->tm_mon]; 1317112872Snjl tmp->tm_mday = (int) (days + 1); 131848992Swpaul tmp->tm_isdst = 0; 131936270Swpaul#ifdef TM_GMTOFF 132036270Swpaul tmp->TM_GMTOFF = offset; 1321113609Snjl#endif /* defined TM_GMTOFF */ 1322113609Snjl} 1323113609Snjl 1324113609Snjlchar * 1325113609Snjlctime(timep) 1326113609Snjlconst time_t * const timep; 1327113609Snjl{ 1328102336Salfred/* 1329102336Salfred** Section 4.12.3.2 of X3.159-1989 requires that 133048992Swpaul** The ctime funciton converts the calendar time pointed to by timer 133148992Swpaul** to local time in the form of a string. It is equivalent to 133248992Swpaul** asctime(localtime(timer)) 133348992Swpaul*/ 133448992Swpaul return asctime(localtime(timep)); 133548992Swpaul} 1336112880Sjhb 1337147256Sbrooks/* 133848992Swpaul** Adapted from code provided by Robert Elz, who writes: 1339113609Snjl** The "best" way to do mktime I think is based on an idea of Bob 1340113812Simp** Kridle's (so its said...) from a long time ago. 1341199560Sjhb** [kridle@xinet.com as of 1996-01-16.] 1342150171Sjhb** It does a binary search of the time_t space. Since time_t's are 1343113609Snjl** just 32 bits, its a max of 32 iterations (even at 64 bits it 1344150171Sjhb** would still be very reasonable). 1345150171Sjhb*/ 1346150213Sru 1347113609Snjl#ifndef WRONG 1348112872Snjl#define WRONG (-1) 1349113609Snjl#endif /* !defined WRONG */ 135048992Swpaul 1351112872Snjl/* 1352112872Snjl** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com). 135350462Swpaul*/ 135450462Swpaul 135548992Swpaulstatic int 1356112872Snjlincrement_overflow(number, delta) 1357112872Snjlint * number; 1358112872Snjlint delta; 1359112872Snjl{ 1360112872Snjl int number0; 1361112872Snjl 136248992Swpaul number0 = *number; 1363151297Sru *number += delta; 1364151297Sru return (*number < number0) != (delta < 0); 1365151297Sru} 136667087Swpaul 136748992Swpaulstatic int 136848992Swpaulnormalize_overflow(tensptr, unitsptr, base) 136948992Swpaulint * const tensptr; 137048992Swpaulint * const unitsptr; 137136270Swpaulconst int base; 137236270Swpaul{ 137336270Swpaul register int tensdelta; 1374102336Salfred 1375102336Salfred tensdelta = (*unitsptr >= 0) ? 137636270Swpaul (*unitsptr / base) : 137736270Swpaul (-1 - (-1 - *unitsptr) / base); 137836270Swpaul *unitsptr -= tensdelta * base; 137936270Swpaul return increment_overflow(tensptr, tensdelta); 138036270Swpaul} 138136270Swpaul 138236270Swpaulstatic int 138336270Swpaultmcomp(atmp, btmp) 138436270Swpaulregister const struct tm * const atmp; 138536270Swpaulregister const struct tm * const btmp; 138636270Swpaul{ 138736270Swpaul register int result; 138836270Swpaul 138936270Swpaul if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && 139036270Swpaul (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && 139136270Swpaul (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && 139236270Swpaul (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && 139336270Swpaul (result = (atmp->tm_min - btmp->tm_min)) == 0) 139436270Swpaul result = atmp->tm_sec - btmp->tm_sec; 139536270Swpaul return result; 139636270Swpaul} 139736270Swpaul 139836270Swpaulstatic time_t 139936270Swpaultime2(tmp, funcp, offset, okayp) 140036270Swpaulstruct tm * const tmp; 140136270Swpaulvoid (* const funcp) P((const time_t*, long, struct tm*)); 1402102336Salfredconst long offset; 1403102336Salfredint * const okayp; 140436270Swpaul{ 140536270Swpaul register const struct state * sp; 140636270Swpaul register int dir; 140736270Swpaul register int bits; 140836270Swpaul register int i, j ; 140936270Swpaul register int saved_seconds; 141036270Swpaul time_t newt; 141136270Swpaul time_t t; 141236270Swpaul struct tm yourtm, mytm; 141340795Swpaul 141436270Swpaul *okayp = FALSE; 141537626Swpaul yourtm = *tmp; 141639583Swpaul if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) 141739583Swpaul return WRONG; 141840795Swpaul if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) 141936270Swpaul return WRONG; 142036270Swpaul if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR)) 142136270Swpaul return WRONG; 142236270Swpaul /* 142336270Swpaul ** Turn yourtm.tm_year into an actual year number for now. 142436270Swpaul ** It is converted back to an offset from TM_YEAR_BASE later. 142536270Swpaul */ 142636270Swpaul if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE)) 142736270Swpaul return WRONG; 142836270Swpaul while (yourtm.tm_mday <= 0) { 142936270Swpaul if (increment_overflow(&yourtm.tm_year, -1)) 143036270Swpaul return WRONG; 143136270Swpaul i = yourtm.tm_year + (1 < yourtm.tm_mon); 143236270Swpaul yourtm.tm_mday += year_lengths[isleap(i)]; 143336270Swpaul } 1434102336Salfred while (yourtm.tm_mday > DAYSPERLYEAR) { 1435102336Salfred i = yourtm.tm_year + (1 < yourtm.tm_mon); 143636270Swpaul yourtm.tm_mday -= year_lengths[isleap(i)]; 143737626Swpaul if (increment_overflow(&yourtm.tm_year, 1)) 143836270Swpaul return WRONG; 143936270Swpaul } 144036270Swpaul for ( ; ; ) { 1441150171Sjhb i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon]; 144287846Sluigi if (yourtm.tm_mday <= i) 144336270Swpaul break; 144436270Swpaul yourtm.tm_mday -= i; 144536270Swpaul if (++yourtm.tm_mon >= MONSPERYEAR) { 144636270Swpaul yourtm.tm_mon = 0; 144736270Swpaul if (increment_overflow(&yourtm.tm_year, 1)) 144836270Swpaul return WRONG; 144937626Swpaul } 145037626Swpaul } 145156060Swpaul if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) 145236270Swpaul return WRONG; 145336270Swpaul if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { 145436270Swpaul /* 145536270Swpaul ** We can't set tm_sec to 0, because that might push the 145636270Swpaul ** time below the minimum representable time. 145736270Swpaul ** Set tm_sec to 59 instead. 145836270Swpaul ** This assumes that the minimum representable time is 145936270Swpaul ** not in the same minute that a leap second was deleted from, 146036270Swpaul ** which is a safer assumption than using 58 would be. 146136270Swpaul */ 146236270Swpaul if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) 146336270Swpaul return WRONG; 146436270Swpaul saved_seconds = yourtm.tm_sec; 146536270Swpaul yourtm.tm_sec = SECSPERMIN - 1; 146636270Swpaul } else { 146736270Swpaul saved_seconds = yourtm.tm_sec; 146836270Swpaul yourtm.tm_sec = 0; 146936270Swpaul } 147036270Swpaul /* 147136270Swpaul ** Divide the search space in half 147236270Swpaul ** (this works whether time_t is signed or unsigned). 147336270Swpaul */ 147436270Swpaul bits = TYPE_BIT(time_t) - 1; 147536270Swpaul /* 147636270Swpaul ** If time_t is signed, then 0 is just above the median, 147736270Swpaul ** assuming two's complement arithmetic. 1478102336Salfred ** If time_t is unsigned, then (1 << bits) is just above the median. 1479102336Salfred */ 148036270Swpaul t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits); 148136270Swpaul for ( ; ; ) { 148236270Swpaul (*funcp)(&t, offset, &mytm); 148336270Swpaul dir = tmcomp(&mytm, &yourtm); 148436270Swpaul if (dir != 0) { 148536270Swpaul if (bits-- < 0) 148636270Swpaul return WRONG; 148736270Swpaul if (bits < 0) 148837626Swpaul --t; /* may be needed if new t is minimal */ 148936270Swpaul else if (dir > 0) 149036270Swpaul t -= ((time_t) 1) << bits; 1491147256Sbrooks else t += ((time_t) 1) << bits; 149236270Swpaul continue; 1493122689Ssam } 1494122689Ssam if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) 149556060Swpaul break; 149656060Swpaul /* 149756060Swpaul ** Right time, wrong type. 149856060Swpaul ** Hunt for right time, right type. 149936270Swpaul ** It's okay to guess wrong since the guess 150036270Swpaul ** gets checked. 150136270Swpaul */ 150236270Swpaul /* 150336270Swpaul ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. 150439583Swpaul */ 150539583Swpaul sp = (const struct state *) 150639583Swpaul (((void *) funcp == (void *) localsub) ? 150739583Swpaul lclptr : gmtptr); 150839583Swpaul#ifdef ALL_STATE 150939583Swpaul if (sp == NULL) 151039583Swpaul return WRONG; 151136270Swpaul#endif /* defined ALL_STATE */ 151236270Swpaul for (i = sp->typecnt - 1; i >= 0; --i) { 151336270Swpaul if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) 151436270Swpaul continue; 151536270Swpaul for (j = sp->typecnt - 1; j >= 0; --j) { 151636270Swpaul if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) 151737626Swpaul continue; 151837626Swpaul newt = t + sp->ttis[j].tt_gmtoff - 151937626Swpaul sp->ttis[i].tt_gmtoff; 152037626Swpaul (*funcp)(&newt, offset, &mytm); 152137626Swpaul if (tmcomp(&mytm, &yourtm) != 0) 152237626Swpaul continue; 1523106936Ssam if (mytm.tm_isdst != yourtm.tm_isdst) 152439583Swpaul continue; 1525152315Sru /* 152637626Swpaul ** We have a match. 152737626Swpaul */ 152837626Swpaul t = newt; 152937626Swpaul goto label; 153037626Swpaul } 1531106936Ssam } 1532106936Ssam return WRONG; 1533106936Ssam } 1534122689Ssamlabel: 1535106936Ssam newt = t + saved_seconds; 1536122689Ssam if ((newt < t) != (saved_seconds < 0)) 153736270Swpaul return WRONG; 153836270Swpaul t = newt; 153936270Swpaul (*funcp)(&t, offset, tmp); 154036270Swpaul *okayp = TRUE; 154136270Swpaul return t; 154236270Swpaul} 154336270Swpaul 154436270Swpaulstatic time_t 154536270Swpaultime1(tmp, funcp, offset) 154636270Swpaulstruct tm * const tmp; 154736270Swpaulvoid (* const funcp) P((const time_t *, long, struct tm *)); 154836270Swpaulconst long offset; 1549102336Salfred{ 1550102336Salfred register time_t t; 155136270Swpaul register const struct state * sp; 155236270Swpaul register int samei, otheri; 155336270Swpaul int okay; 155436270Swpaul 155536270Swpaul if (tmp->tm_isdst > 1) 155656060Swpaul tmp->tm_isdst = 1; 155736270Swpaul t = time2(tmp, funcp, offset, &okay); 155856060Swpaul#ifdef PCTS 155936270Swpaul /* 156056060Swpaul ** PCTS code courtesy Grant Sullivan (grant@osf.org). 156136270Swpaul */ 156236270Swpaul if (okay) 156336270Swpaul return t; 156439583Swpaul if (tmp->tm_isdst < 0) 156536270Swpaul tmp->tm_isdst = 0; /* reset to std and try again */ 156656060Swpaul#endif /* defined PCTS */ 156756060Swpaul#ifndef PCTS 156839583Swpaul if (okay || tmp->tm_isdst < 0) 156936270Swpaul return t; 157036270Swpaul#endif /* !defined PCTS */ 157136270Swpaul /* 157236270Swpaul ** We're supposed to assume that somebody took a time of one type 1573102336Salfred ** and did some math on it that yielded a "struct tm" that's bad. 1574102336Salfred ** We try to divine the type they started from and adjust to the 157536270Swpaul ** type they need. 157636270Swpaul */ 157736270Swpaul /* 157836270Swpaul ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. 157936270Swpaul */ 158036270Swpaul sp = (const struct state *) (((void *) funcp == (void *) localsub) ? 158136270Swpaul lclptr : gmtptr); 158236270Swpaul#ifdef ALL_STATE 158336270Swpaul if (sp == NULL) 158436270Swpaul return WRONG; 158536270Swpaul#endif /* defined ALL_STATE */ 158636270Swpaul for (samei = sp->typecnt - 1; samei >= 0; --samei) { 158736270Swpaul if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) 158836270Swpaul continue; 158936270Swpaul for (otheri = sp->typecnt - 1; otheri >= 0; --otheri) { 159036270Swpaul if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) 159136270Swpaul continue; 159236270Swpaul tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - 159336270Swpaul sp->ttis[samei].tt_gmtoff; 159436270Swpaul tmp->tm_isdst = !tmp->tm_isdst; 159536270Swpaul t = time2(tmp, funcp, offset, &okay); 159636270Swpaul if (okay) 159736270Swpaul return t; 159836270Swpaul tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - 159936270Swpaul sp->ttis[samei].tt_gmtoff; 160037626Swpaul tmp->tm_isdst = !tmp->tm_isdst; 160137626Swpaul } 160236270Swpaul } 160336270Swpaul return WRONG; 160436270Swpaul} 160536270Swpaul 160636270Swpaultime_t 160736270Swpaulmktime(tmp) 160836270Swpaulstruct tm * const tmp; 160936270Swpaul{ 161036270Swpaul time_t mktime_return_value; 161136270Swpaul#ifdef _THREAD_SAFE 161236270Swpaul pthread_mutex_lock(&lcl_mutex); 161336270Swpaul#endif 161436270Swpaul tzset(); 161536270Swpaul mktime_return_value = time1(tmp, localsub, 0L); 161636270Swpaul#ifdef _THREAD_SAFE 161736270Swpaul pthread_mutex_unlock(&lcl_mutex); 161836270Swpaul#endif 161936270Swpaul return(mktime_return_value); 162036270Swpaul} 162136270Swpaul 162236270Swpaul#ifdef STD_INSPIRED 162336270Swpaul 162436270Swpaultime_t 162536270Swpaultimelocal(tmp) 1626102336Salfredstruct tm * const tmp; 1627102336Salfred{ 162836270Swpaul tmp->tm_isdst = -1; /* in case it wasn't initialized */ 162936270Swpaul return mktime(tmp); 163036270Swpaul} 163136270Swpaul 163236270Swpaultime_t 163336270Swpaultimegm(tmp) 163436270Swpaulstruct tm * const tmp; 163536270Swpaul{ 1636147256Sbrooks tmp->tm_isdst = 0; 163736270Swpaul return time1(tmp, gmtsub, 0L); 163836270Swpaul} 1639199560Sjhb 164036270Swpaultime_t 164136270Swpaultimeoff(tmp, offset) 1642148887Srwatsonstruct tm * const tmp; 164336270Swpaulconst long offset; 164436270Swpaul{ 164536270Swpaul tmp->tm_isdst = 0; 164636270Swpaul return time1(tmp, gmtsub, offset); 164736270Swpaul} 164839583Swpaul 164936270Swpaul#endif /* defined STD_INSPIRED */ 165039583Swpaul 165151439Swpaul#ifdef CMUCS 165236270Swpaul 165339583Swpaul/* 165436270Swpaul** The following is supplied for compatibility with 165536270Swpaul** previous versions of the CMUCS runtime library. 165639583Swpaul*/ 165736270Swpaul 165836270Swpaullong 165936270Swpaulgtime(tmp) 166036270Swpaulstruct tm * const tmp; 166136270Swpaul{ 166236270Swpaul const time_t t = mktime(tmp); 1663102336Salfred 1664102336Salfred if (t == WRONG) 166536270Swpaul return -1; 166636270Swpaul return t; 166736270Swpaul} 166836270Swpaul 166936270Swpaul#endif /* defined CMUCS */ 167036270Swpaul 167136270Swpaul/* 167239627Swpaul** XXX--is the below the right way to conditionalize?? 1673162315Sglebius*/ 167441656Swpaul 167536270Swpaul#ifdef STD_INSPIRED 167639583Swpaul 167737626Swpaul/* 1678150171Sjhb** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599 167939583Swpaul** shall correspond to "Wed Dec 31 23:59:59 GMT 1986", which 168036270Swpaul** is not the case if we are accounting for leap seconds. 168136270Swpaul** So, we provide the following conversion routines for use 168236270Swpaul** when exchanging timestamps with POSIX conforming systems. 168336270Swpaul*/ 1684102336Salfred 1685102336Salfredstatic long 168636270Swpaulleapcorr(timep) 168736270Swpaultime_t * timep; 168836270Swpaul{ 168936270Swpaul register struct state * sp; 169036270Swpaul register struct lsinfo * lp; 169136270Swpaul register int i; 169236270Swpaul 169336270Swpaul sp = lclptr; 169439583Swpaul i = sp->leapcnt; 169539583Swpaul while (--i >= 0) { 169636270Swpaul lp = &sp->lsis[i]; 1697162315Sglebius if (*timep >= lp->ls_trans) 169836270Swpaul return lp->ls_corr; 169936270Swpaul } 170036270Swpaul return 0; 170136270Swpaul} 1702102336Salfred 1703102336Salfredtime_t 170439583Swpaultime2posix(t) 170536270Swpaultime_t t; 170636270Swpaul{ 170736270Swpaul tzset(); 170836270Swpaul return t - leapcorr(&t); 170936270Swpaul} 171036270Swpaul 171136270Swpaultime_t 171236270Swpaulposix2time(t) 171339583Swpaultime_t t; 171467087Swpaul{ 171536270Swpaul time_t x; 171636270Swpaul time_t y; 171739583Swpaul 171839583Swpaul tzset(); 171936270Swpaul /* 172036270Swpaul ** For a positive leap second hit, the result 172136270Swpaul ** is not unique. For a negative leap second 172236270Swpaul ** hit, the corresponding time doesn't exist, 1723147256Sbrooks ** so we return an adjacent second. 172436270Swpaul */ 172536270Swpaul x = t + leapcorr(&t); 172636270Swpaul y = x - leapcorr(&x); 172739583Swpaul if (y < t) { 1728162315Sglebius do { 172939583Swpaul x++; 173039583Swpaul y = x - leapcorr(&x); 173139583Swpaul } while (y < t); 173239583Swpaul if (t != y) 173336270Swpaul return x - 1; 173436270Swpaul } else if (y > t) { 173536270Swpaul do { 173636270Swpaul --x; 173736270Swpaul y = x - leapcorr(&x); 173836270Swpaul } while (y > t); 173936270Swpaul if (t != y) 174036270Swpaul return x + 1; 174139583Swpaul } 174239583Swpaul return x; 174336270Swpaul} 174436270Swpaul 174536270Swpaul#endif /* defined STD_INSPIRED */ 174636270Swpaul