seconds.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. /*
  10. * seconds absolute_date ... - convert absolute_date to seconds since epoch
  11. */
  12. #include <u.h>
  13. #include <libc.h>
  14. #include <ctype.h>
  15. typedef uint32_t Time;
  16. enum {
  17. AM, PM, HR24,
  18. /* token types */
  19. Month = 1,
  20. Year,
  21. Day,
  22. Timetok,
  23. Tz,
  24. Dtz,
  25. Ignore,
  26. Ampm,
  27. Maxtok = 6, /* only this many chars are stored in datetktbl */
  28. Maxdateflds = 25,
  29. };
  30. /*
  31. * macros for squeezing values into low 7 bits of "value".
  32. * all timezones we care about are divisible by 10, and the largest value
  33. * (780) when divided is 78.
  34. */
  35. #define TOVAL(tp, v) ((tp)->value = (v) / 10)
  36. #define FROMVAL(tp) ((tp)->value * 10) /* uncompress */
  37. /* keep this struct small since we have an array of them */
  38. typedef struct {
  39. char token[Maxtok];
  40. char type;
  41. uint8_t value;
  42. } Datetok;
  43. int dtok_numparsed;
  44. /* forwards */
  45. Datetok *datetoktype(char *s, int *bigvalp);
  46. static Datetok datetktbl[];
  47. static unsigned szdatetktbl;
  48. /* parse 1- or 2-digit number, advance *cpp past it */
  49. static int
  50. eatnum(char **cpp)
  51. {
  52. int c, x;
  53. char *cp;
  54. cp = *cpp;
  55. c = *cp;
  56. if (!isascii(c) || !isdigit(c))
  57. return -1;
  58. x = c - '0';
  59. c = *++cp;
  60. if (isascii(c) && isdigit(c)) {
  61. x = 10*x + c - '0';
  62. cp++;
  63. }
  64. *cpp = cp;
  65. return x;
  66. }
  67. /* return -1 on failure */
  68. int
  69. parsetime(char *time, Tm *tm)
  70. {
  71. tm->hour = eatnum(&time);
  72. if (tm->hour == -1 || *time++ != ':')
  73. return -1; /* only hour; too short */
  74. tm->min = eatnum(&time);
  75. if (tm->min == -1)
  76. return -1;
  77. if (*time++ != ':') {
  78. tm->sec = 0;
  79. return 0; /* no seconds; okay */
  80. }
  81. tm->sec = eatnum(&time);
  82. if (tm->sec == -1)
  83. return -1;
  84. /* this may be considered too strict. garbage at end of time? */
  85. return (*time == '\0' || (isascii(*time) && isspace(*time)))? 0: -1;
  86. }
  87. /*
  88. * try to parse pre-split timestr in fields as an absolute date
  89. */
  90. int
  91. tryabsdate(char **fields, int nf, Tm *now, Tm *tm)
  92. {
  93. int i, mer = HR24, bigval = -1;
  94. int32_t flg = 0, ty;
  95. char *p;
  96. Datetok *tp;
  97. now = localtime(time(0)); /* default to local time (zone) */
  98. tm->tzoff = now->tzoff;
  99. strncpy(tm->zone, now->zone, sizeof tm->zone - 1);
  100. tm->zone[sizeof tm->zone - 1] = '\0';
  101. tm->mday = tm->mon = tm->year = -1; /* mandatory */
  102. tm->hour = tm->min = tm->sec = 0;
  103. dtok_numparsed = 0;
  104. for (i = 0; i < nf; i++) {
  105. if (fields[i][0] == '\0')
  106. continue;
  107. tp = datetoktype(fields[i], &bigval);
  108. ty = (1L << tp->type) & ~(1L << Ignore);
  109. if (flg & ty)
  110. return -1; /* repeated type */
  111. flg |= ty;
  112. switch (tp->type) {
  113. case Year:
  114. tm->year = bigval;
  115. if (tm->year < 1970 || tm->year > 2106)
  116. return -1; /* can't represent in uint32_t */
  117. /* convert 4-digit year to 1900 origin */
  118. if (tm->year >= 1900)
  119. tm->year -= 1900;
  120. break;
  121. case Day:
  122. tm->mday = bigval;
  123. break;
  124. case Month:
  125. tm->mon = tp->value - 1; /* convert to zero-origin */
  126. break;
  127. case Timetok:
  128. if (parsetime(fields[i], tm) < 0)
  129. return -1;
  130. break;
  131. case Dtz:
  132. case Tz:
  133. tm->tzoff = FROMVAL(tp);
  134. /* tm2sec needs the name in upper case */
  135. strncpy(tm->zone, fields[i], sizeof tm->zone - 1);
  136. tm->zone[sizeof tm->zone - 1] = '\0';
  137. for (p = tm->zone; *p; p++)
  138. if (isascii(*p) && islower(*p))
  139. *p = toupper(*p);
  140. break;
  141. case Ignore:
  142. break;
  143. case Ampm:
  144. mer = tp->value;
  145. break;
  146. default:
  147. return -1; /* bad token type: CANTHAPPEN */
  148. }
  149. }
  150. if (tm->year == -1 || tm->mon == -1 || tm->mday == -1)
  151. return -1; /* missing component */
  152. if (mer == PM)
  153. tm->hour += 12;
  154. return 0;
  155. }
  156. int
  157. prsabsdate(char *timestr, Tm *now, Tm *tm)
  158. {
  159. int nf;
  160. char *fields[Maxdateflds];
  161. static char delims[] = "- \t\n/,";
  162. nf = gettokens(timestr, fields, nelem(fields), delims+1);
  163. if (nf > nelem(fields))
  164. return -1;
  165. if (tryabsdate(fields, nf, now, tm) < 0) {
  166. char *p = timestr;
  167. /*
  168. * could be a DEC-date; glue it all back together, split it
  169. * with dash as a delimiter and try again. Yes, this is a
  170. * hack, but so are DEC-dates.
  171. */
  172. while (--nf > 0) {
  173. while (*p++ != '\0')
  174. ;
  175. p[-1] = ' ';
  176. }
  177. nf = gettokens(timestr, fields, nelem(fields), delims);
  178. if (nf > nelem(fields) || tryabsdate(fields, nf, now, tm) < 0)
  179. return -1;
  180. }
  181. return 0;
  182. }
  183. int
  184. validtm(Tm *tm)
  185. {
  186. if (tm->year < 0 || tm->mon < 0 || tm->mon > 11 ||
  187. tm->mday < 1 || tm->hour < 0 || tm->hour >= 24 ||
  188. tm->min < 0 || tm->min > 59 ||
  189. tm->sec < 0 || tm->sec > 61) /* allow 2 leap seconds */
  190. return 0;
  191. return 1;
  192. }
  193. Time
  194. seconds(char *timestr)
  195. {
  196. Tm date;
  197. memset(&date, 0, sizeof date);
  198. if (prsabsdate(timestr, localtime(time(0)), &date) < 0)
  199. return -1;
  200. return validtm(&date)? tm2sec(&date): -1;
  201. }
  202. int
  203. convert(char *timestr)
  204. {
  205. char *copy;
  206. Time tstime;
  207. copy = strdup(timestr);
  208. if (copy == nil)
  209. sysfatal("out of memory");
  210. tstime = seconds(copy);
  211. free(copy);
  212. if (tstime == -1) {
  213. fprint(2, "%s: `%s' not a valid date\n", argv0, timestr);
  214. return 1;
  215. }
  216. print("%lu\n", tstime);
  217. return 0;
  218. }
  219. static void
  220. usage(void)
  221. {
  222. fprint(2, "usage: %s date-time ...\n", argv0);
  223. exits("usage");
  224. }
  225. void
  226. main(int argc, char **argv)
  227. {
  228. int i, sts;
  229. sts = 0;
  230. ARGBEGIN{
  231. default:
  232. usage();
  233. }ARGEND
  234. if (argc == 0)
  235. usage();
  236. for (i = 0; i < argc; i++)
  237. sts |= convert(argv[i]);
  238. exits(sts != 0? "bad": 0);
  239. }
  240. /*
  241. * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
  242. * is WAY faster than the generic bsearch().
  243. */
  244. Datetok *
  245. datebsearch(char *key, Datetok *base, unsigned nel)
  246. {
  247. int cmp;
  248. Datetok *last = base + nel - 1, *pos;
  249. while (last >= base) {
  250. pos = base + ((last - base) >> 1);
  251. cmp = key[0] - pos->token[0];
  252. if (cmp == 0) {
  253. cmp = strncmp(key, pos->token, Maxtok);
  254. if (cmp == 0)
  255. return pos;
  256. }
  257. if (cmp < 0)
  258. last = pos - 1;
  259. else
  260. base = pos + 1;
  261. }
  262. return 0;
  263. }
  264. Datetok *
  265. datetoktype(char *s, int *bigvalp)
  266. {
  267. char *cp = s;
  268. char c = *cp;
  269. static Datetok t;
  270. Datetok *tp = &t;
  271. if (isascii(c) && isdigit(c)) {
  272. int len = strlen(cp);
  273. if (len > 3 && (cp[1] == ':' || cp[2] == ':'))
  274. tp->type = Timetok;
  275. else {
  276. if (bigvalp != nil)
  277. *bigvalp = atoi(cp); /* won't fit in tp->value */
  278. if (len == 4)
  279. tp->type = Year;
  280. else if (++dtok_numparsed == 1)
  281. tp->type = Day;
  282. else
  283. tp->type = Year;
  284. }
  285. } else if (c == '-' || c == '+') {
  286. int val = atoi(cp + 1);
  287. int hr = val / 100;
  288. int min = val % 100;
  289. val = hr*60 + min;
  290. TOVAL(tp, c == '-'? -val: val);
  291. tp->type = Tz;
  292. } else {
  293. char lowtoken[Maxtok+1];
  294. char *ltp = lowtoken, *endltp = lowtoken+Maxtok;
  295. /* copy to lowtoken to avoid modifying s */
  296. while ((c = *cp++) != '\0' && ltp < endltp)
  297. *ltp++ = (isascii(c) && isupper(c)? tolower(c): c);
  298. *ltp = '\0';
  299. tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
  300. if (tp == nil) {
  301. tp = &t;
  302. tp->type = Ignore;
  303. }
  304. }
  305. return tp;
  306. }
  307. /*
  308. * to keep this table reasonably small, we divide the lexval for Tz and Dtz
  309. * entries by 10 and truncate the text field at MAXTOKLEN characters.
  310. * the text field is not guaranteed to be NUL-terminated.
  311. */
  312. static Datetok datetktbl[] = {
  313. /* text token lexval */
  314. {"acsst", Dtz, 63}, /* Cent. Australia */
  315. {"acst", Tz, 57}, /* Cent. Australia */
  316. {"adt", Dtz, -18}, /* Atlantic Daylight Time */
  317. {"aesst", Dtz, 66}, /* E. Australia */
  318. {"aest", Tz, 60}, /* Australia Eastern Std Time */
  319. {"ahst", Tz, 60}, /* Alaska-Hawaii Std Time */
  320. {"am", Ampm, AM},
  321. {"apr", Month, 4},
  322. {"april", Month, 4},
  323. {"ast", Tz, -24}, /* Atlantic Std Time (Canada) */
  324. {"at", Ignore, 0}, /* "at" (throwaway) */
  325. {"aug", Month, 8},
  326. {"august", Month, 8},
  327. {"awsst", Dtz, 54}, /* W. Australia */
  328. {"awst", Tz, 48}, /* W. Australia */
  329. {"bst", Tz, 6}, /* British Summer Time */
  330. {"bt", Tz, 18}, /* Baghdad Time */
  331. {"cadt", Dtz, 63}, /* Central Australian DST */
  332. {"cast", Tz, 57}, /* Central Australian ST */
  333. {"cat", Tz, -60}, /* Central Alaska Time */
  334. {"cct", Tz, 48}, /* China Coast */
  335. {"cdt", Dtz, -30}, /* Central Daylight Time */
  336. {"cet", Tz, 6}, /* Central European Time */
  337. {"cetdst", Dtz, 12}, /* Central European Dayl.Time */
  338. {"cst", Tz, -36}, /* Central Standard Time */
  339. {"dec", Month, 12},
  340. {"decemb", Month, 12},
  341. {"dnt", Tz, 6}, /* Dansk Normal Tid */
  342. {"dst", Ignore, 0},
  343. {"east", Tz, -60}, /* East Australian Std Time */
  344. {"edt", Dtz, -24}, /* Eastern Daylight Time */
  345. {"eet", Tz, 12}, /* East. Europe, USSR Zone 1 */
  346. {"eetdst", Dtz, 18}, /* Eastern Europe */
  347. {"est", Tz, -30}, /* Eastern Standard Time */
  348. {"feb", Month, 2},
  349. {"februa", Month, 2},
  350. {"fri", Ignore, 5},
  351. {"friday", Ignore, 5},
  352. {"fst", Tz, 6}, /* French Summer Time */
  353. {"fwt", Dtz, 12}, /* French Winter Time */
  354. {"gmt", Tz, 0}, /* Greenwish Mean Time */
  355. {"gst", Tz, 60}, /* Guam Std Time, USSR Zone 9 */
  356. {"hdt", Dtz, -54}, /* Hawaii/Alaska */
  357. {"hmt", Dtz, 18}, /* Hellas ? ? */
  358. {"hst", Tz, -60}, /* Hawaii Std Time */
  359. {"idle", Tz, 72}, /* Intl. Date Line, East */
  360. {"idlw", Tz, -72}, /* Intl. Date Line, West */
  361. {"ist", Tz, 12}, /* Israel */
  362. {"it", Tz, 22}, /* Iran Time */
  363. {"jan", Month, 1},
  364. {"januar", Month, 1},
  365. {"jst", Tz, 54}, /* Japan Std Time,USSR Zone 8 */
  366. {"jt", Tz, 45}, /* Java Time */
  367. {"jul", Month, 7},
  368. {"july", Month, 7},
  369. {"jun", Month, 6},
  370. {"june", Month, 6},
  371. {"kst", Tz, 54}, /* Korea Standard Time */
  372. {"ligt", Tz, 60}, /* From Melbourne, Australia */
  373. {"mar", Month, 3},
  374. {"march", Month, 3},
  375. {"may", Month, 5},
  376. {"mdt", Dtz, -36}, /* Mountain Daylight Time */
  377. {"mest", Dtz, 12}, /* Middle Europe Summer Time */
  378. {"met", Tz, 6}, /* Middle Europe Time */
  379. {"metdst", Dtz, 12}, /* Middle Europe Daylight Time*/
  380. {"mewt", Tz, 6}, /* Middle Europe Winter Time */
  381. {"mez", Tz, 6}, /* Middle Europe Zone */
  382. {"mon", Ignore, 1},
  383. {"monday", Ignore, 1},
  384. {"mst", Tz, -42}, /* Mountain Standard Time */
  385. {"mt", Tz, 51}, /* Moluccas Time */
  386. {"ndt", Dtz, -15}, /* Nfld. Daylight Time */
  387. {"nft", Tz, -21}, /* Newfoundland Standard Time */
  388. {"nor", Tz, 6}, /* Norway Standard Time */
  389. {"nov", Month, 11},
  390. {"novemb", Month, 11},
  391. {"nst", Tz, -21}, /* Nfld. Standard Time */
  392. {"nt", Tz, -66}, /* Nome Time */
  393. {"nzdt", Dtz, 78}, /* New Zealand Daylight Time */
  394. {"nzst", Tz, 72}, /* New Zealand Standard Time */
  395. {"nzt", Tz, 72}, /* New Zealand Time */
  396. {"oct", Month, 10},
  397. {"octobe", Month, 10},
  398. {"on", Ignore, 0}, /* "on" (throwaway) */
  399. {"pdt", Dtz, -42}, /* Pacific Daylight Time */
  400. {"pm", Ampm, PM},
  401. {"pst", Tz, -48}, /* Pacific Standard Time */
  402. {"sadt", Dtz, 63}, /* S. Australian Dayl. Time */
  403. {"sast", Tz, 57}, /* South Australian Std Time */
  404. {"sat", Ignore, 6},
  405. {"saturd", Ignore, 6},
  406. {"sep", Month, 9},
  407. {"sept", Month, 9},
  408. {"septem", Month, 9},
  409. {"set", Tz, -6}, /* Seychelles Time ?? */
  410. {"sst", Dtz, 12}, /* Swedish Summer Time */
  411. {"sun", Ignore, 0},
  412. {"sunday", Ignore, 0},
  413. {"swt", Tz, 6}, /* Swedish Winter Time */
  414. {"thu", Ignore, 4},
  415. {"thur", Ignore, 4},
  416. {"thurs", Ignore, 4},
  417. {"thursd", Ignore, 4},
  418. {"tue", Ignore, 2},
  419. {"tues", Ignore, 2},
  420. {"tuesda", Ignore, 2},
  421. {"ut", Tz, 0},
  422. {"utc", Tz, 0},
  423. {"wadt", Dtz, 48}, /* West Australian DST */
  424. {"wast", Tz, 42}, /* West Australian Std Time */
  425. {"wat", Tz, -6}, /* West Africa Time */
  426. {"wdt", Dtz, 54}, /* West Australian DST */
  427. {"wed", Ignore, 3},
  428. {"wednes", Ignore, 3},
  429. {"weds", Ignore, 3},
  430. {"wet", Tz, 0}, /* Western Europe */
  431. {"wetdst", Dtz, 6}, /* Western Europe */
  432. {"wst", Tz, 48}, /* West Australian Std Time */
  433. {"ydt", Dtz, -48}, /* Yukon Daylight Time */
  434. {"yst", Tz, -54}, /* Yukon Standard Time */
  435. {"zp4", Tz, -24}, /* GMT +4 hours. */
  436. {"zp5", Tz, -30}, /* GMT +5 hours. */
  437. {"zp6", Tz, -36}, /* GMT +6 hours. */
  438. };
  439. static unsigned szdatetktbl = nelem(datetktbl);