archive_getdate.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038
  1. /*
  2. * This code is in the public domain and has no copyright.
  3. *
  4. * This is a plain C recursive-descent translation of an old
  5. * public-domain YACC grammar that has been used for parsing dates in
  6. * very many open-source projects.
  7. *
  8. * Since the original authors were generous enough to donate their
  9. * work to the public domain, I feel compelled to match their
  10. * generosity.
  11. *
  12. * Tim Kientzle, February 2009.
  13. */
  14. /*
  15. * Header comment from original getdate.y:
  16. */
  17. /*
  18. ** Originally written by Steven M. Bellovin <smb@research.att.com> while
  19. ** at the University of North Carolina at Chapel Hill. Later tweaked by
  20. ** a couple of people on Usenet. Completely overhauled by Rich $alz
  21. ** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
  22. **
  23. ** This grammar has 10 shift/reduce conflicts.
  24. **
  25. ** This code is in the public domain and has no copyright.
  26. */
  27. #ifdef __FreeBSD__
  28. #include <sys/cdefs.h>
  29. __FBSDID("$FreeBSD$");
  30. #endif
  31. #include <ctype.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <time.h>
  36. #define __LIBARCHIVE_BUILD 1
  37. #include "archive_getdate.h"
  38. /* Basic time units. */
  39. #define EPOCH 1970
  40. #define MINUTE (60L)
  41. #define HOUR (60L * MINUTE)
  42. #define DAY (24L * HOUR)
  43. /* Daylight-savings mode: on, off, or not yet known. */
  44. enum DSTMODE { DSTon, DSToff, DSTmaybe };
  45. /* Meridian: am or pm. */
  46. enum { tAM, tPM };
  47. /* Token types returned by nexttoken() */
  48. enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT,
  49. tUNUMBER, tZONE, tDST };
  50. struct token { int token; time_t value; };
  51. /*
  52. * Parser state.
  53. */
  54. struct gdstate {
  55. struct token *tokenp; /* Pointer to next token. */
  56. /* HaveXxxx counts how many of this kind of phrase we've seen;
  57. * it's a fatal error to have more than one time, zone, day,
  58. * or date phrase. */
  59. int HaveYear;
  60. int HaveMonth;
  61. int HaveDay;
  62. int HaveWeekDay; /* Day of week */
  63. int HaveTime; /* Hour/minute/second */
  64. int HaveZone; /* timezone and/or DST info */
  65. int HaveRel; /* time offset; we can have more than one */
  66. /* Absolute time values. */
  67. time_t Timezone; /* Seconds offset from GMT */
  68. time_t Day;
  69. time_t Hour;
  70. time_t Minutes;
  71. time_t Month;
  72. time_t Seconds;
  73. time_t Year;
  74. /* DST selection */
  75. enum DSTMODE DSTmode;
  76. /* Day of week accounting, e.g., "3rd Tuesday" */
  77. time_t DayOrdinal; /* "3" in "3rd Tuesday" */
  78. time_t DayNumber; /* "Tuesday" in "3rd Tuesday" */
  79. /* Relative time values: hour/day/week offsets are measured in
  80. * seconds, month/year are counted in months. */
  81. time_t RelMonth;
  82. time_t RelSeconds;
  83. };
  84. /*
  85. * A series of functions that recognize certain common time phrases.
  86. * Each function returns 1 if it managed to make sense of some of the
  87. * tokens, zero otherwise.
  88. */
  89. /*
  90. * hour:minute or hour:minute:second with optional AM, PM, or numeric
  91. * timezone offset
  92. */
  93. static int
  94. timephrase(struct gdstate *gds)
  95. {
  96. if (gds->tokenp[0].token == tUNUMBER
  97. && gds->tokenp[1].token == ':'
  98. && gds->tokenp[2].token == tUNUMBER
  99. && gds->tokenp[3].token == ':'
  100. && gds->tokenp[4].token == tUNUMBER) {
  101. /* "12:14:18" or "22:08:07" */
  102. ++gds->HaveTime;
  103. gds->Hour = gds->tokenp[0].value;
  104. gds->Minutes = gds->tokenp[2].value;
  105. gds->Seconds = gds->tokenp[4].value;
  106. gds->tokenp += 5;
  107. }
  108. else if (gds->tokenp[0].token == tUNUMBER
  109. && gds->tokenp[1].token == ':'
  110. && gds->tokenp[2].token == tUNUMBER) {
  111. /* "12:14" or "22:08" */
  112. ++gds->HaveTime;
  113. gds->Hour = gds->tokenp[0].value;
  114. gds->Minutes = gds->tokenp[2].value;
  115. gds->Seconds = 0;
  116. gds->tokenp += 3;
  117. }
  118. else if (gds->tokenp[0].token == tUNUMBER
  119. && gds->tokenp[1].token == tAMPM) {
  120. /* "7" is a time if it's followed by "am" or "pm" */
  121. ++gds->HaveTime;
  122. gds->Hour = gds->tokenp[0].value;
  123. gds->Minutes = gds->Seconds = 0;
  124. /* We'll handle the AM/PM below. */
  125. gds->tokenp += 1;
  126. } else {
  127. /* We can't handle this. */
  128. return 0;
  129. }
  130. if (gds->tokenp[0].token == tAMPM) {
  131. /* "7:12pm", "12:20:13am" */
  132. if (gds->Hour == 12)
  133. gds->Hour = 0;
  134. if (gds->tokenp[0].value == tPM)
  135. gds->Hour += 12;
  136. gds->tokenp += 1;
  137. }
  138. if (gds->tokenp[0].token == '+'
  139. && gds->tokenp[1].token == tUNUMBER) {
  140. /* "7:14+0700" */
  141. gds->HaveZone++;
  142. gds->DSTmode = DSToff;
  143. gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR
  144. + (gds->tokenp[1].value % 100) * MINUTE);
  145. gds->tokenp += 2;
  146. }
  147. if (gds->tokenp[0].token == '-'
  148. && gds->tokenp[1].token == tUNUMBER) {
  149. /* "19:14:12-0530" */
  150. gds->HaveZone++;
  151. gds->DSTmode = DSToff;
  152. gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR
  153. + (gds->tokenp[1].value % 100) * MINUTE);
  154. gds->tokenp += 2;
  155. }
  156. return 1;
  157. }
  158. /*
  159. * Timezone name, possibly including DST.
  160. */
  161. static int
  162. zonephrase(struct gdstate *gds)
  163. {
  164. if (gds->tokenp[0].token == tZONE
  165. && gds->tokenp[1].token == tDST) {
  166. gds->HaveZone++;
  167. gds->Timezone = gds->tokenp[0].value;
  168. gds->DSTmode = DSTon;
  169. gds->tokenp += 1;
  170. return 1;
  171. }
  172. if (gds->tokenp[0].token == tZONE) {
  173. gds->HaveZone++;
  174. gds->Timezone = gds->tokenp[0].value;
  175. gds->DSTmode = DSToff;
  176. gds->tokenp += 1;
  177. return 1;
  178. }
  179. if (gds->tokenp[0].token == tDAYZONE) {
  180. gds->HaveZone++;
  181. gds->Timezone = gds->tokenp[0].value;
  182. gds->DSTmode = DSTon;
  183. gds->tokenp += 1;
  184. return 1;
  185. }
  186. return 0;
  187. }
  188. /*
  189. * Year/month/day in various combinations.
  190. */
  191. static int
  192. datephrase(struct gdstate *gds)
  193. {
  194. if (gds->tokenp[0].token == tUNUMBER
  195. && gds->tokenp[1].token == '/'
  196. && gds->tokenp[2].token == tUNUMBER
  197. && gds->tokenp[3].token == '/'
  198. && gds->tokenp[4].token == tUNUMBER) {
  199. gds->HaveYear++;
  200. gds->HaveMonth++;
  201. gds->HaveDay++;
  202. if (gds->tokenp[0].value >= 13) {
  203. /* First number is big: 2004/01/29, 99/02/17 */
  204. gds->Year = gds->tokenp[0].value;
  205. gds->Month = gds->tokenp[2].value;
  206. gds->Day = gds->tokenp[4].value;
  207. } else if ((gds->tokenp[4].value >= 13)
  208. || (gds->tokenp[2].value >= 13)) {
  209. /* Last number is big: 01/07/98 */
  210. /* Middle number is big: 01/29/04 */
  211. gds->Month = gds->tokenp[0].value;
  212. gds->Day = gds->tokenp[2].value;
  213. gds->Year = gds->tokenp[4].value;
  214. } else {
  215. /* No significant clues: 02/03/04 */
  216. gds->Month = gds->tokenp[0].value;
  217. gds->Day = gds->tokenp[2].value;
  218. gds->Year = gds->tokenp[4].value;
  219. }
  220. gds->tokenp += 5;
  221. return 1;
  222. }
  223. if (gds->tokenp[0].token == tUNUMBER
  224. && gds->tokenp[1].token == '/'
  225. && gds->tokenp[2].token == tUNUMBER) {
  226. /* "1/15" */
  227. gds->HaveMonth++;
  228. gds->HaveDay++;
  229. gds->Month = gds->tokenp[0].value;
  230. gds->Day = gds->tokenp[2].value;
  231. gds->tokenp += 3;
  232. return 1;
  233. }
  234. if (gds->tokenp[0].token == tUNUMBER
  235. && gds->tokenp[1].token == '-'
  236. && gds->tokenp[2].token == tUNUMBER
  237. && gds->tokenp[3].token == '-'
  238. && gds->tokenp[4].token == tUNUMBER) {
  239. /* ISO 8601 format. yyyy-mm-dd. */
  240. gds->HaveYear++;
  241. gds->HaveMonth++;
  242. gds->HaveDay++;
  243. gds->Year = gds->tokenp[0].value;
  244. gds->Month = gds->tokenp[2].value;
  245. gds->Day = gds->tokenp[4].value;
  246. gds->tokenp += 5;
  247. return 1;
  248. }
  249. if (gds->tokenp[0].token == tUNUMBER
  250. && gds->tokenp[1].token == '-'
  251. && gds->tokenp[2].token == tMONTH
  252. && gds->tokenp[3].token == '-'
  253. && gds->tokenp[4].token == tUNUMBER) {
  254. gds->HaveYear++;
  255. gds->HaveMonth++;
  256. gds->HaveDay++;
  257. if (gds->tokenp[0].value > 31) {
  258. /* e.g. 1992-Jun-17 */
  259. gds->Year = gds->tokenp[0].value;
  260. gds->Month = gds->tokenp[2].value;
  261. gds->Day = gds->tokenp[4].value;
  262. } else {
  263. /* e.g. 17-JUN-1992. */
  264. gds->Day = gds->tokenp[0].value;
  265. gds->Month = gds->tokenp[2].value;
  266. gds->Year = gds->tokenp[4].value;
  267. }
  268. gds->tokenp += 5;
  269. return 1;
  270. }
  271. if (gds->tokenp[0].token == tMONTH
  272. && gds->tokenp[1].token == tUNUMBER
  273. && gds->tokenp[2].token == ','
  274. && gds->tokenp[3].token == tUNUMBER) {
  275. /* "June 17, 2001" */
  276. gds->HaveYear++;
  277. gds->HaveMonth++;
  278. gds->HaveDay++;
  279. gds->Month = gds->tokenp[0].value;
  280. gds->Day = gds->tokenp[1].value;
  281. gds->Year = gds->tokenp[3].value;
  282. gds->tokenp += 4;
  283. return 1;
  284. }
  285. if (gds->tokenp[0].token == tMONTH
  286. && gds->tokenp[1].token == tUNUMBER) {
  287. /* "May 3" */
  288. gds->HaveMonth++;
  289. gds->HaveDay++;
  290. gds->Month = gds->tokenp[0].value;
  291. gds->Day = gds->tokenp[1].value;
  292. gds->tokenp += 2;
  293. return 1;
  294. }
  295. if (gds->tokenp[0].token == tUNUMBER
  296. && gds->tokenp[1].token == tMONTH
  297. && gds->tokenp[2].token == tUNUMBER) {
  298. /* "12 Sept 1997" */
  299. gds->HaveYear++;
  300. gds->HaveMonth++;
  301. gds->HaveDay++;
  302. gds->Day = gds->tokenp[0].value;
  303. gds->Month = gds->tokenp[1].value;
  304. gds->Year = gds->tokenp[2].value;
  305. gds->tokenp += 3;
  306. return 1;
  307. }
  308. if (gds->tokenp[0].token == tUNUMBER
  309. && gds->tokenp[1].token == tMONTH) {
  310. /* "12 Sept" */
  311. gds->HaveMonth++;
  312. gds->HaveDay++;
  313. gds->Day = gds->tokenp[0].value;
  314. gds->Month = gds->tokenp[1].value;
  315. gds->tokenp += 2;
  316. return 1;
  317. }
  318. return 0;
  319. }
  320. /*
  321. * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc.
  322. */
  323. static int
  324. relunitphrase(struct gdstate *gds)
  325. {
  326. if (gds->tokenp[0].token == '-'
  327. && gds->tokenp[1].token == tUNUMBER
  328. && gds->tokenp[2].token == tSEC_UNIT) {
  329. /* "-3 hours" */
  330. gds->HaveRel++;
  331. gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value;
  332. gds->tokenp += 3;
  333. return 1;
  334. }
  335. if (gds->tokenp[0].token == '+'
  336. && gds->tokenp[1].token == tUNUMBER
  337. && gds->tokenp[2].token == tSEC_UNIT) {
  338. /* "+1 minute" */
  339. gds->HaveRel++;
  340. gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value;
  341. gds->tokenp += 3;
  342. return 1;
  343. }
  344. if (gds->tokenp[0].token == tUNUMBER
  345. && gds->tokenp[1].token == tSEC_UNIT) {
  346. /* "1 day" */
  347. gds->HaveRel++;
  348. gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value;
  349. gds->tokenp += 2;
  350. return 1;
  351. }
  352. if (gds->tokenp[0].token == '-'
  353. && gds->tokenp[1].token == tUNUMBER
  354. && gds->tokenp[2].token == tMONTH_UNIT) {
  355. /* "-3 months" */
  356. gds->HaveRel++;
  357. gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value;
  358. gds->tokenp += 3;
  359. return 1;
  360. }
  361. if (gds->tokenp[0].token == '+'
  362. && gds->tokenp[1].token == tUNUMBER
  363. && gds->tokenp[2].token == tMONTH_UNIT) {
  364. /* "+5 years" */
  365. gds->HaveRel++;
  366. gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value;
  367. gds->tokenp += 3;
  368. return 1;
  369. }
  370. if (gds->tokenp[0].token == tUNUMBER
  371. && gds->tokenp[1].token == tMONTH_UNIT) {
  372. /* "2 years" */
  373. gds->HaveRel++;
  374. gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value;
  375. gds->tokenp += 2;
  376. return 1;
  377. }
  378. if (gds->tokenp[0].token == tSEC_UNIT) {
  379. /* "now", "tomorrow" */
  380. gds->HaveRel++;
  381. gds->RelSeconds += gds->tokenp[0].value;
  382. gds->tokenp += 1;
  383. return 1;
  384. }
  385. if (gds->tokenp[0].token == tMONTH_UNIT) {
  386. /* "month" */
  387. gds->HaveRel++;
  388. gds->RelMonth += gds->tokenp[0].value;
  389. gds->tokenp += 1;
  390. return 1;
  391. }
  392. return 0;
  393. }
  394. /*
  395. * Day of the week specification.
  396. */
  397. static int
  398. dayphrase(struct gdstate *gds)
  399. {
  400. if (gds->tokenp[0].token == tDAY) {
  401. /* "tues", "wednesday," */
  402. gds->HaveWeekDay++;
  403. gds->DayOrdinal = 1;
  404. gds->DayNumber = gds->tokenp[0].value;
  405. gds->tokenp += 1;
  406. if (gds->tokenp[0].token == ',')
  407. gds->tokenp += 1;
  408. return 1;
  409. }
  410. if (gds->tokenp[0].token == tUNUMBER
  411. && gds->tokenp[1].token == tDAY) {
  412. /* "second tues" "3 wed" */
  413. gds->HaveWeekDay++;
  414. gds->DayOrdinal = gds->tokenp[0].value;
  415. gds->DayNumber = gds->tokenp[1].value;
  416. gds->tokenp += 2;
  417. return 1;
  418. }
  419. return 0;
  420. }
  421. /*
  422. * Try to match a phrase using one of the above functions.
  423. * This layer also deals with a couple of generic issues.
  424. */
  425. static int
  426. phrase(struct gdstate *gds)
  427. {
  428. if (timephrase(gds))
  429. return 1;
  430. if (zonephrase(gds))
  431. return 1;
  432. if (datephrase(gds))
  433. return 1;
  434. if (dayphrase(gds))
  435. return 1;
  436. if (relunitphrase(gds)) {
  437. if (gds->tokenp[0].token == tAGO) {
  438. gds->RelSeconds = -gds->RelSeconds;
  439. gds->RelMonth = -gds->RelMonth;
  440. gds->tokenp += 1;
  441. }
  442. return 1;
  443. }
  444. /* Bare numbers sometimes have meaning. */
  445. if (gds->tokenp[0].token == tUNUMBER) {
  446. if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) {
  447. gds->HaveYear++;
  448. gds->Year = gds->tokenp[0].value;
  449. gds->tokenp += 1;
  450. return 1;
  451. }
  452. if(gds->tokenp[0].value > 10000) {
  453. /* "20040301" */
  454. gds->HaveYear++;
  455. gds->HaveMonth++;
  456. gds->HaveDay++;
  457. gds->Day= (gds->tokenp[0].value)%100;
  458. gds->Month= (gds->tokenp[0].value/100)%100;
  459. gds->Year = gds->tokenp[0].value/10000;
  460. gds->tokenp += 1;
  461. return 1;
  462. }
  463. if (gds->tokenp[0].value < 24) {
  464. gds->HaveTime++;
  465. gds->Hour = gds->tokenp[0].value;
  466. gds->Minutes = 0;
  467. gds->Seconds = 0;
  468. gds->tokenp += 1;
  469. return 1;
  470. }
  471. if ((gds->tokenp[0].value / 100 < 24)
  472. && (gds->tokenp[0].value % 100 < 60)) {
  473. /* "513" is same as "5:13" */
  474. gds->Hour = gds->tokenp[0].value / 100;
  475. gds->Minutes = gds->tokenp[0].value % 100;
  476. gds->Seconds = 0;
  477. gds->tokenp += 1;
  478. return 1;
  479. }
  480. }
  481. return 0;
  482. }
  483. /*
  484. * A dictionary of time words.
  485. */
  486. static struct LEXICON {
  487. size_t abbrev;
  488. const char *name;
  489. int type;
  490. time_t value;
  491. } const TimeWords[] = {
  492. /* am/pm */
  493. { 0, "am", tAMPM, tAM },
  494. { 0, "pm", tAMPM, tPM },
  495. /* Month names. */
  496. { 3, "january", tMONTH, 1 },
  497. { 3, "february", tMONTH, 2 },
  498. { 3, "march", tMONTH, 3 },
  499. { 3, "april", tMONTH, 4 },
  500. { 3, "may", tMONTH, 5 },
  501. { 3, "june", tMONTH, 6 },
  502. { 3, "july", tMONTH, 7 },
  503. { 3, "august", tMONTH, 8 },
  504. { 3, "september", tMONTH, 9 },
  505. { 3, "october", tMONTH, 10 },
  506. { 3, "november", tMONTH, 11 },
  507. { 3, "december", tMONTH, 12 },
  508. /* Days of the week. */
  509. { 2, "sunday", tDAY, 0 },
  510. { 3, "monday", tDAY, 1 },
  511. { 2, "tuesday", tDAY, 2 },
  512. { 3, "wednesday", tDAY, 3 },
  513. { 2, "thursday", tDAY, 4 },
  514. { 2, "friday", tDAY, 5 },
  515. { 2, "saturday", tDAY, 6 },
  516. /* Timezones: Offsets are in seconds. */
  517. { 0, "gmt", tZONE, 0*HOUR }, /* Greenwich Mean */
  518. { 0, "ut", tZONE, 0*HOUR }, /* Universal (Coordinated) */
  519. { 0, "utc", tZONE, 0*HOUR },
  520. { 0, "wet", tZONE, 0*HOUR }, /* Western European */
  521. { 0, "bst", tDAYZONE, 0*HOUR }, /* British Summer */
  522. { 0, "wat", tZONE, 1*HOUR }, /* West Africa */
  523. { 0, "at", tZONE, 2*HOUR }, /* Azores */
  524. /* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */
  525. /* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/
  526. { 0, "nft", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland */
  527. { 0, "nst", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Standard */
  528. { 0, "ndt", tDAYZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Daylight */
  529. { 0, "ast", tZONE, 4*HOUR }, /* Atlantic Standard */
  530. { 0, "adt", tDAYZONE, 4*HOUR }, /* Atlantic Daylight */
  531. { 0, "est", tZONE, 5*HOUR }, /* Eastern Standard */
  532. { 0, "edt", tDAYZONE, 5*HOUR }, /* Eastern Daylight */
  533. { 0, "cst", tZONE, 6*HOUR }, /* Central Standard */
  534. { 0, "cdt", tDAYZONE, 6*HOUR }, /* Central Daylight */
  535. { 0, "mst", tZONE, 7*HOUR }, /* Mountain Standard */
  536. { 0, "mdt", tDAYZONE, 7*HOUR }, /* Mountain Daylight */
  537. { 0, "pst", tZONE, 8*HOUR }, /* Pacific Standard */
  538. { 0, "pdt", tDAYZONE, 8*HOUR }, /* Pacific Daylight */
  539. { 0, "yst", tZONE, 9*HOUR }, /* Yukon Standard */
  540. { 0, "ydt", tDAYZONE, 9*HOUR }, /* Yukon Daylight */
  541. { 0, "hst", tZONE, 10*HOUR }, /* Hawaii Standard */
  542. { 0, "hdt", tDAYZONE, 10*HOUR }, /* Hawaii Daylight */
  543. { 0, "cat", tZONE, 10*HOUR }, /* Central Alaska */
  544. { 0, "ahst", tZONE, 10*HOUR }, /* Alaska-Hawaii Standard */
  545. { 0, "nt", tZONE, 11*HOUR }, /* Nome */
  546. { 0, "idlw", tZONE, 12*HOUR }, /* Intl Date Line West */
  547. { 0, "cet", tZONE, -1*HOUR }, /* Central European */
  548. { 0, "met", tZONE, -1*HOUR }, /* Middle European */
  549. { 0, "mewt", tZONE, -1*HOUR }, /* Middle European Winter */
  550. { 0, "mest", tDAYZONE, -1*HOUR }, /* Middle European Summer */
  551. { 0, "swt", tZONE, -1*HOUR }, /* Swedish Winter */
  552. { 0, "sst", tDAYZONE, -1*HOUR }, /* Swedish Summer */
  553. { 0, "fwt", tZONE, -1*HOUR }, /* French Winter */
  554. { 0, "fst", tDAYZONE, -1*HOUR }, /* French Summer */
  555. { 0, "eet", tZONE, -2*HOUR }, /* Eastern Eur, USSR Zone 1 */
  556. { 0, "bt", tZONE, -3*HOUR }, /* Baghdad, USSR Zone 2 */
  557. { 0, "it", tZONE, -3*HOUR-30*MINUTE },/* Iran */
  558. { 0, "zp4", tZONE, -4*HOUR }, /* USSR Zone 3 */
  559. { 0, "zp5", tZONE, -5*HOUR }, /* USSR Zone 4 */
  560. { 0, "ist", tZONE, -5*HOUR-30*MINUTE },/* Indian Standard */
  561. { 0, "zp6", tZONE, -6*HOUR }, /* USSR Zone 5 */
  562. /* { 0, "nst", tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */
  563. /* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */
  564. { 0, "wast", tZONE, -7*HOUR }, /* West Australian Standard */
  565. { 0, "wadt", tDAYZONE, -7*HOUR }, /* West Australian Daylight */
  566. { 0, "jt", tZONE, -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/
  567. { 0, "cct", tZONE, -8*HOUR }, /* China Coast, USSR Zone 7 */
  568. { 0, "jst", tZONE, -9*HOUR }, /* Japan Std, USSR Zone 8 */
  569. { 0, "cast", tZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Std */
  570. { 0, "cadt", tDAYZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */
  571. { 0, "east", tZONE, -10*HOUR }, /* Eastern Australian Std */
  572. { 0, "eadt", tDAYZONE, -10*HOUR }, /* Eastern Australian Daylt */
  573. { 0, "gst", tZONE, -10*HOUR }, /* Guam Std, USSR Zone 9 */
  574. { 0, "nzt", tZONE, -12*HOUR }, /* New Zealand */
  575. { 0, "nzst", tZONE, -12*HOUR }, /* New Zealand Standard */
  576. { 0, "nzdt", tDAYZONE, -12*HOUR }, /* New Zealand Daylight */
  577. { 0, "idle", tZONE, -12*HOUR }, /* Intl Date Line East */
  578. { 0, "dst", tDST, 0 },
  579. /* Time units. */
  580. { 4, "years", tMONTH_UNIT, 12 },
  581. { 5, "months", tMONTH_UNIT, 1 },
  582. { 9, "fortnights", tSEC_UNIT, 14 * DAY },
  583. { 4, "weeks", tSEC_UNIT, 7 * DAY },
  584. { 3, "days", tSEC_UNIT, DAY },
  585. { 4, "hours", tSEC_UNIT, HOUR },
  586. { 3, "minutes", tSEC_UNIT, MINUTE },
  587. { 3, "seconds", tSEC_UNIT, 1 },
  588. /* Relative-time words. */
  589. { 0, "tomorrow", tSEC_UNIT, DAY },
  590. { 0, "yesterday", tSEC_UNIT, -DAY },
  591. { 0, "today", tSEC_UNIT, 0 },
  592. { 0, "now", tSEC_UNIT, 0 },
  593. { 0, "last", tUNUMBER, -1 },
  594. { 0, "this", tSEC_UNIT, 0 },
  595. { 0, "next", tUNUMBER, 2 },
  596. { 0, "first", tUNUMBER, 1 },
  597. { 0, "1st", tUNUMBER, 1 },
  598. /* { 0, "second", tUNUMBER, 2 }, */
  599. { 0, "2nd", tUNUMBER, 2 },
  600. { 0, "third", tUNUMBER, 3 },
  601. { 0, "3rd", tUNUMBER, 3 },
  602. { 0, "fourth", tUNUMBER, 4 },
  603. { 0, "4th", tUNUMBER, 4 },
  604. { 0, "fifth", tUNUMBER, 5 },
  605. { 0, "5th", tUNUMBER, 5 },
  606. { 0, "sixth", tUNUMBER, 6 },
  607. { 0, "seventh", tUNUMBER, 7 },
  608. { 0, "eighth", tUNUMBER, 8 },
  609. { 0, "ninth", tUNUMBER, 9 },
  610. { 0, "tenth", tUNUMBER, 10 },
  611. { 0, "eleventh", tUNUMBER, 11 },
  612. { 0, "twelfth", tUNUMBER, 12 },
  613. { 0, "ago", tAGO, 1 },
  614. /* Military timezones. */
  615. { 0, "a", tZONE, 1*HOUR },
  616. { 0, "b", tZONE, 2*HOUR },
  617. { 0, "c", tZONE, 3*HOUR },
  618. { 0, "d", tZONE, 4*HOUR },
  619. { 0, "e", tZONE, 5*HOUR },
  620. { 0, "f", tZONE, 6*HOUR },
  621. { 0, "g", tZONE, 7*HOUR },
  622. { 0, "h", tZONE, 8*HOUR },
  623. { 0, "i", tZONE, 9*HOUR },
  624. { 0, "k", tZONE, 10*HOUR },
  625. { 0, "l", tZONE, 11*HOUR },
  626. { 0, "m", tZONE, 12*HOUR },
  627. { 0, "n", tZONE, -1*HOUR },
  628. { 0, "o", tZONE, -2*HOUR },
  629. { 0, "p", tZONE, -3*HOUR },
  630. { 0, "q", tZONE, -4*HOUR },
  631. { 0, "r", tZONE, -5*HOUR },
  632. { 0, "s", tZONE, -6*HOUR },
  633. { 0, "t", tZONE, -7*HOUR },
  634. { 0, "u", tZONE, -8*HOUR },
  635. { 0, "v", tZONE, -9*HOUR },
  636. { 0, "w", tZONE, -10*HOUR },
  637. { 0, "x", tZONE, -11*HOUR },
  638. { 0, "y", tZONE, -12*HOUR },
  639. { 0, "z", tZONE, 0*HOUR },
  640. /* End of table. */
  641. { 0, NULL, 0, 0 }
  642. };
  643. /*
  644. * Year is either:
  645. * = A number from 0 to 99, which means a year from 1970 to 2069, or
  646. * = The actual year (>=100).
  647. */
  648. static time_t
  649. Convert(time_t Month, time_t Day, time_t Year,
  650. time_t Hours, time_t Minutes, time_t Seconds,
  651. time_t Timezone, enum DSTMODE DSTmode)
  652. {
  653. signed char DaysInMonth[12] = {
  654. 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  655. };
  656. time_t Julian;
  657. int i;
  658. if (Year < 69)
  659. Year += 2000;
  660. else if (Year < 100)
  661. Year += 1900;
  662. DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
  663. ? 29 : 28;
  664. /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
  665. I'm too lazy to try to check for time_t overflow in another way. */
  666. if (Year < EPOCH || Year > 2038
  667. || Month < 1 || Month > 12
  668. /* Lint fluff: "conversion from long may lose accuracy" */
  669. || Day < 1 || Day > DaysInMonth[(int)--Month]
  670. || Hours < 0 || Hours > 23
  671. || Minutes < 0 || Minutes > 59
  672. || Seconds < 0 || Seconds > 59)
  673. return -1;
  674. Julian = Day - 1;
  675. for (i = 0; i < Month; i++)
  676. Julian += DaysInMonth[i];
  677. for (i = EPOCH; i < Year; i++)
  678. Julian += 365 + (i % 4 == 0);
  679. Julian *= DAY;
  680. Julian += Timezone;
  681. Julian += Hours * HOUR + Minutes * MINUTE + Seconds;
  682. if (DSTmode == DSTon
  683. || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
  684. Julian -= HOUR;
  685. return Julian;
  686. }
  687. static time_t
  688. DSTcorrect(time_t Start, time_t Future)
  689. {
  690. time_t StartDay;
  691. time_t FutureDay;
  692. StartDay = (localtime(&Start)->tm_hour + 1) % 24;
  693. FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
  694. return (Future - Start) + (StartDay - FutureDay) * HOUR;
  695. }
  696. static time_t
  697. RelativeDate(time_t Start, time_t zone, int dstmode,
  698. time_t DayOrdinal, time_t DayNumber)
  699. {
  700. struct tm *tm;
  701. time_t t, now;
  702. t = Start - zone;
  703. tm = gmtime(&t);
  704. now = Start;
  705. now += DAY * ((DayNumber - tm->tm_wday + 7) % 7);
  706. now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
  707. if (dstmode == DSTmaybe)
  708. return DSTcorrect(Start, now);
  709. return now - Start;
  710. }
  711. static time_t
  712. RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth)
  713. {
  714. struct tm *tm;
  715. time_t Month;
  716. time_t Year;
  717. if (RelMonth == 0)
  718. return 0;
  719. tm = localtime(&Start);
  720. Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
  721. Year = Month / 12;
  722. Month = Month % 12 + 1;
  723. return DSTcorrect(Start,
  724. Convert(Month, (time_t)tm->tm_mday, Year,
  725. (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
  726. Timezone, DSTmaybe));
  727. }
  728. /*
  729. * Tokenizer.
  730. */
  731. static int
  732. nexttoken(const char **in, time_t *value)
  733. {
  734. char c;
  735. char buff[64];
  736. for ( ; ; ) {
  737. while (isspace((unsigned char)**in))
  738. ++*in;
  739. /* Skip parenthesized comments. */
  740. if (**in == '(') {
  741. int Count = 0;
  742. do {
  743. c = *(*in)++;
  744. if (c == '\0')
  745. return c;
  746. if (c == '(')
  747. Count++;
  748. else if (c == ')')
  749. Count--;
  750. } while (Count > 0);
  751. continue;
  752. }
  753. /* Try the next token in the word table first. */
  754. /* This allows us to match "2nd", for example. */
  755. {
  756. const char *src = *in;
  757. const struct LEXICON *tp;
  758. unsigned i = 0;
  759. /* Force to lowercase and strip '.' characters. */
  760. while (*src != '\0'
  761. && (isalnum((unsigned char)*src) || *src == '.')
  762. && i < sizeof(buff)-1) {
  763. if (*src != '.') {
  764. if (isupper((unsigned char)*src))
  765. buff[i++] = tolower((unsigned char)*src);
  766. else
  767. buff[i++] = *src;
  768. }
  769. src++;
  770. }
  771. buff[i] = '\0';
  772. /*
  773. * Find the first match. If the word can be
  774. * abbreviated, make sure we match at least
  775. * the minimum abbreviation.
  776. */
  777. for (tp = TimeWords; tp->name; tp++) {
  778. size_t abbrev = tp->abbrev;
  779. if (abbrev == 0)
  780. abbrev = strlen(tp->name);
  781. if (strlen(buff) >= abbrev
  782. && strncmp(tp->name, buff, strlen(buff))
  783. == 0) {
  784. /* Skip over token. */
  785. *in = src;
  786. /* Return the match. */
  787. *value = tp->value;
  788. return tp->type;
  789. }
  790. }
  791. }
  792. /*
  793. * Not in the word table, maybe it's a number. Note:
  794. * Because '-' and '+' have other special meanings, I
  795. * don't deal with signed numbers here.
  796. */
  797. if (isdigit((unsigned char)(c = **in))) {
  798. for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); )
  799. *value = 10 * *value + c - '0';
  800. (*in)--;
  801. return (tUNUMBER);
  802. }
  803. return *(*in)++;
  804. }
  805. }
  806. #define TM_YEAR_ORIGIN 1900
  807. /* Yield A - B, measured in seconds. */
  808. static long
  809. difftm (struct tm *a, struct tm *b)
  810. {
  811. int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
  812. int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
  813. int days = (
  814. /* difference in day of year */
  815. a->tm_yday - b->tm_yday
  816. /* + intervening leap days */
  817. + ((ay >> 2) - (by >> 2))
  818. - (ay/100 - by/100)
  819. + ((ay/100 >> 2) - (by/100 >> 2))
  820. /* + difference in years * 365 */
  821. + (long)(ay-by) * 365
  822. );
  823. return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR
  824. + (a->tm_min - b->tm_min) * MINUTE
  825. + (a->tm_sec - b->tm_sec));
  826. }
  827. /*
  828. *
  829. * The public function.
  830. *
  831. * TODO: tokens[] array should be dynamically sized.
  832. */
  833. time_t
  834. __archive_get_date(time_t now, const char *p)
  835. {
  836. struct token tokens[256];
  837. struct gdstate _gds;
  838. struct token *lasttoken;
  839. struct gdstate *gds;
  840. struct tm local, *tm;
  841. struct tm gmt, *gmt_ptr;
  842. time_t Start;
  843. time_t tod;
  844. long tzone;
  845. /* Clear out the parsed token array. */
  846. memset(tokens, 0, sizeof(tokens));
  847. /* Initialize the parser state. */
  848. memset(&_gds, 0, sizeof(_gds));
  849. gds = &_gds;
  850. /* Look up the current time. */
  851. memset(&local, 0, sizeof(local));
  852. tm = localtime (&now);
  853. if (tm == NULL)
  854. return -1;
  855. local = *tm;
  856. /* Look up UTC if we can and use that to determine the current
  857. * timezone offset. */
  858. memset(&gmt, 0, sizeof(gmt));
  859. gmt_ptr = gmtime (&now);
  860. if (gmt_ptr != NULL) {
  861. /* Copy, in case localtime and gmtime use the same buffer. */
  862. gmt = *gmt_ptr;
  863. }
  864. if (gmt_ptr != NULL)
  865. tzone = difftm (&gmt, &local);
  866. else
  867. /* This system doesn't understand timezones; fake it. */
  868. tzone = 0;
  869. if(local.tm_isdst)
  870. tzone += HOUR;
  871. /* Tokenize the input string. */
  872. lasttoken = tokens;
  873. while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) {
  874. ++lasttoken;
  875. if (lasttoken > tokens + 255)
  876. return -1;
  877. }
  878. gds->tokenp = tokens;
  879. /* Match phrases until we run out of input tokens. */
  880. while (gds->tokenp < lasttoken) {
  881. if (!phrase(gds))
  882. return -1;
  883. }
  884. /* Use current local timezone if none was specified. */
  885. if (!gds->HaveZone) {
  886. gds->Timezone = tzone;
  887. gds->DSTmode = DSTmaybe;
  888. }
  889. /* If a timezone was specified, use that for generating the default
  890. * time components instead of the local timezone. */
  891. if (gds->HaveZone && gmt_ptr != NULL) {
  892. now -= gds->Timezone;
  893. gmt_ptr = gmtime (&now);
  894. if (gmt_ptr != NULL)
  895. local = *gmt_ptr;
  896. now += gds->Timezone;
  897. }
  898. if (!gds->HaveYear)
  899. gds->Year = local.tm_year + 1900;
  900. if (!gds->HaveMonth)
  901. gds->Month = local.tm_mon + 1;
  902. if (!gds->HaveDay)
  903. gds->Day = local.tm_mday;
  904. /* Note: No default for hour/min/sec; a specifier that just
  905. * gives date always refers to 00:00 on that date. */
  906. /* If we saw more than one time, timezone, weekday, year, month,
  907. * or day, then give up. */
  908. if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1
  909. || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1)
  910. return -1;
  911. /* Compute an absolute time based on whatever absolute information
  912. * we collected. */
  913. if (gds->HaveYear || gds->HaveMonth || gds->HaveDay
  914. || gds->HaveTime || gds->HaveWeekDay) {
  915. Start = Convert(gds->Month, gds->Day, gds->Year,
  916. gds->Hour, gds->Minutes, gds->Seconds,
  917. gds->Timezone, gds->DSTmode);
  918. if (Start < 0)
  919. return -1;
  920. } else {
  921. Start = now;
  922. if (!gds->HaveRel)
  923. Start -= local.tm_hour * HOUR + local.tm_min * MINUTE
  924. + local.tm_sec;
  925. }
  926. /* Add the relative offset. */
  927. Start += gds->RelSeconds;
  928. Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth);
  929. /* Adjust for day-of-week offsets. */
  930. if (gds->HaveWeekDay
  931. && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) {
  932. tod = RelativeDate(Start, gds->Timezone,
  933. gds->DSTmode, gds->DayOrdinal, gds->DayNumber);
  934. Start += tod;
  935. }
  936. /* -1 is an error indicator, so return 0 instead of -1 if
  937. * that's the actual time. */
  938. return Start == -1 ? 0 : Start;
  939. }
  940. #if defined(TEST)
  941. /* ARGSUSED */
  942. int
  943. main(int argc, char **argv)
  944. {
  945. time_t d;
  946. time_t now = time(NULL);
  947. while (*++argv != NULL) {
  948. (void)printf("Input: %s\n", *argv);
  949. d = get_date(now, *argv);
  950. if (d == -1)
  951. (void)printf("Bad format - couldn't convert.\n");
  952. else
  953. (void)printf("Output: %s\n", ctime(&d));
  954. }
  955. exit(0);
  956. /* NOTREACHED */
  957. }
  958. #endif /* defined(TEST) */