2 * seconds absolute_date ... - convert absolute_date to seconds since epoch
24 Maxtok = 6, /* only this many chars are stored in datetktbl */
29 * macros for squeezing values into low 7 bits of "value".
30 * all timezones we care about are divisible by 10, and the largest value
31 * (780) when divided is 78.
33 #define TOVAL(tp, v) ((tp)->value = (v) / 10)
34 #define FROMVAL(tp) ((tp)->value * 10) /* uncompress */
36 /* keep this struct small since we have an array of them */
46 Datetok *datetoktype(char *s, int *bigvalp);
48 static Datetok datetktbl[];
49 static unsigned szdatetktbl;
51 /* parse 1- or 2-digit number, advance *cpp past it */
60 if (!isascii(c) || !isdigit(c))
65 if (isascii(c) && isdigit(c)) {
73 /* return -1 on failure */
75 parsetime(char *time, Tm *tm)
77 tm->hour = eatnum(&time);
78 if (tm->hour == -1 || *time++ != ':')
79 return -1; /* only hour; too short */
81 tm->min = eatnum(&time);
86 return 0; /* no seconds; okay */
89 tm->sec = eatnum(&time);
93 /* this may be considered too strict. garbage at end of time? */
94 return *time == '\0' || isascii(*time) && isspace(*time)? 0: -1;
98 * try to parse pre-split timestr in fields as an absolute date
101 tryabsdate(char **fields, int nf, Tm *now, Tm *tm)
103 int i, mer = HR24, bigval = -1;
107 now = localtime(time(0)); /* default to local time (zone) */
108 tm->tzoff = now->tzoff;
109 strncpy(tm->zone, now->zone, sizeof tm->zone);
111 tm->mday = tm->mon = tm->year = -1; /* mandatory */
112 tm->hour = tm->min = tm->sec = 0;
115 for (i = 0; i < nf; i++) {
116 if (fields[i][0] == '\0')
118 tp = datetoktype(fields[i], &bigval);
119 ty = (1L << tp->type) & ~(1L << Ignore);
121 return -1; /* repeated type */
126 if (tm->year < 1970 || tm->year > 2106)
127 return -1; /* can't represent in ulong */
128 /* convert 4-digit year to 1900 origin */
129 if (tm->year >= 1900)
136 tm->mon = tp->value - 1; /* convert to zero-origin */
139 if (parsetime(fields[i], tm) < 0)
144 /* tm2sec mangles timezones, so we do our own handling */
145 tm->tzoff = FROMVAL(tp);
146 snprint(tm->zone, sizeof(tm->zone), "GMT");
154 return -1; /* bad token type: CANTHAPPEN */
157 if (tm->year == -1 || tm->mon == -1 || tm->mday == -1)
158 return -1; /* missing component */
165 prsabsdate(char *timestr, Tm *now, Tm *tm)
168 char *fields[Maxdateflds];
169 static char delims[] = "- \t\n/,";
171 nf = gettokens(timestr, fields, nelem(fields), delims+1);
172 if (nf > nelem(fields))
174 if (tryabsdate(fields, nf, now, tm) < 0) {
178 * could be a DEC-date; glue it all back together, split it
179 * with dash as a delimiter and try again. Yes, this is a
180 * hack, but so are DEC-dates.
187 nf = gettokens(timestr, fields, nelem(fields), delims);
188 if (nf > nelem(fields) || tryabsdate(fields, nf, now, tm) < 0)
197 if (tm->year < 0 || tm->mon < 0 || tm->mon > 11 ||
198 tm->mday < 1 || tm->hour < 0 || tm->hour >= 24 ||
199 tm->min < 0 || tm->min > 59 ||
200 tm->sec < 0 || tm->sec > 61) /* allow 2 leap seconds */
206 seconds(char *timestr)
210 memset(&date, 0, sizeof date);
211 if (prsabsdate(timestr, localtime(time(0)), &date) < 0)
213 return validtm(&date)? tm2sec(&date) - 60*date.tzoff: -1;
217 convert(char *timestr)
222 copy = strdup(timestr);
224 sysfatal("out of memory");
225 tstime = seconds(copy);
228 fprint(2, "%s: `%s' not a valid date\n", argv0, timestr);
231 print("%lud\n", tstime);
238 fprint(2, "usage: %s date-time ...\n", argv0);
243 main(int argc, char **argv)
254 for (i = 0; i < argc; i++)
255 sts |= convert(argv[i]);
256 exits(sts != 0? "bad": 0);
260 * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
261 * is WAY faster than the generic bsearch().
264 datebsearch(char *key, Datetok *base, unsigned nel)
267 Datetok *last = base + nel - 1, *pos;
269 while (last >= base) {
270 pos = base + ((last - base) >> 1);
271 cmp = key[0] - pos->token[0];
273 cmp = strncmp(key, pos->token, Maxtok);
286 datetoktype(char *s, int *bigvalp)
293 if (isascii(c) && isdigit(c)) {
294 int len = strlen(cp);
296 if (len > 3 && (cp[1] == ':' || cp[2] == ':'))
300 *bigvalp = atoi(cp); /* won't fit in tp->value */
303 else if (++dtok_numparsed == 1)
308 } else if (c == '-' || c == '+') {
309 int val = atoi(cp + 1);
314 TOVAL(tp, c == '-'? -val: val);
317 char lowtoken[Maxtok+1];
318 char *ltp = lowtoken, *endltp = lowtoken+Maxtok;
320 /* copy to lowtoken to avoid modifying s */
321 while ((c = *cp++) != '\0' && ltp < endltp)
322 *ltp++ = (isascii(c) && isupper(c)? tolower(c): c);
324 tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
335 * to keep this table reasonably small, we divide the lexval for Tz and Dtz
336 * entries by 10 and truncate the text field at MAXTOKLEN characters.
337 * the text field is not guaranteed to be NUL-terminated.
339 static Datetok datetktbl[] = {
340 /* text token lexval */
341 "acsst", Dtz, 63, /* Cent. Australia */
342 "acst", Tz, 57, /* Cent. Australia */
343 "adt", Dtz, -18, /* Atlantic Daylight Time */
344 "aesst", Dtz, 66, /* E. Australia */
345 "aest", Tz, 60, /* Australia Eastern Std Time */
346 "ahst", Tz, 60, /* Alaska-Hawaii Std Time */
350 "ast", Tz, -24, /* Atlantic Std Time (Canada) */
351 "at", Ignore, 0, /* "at" (throwaway) */
354 "awsst", Dtz, 54, /* W. Australia */
355 "awst", Tz, 48, /* W. Australia */
356 "bst", Tz, 6, /* British Summer Time */
357 "bt", Tz, 18, /* Baghdad Time */
358 "cadt", Dtz, 63, /* Central Australian DST */
359 "cast", Tz, 57, /* Central Australian ST */
360 "cat", Tz, -60, /* Central Alaska Time */
361 "cct", Tz, 48, /* China Coast */
362 "cdt", Dtz, -30, /* Central Daylight Time */
363 "cet", Tz, 6, /* Central European Time */
364 "cetdst", Dtz, 12, /* Central European Dayl.Time */
365 "cst", Tz, -36, /* Central Standard Time */
368 "dnt", Tz, 6, /* Dansk Normal Tid */
370 "east", Tz, -60, /* East Australian Std Time */
371 "edt", Dtz, -24, /* Eastern Daylight Time */
372 "eet", Tz, 12, /* East. Europe, USSR Zone 1 */
373 "eetdst", Dtz, 18, /* Eastern Europe */
374 "est", Tz, -30, /* Eastern Standard Time */
379 "fst", Tz, 6, /* French Summer Time */
380 "fwt", Dtz, 12, /* French Winter Time */
381 "gmt", Tz, 0, /* Greenwish Mean Time */
382 "gst", Tz, 60, /* Guam Std Time, USSR Zone 9 */
383 "hdt", Dtz, -54, /* Hawaii/Alaska */
384 "hmt", Dtz, 18, /* Hellas ? ? */
385 "hst", Tz, -60, /* Hawaii Std Time */
386 "idle", Tz, 72, /* Intl. Date Line, East */
387 "idlw", Tz, -72, /* Intl. Date Line, West */
388 "ist", Tz, 12, /* Israel */
389 "it", Tz, 22, /* Iran Time */
392 "jst", Tz, 54, /* Japan Std Time,USSR Zone 8 */
393 "jt", Tz, 45, /* Java Time */
398 "kst", Tz, 54, /* Korea Standard Time */
399 "ligt", Tz, 60, /* From Melbourne, Australia */
403 "mdt", Dtz, -36, /* Mountain Daylight Time */
404 "mest", Dtz, 12, /* Middle Europe Summer Time */
405 "met", Tz, 6, /* Middle Europe Time */
406 "metdst", Dtz, 12, /* Middle Europe Daylight Time*/
407 "mewt", Tz, 6, /* Middle Europe Winter Time */
408 "mez", Tz, 6, /* Middle Europe Zone */
411 "mst", Tz, -42, /* Mountain Standard Time */
412 "mt", Tz, 51, /* Moluccas Time */
413 "ndt", Dtz, -15, /* Nfld. Daylight Time */
414 "nft", Tz, -21, /* Newfoundland Standard Time */
415 "nor", Tz, 6, /* Norway Standard Time */
418 "nst", Tz, -21, /* Nfld. Standard Time */
419 "nt", Tz, -66, /* Nome Time */
420 "nzdt", Dtz, 78, /* New Zealand Daylight Time */
421 "nzst", Tz, 72, /* New Zealand Standard Time */
422 "nzt", Tz, 72, /* New Zealand Time */
425 "on", Ignore, 0, /* "on" (throwaway) */
426 "pdt", Dtz, -42, /* Pacific Daylight Time */
428 "pst", Tz, -48, /* Pacific Standard Time */
429 "sadt", Dtz, 63, /* S. Australian Dayl. Time */
430 "sast", Tz, 57, /* South Australian Std Time */
436 "set", Tz, -6, /* Seychelles Time ?? */
437 "sst", Dtz, 12, /* Swedish Summer Time */
440 "swt", Tz, 6, /* Swedish Winter Time */
450 "wadt", Dtz, 48, /* West Australian DST */
451 "wast", Tz, 42, /* West Australian Std Time */
452 "wat", Tz, -6, /* West Africa Time */
453 "wdt", Dtz, 54, /* West Australian DST */
457 "wet", Tz, 0, /* Western Europe */
458 "wetdst", Dtz, 6, /* Western Europe */
459 "wst", Tz, 48, /* West Australian Std Time */
460 "ydt", Dtz, -48, /* Yukon Daylight Time */
461 "yst", Tz, -54, /* Yukon Standard Time */
462 "zp4", Tz, -24, /* GMT +4 hours. */
463 "zp5", Tz, -30, /* GMT +5 hours. */
464 "zp6", Tz, -36, /* GMT +6 hours. */
466 static unsigned szdatetktbl = nelem(datetktbl);