fix doc example typo
[boost.git] / boost / date_time / time_facet.hpp
blobcdfee73f2961832614354dbce1e158d1aefbe764
2 #ifndef _DATE_TIME_FACET__HPP__
3 #define _DATE_TIME_FACET__HPP__
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: Martin Andrian, Jeff Garland, Bart Garst
10 * $Date$
13 #include <cctype>
14 #include <locale>
15 #include <limits>
16 #include <string>
17 #include <sstream>
18 #include <iomanip>
19 #include <iterator> // i/ostreambuf_iterator
20 #include <exception>
21 #include <boost/assert.hpp>
22 #include <boost/lexical_cast.hpp>
23 #include <boost/throw_exception.hpp>
24 #include <boost/algorithm/string/erase.hpp>
25 #include <boost/algorithm/string/replace.hpp>
26 #include <boost/date_time/compiler_config.hpp>
27 #include <boost/date_time/date_facet.hpp>
28 #include <boost/date_time/string_convert.hpp>
29 #include <boost/date_time/special_defs.hpp>
31 namespace boost {
32 namespace date_time {
34 template <class CharT>
35 struct time_formats {
36 public:
37 typedef CharT char_type;
38 static const char_type fractional_seconds_format[3]; // f
39 static const char_type fractional_seconds_or_none_format[3]; // F
40 static const char_type seconds_with_fractional_seconds_format[3]; // s
41 static const char_type seconds_format[3]; // S
42 static const char_type hours_format[3]; // H
43 static const char_type unrestricted_hours_format[3]; // O
44 static const char_type standard_format[9]; // x X
45 static const char_type zone_abbrev_format[3]; // z
46 static const char_type zone_name_format[3]; // Z
47 static const char_type zone_iso_format[3]; // q
48 static const char_type zone_iso_extended_format[3]; // Q
49 static const char_type posix_zone_string_format[4]; // ZP
50 static const char_type duration_sign_negative_only[3]; // -
51 static const char_type duration_sign_always[3]; // +
52 static const char_type duration_seperator[2];
53 static const char_type negative_sign[2]; //-
54 static const char_type positive_sign[2]; //+
55 static const char_type iso_time_format_specifier[18];
56 static const char_type iso_time_format_extended_specifier[22];
57 //default ptime format is YYYY-Mon-DD HH:MM:SS[.fff...][ zzz]
58 static const char_type default_time_format[23];
59 // default_time_input_format uses a posix_time_zone_string instead of a time zone abbrev
60 static const char_type default_time_input_format[24];
61 //default time_duration format is HH:MM:SS[.fff...]
62 static const char_type default_time_duration_format[11];
65 template <class CharT>
66 const typename time_formats<CharT>::char_type
67 time_formats<CharT>::fractional_seconds_format[3] = {'%','f'};
69 template <class CharT>
70 const typename time_formats<CharT>::char_type
71 time_formats<CharT>::fractional_seconds_or_none_format[3] = {'%','F'};
73 template <class CharT>
74 const typename time_formats<CharT>::char_type
75 time_formats<CharT>::seconds_with_fractional_seconds_format[3] =
76 {'%','s'};
78 template <class CharT>
79 const typename time_formats<CharT>::char_type
80 time_formats<CharT>::seconds_format[3] = {'%','S'};
82 template <class CharT>
83 const typename time_formats<CharT>::char_type
84 time_formats<CharT>::hours_format[3] = {'%','H'};
86 template <class CharT>
87 const typename time_formats<CharT>::char_type
88 time_formats<CharT>::unrestricted_hours_format[3] = {'%','O'};
90 template <class CharT>
91 const typename time_formats<CharT>::char_type
92 //time_formats<CharT>::standard_format[5] = {'%','c',' ','%','z'};
93 time_formats<CharT>::standard_format[9] = {'%','x',' ','%','X',' ','%','z'};
95 template <class CharT>
96 const typename time_formats<CharT>::char_type
97 time_formats<CharT>::zone_abbrev_format[3] = {'%','z'};
99 template <class CharT>
100 const typename time_formats<CharT>::char_type
101 time_formats<CharT>::zone_name_format[3] = {'%','Z'};
103 template <class CharT>
104 const typename time_formats<CharT>::char_type
105 time_formats<CharT>::zone_iso_format[3] = {'%','q'};
107 template <class CharT>
108 const typename time_formats<CharT>::char_type
109 time_formats<CharT>::zone_iso_extended_format[3] ={'%','Q'};
111 template <class CharT>
112 const typename time_formats<CharT>::char_type
113 time_formats<CharT>::posix_zone_string_format[4] ={'%','Z','P'};
115 template <class CharT>
116 const typename time_formats<CharT>::char_type
117 time_formats<CharT>::duration_seperator[2] = {':'};
119 template <class CharT>
120 const typename time_formats<CharT>::char_type
121 time_formats<CharT>::negative_sign[2] = {'-'};
123 template <class CharT>
124 const typename time_formats<CharT>::char_type
125 time_formats<CharT>::positive_sign[2] = {'+'};
127 template <class CharT>
128 const typename time_formats<CharT>::char_type
129 time_formats<CharT>::duration_sign_negative_only[3] ={'%','-'};
131 template <class CharT>
132 const typename time_formats<CharT>::char_type
133 time_formats<CharT>::duration_sign_always[3] ={'%','+'};
135 template <class CharT>
136 const typename time_formats<CharT>::char_type
137 time_formats<CharT>::iso_time_format_specifier[18] =
138 {'%', 'Y', '%', 'm', '%', 'd', 'T',
139 '%', 'H', '%', 'M', '%', 'S', '%', 'F', '%','q' };
141 template <class CharT>
142 const typename time_formats<CharT>::char_type
143 time_formats<CharT>::iso_time_format_extended_specifier[22] =
144 {'%', 'Y', '-', '%', 'm', '-', '%', 'd', ' ',
145 '%', 'H', ':', '%', 'M', ':', '%', 'S', '%', 'F','%','Q'};
147 template <class CharT>
148 const typename time_formats<CharT>::char_type
149 time_formats<CharT>::default_time_format[23] =
150 {'%','Y','-','%','b','-','%','d',' ',
151 '%','H',':','%','M',':','%','S','%','F',' ','%','z'};
153 template <class CharT>
154 const typename time_formats<CharT>::char_type
155 time_formats<CharT>::default_time_input_format[24] =
156 {'%','Y','-','%','b','-','%','d',' ',
157 '%','H',':','%','M',':','%','S','%','F',' ','%','Z','P'};
159 template <class CharT>
160 const typename time_formats<CharT>::char_type
161 time_formats<CharT>::default_time_duration_format[11] =
162 {'%','H',':','%','M',':','%','S','%','F'};
166 /*! Facet used for format-based output of time types
167 * This class provides for the use of format strings to output times. In addition
168 * to the flags for formatting date elements, the following are the allowed format flags:
169 * - %x %X => default format - enables addition of more flags to default (ie. "%x %X %z")
170 * - %f => fractional seconds ".123456"
171 * - %F => fractional seconds or none: like frac sec but empty if frac sec == 0
172 * - %s => seconds w/ fractional sec "02.123" (this is the same as "%S%f)
173 * - %S => seconds "02"
174 * - %z => abbreviated time zone "EDT"
175 * - %Z => full time zone name "Eastern Daylight Time"
177 template <class time_type,
178 class CharT,
179 class OutItrT = std::ostreambuf_iterator<CharT, std::char_traits<CharT> > >
180 class time_facet :
181 public boost::date_time::date_facet<typename time_type::date_type , CharT, OutItrT> {
182 public:
183 typedef typename time_type::date_type date_type;
184 typedef typename time_type::time_duration_type time_duration_type;
185 typedef boost::date_time::period<time_type,time_duration_type> period_type;
186 typedef boost::date_time::date_facet<typename time_type::date_type, CharT, OutItrT> base_type;
187 typedef typename base_type::string_type string_type;
188 typedef typename base_type::char_type char_type;
189 typedef typename base_type::period_formatter_type period_formatter_type;
190 typedef typename base_type::special_values_formatter_type special_values_formatter_type;
191 typedef typename base_type::date_gen_formatter_type date_gen_formatter_type;
192 static const char_type* fractional_seconds_format; // %f
193 static const char_type* fractional_seconds_or_none_format; // %F
194 static const char_type* seconds_with_fractional_seconds_format; // %s
195 static const char_type* seconds_format; // %S
196 static const char_type* hours_format; // %H
197 static const char_type* unrestricted_hours_format; // %O
198 static const char_type* standard_format; // %x X
199 static const char_type* zone_abbrev_format; // %z
200 static const char_type* zone_name_format; // %Z
201 static const char_type* zone_iso_format; // %q
202 static const char_type* zone_iso_extended_format; // %Q
203 static const char_type* posix_zone_string_format; // %ZP
204 static const char_type* duration_seperator;
205 static const char_type* duration_sign_always; // %+
206 static const char_type* duration_sign_negative_only; // %-
207 static const char_type* negative_sign; //-
208 static const char_type* positive_sign; //+
209 static const char_type* iso_time_format_specifier;
210 static const char_type* iso_time_format_extended_specifier;
212 //default ptime format is YYYY-Mon-DD HH:MM:SS[.fff...][ zzz]
213 static const char_type* default_time_format;
214 //default time_duration format is HH:MM:SS[.fff...]
215 static const char_type* default_time_duration_format;
216 static std::locale::id id;
218 #if defined (__SUNPRO_CC) && defined (_RWSTD_VER)
219 std::locale::id& __get_id (void) const { return id; }
220 #endif
222 //! sets default formats for ptime, local_date_time, and time_duration
223 explicit time_facet(::size_t a_ref = 0)
224 : base_type(default_time_format, period_formatter_type(), special_values_formatter_type(), date_gen_formatter_type(), a_ref),
225 m_time_duration_format(string_type(duration_sign_negative_only) + default_time_duration_format)
228 //! Construct the facet with an explicitly specified format
229 explicit time_facet(const char_type* a_format,
230 period_formatter_type period_formatter = period_formatter_type(),
231 const special_values_formatter_type& special_value_formatter = special_values_formatter_type(),
232 date_gen_formatter_type dg_formatter = date_gen_formatter_type(),
233 ::size_t a_ref = 0)
234 : base_type(a_format,
235 period_formatter,
236 special_value_formatter,
237 dg_formatter,
238 a_ref),
239 m_time_duration_format(string_type(duration_sign_negative_only) + default_time_duration_format)
242 //! Changes format for time_duration
243 void time_duration_format(const char_type* const format)
245 m_time_duration_format = format;
248 virtual void set_iso_format()
250 this->m_format = iso_time_format_specifier;
252 virtual void set_iso_extended_format()
254 this->m_format = iso_time_format_extended_specifier;
257 OutItrT put(OutItrT a_next,
258 std::ios_base& a_ios,
259 char_type a_fill,
260 const time_type& a_time) const
262 if (a_time.is_special()) {
263 return this->do_put_special(a_next, a_ios, a_fill,
264 a_time.date().as_special());
266 string_type format(this->m_format);
268 string_type frac_str;
269 if (format.find(seconds_with_fractional_seconds_format) != string_type::npos) {
270 // replace %s with %S.nnn
271 frac_str =
272 fractional_seconds_as_string(a_time.time_of_day(), false);
273 char_type sep = std::use_facet<std::numpunct<char_type> >(a_ios.getloc()).decimal_point();
275 string_type replace_string(seconds_format);
276 replace_string += sep;
277 replace_string += frac_str;
278 boost::algorithm::replace_all(format,
279 seconds_with_fractional_seconds_format,
280 replace_string);
282 /* NOTE: replacing posix_zone_string_format must be done BEFORE
283 * zone_name_format: "%ZP" & "%Z", if Z is checked first it will
284 * incorrectly replace a zone_name where a posix_string should go */
285 if (format.find(posix_zone_string_format) != string_type::npos) {
286 if(a_time.zone_abbrev().empty()) {
287 // if zone_abbrev() returns an empty string, we want to
288 // erase posix_zone_string_format from format
289 boost::algorithm::erase_all(format, posix_zone_string_format);
291 else{
292 boost::algorithm::replace_all(format,
293 posix_zone_string_format,
294 a_time.zone_as_posix_string());
297 if (format.find(zone_name_format) != string_type::npos) {
298 if(a_time.zone_name().empty()) {
299 /* TODO: this'll probably create problems if a user places
300 * the zone_*_format flag in the format with a ptime. This
301 * code removes the flag from the default formats */
303 // if zone_name() returns an empty string, we want to
304 // erase zone_name_format & one preceeding space
305 std::basic_ostringstream<char_type> ss;
306 ss << ' ' << zone_name_format;
307 boost::algorithm::erase_all(format, ss.str());
309 else{
310 boost::algorithm::replace_all(format,
311 zone_name_format,
312 a_time.zone_name());
315 if (format.find(zone_abbrev_format) != string_type::npos) {
316 if(a_time.zone_abbrev(false).empty()) {
317 /* TODO: this'll probably create problems if a user places
318 * the zone_*_format flag in the format with a ptime. This
319 * code removes the flag from the default formats */
321 // if zone_abbrev() returns an empty string, we want to
322 // erase zone_abbrev_format & one preceeding space
323 std::basic_ostringstream<char_type> ss;
324 ss << ' ' << zone_abbrev_format;
325 boost::algorithm::erase_all(format, ss.str());
327 else{
328 boost::algorithm::replace_all(format,
329 zone_abbrev_format,
330 a_time.zone_abbrev(false));
333 if (format.find(zone_iso_extended_format) != string_type::npos) {
334 if(a_time.zone_name(true).empty()) {
335 /* TODO: this'll probably create problems if a user places
336 * the zone_*_format flag in the format with a ptime. This
337 * code removes the flag from the default formats */
339 // if zone_name() returns an empty string, we want to
340 // erase zone_iso_extended_format from format
341 boost::algorithm::erase_all(format, zone_iso_extended_format);
343 else{
344 boost::algorithm::replace_all(format,
345 zone_iso_extended_format,
346 a_time.zone_name(true));
350 if (format.find(zone_iso_format) != string_type::npos) {
351 if(a_time.zone_abbrev(true).empty()) {
352 /* TODO: this'll probably create problems if a user places
353 * the zone_*_format flag in the format with a ptime. This
354 * code removes the flag from the default formats */
356 // if zone_abbrev() returns an empty string, we want to
357 // erase zone_iso_format from format
358 boost::algorithm::erase_all(format, zone_iso_format);
360 else{
361 boost::algorithm::replace_all(format,
362 zone_iso_format,
363 a_time.zone_abbrev(true));
366 if (format.find(fractional_seconds_format) != string_type::npos) {
367 // replace %f with nnnnnnn
368 if (!frac_str.size()) {
369 frac_str = fractional_seconds_as_string(a_time.time_of_day(), false);
371 boost::algorithm::replace_all(format,
372 fractional_seconds_format,
373 frac_str);
376 if (format.find(fractional_seconds_or_none_format) != string_type::npos) {
377 // replace %F with nnnnnnn or nothing if fs == 0
378 frac_str =
379 fractional_seconds_as_string(a_time.time_of_day(), true);
380 if (frac_str.size()) {
381 char_type sep = std::use_facet<std::numpunct<char_type> >(a_ios.getloc()).decimal_point();
382 string_type replace_string;
383 replace_string += sep;
384 replace_string += frac_str;
385 boost::algorithm::replace_all(format,
386 fractional_seconds_or_none_format,
387 replace_string);
389 else {
390 boost::algorithm::erase_all(format,
391 fractional_seconds_or_none_format);
395 return this->do_put_tm(a_next, a_ios, a_fill,
396 to_tm(a_time), format);
399 //! put function for time_duration
400 OutItrT put(OutItrT a_next,
401 std::ios_base& a_ios,
402 char_type a_fill,
403 const time_duration_type& a_time_dur) const
405 if (a_time_dur.is_special()) {
406 return this->do_put_special(a_next, a_ios, a_fill,
407 a_time_dur.get_rep().as_special());
410 string_type format(m_time_duration_format);
411 if (a_time_dur.is_negative()) {
412 // replace %- with minus sign. Should we use the numpunct facet?
413 boost::algorithm::replace_all(format,
414 duration_sign_negative_only,
415 negative_sign);
416 // remove all the %+ in the string with '-'
417 boost::algorithm::replace_all(format,
418 duration_sign_always,
419 negative_sign);
421 else { //duration is positive
422 // remove all the %- combos from the string
423 boost::algorithm::erase_all(format, duration_sign_negative_only);
424 // remove all the %+ in the string with '+'
425 boost::algorithm::replace_all(format,
426 duration_sign_always,
427 positive_sign);
431 * It is possible for a time duration to span more then 24 hours.
432 * Standard time_put::put is obliged to behave the same as strftime
433 * (See ISO 14882-2003 22.2.5.3.1 par. 1) and strftime's behavior is
434 * unspecified for the case when tm_hour field is outside 0-23 range
435 * (See ISO 9899-1999 7.23.3.5 par. 3). So we must output %H and %O
436 * here ourself.
438 string_type hours_str;
439 if (format.find(unrestricted_hours_format) != string_type::npos) {
440 hours_str = hours_as_string(a_time_dur);
441 boost::algorithm::replace_all(format, unrestricted_hours_format, hours_str);
443 // We still have to process restricted hours format specifier. In order to
444 // support parseability of durations in ISO format (%H%M%S), we'll have to
445 // restrict the stringified hours length to 2 characters.
446 if (format.find(hours_format) != string_type::npos) {
447 if (hours_str.empty())
448 hours_str = hours_as_string(a_time_dur);
449 BOOST_ASSERT(hours_str.length() <= 2);
450 boost::algorithm::replace_all(format, hours_format, hours_str);
453 string_type frac_str;
454 if (format.find(seconds_with_fractional_seconds_format) != string_type::npos) {
455 // replace %s with %S.nnn
456 frac_str =
457 fractional_seconds_as_string(a_time_dur, false);
458 char_type sep = std::use_facet<std::numpunct<char_type> >(a_ios.getloc()).decimal_point();
460 string_type replace_string(seconds_format);
461 replace_string += sep;
462 replace_string += frac_str;
463 boost::algorithm::replace_all(format,
464 seconds_with_fractional_seconds_format,
465 replace_string);
467 if (format.find(fractional_seconds_format) != string_type::npos) {
468 // replace %f with nnnnnnn
469 if (!frac_str.size()) {
470 frac_str = fractional_seconds_as_string(a_time_dur, false);
472 boost::algorithm::replace_all(format,
473 fractional_seconds_format,
474 frac_str);
477 if (format.find(fractional_seconds_or_none_format) != string_type::npos) {
478 // replace %F with nnnnnnn or nothing if fs == 0
479 frac_str =
480 fractional_seconds_as_string(a_time_dur, true);
481 if (frac_str.size()) {
482 char_type sep = std::use_facet<std::numpunct<char_type> >(a_ios.getloc()).decimal_point();
483 string_type replace_string;
484 replace_string += sep;
485 replace_string += frac_str;
486 boost::algorithm::replace_all(format,
487 fractional_seconds_or_none_format,
488 replace_string);
490 else {
491 boost::algorithm::erase_all(format,
492 fractional_seconds_or_none_format);
496 return this->do_put_tm(a_next, a_ios, a_fill,
497 to_tm(a_time_dur), format);
500 OutItrT put(OutItrT next, std::ios_base& a_ios,
501 char_type fill, const period_type& p) const
503 return this->m_period_formatter.put_period(next, a_ios, fill,p,*this);
507 protected:
509 static
510 string_type
511 fractional_seconds_as_string(const time_duration_type& a_time,
512 bool null_when_zero)
514 typename time_duration_type::fractional_seconds_type frac_sec =
515 a_time.fractional_seconds();
517 if (null_when_zero && (frac_sec == 0)) {
518 return string_type();
521 //make sure there is no sign
522 return integral_as_string(
523 date_time::absolute_value(frac_sec),
524 time_duration_type::num_fractional_digits());
527 static
528 string_type
529 hours_as_string(const time_duration_type& a_time, int width = 2)
531 return integral_as_string(date_time::absolute_value(a_time.hours()), width);
534 template< typename IntT >
535 static
536 string_type
537 integral_as_string(IntT val, int width = 2)
539 std::basic_ostringstream<char_type> ss;
540 ss.imbue(std::locale::classic()); // don't want any formatting
541 ss << std::setw(width)
542 << std::setfill(static_cast<char_type>('0'));
543 #if (defined(BOOST_MSVC) && (_MSC_VER < 1300))
544 // JDG [7/6/02 VC++ compatibility]
545 char_type buff[34];
546 ss << _i64toa(static_cast<boost::int64_t>(val), buff, 10);
547 #else
548 ss << val;
549 #endif
550 return ss.str();
553 private:
554 string_type m_time_duration_format;
558 template <class time_type, class CharT, class OutItrT>
559 std::locale::id time_facet<time_type, CharT, OutItrT>::id;
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>::fractional_seconds_format = time_formats<CharT>::fractional_seconds_format;
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>::fractional_seconds_or_none_format = time_formats<CharT>::fractional_seconds_or_none_format;
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>::seconds_with_fractional_seconds_format =
572 time_formats<CharT>::seconds_with_fractional_seconds_format;
575 template <class time_type, class CharT, class OutItrT>
576 const typename time_facet<time_type, CharT, OutItrT>::char_type*
577 time_facet<time_type, CharT, OutItrT>::zone_name_format = time_formats<CharT>::zone_name_format;
579 template <class time_type, class CharT, class OutItrT>
580 const typename time_facet<time_type, CharT, OutItrT>::char_type*
581 time_facet<time_type, CharT, OutItrT>::zone_abbrev_format = time_formats<CharT>::zone_abbrev_format;
583 template <class time_type, class CharT, class OutItrT>
584 const typename time_facet<time_type, CharT, OutItrT>::char_type*
585 time_facet<time_type, CharT, OutItrT>::zone_iso_extended_format =time_formats<CharT>::zone_iso_extended_format;
587 template <class time_type, class CharT, class OutItrT>
588 const typename time_facet<time_type, CharT, OutItrT>::char_type*
589 time_facet<time_type, CharT, OutItrT>::posix_zone_string_format =time_formats<CharT>::posix_zone_string_format;
591 template <class time_type, class CharT, class OutItrT>
592 const typename time_facet<time_type, CharT, OutItrT>::char_type*
593 time_facet<time_type, CharT, OutItrT>::zone_iso_format = time_formats<CharT>::zone_iso_format;
595 template <class time_type, class CharT, class OutItrT>
596 const typename time_facet<time_type, CharT, OutItrT>::char_type*
597 time_facet<time_type, CharT, OutItrT>::seconds_format = time_formats<CharT>::seconds_format;
599 template <class time_type, class CharT, class OutItrT>
600 const typename time_facet<time_type, CharT, OutItrT>::char_type*
601 time_facet<time_type, CharT, OutItrT>::hours_format = time_formats<CharT>::hours_format;
603 template <class time_type, class CharT, class OutItrT>
604 const typename time_facet<time_type, CharT, OutItrT>::char_type*
605 time_facet<time_type, CharT, OutItrT>::unrestricted_hours_format = time_formats<CharT>::unrestricted_hours_format;
607 template <class time_type, class CharT, class OutItrT>
608 const typename time_facet<time_type, CharT, OutItrT>::char_type*
609 time_facet<time_type, CharT, OutItrT>::standard_format = time_formats<CharT>::standard_format;
611 template <class time_type, class CharT, class OutItrT>
612 const typename time_facet<time_type, CharT, OutItrT>::char_type*
613 time_facet<time_type, CharT, OutItrT>::duration_seperator = time_formats<CharT>::duration_seperator;
615 template <class time_type, class CharT, class OutItrT>
616 const typename time_facet<time_type, CharT, OutItrT>::char_type*
617 time_facet<time_type, CharT, OutItrT>::negative_sign = time_formats<CharT>::negative_sign;
619 template <class time_type, class CharT, class OutItrT>
620 const typename time_facet<time_type, CharT, OutItrT>::char_type*
621 time_facet<time_type, CharT, OutItrT>::positive_sign = time_formats<CharT>::positive_sign;
623 template <class time_type, class CharT, class OutItrT>
624 const typename time_facet<time_type, CharT, OutItrT>::char_type*
625 time_facet<time_type, CharT, OutItrT>::duration_sign_negative_only = time_formats<CharT>::duration_sign_negative_only;
627 template <class time_type, class CharT, class OutItrT>
628 const typename time_facet<time_type, CharT, OutItrT>::char_type*
629 time_facet<time_type, CharT, OutItrT>::duration_sign_always = time_formats<CharT>::duration_sign_always;
631 template <class time_type, class CharT, class OutItrT>
632 const typename time_facet<time_type,CharT, OutItrT>::char_type*
633 time_facet<time_type,CharT, OutItrT>::iso_time_format_specifier = time_formats<CharT>::iso_time_format_specifier;
635 template <class time_type, class CharT, class OutItrT>
636 const typename time_facet<time_type, CharT, OutItrT>::char_type*
637 time_facet<time_type, CharT, OutItrT>::iso_time_format_extended_specifier = time_formats<CharT>::iso_time_format_extended_specifier;
639 template <class time_type, class CharT, class OutItrT>
640 const typename time_facet<time_type, CharT, OutItrT>::char_type*
641 time_facet<time_type, CharT, OutItrT>::default_time_format =
642 time_formats<CharT>::default_time_format;
644 template <class time_type, class CharT, class OutItrT>
645 const typename time_facet<time_type, CharT, OutItrT>::char_type*
646 time_facet<time_type, CharT, OutItrT>::default_time_duration_format =
647 time_formats<CharT>::default_time_duration_format;
650 //! Facet for format-based input.
653 template <class time_type,
654 class CharT,
655 class InItrT = std::istreambuf_iterator<CharT, std::char_traits<CharT> > >
656 class time_input_facet :
657 public boost::date_time::date_input_facet<typename time_type::date_type , CharT, InItrT> {
658 public:
659 typedef typename time_type::date_type date_type;
660 typedef typename time_type::time_duration_type time_duration_type;
661 typedef typename time_duration_type::fractional_seconds_type fracional_seconds_type;
662 typedef boost::date_time::period<time_type,time_duration_type> period_type;
663 typedef boost::date_time::date_input_facet<typename time_type::date_type, CharT, InItrT> base_type;
664 typedef typename base_type::duration_type date_duration_type;
665 typedef typename base_type::year_type year_type;
666 typedef typename base_type::month_type month_type;
667 typedef typename base_type::day_type day_type;
668 typedef typename base_type::string_type string_type;
669 typedef typename string_type::const_iterator const_itr;
670 typedef typename base_type::char_type char_type;
671 typedef typename base_type::format_date_parser_type format_date_parser_type;
672 typedef typename base_type::period_parser_type period_parser_type;
673 typedef typename base_type::special_values_parser_type special_values_parser_type;
674 typedef typename base_type::date_gen_parser_type date_gen_parser_type;
675 typedef typename base_type::special_values_parser_type::match_results match_results;
677 static const char_type* fractional_seconds_format; // f
678 static const char_type* fractional_seconds_or_none_format; // F
679 static const char_type* seconds_with_fractional_seconds_format; // s
680 static const char_type* seconds_format; // S
681 static const char_type* standard_format; // x X
682 static const char_type* zone_abbrev_format; // z
683 static const char_type* zone_name_format; // Z
684 static const char_type* zone_iso_format; // q
685 static const char_type* zone_iso_extended_format; // Q
686 static const char_type* duration_seperator;
687 static const char_type* iso_time_format_specifier;
688 static const char_type* iso_time_format_extended_specifier;
689 static const char_type* default_time_input_format;
690 static const char_type* default_time_duration_format;
691 static std::locale::id id;
693 //! Constructor that takes a format string for a ptime
694 explicit time_input_facet(const string_type& format, ::size_t a_ref = 0)
695 : base_type(format, a_ref),
696 m_time_duration_format(default_time_duration_format)
699 explicit time_input_facet(const string_type& format,
700 const format_date_parser_type& date_parser,
701 const special_values_parser_type& sv_parser,
702 const period_parser_type& per_parser,
703 const date_gen_parser_type& date_gen_parser,
704 ::size_t a_ref = 0)
705 : base_type(format,
706 date_parser,
707 sv_parser,
708 per_parser,
709 date_gen_parser,
710 a_ref),
711 m_time_duration_format(default_time_duration_format)
714 //! sets default formats for ptime, local_date_time, and time_duration
715 explicit time_input_facet(::size_t a_ref = 0)
716 : base_type(default_time_input_format, a_ref),
717 m_time_duration_format(default_time_duration_format)
720 //! Set the format for time_duration
721 void time_duration_format(const char_type* const format) {
722 m_time_duration_format = format;
724 virtual void set_iso_format()
726 this->m_format = iso_time_format_specifier;
728 virtual void set_iso_extended_format()
730 this->m_format = iso_time_format_extended_specifier;
733 InItrT get(InItrT& sitr,
734 InItrT& stream_end,
735 std::ios_base& a_ios,
736 period_type& p) const
738 p = this->m_period_parser.get_period(sitr,
739 stream_end,
740 a_ios,
742 time_duration_type::unit(),
743 *this);
744 return sitr;
747 //default ptime format is YYYY-Mon-DD HH:MM:SS[.fff...][ zzz]
748 //default time_duration format is %H:%M:%S%F HH:MM:SS[.fff...]
750 InItrT get(InItrT& sitr,
751 InItrT& stream_end,
752 std::ios_base& a_ios,
753 time_duration_type& td) const
755 // skip leading whitespace
756 while((sitr != stream_end) && std::isspace(*sitr)) { ++sitr; }
758 bool use_current_char = false;
760 // num_get will consume the +/-, we may need a copy if special_value
761 char_type c = '\0';
762 if((sitr != stream_end) && (*sitr == '-' || *sitr == '+')) {
763 c = *sitr;
766 typedef typename time_duration_type::hour_type hour_type;
767 typedef typename time_duration_type::min_type min_type;
768 typedef typename time_duration_type::sec_type sec_type;
770 hour_type hour = 0;
771 min_type min = 0;
772 sec_type sec = 0;
773 typename time_duration_type::fractional_seconds_type frac(0);
775 typedef std::num_get<CharT, InItrT> num_get;
776 if(!std::has_facet<num_get>(a_ios.getloc())) {
777 num_get* ng = new num_get();
778 std::locale loc = std::locale(a_ios.getloc(), ng);
779 a_ios.imbue(loc);
782 const_itr itr(m_time_duration_format.begin());
783 while (itr != m_time_duration_format.end() && (sitr != stream_end)) {
784 if (*itr == '%') {
785 ++itr;
786 if (*itr != '%') {
787 switch(*itr) {
788 case 'O':
790 // A period may span more than 24 hours. In that case the format
791 // string should be composed with the unrestricted hours specifier.
792 hour = var_string_to_int<hour_type, CharT>(sitr, stream_end,
793 std::numeric_limits<hour_type>::digits10 + 1);
794 if(hour == -1){
795 return check_special_value(sitr, stream_end, td, c);
797 break;
799 case 'H':
801 match_results mr;
802 hour = fixed_string_to_int<hour_type, CharT>(sitr, stream_end, mr, 2);
803 if(hour == -1){
804 return check_special_value(sitr, stream_end, td, c);
806 break;
808 case 'M':
810 match_results mr;
811 min = fixed_string_to_int<min_type, CharT>(sitr, stream_end, mr, 2);
812 if(min == -1){
813 return check_special_value(sitr, stream_end, td, c);
815 break;
817 case 's':
818 case 'S':
820 match_results mr;
821 sec = fixed_string_to_int<sec_type, CharT>(sitr, stream_end, mr, 2);
822 if(sec == -1){
823 return check_special_value(sitr, stream_end, td, c);
825 if (*itr == 'S')
826 break;
827 // %s is the same as %S%f so we drop through into %f
829 case 'f':
831 // check for decimal, check special_values if missing
832 if(*sitr == '.') {
833 ++sitr;
834 parse_frac_type(sitr, stream_end, frac);
835 // sitr will point to next expected char after this parsing
836 // is complete so no need to advance it
837 use_current_char = true;
839 else {
840 return check_special_value(sitr, stream_end, td, c);
842 break;
844 case 'F':
846 // check for decimal, skip if missing
847 if(*sitr == '.') {
848 ++sitr;
849 parse_frac_type(sitr, stream_end, frac);
850 // sitr will point to next expected char after this parsing
851 // is complete so no need to advance it
852 use_current_char = true;
854 else {
855 // nothing was parsed so we don't want to advance sitr
856 use_current_char = true;
858 break;
860 default:
861 {} // ignore what we don't understand?
862 }// switch
864 else { // itr == '%', second consecutive
865 ++sitr;
868 ++itr; //advance past format specifier
870 else { //skip past chars in format and in buffer
871 ++itr;
872 // set use_current_char when sitr is already
873 // pointing at the next character to process
874 if (use_current_char) {
875 use_current_char = false;
877 else {
878 ++sitr;
883 td = time_duration_type(hour, min, sec, frac);
884 return sitr;
888 //! Parses a time object from the input stream
889 InItrT get(InItrT& sitr,
890 InItrT& stream_end,
891 std::ios_base& a_ios,
892 time_type& t) const
894 string_type tz_str;
895 return get(sitr, stream_end, a_ios, t, tz_str, false);
897 //! Expects a time_zone in the input stream
898 InItrT get_local_time(InItrT& sitr,
899 InItrT& stream_end,
900 std::ios_base& a_ios,
901 time_type& t,
902 string_type& tz_str) const
904 return get(sitr, stream_end, a_ios, t, tz_str, true);
907 protected:
909 InItrT get(InItrT& sitr,
910 InItrT& stream_end,
911 std::ios_base& a_ios,
912 time_type& t,
913 string_type& tz_str,
914 bool time_is_local) const
916 // skip leading whitespace
917 while((sitr != stream_end) && std::isspace(*sitr)) { ++sitr; }
919 bool use_current_char = false;
920 bool use_current_format_char = false; // used whith two character flags
922 // num_get will consume the +/-, we may need a copy if special_value
923 char_type c = '\0';
924 if((sitr != stream_end) && (*sitr == '-' || *sitr == '+')) {
925 c = *sitr;
928 typedef typename time_duration_type::hour_type hour_type;
929 typedef typename time_duration_type::min_type min_type;
930 typedef typename time_duration_type::sec_type sec_type;
932 // time elements
933 hour_type hour = 0;
934 min_type min = 0;
935 sec_type sec = 0;
936 typename time_duration_type::fractional_seconds_type frac(0);
937 // date elements
938 short day_of_year(0);
939 /* Initialized the following to their minimum values. These intermediate
940 * objects are used so we get specific exceptions when part of the input
941 * is unparsable.
942 * Ex: "205-Jan-15" will throw a bad_year, "2005-Jsn-15"- bad_month, etc.*/
943 year_type t_year(1400);
944 month_type t_month(1);
945 day_type t_day(1);
947 typedef std::num_get<CharT, InItrT> num_get;
948 if(!std::has_facet<num_get>(a_ios.getloc())) {
949 num_get* ng = new num_get();
950 std::locale loc = std::locale(a_ios.getloc(), ng);
951 a_ios.imbue(loc);
954 const_itr itr(this->m_format.begin());
955 while (itr != this->m_format.end() && (sitr != stream_end)) {
956 if (*itr == '%') {
957 ++itr;
958 if (*itr != '%') {
959 // the cases are grouped by date & time flags - not alphabetical order
960 switch(*itr) {
961 // date flags
962 case 'Y':
963 case 'y':
965 char_type cs[3] = { '%', *itr };
966 string_type s(cs);
967 match_results mr;
968 try {
969 t_year = this->m_parser.parse_year(sitr, stream_end, s, mr);
971 catch(std::out_of_range&) { // base class for bad_year exception
972 if(this->m_sv_parser.match(sitr, stream_end, mr)) {
973 t = time_type(static_cast<special_values>(mr.current_match));
974 return sitr;
976 else {
977 throw; // rethrow bad_year
980 break;
982 case 'B':
983 case 'b':
984 case 'm':
986 char_type cs[3] = { '%', *itr };
987 string_type s(cs);
988 match_results mr;
989 try {
990 t_month = this->m_parser.parse_month(sitr, stream_end, s, mr);
992 catch(std::out_of_range&) { // base class for bad_month exception
993 if(this->m_sv_parser.match(sitr, stream_end, mr)) {
994 t = time_type(static_cast<special_values>(mr.current_match));
995 return sitr;
997 else {
998 throw; // rethrow bad_month
1001 // did m_parser already advance sitr to next char?
1002 if(mr.has_remaining()) {
1003 use_current_char = true;
1005 break;
1007 case 'a':
1008 case 'A':
1009 case 'w':
1011 // weekday is not used in construction but we need to get it out of the stream
1012 char_type cs[3] = { '%', *itr };
1013 string_type s(cs);
1014 match_results mr;
1015 typename date_type::day_of_week_type wd(0);
1016 try {
1017 wd = this->m_parser.parse_weekday(sitr, stream_end, s, mr);
1019 catch(std::out_of_range&) { // base class for bad_weekday exception
1020 if(this->m_sv_parser.match(sitr, stream_end, mr)) {
1021 t = time_type(static_cast<special_values>(mr.current_match));
1022 return sitr;
1024 else {
1025 throw; // rethrow bad_weekday
1028 // did m_parser already advance sitr to next char?
1029 if(mr.has_remaining()) {
1030 use_current_char = true;
1032 break;
1034 case 'j':
1036 // code that gets julian day (from format_date_parser)
1037 match_results mr;
1038 day_of_year = fixed_string_to_int<unsigned short, CharT>(sitr, stream_end, mr, 3);
1039 if(day_of_year == -1) {
1040 if(this->m_sv_parser.match(sitr, stream_end, mr)) {
1041 t = time_type(static_cast<special_values>(mr.current_match));
1042 return sitr;
1045 // these next two lines are so we get an exception with bad input
1046 typedef typename time_type::date_type::day_of_year_type day_of_year_type;
1047 day_of_year_type t_day_of_year(day_of_year);
1048 break;
1050 case 'd':
1052 try {
1053 t_day = this->m_parser.parse_day_of_month(sitr, stream_end);
1055 catch(std::out_of_range&) { // base class for exception bad_day_of_month
1056 match_results mr;
1057 if(this->m_sv_parser.match(sitr, stream_end, mr)) {
1058 t = time_type(static_cast<special_values>(mr.current_match));
1059 return sitr;
1061 else {
1062 throw; // rethrow bad_day_of_month
1065 break;
1067 // time flags
1068 case 'H':
1070 match_results mr;
1071 hour = fixed_string_to_int<hour_type, CharT>(sitr, stream_end, mr, 2);
1072 if(hour == -1){
1073 return check_special_value(sitr, stream_end, t, c);
1075 break;
1077 case 'M':
1079 match_results mr;
1080 min = fixed_string_to_int<min_type, CharT>(sitr, stream_end, mr, 2);
1081 if(min == -1){
1082 return check_special_value(sitr, stream_end, t, c);
1084 break;
1086 case 's':
1087 case 'S':
1089 match_results mr;
1090 sec = fixed_string_to_int<sec_type, CharT>(sitr, stream_end, mr, 2);
1091 if(sec == -1){
1092 return check_special_value(sitr, stream_end, t, c);
1094 if (*itr == 'S')
1095 break;
1096 // %s is the same as %S%f so we drop through into %f
1098 case 'f':
1100 // check for decimal, check SV if missing
1101 if(*sitr == '.') {
1102 ++sitr;
1103 parse_frac_type(sitr, stream_end, frac);
1104 // sitr will point to next expected char after this parsing
1105 // is complete so no need to advance it
1106 use_current_char = true;
1108 else {
1109 return check_special_value(sitr, stream_end, t, c);
1111 break;
1113 case 'F':
1115 // check for decimal, skip if missing
1116 if(*sitr == '.') {
1117 ++sitr;
1118 parse_frac_type(sitr, stream_end, frac);
1119 // sitr will point to next expected char after this parsing
1120 // is complete so no need to advance it
1121 use_current_char = true;
1123 else {
1124 // nothing was parsed so we don't want to advance sitr
1125 use_current_char = true;
1127 break;
1129 // time_zone flags
1130 //case 'q':
1131 //case 'Q':
1132 //case 'z':
1133 case 'Z':
1135 if(time_is_local) { // skip if 't' is a ptime
1136 ++itr;
1137 if(*itr == 'P') {
1138 // skip leading whitespace
1139 while((sitr != stream_end) && std::isspace(*sitr)) { ++sitr; }
1140 // parse zone
1141 while((sitr != stream_end) && (!std::isspace(*sitr))) {
1142 tz_str += *sitr;
1143 ++sitr;
1146 else {
1147 use_current_format_char = true;
1151 else {
1152 // nothing was parsed so we don't want to advance sitr
1153 use_current_char = true;
1156 break;
1158 default:
1159 {} // ignore what we don't understand?
1160 }// switch
1162 else { // itr == '%', second consecutive
1163 ++sitr;
1166 if(use_current_format_char) {
1167 use_current_format_char = false;
1169 else {
1170 ++itr; //advance past format specifier
1174 else { //skip past chars in format and in buffer
1175 ++itr;
1176 // set use_current_char when sitr is already
1177 // pointing at the next character to process
1178 if (use_current_char) {
1179 use_current_char = false;
1181 else {
1182 ++sitr;
1187 date_type d(not_a_date_time);
1188 if (day_of_year > 0) {
1189 d = date_type(static_cast<unsigned short>(t_year-1),12,31) + date_duration_type(day_of_year);
1191 else {
1192 d = date_type(t_year, t_month, t_day);
1195 time_duration_type td(hour, min, sec, frac);
1196 t = time_type(d, td);
1197 return sitr;
1200 //! Helper function to check for special_value
1201 /*! First character may have been consumed during original parse
1202 * attempt. Parameter 'c' should be a copy of that character.
1203 * Throws ios_base::failure if parse fails. */
1204 template<class temporal_type>
1205 inline
1206 InItrT check_special_value(InItrT& sitr,InItrT& stream_end, temporal_type& tt, char_type c='\0') const
1208 match_results mr;
1209 if((c == '-' || c == '+') && (*sitr != c)) { // was the first character consumed?
1210 mr.cache += c;
1212 this->m_sv_parser.match(sitr, stream_end, mr);
1213 if(mr.current_match == match_results::PARSE_ERROR) {
1214 std::string tmp = convert_string_type<char_type, char>(mr.cache);
1215 boost::throw_exception(std::ios_base::failure("Parse failed. No match found for '" + tmp + "'"));
1216 BOOST_DATE_TIME_UNREACHABLE_EXPRESSION(return sitr); // should never reach
1218 tt = temporal_type(static_cast<special_values>(mr.current_match));
1219 return sitr;
1222 //! Helper function for parsing a fractional second type from the stream
1223 void parse_frac_type(InItrT& sitr,
1224 InItrT& stream_end,
1225 fracional_seconds_type& frac) const
1227 string_type cache;
1228 while((sitr != stream_end) && std::isdigit(*sitr)) {
1229 cache += *sitr;
1230 ++sitr;
1232 if(cache.size() > 0) {
1233 unsigned short precision = time_duration_type::num_fractional_digits();
1234 // input may be only the first few decimal places
1235 if(cache.size() < precision) {
1236 frac = lexical_cast<fracional_seconds_type>(cache);
1237 frac = decimal_adjust(frac, static_cast<unsigned short>(precision - cache.size()));
1239 else {
1240 // if input has too many decimal places, drop excess digits
1241 frac = lexical_cast<fracional_seconds_type>(cache.substr(0, precision));
1246 private:
1247 string_type m_time_duration_format;
1249 //! Helper function to adjust trailing zeros when parsing fractional digits
1250 template<class int_type>
1251 inline
1252 int_type decimal_adjust(int_type val, const unsigned short places) const
1254 unsigned long factor = 1;
1255 for(int i = 0; i < places; ++i){
1256 factor *= 10; // shift decimal to the right
1258 return val * factor;
1263 template <class time_type, class CharT, class InItrT>
1264 std::locale::id time_input_facet<time_type, CharT, InItrT>::id;
1266 template <class time_type, class CharT, class InItrT>
1267 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1268 time_input_facet<time_type, CharT, InItrT>::fractional_seconds_format = time_formats<CharT>::fractional_seconds_format;
1270 template <class time_type, class CharT, class InItrT>
1271 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1272 time_input_facet<time_type, CharT, InItrT>::fractional_seconds_or_none_format = time_formats<CharT>::fractional_seconds_or_none_format;
1274 template <class time_type, class CharT, class InItrT>
1275 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1276 time_input_facet<time_type, CharT, InItrT>::seconds_with_fractional_seconds_format = time_formats<CharT>::seconds_with_fractional_seconds_format;
1278 template <class time_type, class CharT, class InItrT>
1279 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1280 time_input_facet<time_type, CharT, InItrT>::seconds_format = time_formats<CharT>::seconds_format;
1282 template <class time_type, class CharT, class InItrT>
1283 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1284 time_input_facet<time_type, CharT, InItrT>::standard_format = time_formats<CharT>::standard_format;
1286 template <class time_type, class CharT, class InItrT>
1287 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1288 time_input_facet<time_type, CharT, InItrT>::zone_abbrev_format = time_formats<CharT>::zone_abbrev_format;
1290 template <class time_type, class CharT, class InItrT>
1291 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1292 time_input_facet<time_type, CharT, InItrT>::zone_name_format = time_formats<CharT>::zone_name_format;
1294 template <class time_type, class CharT, class InItrT>
1295 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1296 time_input_facet<time_type, CharT, InItrT>::zone_iso_format = time_formats<CharT>::zone_iso_format;
1298 template <class time_type, class CharT, class InItrT>
1299 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1300 time_input_facet<time_type, CharT, InItrT>::zone_iso_extended_format = time_formats<CharT>::zone_iso_extended_format;
1302 template <class time_type, class CharT, class InItrT>
1303 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1304 time_input_facet<time_type, CharT, InItrT>::duration_seperator = time_formats<CharT>::duration_seperator;
1306 template <class time_type, class CharT, class InItrT>
1307 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1308 time_input_facet<time_type, CharT, InItrT>::iso_time_format_specifier = time_formats<CharT>::iso_time_format_specifier;
1310 template <class time_type, class CharT, class InItrT>
1311 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1312 time_input_facet<time_type, CharT, InItrT>::iso_time_format_extended_specifier = time_formats<CharT>::iso_time_format_extended_specifier;
1314 template <class time_type, class CharT, class InItrT>
1315 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1316 time_input_facet<time_type, CharT, InItrT>::default_time_input_format = time_formats<CharT>::default_time_input_format;
1318 template <class time_type, class CharT, class InItrT>
1319 const typename time_input_facet<time_type, CharT, InItrT>::char_type*
1320 time_input_facet<time_type, CharT, InItrT>::default_time_duration_format = time_formats<CharT>::default_time_duration_format;
1323 } } // namespaces
1326 #endif