1: /* 2: * misc - data and miscellaneous routines 3: */ 4: /* $Header: misc.c,v 1.11 91/04/22 13:20:45 ceriel Exp $ */ 5: 6: #include <ctype.h> 7: #include <time.h> 8: #include <stdlib.h> 9: #include <string.h> 10: 11: #if defined(__BSD4_2) 12: 13: struct timeval { 14: long tv_sec; /* seconds */ 15: long tv_usec; /* and microseconds */ 16: }; 17: 18: struct timezone { 19: int tz_minuteswest; /* minutes west of Greenwich */ 20: int tz_dsttime; /* type of dst correction */ 21: }; 22: 23: int _gettimeofday(struct timeval *tp, struct timezone *tzp); 24: 25: #elif !defined(_POSIX_SOURCE) && !defined(__USG) 26: #if !defined(_MINIX) /* MINIX has no ftime() */ 27: struct timeb { 28: long time; 29: unsigned short millitm; 30: short timezone; 31: short dstflag; 32: }; 33: void _ftime(struct timeb *bp); 34: #endif 35: #endif 36: 37: #include "loc_time.h" 38: 39: #define RULE_LEN 120 40: #define TZ_LEN 10 41: 42: /* Make sure that the strings do not end up in ROM. 43: * These strings probably contain the wrong value, and we cannot obtain the 44: * right value from the system. TZ is the only help. 45: */ 46: static char ntstr[TZ_LEN + 1] = "GMT"; /* string for normal time */ 47: static char dststr[TZ_LEN + 1] = "GDT"; /* string for daylight saving */ 48: 49: long _timezone = 0; 50: long _dst_off = 60 * 60; 51: int _daylight = 0; 52: char *_tzname[2] = {ntstr, dststr}; 53: 54: #if defined(__USG) || defined(_POSIX_SOURCE) 55: char *tzname[2] = {ntstr, dststr}; 56: 57: #if defined(__USG) 58: long timezone = 0; 59: int daylight = 0; 60: #endif 61: #endif 62: 63: static struct dsttype { 64: char ds_type; /* Unknown, Julian, Zero-based or M */ 65: int ds_date[3]; /* months, weeks, days */ 66: long ds_sec; /* usually 02:00:00 */ 67: } dststart = { 'U', { 0, 0, 0 }, 2 * 60 * 60 } 68: , dstend = { 'U', { 0, 0, 0 }, 2 * 60 * 60 }; 69: 70: const char *_days[] = { 71: "Sunday", "Monday", "Tuesday", "Wednesday", 72: "Thursday", "Friday", "Saturday" 73: }; 74: 75: const char *_months[] = { 76: "January", "February", "March", 77: "April", "May", "June", 78: "July", "August", "September", 79: "October", "November", "December" 80: }; 81: 82: const int _ytab[2][12] = { 83: { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 84: { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 85: }; 86: 87: static const char * 88: parseZoneName(register char *buf, register const char *p) 89: { 90: register int n = 0; 91: 92: if (*p == ':') return NULL; 93: while (*p && !isdigit(*p) && *p != ',' && *p != '-' && *p != '+') { 94: if (n < TZ_LEN) 95: *buf++ = *p; 96: p++; 97: n++; 98: } 99: if (n < 3) return NULL; /* error */ 100: *buf = '\0'; 101: return p; 102: } 103: 104: static const char * 105: parseTime(register long *tm, const char *p, register struct dsttype *dst) 106: { 107: register int n = 0; 108: register const char *q = p; 109: char ds_type = (dst ? dst->ds_type : '\0'); 110: 111: if (dst) dst->ds_type = 'U'; 112: 113: *tm = 0; 114: while(*p >= '0' && *p <= '9') { 115: n = 10 * n + (*p++ - '0'); 116: } 117: if (q == p) return NULL; /* "The hour shall be required" */ 118: if (n < 0 || n >= 24) return NULL; 119: *tm = n * 60 * 60; 120: if (*p == ':') { 121: p++; 122: n = 0; 123: while(*p >= '0' && *p <= '9') { 124: n = 10 * n + (*p++ - '0'); 125: } 126: if (q == p) return NULL; /* format error */ 127: if (n < 0 || n >= 60) return NULL; 128: *tm += n * 60; 129: if (*p == ':') { 130: p++; 131: n = 0; 132: while(*p >= '0' && *p <= '9') { 133: n = 10 * n + (*p++ - '0'); 134: } 135: if (q == p) return NULL; /* format error */ 136: if (n < 0 || n >= 60) return NULL; 137: *tm += n; 138: } 139: } 140: if (dst) { 141: dst->ds_type = ds_type; 142: dst->ds_sec = *tm; 143: } 144: return p; 145: } 146: 147: static const char * 148: parseDate(register char *buf, register const char *p, struct dsttype *dstinfo) 149: { 150: register const char *q; 151: register int n = 0; 152: int cnt = 0; 153: const int bnds[3][2] = { { 1, 12 }, 154: { 1, 5 }, 155: { 0, 6} 156: }; 157: char ds_type; 158: 159: if (*p != 'M') { 160: if (*p == 'J') { 161: *buf++ = *p++; 162: ds_type = 'J'; 163: } 164: else ds_type = 'Z'; 165: q = p; 166: while(*p >= '0' && *p <= '9') { 167: n = 10 * n + (*p - '0'); 168: *buf++ = *p++; 169: } 170: if (q == p) return NULL; /* format error */ 171: if (n < (ds_type == 'J') || n > 365) return NULL; 172: dstinfo->ds_type = ds_type; 173: dstinfo->ds_date[0] = n; 174: return p; 175: } 176: ds_type = 'M'; 177: do { 178: *buf++ = *p++; 179: q = p; 180: n = 0; 181: while(*p >= '0' && *p <= '9') { 182: n = 10 * n + (*p - '0'); 183: *buf++ = *p++; 184: } 185: if (q == p) return NULL; /* format error */ 186: if (n < bnds[cnt][0] || n > bnds[cnt][1]) return NULL; 187: dstinfo->ds_date[cnt] = n; 188: cnt++; 189: } while (cnt < 3 && *p == '.'); 190: if (cnt != 3) return NULL; 191: *buf = '\0'; 192: dstinfo->ds_type = ds_type; 193: return p; 194: } 195: 196: static const char * 197: parseRule(register char *buf, register const char *p) 198: { 199: long time; 200: register const char *q; 201: 202: if (!(p = parseDate(buf, p, &dststart))) return NULL; 203: buf += strlen(buf); 204: if (*p == '/') { 205: q = ++p; 206: if (!(p = parseTime(&time, p, &dststart))) return NULL; 207: while( p != q) *buf++ = *q++; 208: } 209: if (*p != ',') return NULL; 210: p++; 211: if (!(p = parseDate(buf, p, &dstend))) return NULL; 212: buf += strlen(buf); 213: if (*p == '/') { 214: q = ++p; 215: if (!(p = parseTime(&time, p, &dstend))) return NULL; 216: while(*buf++ = *q++); 217: } 218: if (*p) return NULL; 219: return p; 220: } 221: 222: /* The following routine parses timezone information in POSIX-format. For 223: * the requirements, see IEEE Std 1003.1-1988 section 8.1.1. 224: * The function returns as soon as it spots an error. 225: */ 226: static void 227: parseTZ(const char *p) 228: { 229: long tz, dst = 60 * 60, sign = 1; 230: static char lastTZ[2 * RULE_LEN]; 231: static char buffer[RULE_LEN]; 232: 233: if (!p) return; 234: 235: if (*p == ':') { 236: /* 237: * According to POSIX, this is implementation defined. 238: * Since it depends on the particular operating system, we 239: * can do nothing. 240: */ 241: return; 242: } 243: 244: if (!strcmp(lastTZ, p)) return; /* nothing changed */ 245: 246: *_tzname[0] = '\0'; 247: *_tzname[1] = '\0'; 248: dststart.ds_type = 'U'; 249: dststart.ds_sec = 2 * 60 * 60; 250: dstend.ds_type = 'U'; 251: dstend.ds_sec = 2 * 60 * 60; 252: 253: if (strlen(p) > 2 * RULE_LEN) return; 254: strcpy(lastTZ, p); 255: 256: if (!(p = parseZoneName(buffer, p))) return; 257: 258: if (*p == '-') { 259: sign = -1; 260: p++; 261: } else if (*p == '+') p++; 262: 263: if (!(p = parseTime(&tz, p, NULL))) return; 264: tz *= sign; 265: _timezone = tz; 266: strncpy(_tzname[0], buffer, TZ_LEN); 267: 268: if (!(_daylight = (*p != '\0'))) return; 269: 270: buffer[0] = '\0'; 271: if (!(p = parseZoneName(buffer, p))) return; 272: strncpy(_tzname[1], buffer, TZ_LEN); 273: 274: buffer[0] = '\0'; 275: if (*p && (*p != ',')) 276: if (!(p = parseTime(&dst, p, NULL))) return; 277: _dst_off = dst; /* dst was initialized to 1 hour */ 278: if (*p) { 279: if (*p != ',') return; 280: p++; 281: if (strlen(p) > RULE_LEN) return; 282: if (!(p = parseRule(buffer, p))) return; 283: } 284: } 285: 286: void 287: _tzset(void) 288: { 289: #if defined(__BSD4_2) 290: 291: struct timeval tv; 292: struct timezone tz; 293: 294: _gettimeofday(&tv, &tz); 295: _daylight = tz.tz_dsttime; 296: _timezone = tz.tz_minuteswest * 60L; 297: 298: #elif !defined(_POSIX_SOURCE) && !defined(__USG) 299: 300: #if !defined(_MINIX) /* MINIX has no ftime() */ 301: struct timeb time; 302: 303: _ftime(&time); 304: _timezone = time.timezone * 60L; 305: _daylight = time.dstflag; 306: #endif 307: 308: #endif /* !_POSIX_SOURCE && !__USG */ 309: 310: parseTZ(getenv("TZ")); /* should go inside #if */ 311: 312: #if defined(__USG) || defined(_POSIX_SOURCE) 313: tzname[0] = _tzname[0]; 314: tzname[1] = _tzname[1]; 315: #if defined(__USG) 316: timezone = _timezone; 317: daylight = _daylight; 318: #endif 319: #endif /* __USG || _POSIX_SOURCE */ 320: } 321: 322: static int 323: last_sunday(register int day, register struct tm *timep) 324: { 325: int first = FIRSTSUNDAY(timep); 326: 327: if (day >= 58 && LEAPYEAR(YEAR0 + timep->tm_year)) day++; 328: if (day < first) return first; 329: return day - (day - first) % 7; 330: } 331: 332: static int 333: date_of(register struct dsttype *dst, struct tm *timep) 334: { 335: int leap = LEAPYEAR(YEAR0 + timep->tm_year); 336: int firstday, tmpday; 337: register int day, month; 338: 339: if (dst->ds_type != 'M') { 340: return dst->ds_date[0] - 341: (dst->ds_type == 'J' 342: && leap 343: && dst->ds_date[0] < 58); 344: } 345: day = 0; 346: month = 1; 347: while (month < dst->ds_date[0]) { 348: day += _ytab[leap][month - 1]; 349: month++; 350: } 351: firstday = (day + FIRSTDAYOF(timep)) % 7; 352: tmpday = day; 353: day += (dst->ds_date[2] - firstday + 7) % 7 354: + 7 * (dst->ds_date[1] - 1); 355: if (day >= tmpday + _ytab[leap][month]) day -= 7; 356: return day; 357: } 358: 359: /* 360: * The default dst transitions are those for Western Europe (except Great 361: * Britain). 362: */ 363: unsigned 364: _dstget(register struct tm *timep) 365: { 366: int begindst, enddst; 367: register struct dsttype *dsts = &dststart, *dste = &dstend; 368: int do_dst = 0; 369: 370: if (_daylight == -1) 371: _tzset(); 372: 373: timep->tm_isdst = _daylight; 374: if (!_daylight) return 0; 375: 376: if (dsts->ds_type != 'U') 377: begindst = date_of(dsts, timep); 378: else begindst = last_sunday(89, timep); /* last Sun before Apr */ 379: if (dste->ds_type != 'U') 380: enddst = date_of(dste, timep); 381: else enddst = last_sunday(272, timep); /* last Sun in Sep */ 382: 383: /* assume begindst != enddst (otherwise it would be no use) */ 384: if (begindst < enddst) { /* northern hemisphere */ 385: if (timep->tm_yday > begindst && timep->tm_yday < enddst) 386: do_dst = 1; 387: } else { /* southern hemisphere */ 388: if (timep->tm_yday > begindst || timep->tm_yday < enddst) 389: do_dst = 1; 390: } 391: 392: if (!do_dst 393: && (timep->tm_yday == begindst || timep->tm_yday == enddst)) { 394: long dsttranssec; /* transition when day is this old */ 395: long cursec; 396: 397: if (timep->tm_yday == begindst) 398: dsttranssec = dsts->ds_sec; 399: else dsttranssec = dste->ds_sec; 400: cursec = ((timep->tm_hour * 60) + timep->tm_min) * 60L 401: + timep->tm_sec; 402: 403: if ((timep->tm_yday == begindst && cursec >= dsttranssec) 404: || (timep->tm_yday == enddst && cursec < dsttranssec)) 405: do_dst = 1; 406: } 407: if (do_dst) return _dst_off; 408: timep->tm_isdst = 0; 409: return 0; 410: }