2 * misc - data and miscellaneous routines
4 /* $Id: misc.c,v 1.13 1994/06/24 11:58:17 ceriel Exp $ */
14 long tv_sec; /* seconds */
15 long tv_usec; /* and microseconds */
19 int tz_minuteswest; /* minutes west of Greenwich */
20 int tz_dsttime; /* type of dst correction */
23 int _gettimeofday(struct timeval *tp, struct timezone *tzp);
25 #elif !defined(_POSIX_SOURCE) && !defined(__USG)
26 #if !defined(_MINIX) /* MINIX has no ftime() */
29 unsigned short millitm;
33 void _ftime(struct timeb *bp);
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.
46 static char ntstr[TZ_LEN + 1] = "GMT"; /* string for normal time */
47 static char dststr[TZ_LEN + 1] = "GDT"; /* string for daylight saving */
50 long _dst_off = 60 * 60;
52 char *_tzname[2] = {ntstr, dststr};
54 #if defined(__USG) || defined(_POSIX_SOURCE)
55 char *tzname[2] = {ntstr, dststr};
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 };
70 const char *_days[] = {
71 "Sunday", "Monday", "Tuesday", "Wednesday",
72 "Thursday", "Friday", "Saturday"
75 const char *_months[] = {
76 "January", "February", "March",
77 "April", "May", "June",
78 "July", "August", "September",
79 "October", "November", "December"
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 }
87 #if !defined(_POSIX_SOURCE) && !defined(__USG)
92 static int usetable = 1;
94 typedef struct table {
97 const long zoneoffset;
100 #define HOUR(x) ((x) * 60*60)
102 static TABLE TimezoneTable[] = {
103 {"GMT", 0, HOUR(0) }, /* Greenwich Mean */
104 {"BST", 60*60, HOUR(0) }, /* British Summer */
105 {"WAT", 0, HOUR(1) }, /* West Africa */
106 {"AT", 0, HOUR(2) }, /* Azores */
107 {"BST", 0, HOUR(3) }, /* Brazil Standard */
108 {"NFT", 0, HOUR(3.5) }, /* Newfoundland */
109 {"NDT", 60*60, HOUR(3.5) }, /* Newfoundland Daylight */
110 {"AST", 0, HOUR(4) }, /* Atlantic Standard */
111 {"ADT", 60*60, HOUR(4) }, /* Atlantic Daylight */
112 {"EST", 0, HOUR(5) }, /* Eastern Standard */
113 {"EDT", 60*60, HOUR(5) }, /* Eastern Daylight */
114 {"CST", 0, HOUR(6) }, /* Central Standard */
115 {"CDT", 60*60, HOUR(6) }, /* Central Daylight */
116 {"MST", 0, HOUR(7) }, /* Mountain Standard */
117 {"MDT", 60*60, HOUR(7) }, /* Mountain Daylight */
118 {"PST", 0, HOUR(8) }, /* Pacific Standard */
119 {"PDT", 60*60, HOUR(8) }, /* Pacific Daylight */
120 {"YST", 0, HOUR(9) }, /* Yukon Standard */
121 {"YDT", 60*60, HOUR(9) }, /* Yukon Daylight */
122 {"HST", 0, HOUR(10) }, /* Hawaii Standard */
123 {"HDT", 60*60, HOUR(10) }, /* Hawaii Daylight */
124 {"NT", 0, HOUR(11) }, /* Nome */
125 {"IDLW", 0, HOUR(12) }, /* International Date Line West */
126 {"MET", 0, -HOUR(1) }, /* Middle European */
127 {"MDT", 60*60, -HOUR(1) }, /* Middle European Summer */
128 {"EET", 0, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
129 {"BT", 0, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
130 {"IT", 0, -HOUR(3.5) }, /* Iran */
131 {"ZP4", 0, -HOUR(4) }, /* USSR Zone 3 */
132 {"ZP5", 0, -HOUR(5) }, /* USSR Zone 4 */
133 {"IST", 0, -HOUR(5.5) }, /* Indian Standard */
134 {"ZP6", 0, -HOUR(6) }, /* USSR Zone 5 */
135 {"NST", 0, -HOUR(6.5) }, /* North Sumatra */
136 {"SST", 0, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
137 {"WAST", 0, -HOUR(7) }, /* West Australian Standard */
138 {"WADT", 60*60, -HOUR(7) }, /* West Australian Daylight */
139 {"JT", 0, -HOUR(7.5) }, /* Java (3pm in Cronusland!) */
140 {"CCT", 0, -HOUR(8) }, /* China Coast, USSR Zone 7 */
141 {"JST", 0, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
142 {"CAST", 0, -HOUR(9.5) }, /* Central Australian Standard */
143 {"CADT", 60*60, -HOUR(9.5) }, /* Central Australian Daylight */
144 {"EAST", 0, -HOUR(10) }, /* Eastern Australian Standard */
145 {"EADT", 60*60, -HOUR(10) }, /* Eastern Australian Daylight */
146 {"NZT", 0, -HOUR(12) }, /* New Zealand */
147 {"NZDT", 60*60, -HOUR(12) }, /* New Zealand Daylight */
152 * The function ZoneFromTable() searches the table for the current
153 * timezone. It saves the last one found in ntstr or dststr, depending on
154 * wheter the name is for daylight-saving-time or not.
155 * Both ntstr and dststr are TZ_LEN + 1 chars.
158 ZoneFromTable(long timezone)
160 register TABLE *tptr = TimezoneTable;
162 while (tptr->tz_name != NULL) {
163 if (tptr->zoneoffset == timezone) {
164 if (tptr->daylight == 0) {
165 strncpy(ntstr,tptr->tz_name, TZ_LEN);
166 ntstr[TZ_LEN] = '\0';
168 strncpy(dststr,tptr->tz_name, TZ_LEN);
169 dststr[TZ_LEN] = '\0';
175 #endif /* USE_TABLE */
178 parseZoneName(register char *buf, register const char *p)
182 if (*p == ':') return NULL;
183 while (*p && !isdigit(*p) && *p != ',' && *p != '-' && *p != '+') {
189 if (n < 3) return NULL; /* error */
195 parseTime(register long *tm, const char *p, register struct dsttype *dst)
198 register const char *q = p;
199 char ds_type = (dst ? dst->ds_type : '\0');
201 if (dst) dst->ds_type = 'U';
204 while(*p >= '0' && *p <= '9') {
205 n = 10 * n + (*p++ - '0');
207 if (q == p) return NULL; /* "The hour shall be required" */
208 if (n < 0 || n >= 24) return NULL;
213 while(*p >= '0' && *p <= '9') {
214 n = 10 * n + (*p++ - '0');
216 if (q == p) return NULL; /* format error */
217 if (n < 0 || n >= 60) return NULL;
222 while(*p >= '0' && *p <= '9') {
223 n = 10 * n + (*p++ - '0');
225 if (q == p) return NULL; /* format error */
226 if (n < 0 || n >= 60) return NULL;
231 dst->ds_type = ds_type;
238 parseDate(register char *buf, register const char *p, struct dsttype *dstinfo)
240 register const char *q;
243 const int bnds[3][2] = { { 1, 12 },
256 while(*p >= '0' && *p <= '9') {
257 n = 10 * n + (*p - '0');
260 if (q == p) return NULL; /* format error */
261 if (n < (ds_type == 'J') || n > 365) return NULL;
262 dstinfo->ds_type = ds_type;
263 dstinfo->ds_date[0] = n;
271 while(*p >= '0' && *p <= '9') {
272 n = 10 * n + (*p - '0');
275 if (q == p) return NULL; /* format error */
276 if (n < bnds[cnt][0] || n > bnds[cnt][1]) return NULL;
277 dstinfo->ds_date[cnt] = n;
279 } while (cnt < 3 && *p == '.');
280 if (cnt != 3) return NULL;
282 dstinfo->ds_type = ds_type;
287 parseRule(register char *buf, register const char *p)
290 register const char *q;
292 if (!(p = parseDate(buf, p, &dststart))) return NULL;
296 if (!(p = parseTime(&tim, p, &dststart))) return NULL;
297 while( p != q) *buf++ = *q++;
299 if (*p != ',') return NULL;
301 if (!(p = parseDate(buf, p, &dstend))) return NULL;
305 if (!(p = parseTime(&tim, p, &dstend))) return NULL;
306 while(*buf++ = *q++);
312 /* The following routine parses timezone information in POSIX-format. For
313 * the requirements, see IEEE Std 1003.1-1988 section 8.1.1.
314 * The function returns as soon as it spots an error.
317 parseTZ(const char *p)
319 long tz, dst = 60 * 60, sign = 1;
320 static char lastTZ[2 * RULE_LEN];
321 static char buffer[RULE_LEN];
330 * According to POSIX, this is implementation defined.
331 * Since it depends on the particular operating system, we
337 if (!strcmp(lastTZ, p)) return; /* nothing changed */
341 dststart.ds_type = 'U';
342 dststart.ds_sec = 2 * 60 * 60;
343 dstend.ds_type = 'U';
344 dstend.ds_sec = 2 * 60 * 60;
346 if (strlen(p) > 2 * RULE_LEN) return;
349 if (!(p = parseZoneName(buffer, p))) return;
354 } else if (*p == '+') p++;
356 if (!(p = parseTime(&tz, p, NULL))) return;
359 strncpy(_tzname[0], buffer, TZ_LEN);
361 if (!(_daylight = (*p != '\0'))) return;
364 if (!(p = parseZoneName(buffer, p))) return;
365 strncpy(_tzname[1], buffer, TZ_LEN);
368 if (*p && (*p != ','))
369 if (!(p = parseTime(&dst, p, NULL))) return;
370 _dst_off = dst; /* dst was initialized to 1 hour */
372 if (*p != ',') return;
374 if (strlen(p) > RULE_LEN) return;
375 if (!(p = parseRule(buffer, p))) return;
382 #if defined(__BSD4_2)
387 _gettimeofday(&tv, &tz);
388 _daylight = tz.tz_dsttime;
389 _timezone = tz.tz_minuteswest * 60L;
391 #elif !defined(_POSIX_SOURCE) && !defined(__USG)
393 #if !defined(_MINIX) /* MINIX has no ftime() */
397 _timezone = tim.timezone * 60L;
398 _daylight = tim.dstflag;
401 #endif /* !_POSIX_SOURCE && !__USG */
403 parseTZ(getenv("TZ")); /* should go inside #if */
405 #if defined(__USG) || defined(_POSIX_SOURCE)
406 tzname[0] = _tzname[0];
407 tzname[1] = _tzname[1];
409 timezone = _timezone;
410 daylight = _daylight;
412 #endif /* __USG || _POSIX_SOURCE */
416 last_sunday(register int day, register struct tm *timep)
418 int first = FIRSTSUNDAY(timep);
420 if (day >= 58 && LEAPYEAR(YEAR0 + timep->tm_year)) day++;
421 if (day < first) return first;
422 return day - (day - first) % 7;
426 date_of(register struct dsttype *dst, struct tm *timep)
428 int leap = LEAPYEAR(YEAR0 + timep->tm_year);
429 int firstday, tmpday;
430 register int day, month;
432 if (dst->ds_type != 'M') {
433 return dst->ds_date[0] -
436 && dst->ds_date[0] < 58);
440 while (month < dst->ds_date[0]) {
441 day += _ytab[leap][month - 1];
444 firstday = (day + FIRSTDAYOF(timep)) % 7;
446 day += (dst->ds_date[2] - firstday + 7) % 7
447 + 7 * (dst->ds_date[1] - 1);
448 if (day >= tmpday + _ytab[leap][month]) day -= 7;
453 * The default dst transitions are those for Western Europe (except Great
457 _dstget(register struct tm *timep)
459 int begindst, enddst;
460 register struct dsttype *dsts = &dststart, *dste = &dstend;
466 timep->tm_isdst = _daylight;
467 if (!_daylight) return 0;
469 if (dsts->ds_type != 'U')
470 begindst = date_of(dsts, timep);
471 else begindst = last_sunday(89, timep); /* last Sun before Apr */
472 if (dste->ds_type != 'U')
473 enddst = date_of(dste, timep);
474 else enddst = last_sunday(272, timep); /* last Sun in Sep */
476 /* assume begindst != enddst (otherwise it would be no use) */
477 if (begindst < enddst) { /* northern hemisphere */
478 if (timep->tm_yday > begindst && timep->tm_yday < enddst)
480 } else { /* southern hemisphere */
481 if (timep->tm_yday > begindst || timep->tm_yday < enddst)
486 && (timep->tm_yday == begindst || timep->tm_yday == enddst)) {
487 long dsttranssec; /* transition when day is this old */
490 if (timep->tm_yday == begindst)
491 dsttranssec = dsts->ds_sec;
492 else dsttranssec = dste->ds_sec;
493 cursec = ((timep->tm_hour * 60) + timep->tm_min) * 60L
496 if ((timep->tm_yday == begindst && cursec >= dsttranssec)
497 || (timep->tm_yday == enddst && cursec < dsttranssec))
501 if (usetable) ZoneFromTable(_timezone);
503 if (do_dst) return _dst_off;