PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/lphobos/std/dateparse.d

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