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