Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/boost/date_time/time_facet.hpp @ 13

Last change on this file since 13 was 12, checked in by landauf, 17 years ago

added boost

File size: 52.6 KB
Line 
1
2#ifndef _DATE_TIME_FACET__HPP__
3#define _DATE_TIME_FACET__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 or http://www.boost.org/LICENSE-1.0)
9 * Author:  Martin Andrian, Jeff Garland, Bart Garst
10 * $Date: 2005/07/17 23:57:59 $
11 */
12
13#include "boost/date_time/date_facet.hpp"
14#include "boost/date_time/string_convert.hpp"
15#include "boost/algorithm/string/erase.hpp"
16#include <sstream>
17#include <iomanip>
18#include <exception>
19
20namespace boost {
21namespace date_time {
22
23  template <class CharT>
24  struct time_formats {
25    public:
26      typedef CharT char_type;
27      static const char_type fractional_seconds_format[3];               // f
28      static const char_type fractional_seconds_or_none_format[3];       // F
29      static const char_type seconds_with_fractional_seconds_format[3];  // s
30      static const char_type seconds_format[3];                          // S
31      static const char_type standard_format[9];                         // x X
32      static const char_type zone_abbrev_format[3];                      // z
33      static const char_type zone_name_format[3];                        // Z
34      static const char_type zone_iso_format[3];                         // q
35      static const char_type zone_iso_extended_format[3];                // Q
36      static const char_type posix_zone_string_format[4];                // ZP
37      static const char_type duration_sign_negative_only[3];             // -
38      static const char_type duration_sign_always[3];                    // +
39      static const char_type duration_seperator[2];
40      static const char_type negative_sign[2];                           //-
41      static const char_type positive_sign[2];                           //+
42      static const char_type iso_time_format_specifier[18];
43      static const char_type iso_time_format_extended_specifier[22];
44      //default ptime format is YYYY-Mon-DD HH:MM:SS[.fff...][ zzz]
45      static const char_type default_time_format[23]; 
46      // default_time_input_format uses a posix_time_zone_string instead of a time zone abbrev
47      static const char_type default_time_input_format[24]; 
48      //default time_duration format is HH:MM:SS[.fff...]
49      static const char_type default_time_duration_format[11];
50  };
51
52  template <class CharT> 
53  const typename time_formats<CharT>::char_type
54  time_formats<CharT>::fractional_seconds_format[3] = {'%','f'};
55
56  template <class CharT> 
57  const typename time_formats<CharT>::char_type
58  time_formats<CharT>::fractional_seconds_or_none_format[3] = {'%','F'};
59
60  template <class CharT> 
61  const typename time_formats<CharT>::char_type
62  time_formats<CharT>::seconds_with_fractional_seconds_format[3] = 
63    {'%','s'};
64
65  template <class CharT> 
66  const typename time_formats<CharT>::char_type
67  time_formats<CharT>::seconds_format[3] =  {'%','S'};
68
69  template <class CharT> 
70  const typename time_formats<CharT>::char_type
71  //time_formats<CharT>::standard_format[5] =  {'%','c',' ','%','z'};
72  time_formats<CharT>::standard_format[9] =  {'%','x',' ','%','X',' ','%','z'};
73
74  template <class CharT> 
75  const typename time_formats<CharT>::char_type
76  time_formats<CharT>::zone_abbrev_format[3] =  {'%','z'};
77
78  template <class CharT> 
79  const typename time_formats<CharT>::char_type
80  time_formats<CharT>::zone_name_format[3] =  {'%','Z'};
81
82  template <class CharT> 
83  const typename time_formats<CharT>::char_type
84  time_formats<CharT>::zone_iso_format[3] =  {'%','q'};
85
86  template <class CharT> 
87  const typename time_formats<CharT>::char_type
88  time_formats<CharT>::zone_iso_extended_format[3] ={'%','Q'};
89
90  template <class CharT> 
91  const typename time_formats<CharT>::char_type
92  time_formats<CharT>::posix_zone_string_format[4] ={'%','Z','P'};
93
94  template <class CharT> 
95  const typename time_formats<CharT>::char_type
96  time_formats<CharT>::duration_seperator[2] =  {':'};
97
98  template <class CharT> 
99  const typename time_formats<CharT>::char_type
100  time_formats<CharT>::negative_sign[2] =  {'-'};
101
102  template <class CharT> 
103  const typename time_formats<CharT>::char_type
104  time_formats<CharT>::positive_sign[2] =  {'+'};
105
106  template <class CharT> 
107  const typename time_formats<CharT>::char_type
108  time_formats<CharT>::duration_sign_negative_only[3] ={'%','-'};
109
110  template <class CharT> 
111  const typename time_formats<CharT>::char_type
112  time_formats<CharT>::duration_sign_always[3] ={'%','+'};
113
114  template <class CharT> 
115  const typename time_formats<CharT>::char_type
116  time_formats<CharT>::iso_time_format_specifier[18] = 
117    {'%', 'Y', '%', 'm', '%', 'd', 'T', 
118     '%', 'H', '%', 'M', '%', 'S', '%', 'F', '%','q' };
119
120  template <class CharT> 
121  const typename time_formats<CharT>::char_type
122  time_formats<CharT>::iso_time_format_extended_specifier[22] = 
123    {'%', 'Y', '-', '%', 'm', '-', '%', 'd', ' ', 
124     '%', 'H', ':', '%', 'M', ':', '%', 'S', '%', 'F','%','Q'};
125
126  template <class CharT> 
127  const typename time_formats<CharT>::char_type
128  time_formats<CharT>::default_time_format[23] = 
129    {'%','Y','-','%','b','-','%','d',' ',
130      '%','H',':','%','M',':','%','S','%','F',' ','%','z'};
131
132  template <class CharT> 
133  const typename time_formats<CharT>::char_type
134  time_formats<CharT>::default_time_input_format[24] = 
135    {'%','Y','-','%','b','-','%','d',' ',
136      '%','H',':','%','M',':','%','S','%','F',' ','%','Z','P'};
137
138  template <class CharT> 
139  const typename time_formats<CharT>::char_type
140  time_formats<CharT>::default_time_duration_format[11] = 
141    {'%','H',':','%','M',':','%','S','%','F'};
142
143
144
145  /*! Facet used for format-based output of time types
146   * This class provides for the use of format strings to output times.  In addition
147   * to the flags for formatting date elements, the following are the allowed format flags:
148   *  - %x %X => default format - enables addition of more flags to default (ie. "%x %X %z")
149   *  - %f => fractional seconds ".123456"
150   *  - %F => fractional seconds or none: like frac sec but empty if frac sec == 0
151   *  - %s => seconds w/ fractional sec "02.123" (this is the same as "%S%f)
152   *  - %S => seconds "02"
153   *  - %z => abbreviated time zone "EDT"
154   *  - %Z => full time zone name "Eastern Daylight Time"
155   */
156  template <class time_type,
157            class CharT, 
158            class OutItrT = std::ostreambuf_iterator<CharT, std::char_traits<CharT> > >
159  class time_facet : 
160    public boost::date_time::date_facet<typename time_type::date_type , CharT, OutItrT> {
161   public:
162    typedef typename time_type::date_type date_type;
163    typedef typename time_type::time_duration_type time_duration_type;
164    typedef boost::date_time::period<time_type,time_duration_type> period_type;
165    typedef boost::date_time::date_facet<typename time_type::date_type, CharT, OutItrT> base_type;
166    typedef typename base_type::string_type string_type;
167    typedef typename base_type::char_type   char_type;
168    typedef typename base_type::period_formatter_type period_formatter_type;
169    typedef typename base_type::special_values_formatter_type special_values_formatter_type;
170    typedef typename base_type::date_gen_formatter_type date_gen_formatter_type;
171    static const char_type* fractional_seconds_format;                // %f
172    static const char_type* fractional_seconds_or_none_format;        // %F
173    static const char_type* seconds_with_fractional_seconds_format;   // %s
174    static const char_type* seconds_format;                           // %S
175    static const char_type* standard_format;                          // %x X
176    static const char_type* zone_abbrev_format;                       // %z
177    static const char_type* zone_name_format;                         // %Z
178    static const char_type* zone_iso_format;                          // %q
179    static const char_type* zone_iso_extended_format;                 // %Q
180    static const char_type* posix_zone_string_format;                 // %ZP
181    static const char_type* duration_seperator;
182    static const char_type* duration_sign_always;                     // %+
183    static const char_type* duration_sign_negative_only;              // %-
184    static const char_type* negative_sign;                            //-
185    static const char_type* positive_sign;                            //+
186    static const char_type* iso_time_format_specifier;
187    static const char_type* iso_time_format_extended_specifier;
188
189    //default ptime format is YYYY-Mon-DD HH:MM:SS[.fff...][ zzz]
190    static const char_type* default_time_format; 
191    //default time_duration format is HH:MM:SS[.fff...]
192    static const char_type* default_time_duration_format;
193    static std::locale::id id;
194
195#if defined (__SUNPRO_CC) && defined (_RWSTD_VER)
196      std::locale::id& __get_id (void) const { return id; }
197#endif
198
199    //! sets default formats for ptime, local_date_time, and time_duration
200    explicit time_facet(::size_t a_ref = 0) 
201      //: base_type(standard_format),
202      : base_type(default_time_format), 
203        m_time_duration_format(string_type(duration_sign_negative_only) + default_time_duration_format)
204    {}
205
206    //! Construct the facet with an explicitly specified format
207    explicit time_facet(const char_type* a_format,
208                        period_formatter_type period_formatter = period_formatter_type(), 
209                        const special_values_formatter_type& special_value_formatter = special_values_formatter_type(), 
210                        date_gen_formatter_type dg_formatter = date_gen_formatter_type(),
211                         ::size_t a_ref = 0) 
212      : base_type(a_format, 
213                  period_formatter,
214                  special_value_formatter, 
215                  dg_formatter, 
216                  a_ref),
217        m_time_duration_format(string_type(duration_sign_negative_only) + default_time_duration_format)
218    {}
219
220    //! Changes format for time_duration
221    void time_duration_format(const char_type* const format) 
222    {
223      m_time_duration_format = format;
224    }
225
226    virtual void set_iso_format()
227    {
228      this->m_format = iso_time_format_specifier;
229    }
230    virtual void set_iso_extended_format()
231    {
232      this->m_format = iso_time_format_extended_specifier;
233    }
234
235    OutItrT put(OutItrT a_next, 
236                std::ios_base& a_ios, 
237                char_type a_fill, 
238                const time_type& a_time) const 
239    {
240      if (a_time.is_special()) { 
241        return this->do_put_special(a_next, a_ios, a_fill, 
242                              a_time.date().as_special());
243      }
244      string_type format(this->m_format);
245      string_type frac_str;
246      if (format.find(seconds_with_fractional_seconds_format) != string_type::npos) {
247        // replace %s with %S.nnn
248        frac_str = 
249          fractional_seconds_as_string(a_time.time_of_day(), false);
250        char_type sep = std::use_facet<std::numpunct<char_type> >(a_ios.getloc()).decimal_point();
251       
252        string_type replace_string(seconds_format);
253        replace_string += sep;
254        replace_string += frac_str;
255        boost::algorithm::replace_all(format, 
256                                      seconds_with_fractional_seconds_format, 
257                                      replace_string);
258      }
259      /* NOTE: replacing posix_zone_string_format must be done BEFORE
260       * zone_name_format: "%ZP" & "%Z", if Z is checked first it will
261       * incorrectly replace a zone_name where a posix_string should go */
262      if (format.find(posix_zone_string_format) != string_type::npos) {
263        if(a_time.zone_abbrev().empty()) {
264          // if zone_abbrev() returns an empty string, we want to
265          // erase posix_zone_string_format from format
266          boost::algorithm::replace_all(format,
267                                        posix_zone_string_format,
268                                        "");
269        }
270        else{
271          boost::algorithm::replace_all(format,
272                                        posix_zone_string_format,
273                                        a_time.zone_as_posix_string());
274        }
275      }
276      if (format.find(zone_name_format) != string_type::npos) {
277        if(a_time.zone_name().empty()) {
278          /* TODO: this'll probably create problems if a user places
279           * the zone_*_format flag in the format with a ptime. This
280           * code removes the flag from the default formats */
281
282          // if zone_name() returns an empty string, we want to
283          // erase zone_name_format & one preceeding space
284          std::basic_stringstream<char_type> ss;
285          ss << ' ' << zone_name_format;
286          boost::algorithm::replace_all(format,
287                                        ss.str(),
288                                        "");
289        }
290        else{
291          boost::algorithm::replace_all(format,
292                                        zone_name_format,
293                                        a_time.zone_name());
294        }
295      }
296      if (format.find(zone_abbrev_format) != string_type::npos) {
297        if(a_time.zone_abbrev(false).empty()) {
298          /* TODO: this'll probably create problems if a user places
299           * the zone_*_format flag in the format with a ptime. This
300           * code removes the flag from the default formats */
301
302          // if zone_abbrev() returns an empty string, we want to
303          // erase zone_abbrev_format & one preceeding space
304          std::basic_stringstream<char_type> ss;
305          ss << ' ' << zone_abbrev_format;
306          boost::algorithm::replace_all(format,
307                                        ss.str(),
308                                        "");
309        }
310        else{
311          boost::algorithm::replace_all(format,
312                                        zone_abbrev_format,
313                                        a_time.zone_abbrev(false));
314        }
315      }
316      if (format.find(zone_iso_extended_format) != string_type::npos) {
317        if(a_time.zone_name(true).empty()) {
318          /* TODO: this'll probably create problems if a user places
319           * the zone_*_format flag in the format with a ptime. This
320           * code removes the flag from the default formats */
321
322          // if zone_name() returns an empty string, we want to
323          // erase zone_iso_extended_format from format
324          boost::algorithm::replace_all(format,
325                                        zone_iso_extended_format,
326                                        "");
327        }
328        else{
329          boost::algorithm::replace_all(format,
330                                        zone_iso_extended_format,
331                                        a_time.zone_name(true));
332        }
333      }
334
335      if (format.find(zone_iso_format) != string_type::npos) {
336        if(a_time.zone_abbrev(true).empty()) {
337          /* TODO: this'll probably create problems if a user places
338           * the zone_*_format flag in the format with a ptime. This
339           * code removes the flag from the default formats */
340
341          // if zone_abbrev() returns an empty string, we want to
342          // erase zone_iso_format from format
343          boost::algorithm::replace_all(format,
344                                        zone_iso_format,
345                                        "");
346        }
347        else{
348          boost::algorithm::replace_all(format,
349                                        zone_iso_format,
350                                        a_time.zone_abbrev(true));
351        }
352      }
353      if (format.find(fractional_seconds_format) != string_type::npos) {
354        // replace %f with nnnnnnn
355        if (!frac_str.size()) {
356          frac_str = fractional_seconds_as_string(a_time.time_of_day(), false);
357        }
358        boost::algorithm::replace_all(format,
359                                      fractional_seconds_format, 
360                                      frac_str);
361      }
362
363      if (format.find(fractional_seconds_or_none_format) != string_type::npos) {
364        // replace %F with nnnnnnn or nothing if fs == 0
365        frac_str = 
366          fractional_seconds_as_string(a_time.time_of_day(), true);
367        if (frac_str.size()) {
368          char_type sep = std::use_facet<std::numpunct<char_type> >(a_ios.getloc()).decimal_point();
369          string_type replace_string;
370          replace_string += sep;
371          replace_string += frac_str;
372          boost::algorithm::replace_all(format,
373                                        fractional_seconds_or_none_format, 
374                                        replace_string);
375        }
376        else {
377          boost::algorithm::erase_all(format,
378                                      fractional_seconds_or_none_format);
379        }
380      }
381
382      return this->do_put_tm(a_next, a_ios, a_fill, 
383                       to_tm(a_time), format);
384    }
385
386    //! put function for time_duration
387    OutItrT put(OutItrT a_next, 
388                std::ios_base& a_ios, 
389                char_type a_fill, 
390                const time_duration_type& a_time_dur) const 
391    {
392      if (a_time_dur.is_special()) { 
393        return this->do_put_special(a_next, a_ios, a_fill, 
394                              a_time_dur.get_rep().as_special());
395      }
396
397      string_type format(m_time_duration_format);
398      if (a_time_dur.is_negative()) {
399          // replace %- with minus sign.  Should we use the numpunct facet?
400          boost::algorithm::replace_all(format, 
401                                        duration_sign_negative_only, 
402                                        negative_sign);
403          // remove all the %+ in the string with '-'
404          boost::algorithm::replace_all(format, 
405                                        duration_sign_always, 
406                                        negative_sign);
407      }
408      else { //duration is positive
409          // remove all the %- combos from the string
410          boost::algorithm::replace_all(format, 
411                                        duration_sign_negative_only, 
412                                        "");
413          // remove all the %+ in the string with '+'
414          boost::algorithm::replace_all(format, 
415                                        duration_sign_always, 
416                                        positive_sign);
417      }
418
419      string_type frac_str;
420      if (format.find(seconds_with_fractional_seconds_format) != string_type::npos) {
421        // replace %s with %S.nnn
422        frac_str = 
423          fractional_seconds_as_string(a_time_dur, false);
424        char_type sep = std::use_facet<std::numpunct<char_type> >(a_ios.getloc()).decimal_point();
425       
426        string_type replace_string(seconds_format);
427        replace_string += sep;
428        replace_string += frac_str;
429        boost::algorithm::replace_all(format, 
430                                      seconds_with_fractional_seconds_format, 
431                                      replace_string);
432      }
433      if (format.find(fractional_seconds_format) != string_type::npos) {
434        // replace %f with nnnnnnn
435        if (!frac_str.size()) {
436          frac_str = fractional_seconds_as_string(a_time_dur, false);
437        }
438        boost::algorithm::replace_all(format,
439                                      fractional_seconds_format, 
440                                      frac_str);
441      }
442
443      if (format.find(fractional_seconds_or_none_format) != string_type::npos) {
444        // replace %F with nnnnnnn or nothing if fs == 0
445        frac_str = 
446          fractional_seconds_as_string(a_time_dur, true);
447        if (frac_str.size()) {
448          char_type sep = std::use_facet<std::numpunct<char_type> >(a_ios.getloc()).decimal_point();
449          string_type replace_string;
450          replace_string += sep;
451          replace_string += frac_str;
452          boost::algorithm::replace_all(format,
453                                        fractional_seconds_or_none_format, 
454                                        replace_string);
455        }
456        else {
457          boost::algorithm::erase_all(format,
458                                      fractional_seconds_or_none_format);
459        }
460      }
461
462      return this->do_put_tm(a_next, a_ios, a_fill, 
463                       to_tm(a_time_dur), format);
464    }
465   
466    OutItrT put(OutItrT next, std::ios_base& a_ios, 
467                char_type fill, const period_type& p) const 
468    {
469      return this->m_period_formatter.put_period(next, a_ios, fill,p,*this);
470    }
471
472
473  protected:
474
475    static 
476    string_type
477    fractional_seconds_as_string(const time_duration_type& a_time,
478                                 bool null_when_zero) 
479    {
480      typename time_duration_type::fractional_seconds_type frac_sec = 
481        a_time.fractional_seconds();
482
483      if (null_when_zero && (frac_sec == 0)) {
484        return string_type();
485      }
486
487      //make sure there is no sign
488      frac_sec = date_time::absolute_value(frac_sec);
489      std::basic_ostringstream<char_type> ss;
490      ss.imbue(std::locale::classic()); // don't want any formatting
491      ss << std::setw(time_duration_type::num_fractional_digits())
492         << std::setfill(static_cast<char_type>('0'));
493#if (defined(BOOST_MSVC) && (_MSC_VER <= 1200))  // 1200 == VC++ 6.0
494      // JDG [7/6/02 VC++ compatibility]
495      char_type buff[34];
496      ss << _i64toa(static_cast<boost::int64_t>(frac_sec), buff, 10);
497#else
498      ss << frac_sec;
499#endif
500      return ss.str();
501    }
502
503  private:
504    string_type m_time_duration_format;
505
506  };
507 
508  template <class time_type, class CharT, class OutItrT> 
509  std::locale::id time_facet<time_type, CharT, OutItrT>::id;
510
511  template <class time_type, class CharT, class OutItrT> 
512  const typename time_facet<time_type, CharT, OutItrT>::char_type* 
513  time_facet<time_type, CharT, OutItrT>::fractional_seconds_format = time_formats<CharT>::fractional_seconds_format;
514
515  template <class time_type, class CharT, class OutItrT> 
516  const typename time_facet<time_type, CharT, OutItrT>::char_type* 
517  time_facet<time_type, CharT, OutItrT>::fractional_seconds_or_none_format = time_formats<CharT>::fractional_seconds_or_none_format;
518
519  template <class time_type, class CharT, class OutItrT> 
520  const typename time_facet<time_type, CharT, OutItrT>::char_type* 
521  time_facet<time_type, CharT, OutItrT>::seconds_with_fractional_seconds_format = 
522    time_formats<CharT>::seconds_with_fractional_seconds_format;
523
524
525  template <class time_type, class CharT, class OutItrT> 
526  const typename time_facet<time_type, CharT, OutItrT>::char_type*
527  time_facet<time_type, CharT, OutItrT>::zone_name_format =  time_formats<CharT>::zone_name_format;
528
529  template <class time_type, class CharT, class OutItrT> 
530  const typename time_facet<time_type, CharT, OutItrT>::char_type*
531  time_facet<time_type, CharT, OutItrT>::zone_abbrev_format =  time_formats<CharT>::zone_abbrev_format;
532
533  template <class time_type, class CharT, class OutItrT> 
534  const typename time_facet<time_type, CharT, OutItrT>::char_type*
535  time_facet<time_type, CharT, OutItrT>::zone_iso_extended_format =time_formats<CharT>::zone_iso_extended_format;
536
537  template <class time_type, class CharT, class OutItrT> 
538  const typename time_facet<time_type, CharT, OutItrT>::char_type*
539  time_facet<time_type, CharT, OutItrT>::posix_zone_string_format =time_formats<CharT>::posix_zone_string_format;
540
541  template <class time_type, class CharT, class OutItrT> 
542  const typename time_facet<time_type, CharT, OutItrT>::char_type*
543  time_facet<time_type, CharT, OutItrT>::zone_iso_format =  time_formats<CharT>::zone_iso_format;
544
545  template <class time_type, class CharT, class OutItrT> 
546  const typename time_facet<time_type, CharT, OutItrT>::char_type*
547  time_facet<time_type, CharT, OutItrT>::seconds_format =  time_formats<CharT>::seconds_format;
548
549  template <class time_type, class CharT, class OutItrT> 
550  const typename time_facet<time_type, CharT, OutItrT>::char_type*
551  time_facet<time_type, CharT, OutItrT>::standard_format =  time_formats<CharT>::standard_format;
552
553  template <class time_type, class CharT, class OutItrT> 
554  const typename time_facet<time_type, CharT, OutItrT>::char_type*
555  time_facet<time_type, CharT, OutItrT>::duration_seperator =  time_formats<CharT>::duration_seperator;
556
557  template <class time_type, class CharT, class OutItrT> 
558  const typename time_facet<time_type, CharT, OutItrT>::char_type*
559  time_facet<time_type, CharT, OutItrT>::negative_sign =  time_formats<CharT>::negative_sign;
560
561  template <class time_type, class CharT, class OutItrT> 
562  const typename time_facet<time_type, CharT, OutItrT>::char_type*
563  time_facet<time_type, CharT, OutItrT>::positive_sign =  time_formats<CharT>::positive_sign;
564
565  template <class time_type, class CharT, class OutItrT> 
566  const typename time_facet<time_type, CharT, OutItrT>::char_type*
567  time_facet<time_type, CharT, OutItrT>::duration_sign_negative_only =  time_formats<CharT>::duration_sign_negative_only;
568
569  template <class time_type, class CharT, class OutItrT> 
570  const typename time_facet<time_type, CharT, OutItrT>::char_type*
571  time_facet<time_type, CharT, OutItrT>::duration_sign_always =  time_formats<CharT>::duration_sign_always;
572
573  template <class time_type, class CharT, class OutItrT> 
574  const typename time_facet<time_type,CharT, OutItrT>::char_type*
575  time_facet<time_type,CharT, OutItrT>::iso_time_format_specifier = time_formats<CharT>::iso_time_format_specifier;
576
577  template <class time_type, class CharT, class OutItrT> 
578  const typename time_facet<time_type, CharT, OutItrT>::char_type*
579  time_facet<time_type, CharT, OutItrT>::iso_time_format_extended_specifier = time_formats<CharT>::iso_time_format_extended_specifier;
580
581  template <class time_type, class CharT, class OutItrT> 
582  const typename time_facet<time_type, CharT, OutItrT>::char_type*
583  time_facet<time_type, CharT, OutItrT>::default_time_format = 
584    time_formats<CharT>::default_time_format;
585
586  template <class time_type, class CharT, class OutItrT> 
587  const typename time_facet<time_type, CharT, OutItrT>::char_type* 
588  time_facet<time_type, CharT, OutItrT>::default_time_duration_format = 
589    time_formats<CharT>::default_time_duration_format;
590
591
592  //! Facet for format-based input. 
593  /*!
594   */
595  template <class time_type,
596            class CharT, 
597            class InItrT = std::istreambuf_iterator<CharT, std::char_traits<CharT> > >
598  class time_input_facet : 
599    public boost::date_time::date_input_facet<typename time_type::date_type , CharT, InItrT> {
600    public:
601      typedef typename time_type::date_type date_type;
602      typedef typename time_type::time_duration_type time_duration_type;
603      typedef typename time_duration_type::fractional_seconds_type fracional_seconds_type;
604      typedef boost::date_time::period<time_type,time_duration_type> period_type;
605      typedef boost::date_time::date_input_facet<typename time_type::date_type, CharT, InItrT> base_type;
606      typedef typename base_type::duration_type date_duration_type;
607      typedef typename base_type::year_type year_type;
608      typedef typename base_type::month_type month_type;
609      typedef typename base_type::day_type day_type;
610      typedef typename base_type::string_type string_type;
611      typedef typename string_type::const_iterator const_itr;
612      typedef typename base_type::char_type   char_type;
613      typedef typename base_type::format_date_parser_type format_date_parser_type;
614      typedef typename base_type::period_parser_type period_parser_type;
615      typedef typename base_type::special_values_parser_type special_values_parser_type;
616      typedef typename base_type::date_gen_parser_type date_gen_parser_type;
617      typedef typename base_type::special_values_parser_type::match_results match_results;
618
619      static const char_type* fractional_seconds_format;                // f
620      static const char_type* fractional_seconds_or_none_format;        // F
621      static const char_type* seconds_with_fractional_seconds_format;   // s
622      static const char_type* seconds_format;                           // S
623      static const char_type* standard_format;                          // x X
624      static const char_type* zone_abbrev_format;                       // z
625      static const char_type* zone_name_format;                         // Z
626      static const char_type* zone_iso_format;                          // q
627      static const char_type* zone_iso_extended_format;                 // Q
628      static const char_type* duration_seperator;
629      static const char_type* iso_time_format_specifier;
630      static const char_type* iso_time_format_extended_specifier;
631      static const char_type* default_time_input_format; 
632      static const char_type* default_time_duration_format;
633      static std::locale::id id;
634
635      //! Constructor that takes a format string for a ptime
636      explicit time_input_facet(const string_type& format, ::size_t a_ref = 0) 
637        : base_type(format, a_ref), 
638          m_time_duration_format(default_time_duration_format)
639      { }
640
641      explicit time_input_facet(const string_type& format,
642                                const format_date_parser_type& date_parser,
643                                const special_values_parser_type& sv_parser,
644                                const period_parser_type& per_parser,
645                                const date_gen_parser_type& date_gen_parser,
646                                ::size_t a_ref = 0)
647        : base_type(format,
648                    date_parser,
649                    sv_parser,
650                    per_parser,
651                    date_gen_parser,
652                    a_ref), 
653          m_time_duration_format(default_time_duration_format)
654      {}
655
656      //! sets default formats for ptime, local_date_time, and time_duration
657      explicit time_input_facet(::size_t a_ref = 0) 
658        : base_type(default_time_input_format, a_ref), 
659          m_time_duration_format(default_time_duration_format)
660      { }
661     
662      //! Set the format for time_duration
663      void time_duration_format(const char_type* const format) {
664        m_time_duration_format = format;
665      }
666      virtual void set_iso_format()
667      {
668        this->m_format = iso_time_format_specifier;
669      }
670      virtual void set_iso_extended_format()
671      {
672        this->m_format = iso_time_format_extended_specifier;
673      }
674     
675      InItrT get(InItrT& sitr,
676                 InItrT& stream_end,
677                 std::ios_base& a_ios,
678                 period_type& p) const
679      {
680        p = this->m_period_parser.get_period(sitr, 
681                                             stream_end, 
682                                             a_ios, 
683                                             p, 
684                                             time_duration_type::unit(), 
685                                             *this);
686        return sitr;
687      }
688     
689      //default ptime format is YYYY-Mon-DD HH:MM:SS[.fff...][ zzz]
690      //default time_duration format is %H:%M:%S%F HH:MM:SS[.fff...]
691
692      InItrT get(InItrT& sitr, 
693                 InItrT& stream_end, 
694                 std::ios_base& a_ios, 
695                 time_duration_type& td) const
696      {
697        // skip leading whitespace
698        while((sitr != stream_end) && std::isspace(*sitr)) { ++sitr; }
699       
700        bool use_current_char = false;
701       
702        // num_get will consume the +/-, we may need a copy if special_value
703        char_type c = '\0';
704        if((sitr != stream_end) && (*sitr == '-' || *sitr == '+')) {
705          c = *sitr;
706        }
707       
708        long hour = 0; 
709        long min = 0; 
710        long sec = 0; 
711        typename time_duration_type::fractional_seconds_type frac(0);
712       
713        typedef std::num_get<CharT, InItrT> num_get;
714        if(!std::has_facet<num_get>(a_ios.getloc())) {
715          num_get* ng = new num_get();
716          std::locale loc = std::locale(a_ios.getloc(), ng);
717          a_ios.imbue(loc);
718        }
719       
720        const_itr itr(m_time_duration_format.begin());
721        while (itr != m_time_duration_format.end() && (sitr != stream_end)) {
722          if (*itr == '%') {
723            itr++;
724            if (*itr != '%') {
725              switch(*itr) {
726              case 'H': 
727                {
728                  match_results mr;
729                  hour = fixed_string_to_int<short, CharT>(sitr, stream_end, mr, 2);
730                  if(hour == -1){
731                     return check_special_value(sitr, stream_end, td, c);
732                  }
733                  break;
734                }
735              case 'M': 
736                {
737                  match_results mr;
738                  min = fixed_string_to_int<short, CharT>(sitr, stream_end, mr, 2);
739                  if(min == -1){
740                     return check_special_value(sitr, stream_end, td, c);
741                  }
742                  break;
743                }
744              case 'S': 
745                {
746                  match_results mr;
747                  sec = fixed_string_to_int<short, CharT>(sitr, stream_end, mr, 2);
748                  if(sec == -1){
749                     return check_special_value(sitr, stream_end, td, c);
750                  }
751                  break;
752                }
753              case 's':
754                {
755                  match_results mr;
756                  sec = fixed_string_to_int<short, CharT>(sitr, stream_end, mr, 2);
757                  if(sec == -1){
758                     return check_special_value(sitr, stream_end, td, c);
759                  }
760                  // %s is the same as %S%f so we drop through into %f
761                  //break;
762                }
763              case 'f':
764                {
765                  // check for decimal, check special_values if missing
766                  if(*sitr == '.') {
767                    ++sitr;
768                    parse_frac_type(sitr, stream_end, frac);
769                    // sitr will point to next expected char after this parsing
770                    // is complete so no need to advance it
771                    use_current_char = true;
772                  }
773                  else {
774                    return check_special_value(sitr, stream_end, td, c);
775                  }
776                  break;
777                }
778              case 'F': 
779                {
780                  // check for decimal, skip if missing
781                  if(*sitr == '.') {
782                    ++sitr;
783                    parse_frac_type(sitr, stream_end, frac);
784                    // sitr will point to next expected char after this parsing
785                    // is complete so no need to advance it
786                    use_current_char = true;
787                  }
788                  else {
789                    // nothing was parsed so we don't want to advance sitr
790                    use_current_char = true;
791                  }
792                  break;
793                }
794              default:
795                {} // ignore what we don't understand?
796              }// switch
797            }
798            else { // itr == '%', second consecutive
799              sitr++;
800            }
801       
802            itr++; //advance past format specifier
803          }
804          else {  //skip past chars in format and in buffer
805            itr++;
806            // set use_current_char when sitr is already
807            // pointing at the next character to process
808            if (use_current_char) {
809              use_current_char = false;
810            }
811            else {
812              sitr++;
813            }
814          }
815        }
816
817        td = time_duration_type(hour, min, sec, frac);
818        return sitr;
819      }
820   
821
822      //! Parses a time object from the input stream
823      InItrT get(InItrT& sitr, 
824                 InItrT& stream_end, 
825                 std::ios_base& a_ios, 
826                 time_type& t) const
827      {
828        string_type tz_str;
829        return get(sitr, stream_end, a_ios, t, tz_str, false);
830      }
831      //! Expects a time_zone in the input stream
832      InItrT get_local_time(InItrT& sitr, 
833                            InItrT& stream_end, 
834                            std::ios_base& a_ios, 
835                            time_type& t,
836                            string_type& tz_str) const
837      {
838        return get(sitr, stream_end, a_ios, t, tz_str, true);
839      }
840
841    protected:
842
843      InItrT get(InItrT& sitr, 
844                 InItrT& stream_end, 
845                 std::ios_base& a_ios, 
846                 time_type& t,
847                 string_type& tz_str,
848                 bool time_is_local) const
849      {
850        // skip leading whitespace
851        while((sitr != stream_end) && std::isspace(*sitr)) { ++sitr; }
852       
853        bool use_current_char = false;
854        bool use_current_format_char = false; // used whith two character flags
855       
856        // num_get will consume the +/-, we may need a copy if special_value
857        char_type c = '\0';
858        if((sitr != stream_end) && (*sitr == '-' || *sitr == '+')) {
859          c = *sitr;
860        }
861       
862        // time elements
863        long hour = 0; 
864        long min = 0; 
865        long sec = 0; 
866        typename time_duration_type::fractional_seconds_type frac(0);
867        // date elements
868        short day_of_year(0);
869        /* Initialized the following to their minimum values. These intermediate
870         * objects are used so we get specific exceptions when part of the input
871         * is unparsable.
872         * Ex: "205-Jan-15" will throw a bad_year, "2005-Jsn-15"- bad_month, etc.*/
873        year_type t_year(1400);
874        month_type t_month(1);
875        day_type t_day(1);
876       
877        typedef std::num_get<CharT, InItrT> num_get;
878        if(!std::has_facet<num_get>(a_ios.getloc())) {
879          num_get* ng = new num_get();
880          std::locale loc = std::locale(a_ios.getloc(), ng);
881          a_ios.imbue(loc);
882        }
883       
884        const_itr itr(this->m_format.begin());
885        while (itr != this->m_format.end() && (sitr != stream_end)) {
886          if (*itr == '%') {
887            itr++;
888            if (*itr != '%') {
889              // the cases are grouped by date & time flags - not alphabetical order
890              switch(*itr) {
891                // date flags
892                case 'Y':
893                case 'y':
894                  {
895                    char_type cs[3] = { '%', *itr };
896                    string_type s(cs);
897                    match_results mr;
898                    try {
899                      t_year = this->m_parser.parse_year(sitr, stream_end, s, mr);
900                    }
901                    catch(std::out_of_range bad_year) { // base class for bad_year exception
902                      if(this->m_sv_parser.match(sitr, stream_end, mr)) {
903                        t = time_type(static_cast<special_values>(mr.current_match));
904                        return sitr;
905                      }
906                      else {
907                        throw; // rethrow bad_year
908                      }
909                    }
910                    break;
911                  }
912                case 'B':
913                case 'b':
914                case 'm':
915                  {
916                    char_type cs[3] = { '%', *itr };
917                    string_type s(cs);
918                    match_results mr;
919                    try {
920                      t_month = this->m_parser.parse_month(sitr, stream_end, s, mr);
921                    }
922                    catch(std::out_of_range bad_month) { // base class for bad_month exception
923                      if(this->m_sv_parser.match(sitr, stream_end, mr)) {
924                        t = time_type(static_cast<special_values>(mr.current_match));
925                        return sitr;
926                      }
927                      else {
928                        throw; // rethrow bad_year
929                      }
930                    }
931                    // did m_parser already advance sitr to next char?
932                    if(mr.has_remaining()) {
933                      use_current_char = true;
934                    }
935                    break;
936                  }
937                case 'a':
938                case 'A':
939                case 'w':
940                  {
941                    // weekday is not used in construction but we need to get it out of the stream
942                    char_type cs[3] = { '%', *itr };
943                    string_type s(cs);
944                    match_results mr;
945                    typename date_type::day_of_week_type wd(0);
946                    try {
947                      wd = this->m_parser.parse_weekday(sitr, stream_end, s, mr);
948                    }
949                    catch(std::out_of_range bad_weekday) { // base class for bad_weekday exception
950                      if(this->m_sv_parser.match(sitr, stream_end, mr)) {
951                        t = time_type(static_cast<special_values>(mr.current_match));
952                        return sitr;
953                      }
954                      else {
955                        throw; // rethrow bad_weekday
956                      }
957                    }
958                    // did m_parser already advance sitr to next char?
959                    if(mr.has_remaining()) {
960                      use_current_char = true;
961                    }
962                    break;
963                  }
964                case 'j':
965                  {
966                    // code that gets julian day (from format_date_parser)
967                    match_results mr;
968                    day_of_year = fixed_string_to_int<unsigned short, CharT>(sitr, stream_end, mr, 3);
969                    if(day_of_year == -1) {
970                      if(this->m_sv_parser.match(sitr, stream_end, mr)) {
971                        t = time_type(static_cast<special_values>(mr.current_match));
972                        return sitr;
973                      }
974                    }
975                    // these next two lines are so we get an exception with bad input
976                    typedef typename time_type::date_type::day_of_year_type day_of_year_type;
977                    day_of_year_type t_day_of_year(day_of_year);
978                    break;
979                  }
980                case 'd':
981                  {
982                    try {
983                      t_day = this->m_parser.parse_day_of_month(sitr, stream_end);
984                    }
985                    catch(std::out_of_range bad_day_of_month) { // base class for exception
986                      match_results mr;
987                      if(this->m_sv_parser.match(sitr, stream_end, mr)) {
988                        t = time_type(static_cast<special_values>(mr.current_match));
989                        return sitr;
990                      }
991                      else {
992                        throw; // rethrow bad_year
993                      }
994                    }
995                    break;
996                  }
997                // time flags
998                case 'H': 
999                  {
1000                    match_results mr;
1001                    hour = fixed_string_to_int<short, CharT>(sitr, stream_end, mr, 2);
1002                    if(hour == -1){
1003                       return check_special_value(sitr, stream_end, t, c);
1004                    }
1005                    break;
1006                  }
1007                case 'M': 
1008                  {
1009                    match_results mr;
1010                    min = fixed_string_to_int<short, CharT>(sitr, stream_end, mr, 2);
1011                    if(min == -1){
1012                       return check_special_value(sitr, stream_end, t, c);
1013                    }
1014                    break;
1015                  }
1016                case 'S': 
1017                  {
1018                    match_results mr;
1019                    sec = fixed_string_to_int<short, CharT>(sitr, stream_end, mr, 2);
1020                    if(sec == -1){
1021                       return check_special_value(sitr, stream_end, t, c);
1022                    }
1023                    break;
1024                  }
1025                case 's':
1026                  {
1027                    match_results mr;
1028                    sec = fixed_string_to_int<short, CharT>(sitr, stream_end, mr, 2);
1029                    if(sec == -1){
1030                       return check_special_value(sitr, stream_end, t, c);
1031                    }
1032                    // %s is the same as %S%f so we drop through into %f
1033                    //break;
1034                  }
1035                case 'f':
1036                  {
1037                    // check for decimal, check SV if missing
1038                    if(*sitr == '.') {
1039                      ++sitr;
1040                      parse_frac_type(sitr, stream_end, frac);
1041                      // sitr will point to next expected char after this parsing
1042                      // is complete so no need to advance it
1043                      use_current_char = true;
1044                    }
1045                    else {
1046                      return check_special_value(sitr, stream_end, t, c);
1047                    }
1048                    break;
1049                  }
1050                case 'F': 
1051                  {
1052                    // check for decimal, skip if missing
1053                    if(*sitr == '.') {
1054                      ++sitr;
1055                      parse_frac_type(sitr, stream_end, frac);
1056                      // sitr will point to next expected char after this parsing
1057                      // is complete so no need to advance it
1058                      use_current_char = true;
1059                    }
1060                    else {
1061                      // nothing was parsed so we don't want to advance sitr
1062                      use_current_char = true;
1063                    }
1064                    break;
1065                  }
1066                  // time_zone flags
1067                //case 'q':
1068                //case 'Q':
1069                //case 'z':
1070                case 'Z':
1071                  {
1072                    if(time_is_local) { // skip if 't' is a ptime
1073                      ++itr;
1074                      if(*itr == 'P') {
1075                        // skip leading whitespace
1076                        while((sitr != stream_end) && std::isspace(*sitr)) { ++sitr; }
1077                        // parse zone
1078                        while((sitr != stream_end) && (!std::isspace(*sitr))) {
1079                          tz_str += *sitr;
1080                          ++sitr;
1081                        }
1082                      }
1083                      else {
1084                        use_current_format_char = true;
1085                      }
1086                   
1087                    }
1088                    else {
1089                      // nothing was parsed so we don't want to advance sitr
1090                      use_current_char = true;
1091                    }
1092                   
1093                    break;
1094                  }
1095                default:
1096                {} // ignore what we don't understand?
1097              }// switch
1098            } 
1099            else { // itr == '%', second consecutive
1100              sitr++;
1101            }
1102       
1103            if(use_current_format_char) {
1104              use_current_format_char = false;
1105            }
1106            else {
1107              itr++; //advance past format specifier
1108            }
1109             
1110          }
1111          else {  //skip past chars in format and in buffer
1112            itr++;
1113            // set use_current_char when sitr is already
1114            // pointing at the next character to process
1115            if (use_current_char) {
1116              use_current_char = false;
1117            }
1118            else {
1119              sitr++;
1120            }
1121          }
1122        }
1123       
1124        date_type d(not_a_date_time);
1125        if (day_of_year > 0) {
1126          d = date_type(static_cast<unsigned short>(t_year-1),12,31) + date_duration_type(day_of_year);
1127        }
1128        else {
1129          d = date_type(t_year, t_month, t_day);
1130        }
1131
1132        time_duration_type td(hour, min, sec, frac);
1133        t = time_type(d, td);
1134        return sitr;
1135      }
1136
1137      //! Helper function to check for special_value
1138      /*! First character may have been consumed during original parse
1139       * attempt. Parameter 'c' should be a copy of that character.
1140       * Throws ios_base::failure if parse fails. */
1141      template<class temporal_type>
1142      inline
1143      InItrT check_special_value(InItrT& sitr,InItrT& stream_end, temporal_type& tt, char_type c='\0') const
1144      {
1145        match_results mr;
1146        if((c == '-' || c == '+') && (*sitr != c)) { // was the first character consumed?
1147          mr.cache += c;
1148        }
1149        this->m_sv_parser.match(sitr, stream_end, mr);
1150        if(mr.current_match == match_results::PARSE_ERROR) {
1151          std::string tmp = convert_string_type<char_type, char>(mr.cache);
1152          throw std::ios_base::failure("Parse failed. No match found for '" + tmp + "'");
1153        }
1154        tt = temporal_type(static_cast<special_values>(mr.current_match)); 
1155        return sitr;
1156      }
1157
1158      //! Helper function for parsing a fractional second type from the stream
1159      void parse_frac_type(InItrT& sitr, 
1160                           InItrT& stream_end, 
1161                           fracional_seconds_type& frac) const
1162      {
1163        string_type cache;
1164        while((sitr != stream_end) && std::isdigit(*sitr)) {
1165          cache += *sitr;
1166          ++sitr;
1167        }
1168        if(cache.size() > 0) {
1169          unsigned short precision = time_duration_type::num_fractional_digits();
1170          // input may be only the first few decimal places
1171          if(cache.size() < precision) {
1172            frac = lexical_cast<fracional_seconds_type>(cache);
1173            frac = decimal_adjust(frac, static_cast<unsigned short>(precision - cache.size()));
1174          }
1175          else {
1176            // if input has too many decimal places, drop excess digits
1177            frac = lexical_cast<fracional_seconds_type>(cache.substr(0, precision));
1178          }
1179        }
1180      }
1181     
1182    private:
1183      string_type m_time_duration_format;
1184
1185      //! Helper function to adjust trailing zeros when parsing fractional digits
1186      template<class int_type>
1187      inline
1188      int_type decimal_adjust(int_type val, const unsigned short places) const
1189      {
1190        unsigned long factor = 1;
1191        for(int i = 0; i < places; ++i){
1192          factor *= 10; // shift decimal to the right
1193        }
1194        return val * factor;
1195      }
1196
1197  };
1198
1199template <class time_type, class CharT, class InItrT>
1200  std::locale::id time_input_facet<time_type, CharT, InItrT>::id;
1201
1202template <class time_type, class CharT, class InItrT> 
1203  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1204  time_input_facet<time_type, CharT, InItrT>::fractional_seconds_format = time_formats<CharT>::fractional_seconds_format;
1205
1206  template <class time_type, class CharT, class InItrT> 
1207  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1208  time_input_facet<time_type, CharT, InItrT>::fractional_seconds_or_none_format = time_formats<CharT>::fractional_seconds_or_none_format;
1209
1210  template <class time_type, class CharT, class InItrT> 
1211  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1212  time_input_facet<time_type, CharT, InItrT>::seconds_with_fractional_seconds_format = time_formats<CharT>::seconds_with_fractional_seconds_format;
1213
1214  template <class time_type, class CharT, class InItrT> 
1215  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1216  time_input_facet<time_type, CharT, InItrT>::seconds_format = time_formats<CharT>::seconds_format;
1217
1218  template <class time_type, class CharT, class InItrT> 
1219  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1220  time_input_facet<time_type, CharT, InItrT>::standard_format = time_formats<CharT>::standard_format;
1221
1222  template <class time_type, class CharT, class InItrT> 
1223  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1224  time_input_facet<time_type, CharT, InItrT>::zone_abbrev_format = time_formats<CharT>::zone_abbrev_format;
1225
1226  template <class time_type, class CharT, class InItrT> 
1227  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1228  time_input_facet<time_type, CharT, InItrT>::zone_name_format = time_formats<CharT>::zone_name_format;
1229
1230  template <class time_type, class CharT, class InItrT> 
1231  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1232  time_input_facet<time_type, CharT, InItrT>::zone_iso_format = time_formats<CharT>::zone_iso_format;
1233
1234  template <class time_type, class CharT, class InItrT> 
1235  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1236  time_input_facet<time_type, CharT, InItrT>::zone_iso_extended_format = time_formats<CharT>::zone_iso_extended_format;
1237
1238  template <class time_type, class CharT, class InItrT> 
1239  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1240  time_input_facet<time_type, CharT, InItrT>::duration_seperator = time_formats<CharT>::duration_seperator;
1241
1242  template <class time_type, class CharT, class InItrT> 
1243  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1244  time_input_facet<time_type, CharT, InItrT>::iso_time_format_specifier = time_formats<CharT>::iso_time_format_specifier;
1245
1246  template <class time_type, class CharT, class InItrT> 
1247  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1248  time_input_facet<time_type, CharT, InItrT>::iso_time_format_extended_specifier = time_formats<CharT>::iso_time_format_extended_specifier;
1249
1250  template <class time_type, class CharT, class InItrT> 
1251  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1252  time_input_facet<time_type, CharT, InItrT>::default_time_input_format = time_formats<CharT>::default_time_input_format;
1253
1254  template <class time_type, class CharT, class InItrT> 
1255  const typename time_input_facet<time_type, CharT, InItrT>::char_type* 
1256  time_input_facet<time_type, CharT, InItrT>::default_time_duration_format = time_formats<CharT>::default_time_duration_format;
1257
1258
1259} } // namespaces
1260
1261
1262#endif
1263
Note: See TracBrowser for help on using the repository browser.