PageRenderTime 49ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/d/phobos/std/dateparse.d

https://bitbucket.org/goshawk/gdc/
D | 790 lines | 662 code | 74 blank | 54 comment | 201 complexity | 04729f94995ba3502002d6f8436fba7d MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0
  1. /*
  2. * Copyright (C) 1999-2004 by Digital Mars, www.digitalmars.com
  3. * Written by Walter Bright
  4. *
  5. * This software is provided 'as-is', without any express or implied
  6. * warranty. In no event will the authors be held liable for any damages
  7. * arising from the use of this software.
  8. *
  9. * Permission is granted to anyone to use this software for any purpose,
  10. * including commercial applications, and to alter it and redistribute it
  11. * freely, subject to the following restrictions:
  12. *
  13. * o The origin of this software must not be misrepresented; you must not
  14. * claim that you wrote the original software. If you use this software
  15. * in a product, an acknowledgment in the product documentation would be
  16. * appreciated but is not required.
  17. * o Altered source versions must be plainly marked as such, and must not
  18. * be misrepresented as being the original software.
  19. * o This notice may not be removed or altered from any source
  20. * distribution.
  21. */
  22. /**
  23. * Source: $(PHOBOSSRC std/_dateparse.d)
  24. */
  25. module std.dateparse;
  26. private
  27. {
  28. import std.string;
  29. import std.c.stdlib;
  30. import std.date;
  31. }
  32. //debug=dateparse;
  33. class DateParseError : Error
  34. {
  35. this(char[] s)
  36. {
  37. super("Invalid date string: " ~ s);
  38. }
  39. }
  40. struct DateParse
  41. {
  42. void parse(char[] s, out Date date)
  43. {
  44. *this = DateParse.init;
  45. //version (Win32)
  46. buffer = (cast(char *)alloca(s.length))[0 .. s.length];
  47. //else
  48. //buffer = new char[s.length];
  49. debug(dateparse) printf("DateParse.parse('%.*s')\n", s);
  50. if (!parseString(s))
  51. {
  52. goto Lerror;
  53. }
  54. /+
  55. if (year == year.init)
  56. year = 0;
  57. else
  58. +/
  59. debug(dateparse)
  60. printf("year = %d, month = %d, day = %d\n%02d:%02d:%02d.%03d\nweekday = %d, tzcorrection = %d\n",
  61. year, month, day,
  62. hours, minutes, seconds, ms,
  63. weekday, tzcorrection);
  64. if (
  65. year == year.init ||
  66. (month < 1 || month > 12) ||
  67. (day < 1 || day > 31) ||
  68. (hours < 0 || hours > 23) ||
  69. (minutes < 0 || minutes > 59) ||
  70. (seconds < 0 || seconds > 59) ||
  71. (tzcorrection != int.min &&
  72. ((tzcorrection < -2300 || tzcorrection > 2300) ||
  73. (tzcorrection % 10)))
  74. )
  75. {
  76. Lerror:
  77. throw new DateParseError(s);
  78. }
  79. if (ampm)
  80. { if (hours > 12)
  81. goto Lerror;
  82. if (hours < 12)
  83. {
  84. if (ampm == 2) // if P.M.
  85. hours += 12;
  86. }
  87. else if (ampm == 1) // if 12am
  88. {
  89. hours = 0; // which is midnight
  90. }
  91. }
  92. // if (tzcorrection != tzcorrection.init)
  93. // tzcorrection /= 100;
  94. if (year >= 0 && year <= 99)
  95. year += 1900;
  96. date.year = year;
  97. date.month = month;
  98. date.day = day;
  99. date.hour = hours;
  100. date.minute = minutes;
  101. date.second = seconds;
  102. date.ms = ms;
  103. date.weekday = weekday;
  104. date.tzcorrection = tzcorrection;
  105. }
  106. private:
  107. int year = int.min; // our "nan" Date value
  108. int month; // 1..12
  109. int day; // 1..31
  110. int hours; // 0..23
  111. int minutes; // 0..59
  112. int seconds; // 0..59
  113. int ms; // 0..999
  114. int weekday; // 1..7
  115. int ampm; // 0: not specified
  116. // 1: AM
  117. // 2: PM
  118. int tzcorrection = int.min; // -1200..1200 correction in hours
  119. char[] s;
  120. int si;
  121. int number;
  122. char[] buffer;
  123. enum DP : byte
  124. {
  125. err,
  126. weekday,
  127. month,
  128. number,
  129. end,
  130. colon,
  131. minus,
  132. slash,
  133. ampm,
  134. plus,
  135. tz,
  136. dst,
  137. dsttz,
  138. }
  139. DP nextToken()
  140. { int nest;
  141. uint c;
  142. int bi;
  143. DP result = DP.err;
  144. //printf("DateParse::nextToken()\n");
  145. for (;;)
  146. {
  147. assert(si <= s.length);
  148. if (si == s.length)
  149. { result = DP.end;
  150. goto Lret;
  151. }
  152. //printf("\ts[%d] = '%c'\n", si, s[si]);
  153. switch (s[si])
  154. {
  155. case ':': result = DP.colon; goto ret_inc;
  156. case '+': result = DP.plus; goto ret_inc;
  157. case '-': result = DP.minus; goto ret_inc;
  158. case '/': result = DP.slash; goto ret_inc;
  159. case '.':
  160. version(DATE_DOT_DELIM)
  161. {
  162. result = DP.slash;
  163. goto ret_inc;
  164. }
  165. else
  166. {
  167. si++;
  168. break;
  169. }
  170. ret_inc:
  171. si++;
  172. goto Lret;
  173. case ' ':
  174. case '\n':
  175. case '\r':
  176. case '\t':
  177. case ',':
  178. si++;
  179. break;
  180. case '(': // comment
  181. nest = 1;
  182. for (;;)
  183. {
  184. si++;
  185. if (si == s.length)
  186. goto Lret; // error
  187. switch (s[si])
  188. {
  189. case '(':
  190. nest++;
  191. break;
  192. case ')':
  193. if (--nest == 0)
  194. goto Lendofcomment;
  195. break;
  196. default:
  197. break;
  198. }
  199. }
  200. Lendofcomment:
  201. si++;
  202. break;
  203. default:
  204. number = 0;
  205. for (;;)
  206. {
  207. if (si == s.length)
  208. // c cannot be undefined here
  209. break;
  210. c = s[si];
  211. if (!(c >= '0' && c <= '9'))
  212. break;
  213. result = DP.number;
  214. number = number * 10 + (c - '0');
  215. si++;
  216. }
  217. if (result == DP.number)
  218. goto Lret;
  219. bi = 0;
  220. bufloop:
  221. while (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
  222. {
  223. if (c < 'a') // if upper case
  224. c += cast(uint)'a' - cast(uint)'A'; // to lower case
  225. buffer[bi] = cast(char)c;
  226. bi++;
  227. do
  228. {
  229. si++;
  230. if (si == s.length)
  231. break bufloop;
  232. c = s[si];
  233. } while (c == '.'); // ignore embedded '.'s
  234. }
  235. result = classify(buffer[0 .. bi]);
  236. goto Lret;
  237. }
  238. }
  239. Lret:
  240. //printf("-DateParse::nextToken()\n");
  241. return result;
  242. }
  243. DP classify(char[] buf)
  244. {
  245. struct DateID
  246. {
  247. char[] name;
  248. DP tok;
  249. short value;
  250. }
  251. static DateID dateidtab[] =
  252. [
  253. { "january", DP.month, 1},
  254. { "february", DP.month, 2},
  255. { "march", DP.month, 3},
  256. { "april", DP.month, 4},
  257. { "may", DP.month, 5},
  258. { "june", DP.month, 6},
  259. { "july", DP.month, 7},
  260. { "august", DP.month, 8},
  261. { "september", DP.month, 9},
  262. { "october", DP.month, 10},
  263. { "november", DP.month, 11},
  264. { "december", DP.month, 12},
  265. { "jan", DP.month, 1},
  266. { "feb", DP.month, 2},
  267. { "mar", DP.month, 3},
  268. { "apr", DP.month, 4},
  269. { "jun", DP.month, 6},
  270. { "jul", DP.month, 7},
  271. { "aug", DP.month, 8},
  272. { "sep", DP.month, 9},
  273. { "sept", DP.month, 9},
  274. { "oct", DP.month, 10},
  275. { "nov", DP.month, 11},
  276. { "dec", DP.month, 12},
  277. { "sunday", DP.weekday, 1},
  278. { "monday", DP.weekday, 2},
  279. { "tuesday", DP.weekday, 3},
  280. { "tues", DP.weekday, 3},
  281. { "wednesday", DP.weekday, 4},
  282. { "wednes", DP.weekday, 4},
  283. { "thursday", DP.weekday, 5},
  284. { "thur", DP.weekday, 5},
  285. { "thurs", DP.weekday, 5},
  286. { "friday", DP.weekday, 6},
  287. { "saturday", DP.weekday, 7},
  288. { "sun", DP.weekday, 1},
  289. { "mon", DP.weekday, 2},
  290. { "tue", DP.weekday, 3},
  291. { "wed", DP.weekday, 4},
  292. { "thu", DP.weekday, 5},
  293. { "fri", DP.weekday, 6},
  294. { "sat", DP.weekday, 7},
  295. { "am", DP.ampm, 1},
  296. { "pm", DP.ampm, 2},
  297. { "gmt", DP.tz, +000},
  298. { "ut", DP.tz, +000},
  299. { "utc", DP.tz, +000},
  300. { "wet", DP.tz, +000},
  301. { "z", DP.tz, +000},
  302. { "wat", DP.tz, +100},
  303. { "a", DP.tz, +100},
  304. { "at", DP.tz, +200},
  305. { "b", DP.tz, +200},
  306. { "c", DP.tz, +300},
  307. { "ast", DP.tz, +400},
  308. { "d", DP.tz, +400},
  309. { "est", DP.tz, +500},
  310. { "e", DP.tz, +500},
  311. { "cst", DP.tz, +600},
  312. { "f", DP.tz, +600},
  313. { "mst", DP.tz, +700},
  314. { "g", DP.tz, +700},
  315. { "pst", DP.tz, +800},
  316. { "h", DP.tz, +800},
  317. { "yst", DP.tz, +900},
  318. { "i", DP.tz, +900},
  319. { "ahst", DP.tz, +1000},
  320. { "cat", DP.tz, +1000},
  321. { "hst", DP.tz, +1000},
  322. { "k", DP.tz, +1000},
  323. { "nt", DP.tz, +1100},
  324. { "l", DP.tz, +1100},
  325. { "idlw", DP.tz, +1200},
  326. { "m", DP.tz, +1200},
  327. { "cet", DP.tz, -100},
  328. { "fwt", DP.tz, -100},
  329. { "met", DP.tz, -100},
  330. { "mewt", DP.tz, -100},
  331. { "swt", DP.tz, -100},
  332. { "n", DP.tz, -100},
  333. { "eet", DP.tz, -200},
  334. { "o", DP.tz, -200},
  335. { "bt", DP.tz, -300},
  336. { "p", DP.tz, -300},
  337. { "zp4", DP.tz, -400},
  338. { "q", DP.tz, -400},
  339. { "zp5", DP.tz, -500},
  340. { "r", DP.tz, -500},
  341. { "zp6", DP.tz, -600},
  342. { "s", DP.tz, -600},
  343. { "wast", DP.tz, -700},
  344. { "t", DP.tz, -700},
  345. { "cct", DP.tz, -800},
  346. { "u", DP.tz, -800},
  347. { "jst", DP.tz, -900},
  348. { "v", DP.tz, -900},
  349. { "east", DP.tz, -1000},
  350. { "gst", DP.tz, -1000},
  351. { "w", DP.tz, -1000},
  352. { "x", DP.tz, -1100},
  353. { "idle", DP.tz, -1200},
  354. { "nzst", DP.tz, -1200},
  355. { "nzt", DP.tz, -1200},
  356. { "y", DP.tz, -1200},
  357. { "bst", DP.dsttz, 000},
  358. { "adt", DP.dsttz, +400},
  359. { "edt", DP.dsttz, +500},
  360. { "cdt", DP.dsttz, +600},
  361. { "mdt", DP.dsttz, +700},
  362. { "pdt", DP.dsttz, +800},
  363. { "ydt", DP.dsttz, +900},
  364. { "hdt", DP.dsttz, +1000},
  365. { "mest", DP.dsttz, -100},
  366. { "mesz", DP.dsttz, -100},
  367. { "sst", DP.dsttz, -100},
  368. { "fst", DP.dsttz, -100},
  369. { "wadt", DP.dsttz, -700},
  370. { "eadt", DP.dsttz, -1000},
  371. { "nzdt", DP.dsttz, -1200},
  372. { "dst", DP.dst, 0},
  373. ];
  374. //message(DTEXT("DateParse::classify('%s')\n"), buf);
  375. // Do a linear search. Yes, it would be faster with a binary
  376. // one.
  377. for (uint i = 0; i < dateidtab.length; i++)
  378. {
  379. if (std.string.cmp(dateidtab[i].name, buf) == 0)
  380. {
  381. number = dateidtab[i].value;
  382. return dateidtab[i].tok;
  383. }
  384. }
  385. return DP.err;
  386. }
  387. int parseString(char[] s)
  388. {
  389. int n1;
  390. int dp;
  391. int sisave;
  392. int result;
  393. //message(DTEXT("DateParse::parseString('%ls')\n"), s);
  394. this.s = s;
  395. si = 0;
  396. dp = nextToken();
  397. for (;;)
  398. {
  399. //message(DTEXT("\tdp = %d\n"), dp);
  400. switch (dp)
  401. {
  402. case DP.end:
  403. result = 1;
  404. Lret:
  405. return result;
  406. case DP.err:
  407. case_error:
  408. //message(DTEXT("\terror\n"));
  409. default:
  410. result = 0;
  411. goto Lret;
  412. case DP.minus:
  413. break; // ignore spurious '-'
  414. case DP.weekday:
  415. weekday = number;
  416. break;
  417. case DP.month: // month day, [year]
  418. month = number;
  419. dp = nextToken();
  420. if (dp == DP.number)
  421. {
  422. day = number;
  423. sisave = si;
  424. dp = nextToken();
  425. if (dp == DP.number)
  426. {
  427. n1 = number;
  428. dp = nextToken();
  429. if (dp == DP.colon)
  430. { // back up, not a year
  431. si = sisave;
  432. }
  433. else
  434. { year = n1;
  435. continue;
  436. }
  437. break;
  438. }
  439. }
  440. continue;
  441. case DP.number:
  442. n1 = number;
  443. dp = nextToken();
  444. switch (dp)
  445. {
  446. case DP.end:
  447. year = n1;
  448. break;
  449. case DP.minus:
  450. case DP.slash: // n1/ ? ? ?
  451. dp = parseCalendarDate(n1);
  452. if (dp == DP.err)
  453. goto case_error;
  454. break;
  455. case DP.colon: // hh:mm [:ss] [am | pm]
  456. dp = parseTimeOfDay(n1);
  457. if (dp == DP.err)
  458. goto case_error;
  459. break;
  460. case DP.ampm:
  461. hours = n1;
  462. minutes = 0;
  463. seconds = 0;
  464. ampm = number;
  465. break;
  466. case DP.month:
  467. day = n1;
  468. month = number;
  469. dp = nextToken();
  470. if (dp == DP.number)
  471. { // day month year
  472. year = number;
  473. dp = nextToken();
  474. }
  475. break;
  476. default:
  477. year = n1;
  478. break;
  479. }
  480. continue;
  481. }
  482. dp = nextToken();
  483. }
  484. assert(0);
  485. }
  486. int parseCalendarDate(int n1)
  487. {
  488. int n2;
  489. int n3;
  490. int dp;
  491. debug(dateparse) printf("DateParse.parseCalendarDate(%d)\n", n1);
  492. dp = nextToken();
  493. if (dp == DP.month) // day/month
  494. {
  495. day = n1;
  496. month = number;
  497. dp = nextToken();
  498. if (dp == DP.number)
  499. { // day/month year
  500. year = number;
  501. dp = nextToken();
  502. }
  503. else if (dp == DP.minus || dp == DP.slash)
  504. { // day/month/year
  505. dp = nextToken();
  506. if (dp != DP.number)
  507. goto case_error;
  508. year = number;
  509. dp = nextToken();
  510. }
  511. return dp;
  512. }
  513. if (dp != DP.number)
  514. goto case_error;
  515. n2 = number;
  516. //message(DTEXT("\tn2 = %d\n"), n2);
  517. dp = nextToken();
  518. if (dp == DP.minus || dp == DP.slash)
  519. {
  520. dp = nextToken();
  521. if (dp != DP.number)
  522. goto case_error;
  523. n3 = number;
  524. //message(DTEXT("\tn3 = %d\n"), n3);
  525. dp = nextToken();
  526. // case1: year/month/day
  527. // case2: month/day/year
  528. int case1, case2;
  529. case1 = (n1 > 12 ||
  530. (n2 >= 1 && n2 <= 12) &&
  531. (n3 >= 1 && n3 <= 31));
  532. case2 = ((n1 >= 1 && n1 <= 12) &&
  533. (n2 >= 1 && n2 <= 31) ||
  534. n3 > 31);
  535. if (case1 == case2)
  536. goto case_error;
  537. if (case1)
  538. {
  539. year = n1;
  540. month = n2;
  541. day = n3;
  542. }
  543. else
  544. {
  545. month = n1;
  546. day = n2;
  547. year = n3;
  548. }
  549. }
  550. else
  551. { // must be month/day
  552. month = n1;
  553. day = n2;
  554. }
  555. return dp;
  556. case_error:
  557. return DP.err;
  558. }
  559. int parseTimeOfDay(int n1)
  560. {
  561. int dp;
  562. int sign;
  563. // 12am is midnight
  564. // 12pm is noon
  565. //message(DTEXT("DateParse::parseTimeOfDay(%d)\n"), n1);
  566. hours = n1;
  567. dp = nextToken();
  568. if (dp != DP.number)
  569. goto case_error;
  570. minutes = number;
  571. dp = nextToken();
  572. if (dp == DP.colon)
  573. {
  574. dp = nextToken();
  575. if (dp != DP.number)
  576. goto case_error;
  577. seconds = number;
  578. dp = nextToken();
  579. }
  580. else
  581. seconds = 0;
  582. if (dp == DP.ampm)
  583. {
  584. ampm = number;
  585. dp = nextToken();
  586. }
  587. else if (dp == DP.plus || dp == DP.minus)
  588. {
  589. Loffset:
  590. sign = (dp == DP.minus) ? -1 : 1;
  591. dp = nextToken();
  592. if (dp != DP.number)
  593. goto case_error;
  594. tzcorrection = -sign * number;
  595. dp = nextToken();
  596. }
  597. else if (dp == DP.tz)
  598. {
  599. tzcorrection = number;
  600. dp = nextToken();
  601. if (number == 0 && (dp == DP.plus || dp == DP.minus))
  602. goto Loffset;
  603. if (dp == DP.dst)
  604. { tzcorrection += 100;
  605. dp = nextToken();
  606. }
  607. }
  608. else if (dp == DP.dsttz)
  609. {
  610. tzcorrection = number;
  611. dp = nextToken();
  612. }
  613. return dp;
  614. case_error:
  615. return DP.err;
  616. }
  617. }
  618. unittest
  619. {
  620. DateParse dp;
  621. Date d;
  622. dp.parse("March 10, 1959 12:00 -800", d);
  623. assert(d.year == 1959);
  624. assert(d.month == 3);
  625. assert(d.day == 10);
  626. assert(d.hour == 12);
  627. assert(d.minute == 0);
  628. assert(d.second == 0);
  629. assert(d.ms == 0);
  630. assert(d.weekday == 0);
  631. assert(d.tzcorrection == 800);
  632. dp.parse("Tue Apr 02 02:04:57 GMT-0800 1996", d);
  633. assert(d.year == 1996);
  634. assert(d.month == 4);
  635. assert(d.day == 2);
  636. assert(d.hour == 2);
  637. assert(d.minute == 4);
  638. assert(d.second == 57);
  639. assert(d.ms == 0);
  640. assert(d.weekday == 3);
  641. assert(d.tzcorrection == 800);
  642. dp.parse("March 14, -1980 21:14:50", d);
  643. assert(d.year == 1980);
  644. assert(d.month == 3);
  645. assert(d.day == 14);
  646. assert(d.hour == 21);
  647. assert(d.minute == 14);
  648. assert(d.second == 50);
  649. assert(d.ms == 0);
  650. assert(d.weekday == 0);
  651. assert(d.tzcorrection == int.min);
  652. dp.parse("Tue Apr 02 02:04:57 1996", d);
  653. assert(d.year == 1996);
  654. assert(d.month == 4);
  655. assert(d.day == 2);
  656. assert(d.hour == 2);
  657. assert(d.minute == 4);
  658. assert(d.second == 57);
  659. assert(d.ms == 0);
  660. assert(d.weekday == 3);
  661. assert(d.tzcorrection == int.min);
  662. dp.parse("Tue, 02 Apr 1996 02:04:57 G.M.T.", d);
  663. assert(d.year == 1996);
  664. assert(d.month == 4);
  665. assert(d.day == 2);
  666. assert(d.hour == 2);
  667. assert(d.minute == 4);
  668. assert(d.second == 57);
  669. assert(d.ms == 0);
  670. assert(d.weekday == 3);
  671. assert(d.tzcorrection == 0);
  672. dp.parse("December 31, 3000", d);
  673. assert(d.year == 3000);
  674. assert(d.month == 12);
  675. assert(d.day == 31);
  676. assert(d.hour == 0);
  677. assert(d.minute == 0);
  678. assert(d.second == 0);
  679. assert(d.ms == 0);
  680. assert(d.weekday == 0);
  681. assert(d.tzcorrection == int.min);
  682. dp.parse("Wed, 31 Dec 1969 16:00:00 GMT", d);
  683. assert(d.year == 1969);
  684. assert(d.month == 12);
  685. assert(d.day == 31);
  686. assert(d.hour == 16);
  687. assert(d.minute == 0);
  688. assert(d.second == 0);
  689. assert(d.ms == 0);
  690. assert(d.weekday == 4);
  691. assert(d.tzcorrection == 0);
  692. dp.parse("1/1/1999 12:30 AM", d);
  693. assert(d.year == 1999);
  694. assert(d.month == 1);
  695. assert(d.day == 1);
  696. assert(d.hour == 0);
  697. assert(d.minute == 30);
  698. assert(d.second == 0);
  699. assert(d.ms == 0);
  700. assert(d.weekday == 0);
  701. assert(d.tzcorrection == int.min);
  702. dp.parse("Tue, 20 May 2003 15:38:58 +0530", d);
  703. assert(d.year == 2003);
  704. assert(d.month == 5);
  705. assert(d.day == 20);
  706. assert(d.hour == 15);
  707. assert(d.minute == 38);
  708. assert(d.second == 58);
  709. assert(d.ms == 0);
  710. assert(d.weekday == 3);
  711. assert(d.tzcorrection == -530);
  712. debug(dateparse) printf("year = %d, month = %d, day = %d\n%02d:%02d:%02d.%03d\nweekday = %d, tzcorrection = %d\n",
  713. d.year, d.month, d.day,
  714. d.hour, d.minute, d.second, d.ms,
  715. d.weekday, d.tzcorrection);
  716. }