Chris@16: Chris@16: #ifndef DATE_TIME_FORMAT_DATE_PARSER_HPP__ Chris@16: #define DATE_TIME_FORMAT_DATE_PARSER_HPP__ Chris@16: Chris@16: /* Copyright (c) 2004-2005 CrystalClear Software, Inc. Chris@16: * Use, modification and distribution is subject to the Chris@16: * Boost Software License, Version 1.0. (See accompanying Chris@16: * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) Chris@16: * Author: Jeff Garland, Bart Garst Chris@101: * $Date$ Chris@16: */ Chris@16: Chris@16: Chris@16: #include "boost/lexical_cast.hpp" Chris@16: #include "boost/date_time/string_parse_tree.hpp" Chris@16: #include "boost/date_time/strings_from_facet.hpp" Chris@16: #include "boost/date_time/special_values_parser.hpp" Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #ifndef BOOST_NO_STDC_NAMESPACE Chris@16: # include Chris@16: #else Chris@16: # include Chris@16: #endif Chris@16: Chris@16: #ifdef BOOST_NO_STDC_NAMESPACE Chris@16: namespace std { Chris@16: using ::isspace; Chris@16: using ::isdigit; Chris@16: } Chris@16: #endif Chris@16: namespace boost { namespace date_time { Chris@16: Chris@16: //! Helper function for parsing fixed length strings into integers Chris@16: /*! Will consume 'length' number of characters from stream. Consumed Chris@16: * character are transfered to parse_match_result struct. Chris@16: * Returns '-1' if no number can be parsed or incorrect number of Chris@16: * digits in stream. */ Chris@16: template Chris@16: inline Chris@16: int_type Chris@16: fixed_string_to_int(std::istreambuf_iterator& itr, Chris@16: std::istreambuf_iterator& stream_end, Chris@16: parse_match_result& mr, Chris@16: unsigned int length, Chris@16: const charT& fill_char) Chris@16: { Chris@16: //typedef std::basic_string string_type; Chris@16: unsigned int j = 0; Chris@16: //string_type s; Chris@16: while (j < length && itr != stream_end && Chris@16: (std::isdigit(*itr) || *itr == fill_char)) { Chris@16: if(*itr == fill_char) { Chris@16: /* Since a fill_char can be anything, we convert it to a zero. Chris@16: * lexical_cast will behave predictably when zero is used as fill. */ Chris@16: mr.cache += ('0'); Chris@16: } Chris@16: else { Chris@16: mr.cache += (*itr); Chris@16: } Chris@16: itr++; Chris@16: j++; Chris@16: } Chris@101: int_type i = static_cast(-1); Chris@16: // mr.cache will hold leading zeros. size() tells us when input is too short. Chris@16: if(mr.cache.size() < length) { Chris@16: return i; Chris@16: } Chris@16: try { Chris@16: i = boost::lexical_cast(mr.cache); Chris@16: }catch(bad_lexical_cast&){ Chris@16: // we want to return -1 if the cast fails so nothing to do here Chris@16: } Chris@16: return i; Chris@16: } Chris@16: Chris@16: //! Helper function for parsing fixed length strings into integers Chris@16: /*! Will consume 'length' number of characters from stream. Consumed Chris@16: * character are transfered to parse_match_result struct. Chris@16: * Returns '-1' if no number can be parsed or incorrect number of Chris@16: * digits in stream. */ Chris@16: template Chris@16: inline Chris@16: int_type Chris@16: fixed_string_to_int(std::istreambuf_iterator& itr, Chris@16: std::istreambuf_iterator& stream_end, Chris@16: parse_match_result& mr, Chris@16: unsigned int length) Chris@16: { Chris@16: return fixed_string_to_int(itr, stream_end, mr, length, '0'); Chris@16: } Chris@16: Chris@16: //! Helper function for parsing varied length strings into integers Chris@16: /*! Will consume 'max_length' characters from stream only if those Chris@16: * characters are digits. Returns '-1' if no number can be parsed. Chris@16: * Will not parse a number preceeded by a '+' or '-'. */ Chris@16: template Chris@16: inline Chris@16: int_type Chris@16: var_string_to_int(std::istreambuf_iterator& itr, Chris@16: const std::istreambuf_iterator& stream_end, Chris@16: unsigned int max_length) Chris@16: { Chris@16: typedef std::basic_string string_type; Chris@16: unsigned int j = 0; Chris@16: string_type s; Chris@16: while (itr != stream_end && (j < max_length) && std::isdigit(*itr)) { Chris@16: s += (*itr); Chris@16: ++itr; Chris@16: ++j; Chris@16: } Chris@101: int_type i = static_cast(-1); Chris@16: if(!s.empty()) { Chris@16: i = boost::lexical_cast(s); Chris@16: } Chris@16: return i; Chris@16: } Chris@16: Chris@16: Chris@16: //! Class with generic date parsing using a format string Chris@16: /*! The following is the set of recognized format specifiers Chris@16: - %a - Short weekday name Chris@16: - %A - Long weekday name Chris@16: - %b - Abbreviated month name Chris@16: - %B - Full month name Chris@16: - %d - Day of the month as decimal 01 to 31 Chris@16: - %j - Day of year as decimal from 001 to 366 Chris@16: - %m - Month name as a decimal 01 to 12 Chris@16: - %U - Week number 00 to 53 with first Sunday as the first day of week 1? Chris@16: - %w - Weekday as decimal number 0 to 6 where Sunday == 0 Chris@16: - %W - Week number 00 to 53 where Monday is first day of week 1 Chris@16: - %x - facet default date representation Chris@16: - %y - Year without the century - eg: 04 for 2004 Chris@16: - %Y - Year with century Chris@16: Chris@16: The weekday specifiers (%a and %A) do not add to the date construction, Chris@16: but they provide a way to skip over the weekday names for formats that Chris@16: provide them. Chris@16: Chris@16: todo -- Another interesting feature that this approach could provide is Chris@16: an option to fill in any missing fields with the current values Chris@16: from the clock. So if you have %m-%d the parser would detect Chris@16: the missing year value and fill it in using the clock. Chris@16: Chris@16: todo -- What to do with the %x. %x in the classic facet is just bad... Chris@16: Chris@16: */ Chris@16: template Chris@16: class format_date_parser Chris@16: { Chris@16: public: Chris@16: typedef std::basic_string string_type; Chris@16: typedef std::basic_istringstream stringstream_type; Chris@16: typedef std::istreambuf_iterator stream_itr_type; Chris@16: typedef typename string_type::const_iterator const_itr; Chris@16: typedef typename date_type::year_type year_type; Chris@16: typedef typename date_type::month_type month_type; Chris@16: typedef typename date_type::day_type day_type; Chris@16: typedef typename date_type::duration_type duration_type; Chris@16: typedef typename date_type::day_of_week_type day_of_week_type; Chris@16: typedef typename date_type::day_of_year_type day_of_year_type; Chris@16: typedef string_parse_tree parse_tree_type; Chris@16: typedef typename parse_tree_type::parse_match_result_type match_results; Chris@16: typedef std::vector > input_collection_type; Chris@16: Chris@16: // TODO sv_parser uses its default constructor - write the others Chris@16: Chris@16: format_date_parser(const string_type& format_str, Chris@16: const input_collection_type& month_short_names, Chris@16: const input_collection_type& month_long_names, Chris@16: const input_collection_type& weekday_short_names, Chris@16: const input_collection_type& weekday_long_names) : Chris@16: m_format(format_str), Chris@16: m_month_short_names(month_short_names, 1), Chris@16: m_month_long_names(month_long_names, 1), Chris@16: m_weekday_short_names(weekday_short_names), Chris@16: m_weekday_long_names(weekday_long_names) Chris@16: {} Chris@16: Chris@16: format_date_parser(const string_type& format_str, Chris@16: const std::locale& locale) : Chris@16: m_format(format_str), Chris@16: m_month_short_names(gather_month_strings(locale), 1), Chris@16: m_month_long_names(gather_month_strings(locale, false), 1), Chris@16: m_weekday_short_names(gather_weekday_strings(locale)), Chris@16: m_weekday_long_names(gather_weekday_strings(locale, false)) Chris@16: {} Chris@16: Chris@16: format_date_parser(const format_date_parser& fdp) Chris@16: { Chris@16: this->m_format = fdp.m_format; Chris@16: this->m_month_short_names = fdp.m_month_short_names; Chris@16: this->m_month_long_names = fdp.m_month_long_names; Chris@16: this->m_weekday_short_names = fdp.m_weekday_short_names; Chris@16: this->m_weekday_long_names = fdp.m_weekday_long_names; Chris@16: } Chris@16: Chris@16: string_type format() const Chris@16: { Chris@16: return m_format; Chris@16: } Chris@16: Chris@16: void format(string_type format_str) Chris@16: { Chris@16: m_format = format_str; Chris@16: } Chris@16: Chris@16: void short_month_names(const input_collection_type& month_names) Chris@16: { Chris@16: m_month_short_names = parse_tree_type(month_names, 1); Chris@16: } Chris@16: void long_month_names(const input_collection_type& month_names) Chris@16: { Chris@16: m_month_long_names = parse_tree_type(month_names, 1); Chris@16: } Chris@16: void short_weekday_names(const input_collection_type& weekday_names) Chris@16: { Chris@16: m_weekday_short_names = parse_tree_type(weekday_names); Chris@16: } Chris@16: void long_weekday_names(const input_collection_type& weekday_names) Chris@16: { Chris@16: m_weekday_long_names = parse_tree_type(weekday_names); Chris@16: } Chris@16: Chris@16: date_type Chris@16: parse_date(const string_type& value, Chris@16: const string_type& format_str, Chris@16: const special_values_parser& sv_parser) const Chris@16: { Chris@16: stringstream_type ss(value); Chris@16: stream_itr_type sitr(ss); Chris@16: stream_itr_type stream_end; Chris@16: return parse_date(sitr, stream_end, format_str, sv_parser); Chris@16: } Chris@16: Chris@16: date_type Chris@16: parse_date(std::istreambuf_iterator& sitr, Chris@16: std::istreambuf_iterator& stream_end, Chris@16: const special_values_parser& sv_parser) const Chris@16: { Chris@16: return parse_date(sitr, stream_end, m_format, sv_parser); Chris@16: } Chris@16: Chris@16: /*! Of all the objects that the format_date_parser can parse, only a Chris@16: * date can be a special value. Therefore, only parse_date checks Chris@16: * for special_values. */ Chris@16: date_type Chris@16: parse_date(std::istreambuf_iterator& sitr, Chris@16: std::istreambuf_iterator& stream_end, Chris@16: string_type format_str, Chris@16: const special_values_parser& sv_parser) const Chris@16: { Chris@16: bool use_current_char = false; Chris@16: Chris@16: // skip leading whitespace Chris@16: while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; } Chris@16: Chris@16: short year(0), month(0), day(0), day_of_year(0);// wkday(0); Chris@16: /* Initialized the following to their minimum values. These intermediate Chris@16: * objects are used so we get specific exceptions when part of the input Chris@16: * is unparsable. Chris@16: * Ex: "205-Jan-15" will throw a bad_year, "2005-Jsn-15"- bad_month, etc.*/ Chris@16: year_type t_year(1400); Chris@16: month_type t_month(1); Chris@16: day_type t_day(1); Chris@16: day_of_week_type wkday(0); Chris@16: Chris@16: Chris@16: const_itr itr(format_str.begin()); Chris@16: while (itr != format_str.end() && (sitr != stream_end)) { Chris@16: if (*itr == '%') { Chris@16: if ( ++itr == format_str.end()) Chris@16: break; Chris@16: if (*itr != '%') { Chris@16: switch(*itr) { Chris@16: case 'a': Chris@16: { Chris@16: //this value is just throw away. It could be used for Chris@16: //error checking potentially, but it isn't helpful in Chris@16: //actually constructing the date - we just need to get it Chris@16: //out of the stream Chris@16: match_results mr = m_weekday_short_names.match(sitr, stream_end); Chris@16: if(mr.current_match == match_results::PARSE_ERROR) { Chris@16: // check special_values Chris@16: if(sv_parser.match(sitr, stream_end, mr)) { Chris@16: return date_type(static_cast(mr.current_match)); Chris@16: } Chris@16: } Chris@16: wkday = mr.current_match; Chris@16: if (mr.has_remaining()) { Chris@16: use_current_char = true; Chris@16: } Chris@16: break; Chris@16: } Chris@16: case 'A': Chris@16: { Chris@16: //this value is just throw away. It could be used for Chris@16: //error checking potentially, but it isn't helpful in Chris@16: //actually constructing the date - we just need to get it Chris@16: //out of the stream Chris@16: match_results mr = m_weekday_long_names.match(sitr, stream_end); Chris@16: if(mr.current_match == match_results::PARSE_ERROR) { Chris@16: // check special_values Chris@16: if(sv_parser.match(sitr, stream_end, mr)) { Chris@16: return date_type(static_cast(mr.current_match)); Chris@16: } Chris@16: } Chris@16: wkday = mr.current_match; Chris@16: if (mr.has_remaining()) { Chris@16: use_current_char = true; Chris@16: } Chris@16: break; Chris@16: } Chris@16: case 'b': Chris@16: { Chris@16: match_results mr = m_month_short_names.match(sitr, stream_end); Chris@16: if(mr.current_match == match_results::PARSE_ERROR) { Chris@16: // check special_values Chris@16: if(sv_parser.match(sitr, stream_end, mr)) { Chris@16: return date_type(static_cast(mr.current_match)); Chris@16: } Chris@16: } Chris@16: t_month = month_type(mr.current_match); Chris@16: if (mr.has_remaining()) { Chris@16: use_current_char = true; Chris@16: } Chris@16: break; Chris@16: } Chris@16: case 'B': Chris@16: { Chris@16: match_results mr = m_month_long_names.match(sitr, stream_end); Chris@16: if(mr.current_match == match_results::PARSE_ERROR) { Chris@16: // check special_values Chris@16: if(sv_parser.match(sitr, stream_end, mr)) { Chris@16: return date_type(static_cast(mr.current_match)); Chris@16: } Chris@16: } Chris@16: t_month = month_type(mr.current_match); Chris@16: if (mr.has_remaining()) { Chris@16: use_current_char = true; Chris@16: } Chris@16: break; Chris@16: } Chris@16: case 'd': Chris@16: { Chris@16: match_results mr; Chris@16: day = fixed_string_to_int(sitr, stream_end, mr, 2); Chris@16: if(day == -1) { Chris@16: if(sv_parser.match(sitr, stream_end, mr)) { Chris@16: return date_type(static_cast(mr.current_match)); Chris@16: } Chris@16: } Chris@16: t_day = day_type(day); Chris@16: break; Chris@16: } Chris@16: case 'e': Chris@16: { Chris@16: match_results mr; Chris@16: day = fixed_string_to_int(sitr, stream_end, mr, 2, ' '); Chris@16: if(day == -1) { Chris@16: if(sv_parser.match(sitr, stream_end, mr)) { Chris@16: return date_type(static_cast(mr.current_match)); Chris@16: } Chris@16: } Chris@16: t_day = day_type(day); Chris@16: break; Chris@16: } Chris@16: case 'j': Chris@16: { Chris@16: match_results mr; Chris@16: day_of_year = fixed_string_to_int(sitr, stream_end, mr, 3); Chris@16: if(day_of_year == -1) { Chris@16: if(sv_parser.match(sitr, stream_end, mr)) { Chris@16: return date_type(static_cast(mr.current_match)); Chris@16: } Chris@16: } Chris@16: // these next two lines are so we get an exception with bad input Chris@16: day_of_year_type t_day_of_year(1); Chris@16: t_day_of_year = day_of_year_type(day_of_year); Chris@16: break; Chris@16: } Chris@16: case 'm': Chris@16: { Chris@16: match_results mr; Chris@16: month = fixed_string_to_int(sitr, stream_end, mr, 2); Chris@16: if(month == -1) { Chris@16: if(sv_parser.match(sitr, stream_end, mr)) { Chris@16: return date_type(static_cast(mr.current_match)); Chris@16: } Chris@16: } Chris@16: t_month = month_type(month); Chris@16: break; Chris@16: } Chris@16: case 'Y': Chris@16: { Chris@16: match_results mr; Chris@16: year = fixed_string_to_int(sitr, stream_end, mr, 4); Chris@16: if(year == -1) { Chris@16: if(sv_parser.match(sitr, stream_end, mr)) { Chris@16: return date_type(static_cast(mr.current_match)); Chris@16: } Chris@16: } Chris@16: t_year = year_type(year); Chris@16: break; Chris@16: } Chris@16: case 'y': Chris@16: { Chris@16: match_results mr; Chris@16: year = fixed_string_to_int(sitr, stream_end, mr, 2); Chris@16: if(year == -1) { Chris@16: if(sv_parser.match(sitr, stream_end, mr)) { Chris@16: return date_type(static_cast(mr.current_match)); Chris@16: } Chris@16: } Chris@16: year += 2000; //make 2 digit years in this century Chris@16: t_year = year_type(year); Chris@16: break; Chris@16: } Chris@16: default: Chris@16: {} //ignore those we don't understand Chris@16: Chris@16: }//switch Chris@16: Chris@16: } Chris@16: else { // itr == '%', second consecutive Chris@16: sitr++; Chris@16: } Chris@16: Chris@16: itr++; //advance past format specifier Chris@16: } Chris@16: else { //skip past chars in format and in buffer Chris@16: itr++; Chris@16: if (use_current_char) { Chris@16: use_current_char = false; Chris@16: } Chris@16: else { Chris@16: sitr++; Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: if (day_of_year > 0) { Chris@16: date_type d(static_cast(year-1),12,31); //end of prior year Chris@16: return d + duration_type(day_of_year); Chris@16: } Chris@16: Chris@16: return date_type(t_year, t_month, t_day); // exceptions were thrown earlier Chris@16: // if input was no good Chris@16: } Chris@16: Chris@16: //! Throws bad_month if unable to parse Chris@16: month_type Chris@16: parse_month(std::istreambuf_iterator& sitr, Chris@16: std::istreambuf_iterator& stream_end, Chris@16: string_type format_str) const Chris@16: { Chris@16: match_results mr; Chris@16: return parse_month(sitr, stream_end, format_str, mr); Chris@16: } Chris@16: Chris@16: //! Throws bad_month if unable to parse Chris@16: month_type Chris@16: parse_month(std::istreambuf_iterator& sitr, Chris@16: std::istreambuf_iterator& stream_end, Chris@16: string_type format_str, Chris@16: match_results& mr) const Chris@16: { Chris@16: bool use_current_char = false; Chris@16: Chris@16: // skip leading whitespace Chris@16: while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; } Chris@16: Chris@16: short month(0); Chris@16: Chris@16: const_itr itr(format_str.begin()); Chris@16: while (itr != format_str.end() && (sitr != stream_end)) { Chris@16: if (*itr == '%') { Chris@16: if ( ++itr == format_str.end()) Chris@16: break; Chris@16: if (*itr != '%') { Chris@16: switch(*itr) { Chris@16: case 'b': Chris@16: { Chris@16: mr = m_month_short_names.match(sitr, stream_end); Chris@16: month = mr.current_match; Chris@16: if (mr.has_remaining()) { Chris@16: use_current_char = true; Chris@16: } Chris@16: break; Chris@16: } Chris@16: case 'B': Chris@16: { Chris@16: mr = m_month_long_names.match(sitr, stream_end); Chris@16: month = mr.current_match; Chris@16: if (mr.has_remaining()) { Chris@16: use_current_char = true; Chris@16: } Chris@16: break; Chris@16: } Chris@16: case 'm': Chris@16: { Chris@16: month = var_string_to_int(sitr, stream_end, 2); Chris@16: // var_string_to_int returns -1 if parse failed. That will Chris@16: // cause a bad_month exception to be thrown so we do nothing here Chris@16: break; Chris@16: } Chris@16: default: Chris@16: {} //ignore those we don't understand Chris@16: Chris@16: }//switch Chris@16: Chris@16: } Chris@16: else { // itr == '%', second consecutive Chris@16: sitr++; Chris@16: } Chris@16: Chris@16: itr++; //advance past format specifier Chris@16: } Chris@16: else { //skip past chars in format and in buffer Chris@16: itr++; Chris@16: if (use_current_char) { Chris@16: use_current_char = false; Chris@16: } Chris@16: else { Chris@16: sitr++; Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: return month_type(month); // throws bad_month exception when values are zero Chris@16: } Chris@16: Chris@16: //! Expects 1 or 2 digits 1-31. Throws bad_day_of_month if unable to parse Chris@16: day_type Chris@16: parse_var_day_of_month(std::istreambuf_iterator& sitr, Chris@16: std::istreambuf_iterator& stream_end) const Chris@16: { Chris@16: // skip leading whitespace Chris@16: while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; } Chris@16: Chris@16: return day_type(var_string_to_int(sitr, stream_end, 2)); Chris@16: } Chris@16: //! Expects 2 digits 01-31. Throws bad_day_of_month if unable to parse Chris@16: day_type Chris@16: parse_day_of_month(std::istreambuf_iterator& sitr, Chris@16: std::istreambuf_iterator& stream_end) const Chris@16: { Chris@16: // skip leading whitespace Chris@16: while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; } Chris@16: Chris@16: //return day_type(var_string_to_int(sitr, stream_end, 2)); Chris@16: match_results mr; Chris@16: return day_type(fixed_string_to_int(sitr, stream_end, mr, 2)); Chris@16: } Chris@16: Chris@16: day_of_week_type Chris@16: parse_weekday(std::istreambuf_iterator& sitr, Chris@16: std::istreambuf_iterator& stream_end, Chris@16: string_type format_str) const Chris@16: { Chris@16: match_results mr; Chris@16: return parse_weekday(sitr, stream_end, format_str, mr); Chris@16: } Chris@16: day_of_week_type Chris@16: parse_weekday(std::istreambuf_iterator& sitr, Chris@16: std::istreambuf_iterator& stream_end, Chris@16: string_type format_str, Chris@16: match_results& mr) const Chris@16: { Chris@16: bool use_current_char = false; Chris@16: Chris@16: // skip leading whitespace Chris@16: while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; } Chris@16: Chris@16: short wkday(0); Chris@16: Chris@16: const_itr itr(format_str.begin()); Chris@16: while (itr != format_str.end() && (sitr != stream_end)) { Chris@16: if (*itr == '%') { Chris@16: if ( ++itr == format_str.end()) Chris@16: break; Chris@16: if (*itr != '%') { Chris@16: switch(*itr) { Chris@16: case 'a': Chris@16: { Chris@16: //this value is just throw away. It could be used for Chris@16: //error checking potentially, but it isn't helpful in Chris@16: //actually constructing the date - we just need to get it Chris@16: //out of the stream Chris@16: mr = m_weekday_short_names.match(sitr, stream_end); Chris@16: wkday = mr.current_match; Chris@16: if (mr.has_remaining()) { Chris@16: use_current_char = true; Chris@16: } Chris@16: break; Chris@16: } Chris@16: case 'A': Chris@16: { Chris@16: //this value is just throw away. It could be used for Chris@16: //error checking potentially, but it isn't helpful in Chris@16: //actually constructing the date - we just need to get it Chris@16: //out of the stream Chris@16: mr = m_weekday_long_names.match(sitr, stream_end); Chris@16: wkday = mr.current_match; Chris@16: if (mr.has_remaining()) { Chris@16: use_current_char = true; Chris@16: } Chris@16: break; Chris@16: } Chris@16: case 'w': Chris@16: { Chris@16: // weekday as number 0-6, Sunday == 0 Chris@16: wkday = var_string_to_int(sitr, stream_end, 2); Chris@16: break; Chris@16: } Chris@16: default: Chris@16: {} //ignore those we don't understand Chris@16: Chris@16: }//switch Chris@16: Chris@16: } Chris@16: else { // itr == '%', second consecutive Chris@16: sitr++; Chris@16: } Chris@16: Chris@16: itr++; //advance past format specifier Chris@16: } Chris@16: else { //skip past chars in format and in buffer Chris@16: itr++; Chris@16: if (use_current_char) { Chris@16: use_current_char = false; Chris@16: } Chris@16: else { Chris@16: sitr++; Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: return day_of_week_type(wkday); // throws bad_day_of_month exception Chris@16: // when values are zero Chris@16: } Chris@16: Chris@16: //! throws bad_year if unable to parse Chris@16: year_type Chris@16: parse_year(std::istreambuf_iterator& sitr, Chris@16: std::istreambuf_iterator& stream_end, Chris@16: string_type format_str) const Chris@16: { Chris@16: match_results mr; Chris@16: return parse_year(sitr, stream_end, format_str, mr); Chris@16: } Chris@16: Chris@16: //! throws bad_year if unable to parse Chris@16: year_type Chris@16: parse_year(std::istreambuf_iterator& sitr, Chris@16: std::istreambuf_iterator& stream_end, Chris@16: string_type format_str, Chris@16: match_results& mr) const Chris@16: { Chris@16: bool use_current_char = false; Chris@16: Chris@16: // skip leading whitespace Chris@16: while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; } Chris@16: Chris@16: unsigned short year(0); Chris@16: Chris@16: const_itr itr(format_str.begin()); Chris@16: while (itr != format_str.end() && (sitr != stream_end)) { Chris@16: if (*itr == '%') { Chris@16: if ( ++itr == format_str.end()) Chris@16: break; Chris@16: if (*itr != '%') { Chris@16: //match_results mr; Chris@16: switch(*itr) { Chris@16: case 'Y': Chris@16: { Chris@16: // year from 4 digit string Chris@16: year = fixed_string_to_int(sitr, stream_end, mr, 4); Chris@16: break; Chris@16: } Chris@16: case 'y': Chris@16: { Chris@16: // year from 2 digit string (no century) Chris@16: year = fixed_string_to_int(sitr, stream_end, mr, 2); Chris@16: year += 2000; //make 2 digit years in this century Chris@16: break; Chris@16: } Chris@16: default: Chris@16: {} //ignore those we don't understand Chris@16: Chris@16: }//switch Chris@16: Chris@16: } Chris@16: else { // itr == '%', second consecutive Chris@16: sitr++; Chris@16: } Chris@16: Chris@16: itr++; //advance past format specifier Chris@16: } Chris@16: else { //skip past chars in format and in buffer Chris@16: itr++; Chris@16: if (use_current_char) { Chris@16: use_current_char = false; Chris@16: } Chris@16: else { Chris@16: sitr++; Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: return year_type(year); // throws bad_year exception when values are zero Chris@16: } Chris@16: Chris@16: Chris@16: private: Chris@16: string_type m_format; Chris@16: parse_tree_type m_month_short_names; Chris@16: parse_tree_type m_month_long_names; Chris@16: parse_tree_type m_weekday_short_names; Chris@16: parse_tree_type m_weekday_long_names; Chris@16: Chris@16: }; Chris@16: Chris@16: } } //namespace Chris@16: Chris@16: #endif Chris@16: Chris@16: Chris@16: