1 /*
2  *          Copyright Andrey Semashev 2007 - 2015.
3  * Distributed under the Boost Software License, Version 1.0.
4  *    (See accompanying file LICENSE_1_0.txt or copy at
5  *          http://www.boost.org/LICENSE_1_0.txt)
6  */
7 /*!
8  * \file   date_time_format_parser.hpp
9  * \author Andrey Semashev
10  * \date   16.09.2012
11  *
12  * The header contains a parser for date and time format strings.
13  */
14 
15 #ifndef BOOST_LOG_DETAIL_DATE_TIME_FORMAT_PARSER_HPP_INCLUDED_
16 #define BOOST_LOG_DETAIL_DATE_TIME_FORMAT_PARSER_HPP_INCLUDED_
17 
18 #include <string>
19 #include <boost/range/as_literal.hpp>
20 #include <boost/range/iterator_range_core.hpp>
21 #include <boost/log/detail/config.hpp>
22 #include <boost/log/detail/header.hpp>
23 
24 #ifdef BOOST_HAS_PRAGMA_ONCE
25 #pragma once
26 #endif
27 
28 namespace boost {
29 
30 BOOST_LOG_OPEN_NAMESPACE
31 
32 namespace aux {
33 
34 /*!
35  * This is the interface the parser will use to notify the caller about various components of date in the format string.
36  */
37 template< typename CharT >
38 struct date_format_parser_callback
39 {
40     //! Character type used by the parser
41     typedef CharT char_type;
42 
43     //! Destructor
~date_format_parser_callbackboost::aux::date_format_parser_callback44     virtual ~date_format_parser_callback() {}
45 
46     /*!
47      * \brief The function is called when the parser discovers a string literal in the format string
48      *
49      * \param lit The string of characters not interpreted as a placeholder
50      */
51     virtual void on_literal(iterator_range< const char_type* > const& lit) = 0;
52 
53     /*!
54      * \brief The method is called when an unknown placeholder is found in the format string
55      *
56      * \param ph The placeholder with the leading percent sign
57      */
on_placeholderboost::aux::date_format_parser_callback58     virtual void on_placeholder(iterator_range< const char_type* > const& ph)
59     {
60         // By default interpret all unrecognized placeholders as literals
61         on_literal(ph);
62     }
63 
64     /*!
65      * \brief The function is called when the short year placeholder is found in the format string
66      */
on_short_yearboost::aux::date_format_parser_callback67     virtual void on_short_year()
68     {
69         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('y'), static_cast< char_type >('\0') };
70         on_placeholder(boost::as_literal(placeholder));
71     }
72 
73     /*!
74      * \brief The function is called when the full year placeholder is found in the format string
75      */
on_full_yearboost::aux::date_format_parser_callback76     virtual void on_full_year()
77     {
78         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('Y'), static_cast< char_type >('\0') };
79         on_placeholder(boost::as_literal(placeholder));
80     }
81 
82     /*!
83      * \brief The function is called when the numeric month placeholder is found in the format string
84      */
on_numeric_monthboost::aux::date_format_parser_callback85     virtual void on_numeric_month()
86     {
87         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('m'), static_cast< char_type >('\0') };
88         on_placeholder(boost::as_literal(placeholder));
89     }
90 
91     /*!
92      * \brief The function is called when the short alphabetic month placeholder is found in the format string
93      */
on_short_monthboost::aux::date_format_parser_callback94     virtual void on_short_month()
95     {
96         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('b'), static_cast< char_type >('\0') };
97         on_placeholder(boost::as_literal(placeholder));
98     }
99 
100     /*!
101      * \brief The function is called when the full alphabetic month placeholder is found in the format string
102      */
on_full_monthboost::aux::date_format_parser_callback103     virtual void on_full_month()
104     {
105         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('B'), static_cast< char_type >('\0') };
106         on_placeholder(boost::as_literal(placeholder));
107     }
108 
109     /*!
110      * \brief The function is called when the numeric day of month placeholder is found in the format string
111      *
112      * \param leading_zero If \c true, the day should be formatted with leading zero, otherwise with leading space
113      */
on_month_dayboost::aux::date_format_parser_callback114     virtual void on_month_day(bool leading_zero)
115     {
116         const char_type placeholder[3] = { static_cast< char_type >('%'), (leading_zero ? static_cast< char_type >('d') : static_cast< char_type >('e')), static_cast< char_type >('\0') };
117         on_placeholder(boost::as_literal(placeholder));
118     }
119 
120     /*!
121      * \brief The function is called when the numeric day of week placeholder is found in the format string
122      */
on_numeric_week_dayboost::aux::date_format_parser_callback123     virtual void on_numeric_week_day()
124     {
125         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('w'), static_cast< char_type >('\0') };
126         on_placeholder(boost::as_literal(placeholder));
127     }
128 
129     /*!
130      * \brief The function is called when the short alphabetic day of week placeholder is found in the format string
131      */
on_short_week_dayboost::aux::date_format_parser_callback132     virtual void on_short_week_day()
133     {
134         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('a'), static_cast< char_type >('\0') };
135         on_placeholder(boost::as_literal(placeholder));
136     }
137 
138     /*!
139      * \brief The function is called when the full alphabetic day of week placeholder is found in the format string
140      */
on_full_week_dayboost::aux::date_format_parser_callback141     virtual void on_full_week_day()
142     {
143         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('A'), static_cast< char_type >('\0') };
144         on_placeholder(boost::as_literal(placeholder));
145     }
146 
147     /*!
148      * \brief The function is called when the ISO-formatted date is found in the format string
149      */
on_iso_dateboost::aux::date_format_parser_callback150     virtual void on_iso_date()
151     {
152         on_full_year();
153         on_numeric_month();
154         on_month_day(true);
155     }
156 
157     /*!
158      * \brief The function is called when the extended ISO-formatted date is found in the format string
159      */
on_extended_iso_dateboost::aux::date_format_parser_callback160     virtual void on_extended_iso_date()
161     {
162         const char_type delimiter[2] = { static_cast< char_type >('-'), static_cast< char_type >('\0') };
163         on_full_year();
164         on_literal(boost::as_literal(delimiter));
165         on_numeric_month();
166         on_literal(boost::as_literal(delimiter));
167         on_month_day(true);
168     }
169 };
170 
171 /*!
172  * This is the interface the parser will use to notify the caller about various components of date in the format string.
173  */
174 template< typename CharT >
175 struct time_format_parser_callback
176 {
177     //! Character type used by the parser
178     typedef CharT char_type;
179 
180     //! Destructor
~time_format_parser_callbackboost::aux::time_format_parser_callback181     virtual ~time_format_parser_callback() {}
182 
183     /*!
184      * \brief The function is called when the parser discovers a string literal in the format string
185      *
186      * \param lit The string of characters not interpreted as a placeholder
187      */
188     virtual void on_literal(iterator_range< const char_type* > const& lit) = 0;
189 
190     /*!
191      * \brief The method is called when an unknown placeholder is found in the format string
192      *
193      * \param ph The placeholder with the leading percent sign
194      */
on_placeholderboost::aux::time_format_parser_callback195     virtual void on_placeholder(iterator_range< const char_type* > const& ph)
196     {
197         // By default interpret all unrecognized placeholders as literals
198         on_literal(ph);
199     }
200 
201     /*!
202      * \brief The function is called when the hours placeholder is found in the format string
203      *
204      * The placeholder is used for 24-hour clock and duration formatting.
205      *
206      * \param leading_zero If \c true, the one-digit number of hours should be formatted with leading zero, otherwise with leading space
207      */
on_hoursboost::aux::time_format_parser_callback208     virtual void on_hours(bool leading_zero)
209     {
210         const char_type placeholder[3] = { static_cast< char_type >('%'), (leading_zero ? static_cast< char_type >('O') : static_cast< char_type >('k')), static_cast< char_type >('\0') };
211         on_placeholder(boost::as_literal(placeholder));
212     }
213 
214     /*!
215      * \brief The function is called when the hours placeholder is found in the format string
216      *
217      * The placeholder is used for 12-hour clock formatting. It should not be used for duration formatting.
218      *
219      * \param leading_zero If \c true, the one-digit number of hours should be formatted with leading zero, otherwise with leading space
220      */
on_hours_12boost::aux::time_format_parser_callback221     virtual void on_hours_12(bool leading_zero)
222     {
223         const char_type placeholder[3] = { static_cast< char_type >('%'), (leading_zero ? static_cast< char_type >('I') : static_cast< char_type >('l')), static_cast< char_type >('\0') };
224         on_placeholder(boost::as_literal(placeholder));
225     }
226 
227     /*!
228      * \brief The function is called when the minutes placeholder is found in the format string
229      */
on_minutesboost::aux::time_format_parser_callback230     virtual void on_minutes()
231     {
232         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('M'), static_cast< char_type >('\0') };
233         on_placeholder(boost::as_literal(placeholder));
234     }
235 
236     /*!
237      * \brief The function is called when the seconds placeholder is found in the format string
238      */
on_secondsboost::aux::time_format_parser_callback239     virtual void on_seconds()
240     {
241         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('S'), static_cast< char_type >('\0') };
242         on_placeholder(boost::as_literal(placeholder));
243     }
244 
245     /*!
246      * \brief The function is called when the fractional seconds placeholder is found in the format string
247      */
on_fractional_secondsboost::aux::time_format_parser_callback248     virtual void on_fractional_seconds()
249     {
250         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('f'), static_cast< char_type >('\0') };
251         on_placeholder(boost::as_literal(placeholder));
252     }
253 
254     /*!
255      * \brief The function is called when the day period (AM/PM) placeholder is found in the format string
256      *
257      * The placeholder is used for 12-hour clock formatting. It should not be used for duration formatting.
258      *
259      * \param upper_case If \c true, the day period will be upper case, and lower case otherwise
260      */
on_am_pmboost::aux::time_format_parser_callback261     virtual void on_am_pm(bool upper_case)
262     {
263         const char_type placeholder[3] = { static_cast< char_type >('%'), (upper_case ? static_cast< char_type >('p') : static_cast< char_type >('P')), static_cast< char_type >('\0') };
264         on_placeholder(boost::as_literal(placeholder));
265     }
266 
267     /*!
268      * \brief The function is called when the time duration sign placeholder is found in the format string
269      *
270      * The placeholder is used for duration formatting. It should not be used for time point formatting.
271      *
272      * \param display_positive If \c true, the positive sign will be explicitly displayed, otherwise only negative sign will be displayed
273      */
on_duration_signboost::aux::time_format_parser_callback274     virtual void on_duration_sign(bool display_positive)
275     {
276         const char_type placeholder[3] = { static_cast< char_type >('%'), (display_positive ? static_cast< char_type >('+') : static_cast< char_type >('-')), static_cast< char_type >('\0') };
277         on_placeholder(boost::as_literal(placeholder));
278     }
279 
280     /*!
281      * \brief The function is called when the ISO time zone placeholder is found in the format string
282      */
on_iso_time_zoneboost::aux::time_format_parser_callback283     virtual void on_iso_time_zone()
284     {
285         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('q'), static_cast< char_type >('\0') };
286         on_placeholder(boost::as_literal(placeholder));
287     }
288 
289     /*!
290      * \brief The function is called when the extended ISO time zone placeholder is found in the format string
291      */
on_extended_iso_time_zoneboost::aux::time_format_parser_callback292     virtual void on_extended_iso_time_zone()
293     {
294         const char_type placeholder[3] = { static_cast< char_type >('%'), static_cast< char_type >('Q'), static_cast< char_type >('\0') };
295         on_placeholder(boost::as_literal(placeholder));
296     }
297 
298     /*!
299      * \brief The function is called when the ISO-formatted time is found in the format string
300      */
on_iso_timeboost::aux::time_format_parser_callback301     virtual void on_iso_time()
302     {
303         on_hours(true);
304         on_minutes();
305         on_seconds();
306     }
307 
308     /*!
309      * \brief The function is called when the extended ISO-formatted time is found in the format string
310      */
on_extended_iso_timeboost::aux::time_format_parser_callback311     virtual void on_extended_iso_time()
312     {
313         const char_type delimiter[2] = { static_cast< char_type >(':'), static_cast< char_type >('\0') };
314         on_hours(true);
315         on_literal(boost::as_literal(delimiter));
316         on_minutes();
317         on_literal(boost::as_literal(delimiter));
318         on_seconds();
319     }
320 
321     /*!
322      * \brief The function is called when the extended ISO-formatted time with fractional seconds is found in the format string
323      */
on_default_timeboost::aux::time_format_parser_callback324     virtual void on_default_time()
325     {
326         on_extended_iso_time();
327 
328         const char_type delimiter[2] = { static_cast< char_type >('.'), static_cast< char_type >('\0') };
329         on_literal(boost::as_literal(delimiter));
330         on_fractional_seconds();
331     }
332 };
333 
334 /*!
335  * This is the interface the parser will use to notify the caller about various components of date in the format string.
336  */
337 template< typename CharT >
338 struct date_time_format_parser_callback :
339     public date_format_parser_callback< CharT >,
340     public time_format_parser_callback< CharT >
341 {
342     //! Character type used by the parser
343     typedef CharT char_type;
344 
345     //! Destructor
~date_time_format_parser_callbackboost::aux::date_time_format_parser_callback346     virtual ~date_time_format_parser_callback() {}
347 
348     /*!
349      * \brief The function is called when the parser discovers a string literal in the format string
350      *
351      * \param lit The string of characters not interpreted as a placeholder
352      */
353     virtual void on_literal(iterator_range< const char_type* > const& lit) = 0;
354 
355     /*!
356      * \brief The method is called when an unknown placeholder is found in the format string
357      *
358      * \param ph The placeholder with the leading percent sign
359      */
on_placeholderboost::aux::date_time_format_parser_callback360     virtual void on_placeholder(iterator_range< const char_type* > const& ph)
361     {
362         // By default interpret all unrecognized placeholders as literals
363         on_literal(ph);
364     }
365 };
366 
367 /*!
368  * \brief Parses the date format string and invokes the callback object
369  *
370  * \pre <tt>begin <= end</tt>, both pointers must not be \c NULL
371  * \param begin Pointer to the first character of the sequence
372  * \param end Pointer to the after-the-last character of the sequence
373  * \param callback Reference to the callback object that will be invoked by the parser as it processes the input
374  */
375 template< typename CharT >
376 BOOST_LOG_API void parse_date_format(const CharT* begin, const CharT* end, date_format_parser_callback< CharT >& callback);
377 
378 /*!
379  * \brief Parses the date format string and invokes the callback object
380  *
381  * \param str The format string to parse
382  * \param callback Reference to the callback object that will be invoked by the parser as it processes the input
383  */
384 template< typename CharT, typename TraitsT, typename AllocatorT >
parse_date_format(std::basic_string<CharT,TraitsT,AllocatorT> const & str,date_format_parser_callback<CharT> & callback)385 inline void parse_date_format(std::basic_string< CharT, TraitsT, AllocatorT > const& str, date_format_parser_callback< CharT >& callback)
386 {
387     const CharT* p = str.c_str();
388     return parse_date_format(p, p + str.size(), callback);
389 }
390 
391 /*!
392  * \brief Parses the date format string and invokes the callback object
393  *
394  * \pre <tt>str != NULL</tt>, <tt>str</tt> points to a zero-terminated string.
395  * \param str The format string to parse
396  * \param callback Reference to the callback object that will be invoked by the parser as it processes the input
397  */
398 template< typename CharT >
parse_date_format(const CharT * str,date_format_parser_callback<CharT> & callback)399 inline void parse_date_format(const CharT* str, date_format_parser_callback< CharT >& callback)
400 {
401     return parse_date_format(str, str + std::char_traits< CharT >::length(str), callback);
402 }
403 
404 /*!
405  * \brief Parses the time format string and invokes the callback object
406  *
407  * \pre <tt>begin <= end</tt>, both pointers must not be \c NULL
408  * \param begin Pointer to the first character of the sequence
409  * \param end Pointer to the after-the-last character of the sequence
410  * \param callback Reference to the callback object that will be invoked by the parser as it processes the input
411  */
412 template< typename CharT >
413 BOOST_LOG_API void parse_time_format(const CharT* begin, const CharT* end, time_format_parser_callback< CharT >& callback);
414 
415 /*!
416  * \brief Parses the time format string and invokes the callback object
417  *
418  * \param str The format string to parse
419  * \param callback Reference to the callback object that will be invoked by the parser as it processes the input
420  */
421 template< typename CharT, typename TraitsT, typename AllocatorT >
parse_time_format(std::basic_string<CharT,TraitsT,AllocatorT> const & str,time_format_parser_callback<CharT> & callback)422 inline void parse_time_format(std::basic_string< CharT, TraitsT, AllocatorT > const& str, time_format_parser_callback< CharT >& callback)
423 {
424     const CharT* p = str.c_str();
425     return parse_time_format(p, p + str.size(), callback);
426 }
427 
428 /*!
429  * \brief Parses the time format string and invokes the callback object
430  *
431  * \pre <tt>str != NULL</tt>, <tt>str</tt> points to a zero-terminated string.
432  * \param str The format string to parse
433  * \param callback Reference to the callback object that will be invoked by the parser as it processes the input
434  */
435 template< typename CharT >
parse_time_format(const CharT * str,time_format_parser_callback<CharT> & callback)436 inline void parse_time_format(const CharT* str, time_format_parser_callback< CharT >& callback)
437 {
438     return parse_time_format(str, str + std::char_traits< CharT >::length(str), callback);
439 }
440 
441 /*!
442  * \brief Parses the date and time format string and invokes the callback object
443  *
444  * \pre <tt>begin <= end</tt>, both pointers must not be \c NULL
445  * \param begin Pointer to the first character of the sequence
446  * \param end Pointer to the after-the-last character of the sequence
447  * \param callback Reference to the callback object that will be invoked by the parser as it processes the input
448  */
449 template< typename CharT >
450 BOOST_LOG_API void parse_date_time_format(const CharT* begin, const CharT* end, date_time_format_parser_callback< CharT >& callback);
451 
452 /*!
453  * \brief Parses the date and time format string and invokes the callback object
454  *
455  * \param str The format string to parse
456  * \param callback Reference to the callback object that will be invoked by the parser as it processes the input
457  */
458 template< typename CharT, typename TraitsT, typename AllocatorT >
parse_date_time_format(std::basic_string<CharT,TraitsT,AllocatorT> const & str,date_time_format_parser_callback<CharT> & callback)459 inline void parse_date_time_format(std::basic_string< CharT, TraitsT, AllocatorT > const& str, date_time_format_parser_callback< CharT >& callback)
460 {
461     const CharT* p = str.c_str();
462     return parse_date_time_format(p, p + str.size(), callback);
463 }
464 
465 /*!
466  * \brief Parses the date and time format string and invokes the callback object
467  *
468  * \pre <tt>str != NULL</tt>, <tt>str</tt> points to a zero-terminated string.
469  * \param str The format string to parse
470  * \param callback Reference to the callback object that will be invoked by the parser as it processes the input
471  */
472 template< typename CharT >
parse_date_time_format(const CharT * str,date_time_format_parser_callback<CharT> & callback)473 inline void parse_date_time_format(const CharT* str, date_time_format_parser_callback< CharT >& callback)
474 {
475     return parse_date_time_format(str, str + std::char_traits< CharT >::length(str), callback);
476 }
477 
478 } // namespace aux
479 
480 BOOST_LOG_CLOSE_NAMESPACE // namespace log
481 
482 } // namespace boost
483 
484 #include <boost/log/detail/footer.hpp>
485 
486 #endif // BOOST_LOG_DETAIL_DATE_TIME_FORMAT_PARSER_HPP_INCLUDED_
487