archive_getdate.c 30 KB

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