format_date_parser.hpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. #ifndef DATE_TIME_FORMAT_DATE_PARSER_HPP__
  2. #define DATE_TIME_FORMAT_DATE_PARSER_HPP__
  3. /* Copyright (c) 2004-2005 CrystalClear Software, Inc.
  4. * Use, modification and distribution is subject to the
  5. * Boost Software License, Version 1.0. (See accompanying
  6. * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
  7. * Author: Jeff Garland, Bart Garst
  8. * $Date: 2009-06-04 04:24:49 -0400 (Thu, 04 Jun 2009) $
  9. */
  10. #include "boost/lexical_cast.hpp"
  11. #include "boost/date_time/string_parse_tree.hpp"
  12. #include "boost/date_time/strings_from_facet.hpp"
  13. #include "boost/date_time/special_values_parser.hpp"
  14. #include <string>
  15. #include <vector>
  16. #include <sstream>
  17. #include <iterator>
  18. #ifndef BOOST_NO_STDC_NAMESPACE
  19. # include <cctype>
  20. #else
  21. # include <ctype.h>
  22. #endif
  23. #ifdef BOOST_NO_STDC_NAMESPACE
  24. namespace std {
  25. using ::isspace;
  26. using ::isdigit;
  27. }
  28. #endif
  29. namespace boost { namespace date_time {
  30. //! Helper function for parsing fixed length strings into integers
  31. /*! Will consume 'length' number of characters from stream. Consumed
  32. * character are transfered to parse_match_result struct.
  33. * Returns '-1' if no number can be parsed or incorrect number of
  34. * digits in stream. */
  35. template<typename int_type, typename charT>
  36. inline
  37. int_type
  38. fixed_string_to_int(std::istreambuf_iterator<charT>& itr,
  39. std::istreambuf_iterator<charT>& stream_end,
  40. parse_match_result<charT>& mr,
  41. unsigned int length,
  42. const charT& fill_char)
  43. {
  44. //typedef std::basic_string<charT> string_type;
  45. unsigned int j = 0;
  46. //string_type s;
  47. while (j < length && itr != stream_end &&
  48. (std::isdigit(*itr) || *itr == fill_char)) {
  49. if(*itr == fill_char) {
  50. /* Since a fill_char can be anything, we convert it to a zero.
  51. * lexical_cast will behave predictably when zero is used as fill. */
  52. mr.cache += ('0');
  53. }
  54. else {
  55. mr.cache += (*itr);
  56. }
  57. itr++;
  58. j++;
  59. }
  60. int_type i = -1;
  61. // mr.cache will hold leading zeros. size() tells us when input is too short.
  62. if(mr.cache.size() < length) {
  63. return i;
  64. }
  65. try {
  66. i = boost::lexical_cast<int_type>(mr.cache);
  67. }catch(bad_lexical_cast&){
  68. // we want to return -1 if the cast fails so nothing to do here
  69. }
  70. return i;
  71. }
  72. //! Helper function for parsing fixed length strings into integers
  73. /*! Will consume 'length' number of characters from stream. Consumed
  74. * character are transfered to parse_match_result struct.
  75. * Returns '-1' if no number can be parsed or incorrect number of
  76. * digits in stream. */
  77. template<typename int_type, typename charT>
  78. inline
  79. int_type
  80. fixed_string_to_int(std::istreambuf_iterator<charT>& itr,
  81. std::istreambuf_iterator<charT>& stream_end,
  82. parse_match_result<charT>& mr,
  83. unsigned int length)
  84. {
  85. return fixed_string_to_int<int_type, charT>(itr, stream_end, mr, length, '0');
  86. }
  87. //! Helper function for parsing varied length strings into integers
  88. /*! Will consume 'max_length' characters from stream only if those
  89. * characters are digits. Returns '-1' if no number can be parsed.
  90. * Will not parse a number preceeded by a '+' or '-'. */
  91. template<typename int_type, typename charT>
  92. inline
  93. int_type
  94. var_string_to_int(std::istreambuf_iterator<charT>& itr,
  95. const std::istreambuf_iterator<charT>& stream_end,
  96. unsigned int max_length)
  97. {
  98. typedef std::basic_string<charT> string_type;
  99. unsigned int j = 0;
  100. string_type s;
  101. while (itr != stream_end && (j < max_length) && std::isdigit(*itr)) {
  102. s += (*itr);
  103. ++itr;
  104. ++j;
  105. }
  106. int_type i = -1;
  107. if(!s.empty()) {
  108. i = boost::lexical_cast<int_type>(s);
  109. }
  110. return i;
  111. }
  112. //! Class with generic date parsing using a format string
  113. /*! The following is the set of recognized format specifiers
  114. - %a - Short weekday name
  115. - %A - Long weekday name
  116. - %b - Abbreviated month name
  117. - %B - Full month name
  118. - %d - Day of the month as decimal 01 to 31
  119. - %j - Day of year as decimal from 001 to 366
  120. - %m - Month name as a decimal 01 to 12
  121. - %U - Week number 00 to 53 with first Sunday as the first day of week 1?
  122. - %w - Weekday as decimal number 0 to 6 where Sunday == 0
  123. - %W - Week number 00 to 53 where Monday is first day of week 1
  124. - %x - facet default date representation
  125. - %y - Year without the century - eg: 04 for 2004
  126. - %Y - Year with century
  127. The weekday specifiers (%a and %A) do not add to the date construction,
  128. but they provide a way to skip over the weekday names for formats that
  129. provide them.
  130. todo -- Another interesting feature that this approach could provide is
  131. an option to fill in any missing fields with the current values
  132. from the clock. So if you have %m-%d the parser would detect
  133. the missing year value and fill it in using the clock.
  134. todo -- What to do with the %x. %x in the classic facet is just bad...
  135. */
  136. template<class date_type, typename charT>
  137. class format_date_parser
  138. {
  139. public:
  140. typedef std::basic_string<charT> string_type;
  141. typedef std::basic_istringstream<charT> stringstream_type;
  142. typedef std::istreambuf_iterator<charT> stream_itr_type;
  143. typedef typename string_type::const_iterator const_itr;
  144. typedef typename date_type::year_type year_type;
  145. typedef typename date_type::month_type month_type;
  146. typedef typename date_type::day_type day_type;
  147. typedef typename date_type::duration_type duration_type;
  148. typedef typename date_type::day_of_week_type day_of_week_type;
  149. typedef typename date_type::day_of_year_type day_of_year_type;
  150. typedef string_parse_tree<charT> parse_tree_type;
  151. typedef typename parse_tree_type::parse_match_result_type match_results;
  152. typedef std::vector<std::basic_string<charT> > input_collection_type;
  153. // TODO sv_parser uses its default constructor - write the others
  154. format_date_parser(const string_type& format_str,
  155. const input_collection_type& month_short_names,
  156. const input_collection_type& month_long_names,
  157. const input_collection_type& weekday_short_names,
  158. const input_collection_type& weekday_long_names) :
  159. m_format(format_str),
  160. m_month_short_names(month_short_names, 1),
  161. m_month_long_names(month_long_names, 1),
  162. m_weekday_short_names(weekday_short_names),
  163. m_weekday_long_names(weekday_long_names)
  164. {}
  165. format_date_parser(const string_type& format_str,
  166. const std::locale& locale) :
  167. m_format(format_str),
  168. m_month_short_names(gather_month_strings<charT>(locale), 1),
  169. m_month_long_names(gather_month_strings<charT>(locale, false), 1),
  170. m_weekday_short_names(gather_weekday_strings<charT>(locale)),
  171. m_weekday_long_names(gather_weekday_strings<charT>(locale, false))
  172. {}
  173. format_date_parser(const format_date_parser<date_type,charT>& fdp)
  174. {
  175. this->m_format = fdp.m_format;
  176. this->m_month_short_names = fdp.m_month_short_names;
  177. this->m_month_long_names = fdp.m_month_long_names;
  178. this->m_weekday_short_names = fdp.m_weekday_short_names;
  179. this->m_weekday_long_names = fdp.m_weekday_long_names;
  180. }
  181. string_type format() const
  182. {
  183. return m_format;
  184. }
  185. void format(string_type format_str)
  186. {
  187. m_format = format_str;
  188. }
  189. void short_month_names(const input_collection_type& month_names)
  190. {
  191. m_month_short_names = parse_tree_type(month_names, 1);
  192. }
  193. void long_month_names(const input_collection_type& month_names)
  194. {
  195. m_month_long_names = parse_tree_type(month_names, 1);
  196. }
  197. void short_weekday_names(const input_collection_type& weekday_names)
  198. {
  199. m_weekday_short_names = parse_tree_type(weekday_names);
  200. }
  201. void long_weekday_names(const input_collection_type& weekday_names)
  202. {
  203. m_weekday_long_names = parse_tree_type(weekday_names);
  204. }
  205. date_type
  206. parse_date(const string_type& value,
  207. const string_type& format_str,
  208. const special_values_parser<date_type,charT>& sv_parser) const
  209. {
  210. stringstream_type ss(value);
  211. stream_itr_type sitr(ss);
  212. stream_itr_type stream_end;
  213. return parse_date(sitr, stream_end, format_str, sv_parser);
  214. }
  215. date_type
  216. parse_date(std::istreambuf_iterator<charT>& sitr,
  217. std::istreambuf_iterator<charT>& stream_end,
  218. const special_values_parser<date_type,charT>& sv_parser) const
  219. {
  220. return parse_date(sitr, stream_end, m_format, sv_parser);
  221. }
  222. /*! Of all the objects that the format_date_parser can parse, only a
  223. * date can be a special value. Therefore, only parse_date checks
  224. * for special_values. */
  225. date_type
  226. parse_date(std::istreambuf_iterator<charT>& sitr,
  227. std::istreambuf_iterator<charT>& stream_end,
  228. string_type format_str,
  229. const special_values_parser<date_type,charT>& sv_parser) const
  230. {
  231. bool use_current_char = false;
  232. // skip leading whitespace
  233. while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }
  234. charT current_char = *sitr;
  235. short year(0), month(0), day(0), day_of_year(0);// wkday(0);
  236. /* Initialized the following to their minimum values. These intermediate
  237. * objects are used so we get specific exceptions when part of the input
  238. * is unparsable.
  239. * Ex: "205-Jan-15" will throw a bad_year, "2005-Jsn-15"- bad_month, etc.*/
  240. year_type t_year(1400);
  241. month_type t_month(1);
  242. day_type t_day(1);
  243. day_of_week_type wkday(0);
  244. const_itr itr(format_str.begin());
  245. while (itr != format_str.end() && (sitr != stream_end)) {
  246. if (*itr == '%') {
  247. itr++;
  248. if (*itr != '%') {
  249. switch(*itr) {
  250. case 'a':
  251. {
  252. //this value is just throw away. It could be used for
  253. //error checking potentially, but it isn't helpful in
  254. //actually constructing the date - we just need to get it
  255. //out of the stream
  256. match_results mr = m_weekday_short_names.match(sitr, stream_end);
  257. if(mr.current_match == match_results::PARSE_ERROR) {
  258. // check special_values
  259. if(sv_parser.match(sitr, stream_end, mr)) {
  260. return date_type(static_cast<special_values>(mr.current_match));
  261. }
  262. }
  263. wkday = mr.current_match;
  264. if (mr.has_remaining()) {
  265. current_char = mr.last_char();
  266. use_current_char = true;
  267. }
  268. break;
  269. }
  270. case 'A':
  271. {
  272. //this value is just throw away. It could be used for
  273. //error checking potentially, but it isn't helpful in
  274. //actually constructing the date - we just need to get it
  275. //out of the stream
  276. match_results mr = m_weekday_long_names.match(sitr, stream_end);
  277. if(mr.current_match == match_results::PARSE_ERROR) {
  278. // check special_values
  279. if(sv_parser.match(sitr, stream_end, mr)) {
  280. return date_type(static_cast<special_values>(mr.current_match));
  281. }
  282. }
  283. wkday = mr.current_match;
  284. if (mr.has_remaining()) {
  285. current_char = mr.last_char();
  286. use_current_char = true;
  287. }
  288. break;
  289. }
  290. case 'b':
  291. {
  292. match_results mr = m_month_short_names.match(sitr, stream_end);
  293. if(mr.current_match == match_results::PARSE_ERROR) {
  294. // check special_values
  295. if(sv_parser.match(sitr, stream_end, mr)) {
  296. return date_type(static_cast<special_values>(mr.current_match));
  297. }
  298. }
  299. t_month = month_type(mr.current_match);
  300. if (mr.has_remaining()) {
  301. current_char = mr.last_char();
  302. use_current_char = true;
  303. }
  304. break;
  305. }
  306. case 'B':
  307. {
  308. match_results mr = m_month_long_names.match(sitr, stream_end);
  309. if(mr.current_match == match_results::PARSE_ERROR) {
  310. // check special_values
  311. if(sv_parser.match(sitr, stream_end, mr)) {
  312. return date_type(static_cast<special_values>(mr.current_match));
  313. }
  314. }
  315. t_month = month_type(mr.current_match);
  316. if (mr.has_remaining()) {
  317. current_char = mr.last_char();
  318. use_current_char = true;
  319. }
  320. break;
  321. }
  322. case 'd':
  323. {
  324. match_results mr;
  325. day = fixed_string_to_int<short, charT>(sitr, stream_end, mr, 2);
  326. if(day == -1) {
  327. if(sv_parser.match(sitr, stream_end, mr)) {
  328. return date_type(static_cast<special_values>(mr.current_match));
  329. }
  330. }
  331. t_day = day_type(day);
  332. break;
  333. }
  334. case 'e':
  335. {
  336. match_results mr;
  337. day = fixed_string_to_int<short, charT>(sitr, stream_end, mr, 2, ' ');
  338. if(day == -1) {
  339. if(sv_parser.match(sitr, stream_end, mr)) {
  340. return date_type(static_cast<special_values>(mr.current_match));
  341. }
  342. }
  343. t_day = day_type(day);
  344. break;
  345. }
  346. case 'j':
  347. {
  348. match_results mr;
  349. day_of_year = fixed_string_to_int<short, charT>(sitr, stream_end, mr, 3);
  350. if(day_of_year == -1) {
  351. if(sv_parser.match(sitr, stream_end, mr)) {
  352. return date_type(static_cast<special_values>(mr.current_match));
  353. }
  354. }
  355. // these next two lines are so we get an exception with bad input
  356. day_of_year_type t_day_of_year(1);
  357. t_day_of_year = day_of_year_type(day_of_year);
  358. break;
  359. }
  360. case 'm':
  361. {
  362. match_results mr;
  363. month = fixed_string_to_int<short, charT>(sitr, stream_end, mr, 2);
  364. if(month == -1) {
  365. if(sv_parser.match(sitr, stream_end, mr)) {
  366. return date_type(static_cast<special_values>(mr.current_match));
  367. }
  368. }
  369. t_month = month_type(month);
  370. break;
  371. }
  372. case 'Y':
  373. {
  374. match_results mr;
  375. year = fixed_string_to_int<short, charT>(sitr, stream_end, mr, 4);
  376. if(year == -1) {
  377. if(sv_parser.match(sitr, stream_end, mr)) {
  378. return date_type(static_cast<special_values>(mr.current_match));
  379. }
  380. }
  381. t_year = year_type(year);
  382. break;
  383. }
  384. case 'y':
  385. {
  386. match_results mr;
  387. year = fixed_string_to_int<short, charT>(sitr, stream_end, mr, 2);
  388. if(year == -1) {
  389. if(sv_parser.match(sitr, stream_end, mr)) {
  390. return date_type(static_cast<special_values>(mr.current_match));
  391. }
  392. }
  393. year += 2000; //make 2 digit years in this century
  394. t_year = year_type(year);
  395. break;
  396. }
  397. default:
  398. {} //ignore those we don't understand
  399. }//switch
  400. }
  401. else { // itr == '%', second consecutive
  402. sitr++;
  403. }
  404. itr++; //advance past format specifier
  405. }
  406. else { //skip past chars in format and in buffer
  407. itr++;
  408. if (use_current_char) {
  409. use_current_char = false;
  410. current_char = *sitr;
  411. }
  412. else {
  413. sitr++;
  414. }
  415. }
  416. }
  417. if (day_of_year > 0) {
  418. date_type d(static_cast<unsigned short>(year-1),12,31); //end of prior year
  419. return d + duration_type(day_of_year);
  420. }
  421. return date_type(t_year, t_month, t_day); // exceptions were thrown earlier
  422. // if input was no good
  423. }
  424. //! Throws bad_month if unable to parse
  425. month_type
  426. parse_month(std::istreambuf_iterator<charT>& sitr,
  427. std::istreambuf_iterator<charT>& stream_end,
  428. string_type format_str) const
  429. {
  430. match_results mr;
  431. return parse_month(sitr, stream_end, format_str, mr);
  432. }
  433. //! Throws bad_month if unable to parse
  434. month_type
  435. parse_month(std::istreambuf_iterator<charT>& sitr,
  436. std::istreambuf_iterator<charT>& stream_end,
  437. string_type format_str,
  438. match_results& mr) const
  439. {
  440. bool use_current_char = false;
  441. // skip leading whitespace
  442. while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }
  443. charT current_char = *sitr;
  444. short month(0);
  445. const_itr itr(format_str.begin());
  446. while (itr != format_str.end() && (sitr != stream_end)) {
  447. if (*itr == '%') {
  448. itr++;
  449. if (*itr != '%') {
  450. switch(*itr) {
  451. case 'b':
  452. {
  453. mr = m_month_short_names.match(sitr, stream_end);
  454. month = mr.current_match;
  455. if (mr.has_remaining()) {
  456. current_char = mr.last_char();
  457. use_current_char = true;
  458. }
  459. break;
  460. }
  461. case 'B':
  462. {
  463. mr = m_month_long_names.match(sitr, stream_end);
  464. month = mr.current_match;
  465. if (mr.has_remaining()) {
  466. current_char = mr.last_char();
  467. use_current_char = true;
  468. }
  469. break;
  470. }
  471. case 'm':
  472. {
  473. month = var_string_to_int<short, charT>(sitr, stream_end, 2);
  474. // var_string_to_int returns -1 if parse failed. That will
  475. // cause a bad_month exception to be thrown so we do nothing here
  476. break;
  477. }
  478. default:
  479. {} //ignore those we don't understand
  480. }//switch
  481. }
  482. else { // itr == '%', second consecutive
  483. sitr++;
  484. }
  485. itr++; //advance past format specifier
  486. }
  487. else { //skip past chars in format and in buffer
  488. itr++;
  489. if (use_current_char) {
  490. use_current_char = false;
  491. current_char = *sitr;
  492. }
  493. else {
  494. sitr++;
  495. }
  496. }
  497. }
  498. return month_type(month); // throws bad_month exception when values are zero
  499. }
  500. //! Expects 1 or 2 digits 1-31. Throws bad_day_of_month if unable to parse
  501. day_type
  502. parse_var_day_of_month(std::istreambuf_iterator<charT>& sitr,
  503. std::istreambuf_iterator<charT>& stream_end) const
  504. {
  505. // skip leading whitespace
  506. while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }
  507. return day_type(var_string_to_int<short, charT>(sitr, stream_end, 2));
  508. }
  509. //! Expects 2 digits 01-31. Throws bad_day_of_month if unable to parse
  510. day_type
  511. parse_day_of_month(std::istreambuf_iterator<charT>& sitr,
  512. std::istreambuf_iterator<charT>& stream_end) const
  513. {
  514. // skip leading whitespace
  515. while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }
  516. //return day_type(var_string_to_int<short, charT>(sitr, stream_end, 2));
  517. match_results mr;
  518. return day_type(fixed_string_to_int<short, charT>(sitr, stream_end, mr, 2));
  519. }
  520. day_of_week_type
  521. parse_weekday(std::istreambuf_iterator<charT>& sitr,
  522. std::istreambuf_iterator<charT>& stream_end,
  523. string_type format_str) const
  524. {
  525. match_results mr;
  526. return parse_weekday(sitr, stream_end, format_str, mr);
  527. }
  528. day_of_week_type
  529. parse_weekday(std::istreambuf_iterator<charT>& sitr,
  530. std::istreambuf_iterator<charT>& stream_end,
  531. string_type format_str,
  532. match_results& mr) const
  533. {
  534. bool use_current_char = false;
  535. // skip leading whitespace
  536. while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }
  537. charT current_char = *sitr;
  538. short wkday(0);
  539. const_itr itr(format_str.begin());
  540. while (itr != format_str.end() && (sitr != stream_end)) {
  541. if (*itr == '%') {
  542. itr++;
  543. if (*itr != '%') {
  544. switch(*itr) {
  545. case 'a':
  546. {
  547. //this value is just throw away. It could be used for
  548. //error checking potentially, but it isn't helpful in
  549. //actually constructing the date - we just need to get it
  550. //out of the stream
  551. mr = m_weekday_short_names.match(sitr, stream_end);
  552. wkday = mr.current_match;
  553. if (mr.has_remaining()) {
  554. current_char = mr.last_char();
  555. use_current_char = true;
  556. }
  557. break;
  558. }
  559. case 'A':
  560. {
  561. //this value is just throw away. It could be used for
  562. //error checking potentially, but it isn't helpful in
  563. //actually constructing the date - we just need to get it
  564. //out of the stream
  565. mr = m_weekday_long_names.match(sitr, stream_end);
  566. wkday = mr.current_match;
  567. if (mr.has_remaining()) {
  568. current_char = mr.last_char();
  569. use_current_char = true;
  570. }
  571. break;
  572. }
  573. case 'w':
  574. {
  575. // weekday as number 0-6, Sunday == 0
  576. wkday = var_string_to_int<short, charT>(sitr, stream_end, 2);
  577. break;
  578. }
  579. default:
  580. {} //ignore those we don't understand
  581. }//switch
  582. }
  583. else { // itr == '%', second consecutive
  584. sitr++;
  585. }
  586. itr++; //advance past format specifier
  587. }
  588. else { //skip past chars in format and in buffer
  589. itr++;
  590. if (use_current_char) {
  591. use_current_char = false;
  592. current_char = *sitr;
  593. }
  594. else {
  595. sitr++;
  596. }
  597. }
  598. }
  599. return day_of_week_type(wkday); // throws bad_day_of_month exception
  600. // when values are zero
  601. }
  602. //! throws bad_year if unable to parse
  603. year_type
  604. parse_year(std::istreambuf_iterator<charT>& sitr,
  605. std::istreambuf_iterator<charT>& stream_end,
  606. string_type format_str) const
  607. {
  608. match_results mr;
  609. return parse_year(sitr, stream_end, format_str, mr);
  610. }
  611. //! throws bad_year if unable to parse
  612. year_type
  613. parse_year(std::istreambuf_iterator<charT>& sitr,
  614. std::istreambuf_iterator<charT>& stream_end,
  615. string_type format_str,
  616. match_results& mr) const
  617. {
  618. bool use_current_char = false;
  619. // skip leading whitespace
  620. while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }
  621. charT current_char = *sitr;
  622. unsigned short year(0);
  623. const_itr itr(format_str.begin());
  624. while (itr != format_str.end() && (sitr != stream_end)) {
  625. if (*itr == '%') {
  626. itr++;
  627. if (*itr != '%') {
  628. //match_results mr;
  629. switch(*itr) {
  630. case 'Y':
  631. {
  632. // year from 4 digit string
  633. year = fixed_string_to_int<short, charT>(sitr, stream_end, mr, 4);
  634. break;
  635. }
  636. case 'y':
  637. {
  638. // year from 2 digit string (no century)
  639. year = fixed_string_to_int<short, charT>(sitr, stream_end, mr, 2);
  640. year += 2000; //make 2 digit years in this century
  641. break;
  642. }
  643. default:
  644. {} //ignore those we don't understand
  645. }//switch
  646. }
  647. else { // itr == '%', second consecutive
  648. sitr++;
  649. }
  650. itr++; //advance past format specifier
  651. }
  652. else { //skip past chars in format and in buffer
  653. itr++;
  654. if (use_current_char) {
  655. use_current_char = false;
  656. current_char = *sitr;
  657. }
  658. else {
  659. sitr++;
  660. }
  661. }
  662. }
  663. return year_type(year); // throws bad_year exception when values are zero
  664. }
  665. private:
  666. string_type m_format;
  667. parse_tree_type m_month_short_names;
  668. parse_tree_type m_month_long_names;
  669. parse_tree_type m_weekday_short_names;
  670. parse_tree_type m_weekday_long_names;
  671. };
  672. } } //namespace
  673. #endif