[1022] | 1 | #ifndef _DATE_TIME_TIME_PARSING_HPP___ |
---|
| 2 | #define _DATE_TIME_TIME_PARSING_HPP___ |
---|
| 3 | |
---|
| 4 | /* Copyright (c) 2002,2003,2005 CrystalClear Software, Inc. |
---|
| 5 | * Use, modification and distribution is subject to the |
---|
| 6 | * Boost Software License, Version 1.0. (See accompanying |
---|
| 7 | * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) |
---|
| 8 | * Author: Jeff Garland, Bart Garst |
---|
| 9 | * $Date: 2008-02-27 15:00:24 -0500 (Wed, 27 Feb 2008) $ |
---|
| 10 | */ |
---|
| 11 | |
---|
| 12 | #include "boost/tokenizer.hpp" |
---|
| 13 | #include "boost/lexical_cast.hpp" |
---|
| 14 | #include "boost/date_time/date_parsing.hpp" |
---|
| 15 | #include "boost/cstdint.hpp" |
---|
| 16 | #include <iostream> |
---|
| 17 | |
---|
| 18 | namespace boost { |
---|
| 19 | namespace date_time { |
---|
| 20 | |
---|
| 21 | //! computes exponential math like 2^8 => 256, only works with positive integers |
---|
| 22 | //Not general purpose, but needed b/c std::pow is not available |
---|
| 23 | //everywehere. Hasn't been tested with negatives and zeros |
---|
| 24 | template<class int_type> |
---|
| 25 | inline |
---|
| 26 | int_type power(int_type base, int_type exponent) |
---|
| 27 | { |
---|
| 28 | int_type result = 1; |
---|
| 29 | for(int i = 0; i < exponent; ++i){ |
---|
| 30 | result *= base; |
---|
| 31 | } |
---|
| 32 | return result; |
---|
| 33 | } |
---|
| 34 | |
---|
| 35 | //! Creates a time_duration object from a delimited string |
---|
| 36 | /*! Expected format for string is "[-]h[h][:mm][:ss][.fff]". |
---|
| 37 | * If the number of fractional digits provided is greater than the |
---|
| 38 | * precision of the time duration type then the extra digits are |
---|
| 39 | * truncated. |
---|
| 40 | * |
---|
| 41 | * A negative duration will be created if the first character in |
---|
| 42 | * string is a '-', all other '-' will be treated as delimiters. |
---|
| 43 | * Accepted delimiters are "-:,.". |
---|
| 44 | */ |
---|
| 45 | template<class time_duration, class char_type> |
---|
| 46 | inline |
---|
| 47 | time_duration |
---|
| 48 | str_from_delimited_time_duration(const std::basic_string<char_type>& s) |
---|
| 49 | { |
---|
| 50 | unsigned short min=0, sec =0; |
---|
| 51 | int hour =0; |
---|
| 52 | bool is_neg = (s.at(0) == '-'); |
---|
| 53 | boost::int64_t fs=0; |
---|
| 54 | int pos = 0; |
---|
| 55 | |
---|
| 56 | typedef typename std::basic_string<char_type>::traits_type traits_type; |
---|
| 57 | typedef boost::char_separator<char_type, traits_type> char_separator_type; |
---|
| 58 | typedef boost::tokenizer<char_separator_type, |
---|
| 59 | typename std::basic_string<char_type>::const_iterator, |
---|
| 60 | std::basic_string<char_type> > tokenizer; |
---|
| 61 | typedef typename boost::tokenizer<char_separator_type, |
---|
| 62 | typename std::basic_string<char_type>::const_iterator, |
---|
| 63 | typename std::basic_string<char_type> >::iterator tokenizer_iterator; |
---|
| 64 | |
---|
| 65 | char_type sep_chars[5] = {'-',':',',','.'}; |
---|
| 66 | char_separator_type sep(sep_chars); |
---|
| 67 | tokenizer tok(s,sep); |
---|
| 68 | for(tokenizer_iterator beg=tok.begin(); beg!=tok.end();++beg){ |
---|
| 69 | switch(pos) { |
---|
| 70 | case 0: { |
---|
| 71 | hour = boost::lexical_cast<int>(*beg); |
---|
| 72 | break; |
---|
| 73 | } |
---|
| 74 | case 1: { |
---|
| 75 | min = boost::lexical_cast<unsigned short>(*beg); |
---|
| 76 | break; |
---|
| 77 | } |
---|
| 78 | case 2: { |
---|
| 79 | sec = boost::lexical_cast<unsigned short>(*beg); |
---|
| 80 | break; |
---|
| 81 | }; |
---|
| 82 | case 3: { |
---|
| 83 | int digits = static_cast<int>(beg->length()); |
---|
| 84 | //Works around a bug in MSVC 6 library that does not support |
---|
| 85 | //operator>> thus meaning lexical_cast will fail to compile. |
---|
| 86 | #if (defined(BOOST_MSVC) && (_MSC_VER < 1300)) |
---|
| 87 | // msvc wouldn't compile 'time_duration::num_fractional_digits()' |
---|
| 88 | // (required template argument list) as a workaround a temp |
---|
| 89 | // time_duration object was used |
---|
| 90 | time_duration td(hour,min,sec,fs); |
---|
| 91 | int precision = td.num_fractional_digits(); |
---|
| 92 | // _atoi64 is an MS specific function |
---|
| 93 | if(digits >= precision) { |
---|
| 94 | // drop excess digits |
---|
| 95 | fs = _atoi64(beg->substr(0, precision).c_str()); |
---|
| 96 | } |
---|
| 97 | else { |
---|
| 98 | fs = _atoi64(beg->c_str()); |
---|
| 99 | } |
---|
| 100 | #else |
---|
| 101 | int precision = time_duration::num_fractional_digits(); |
---|
| 102 | if(digits >= precision) { |
---|
| 103 | // drop excess digits |
---|
| 104 | fs = boost::lexical_cast<boost::int64_t>(beg->substr(0, precision)); |
---|
| 105 | } |
---|
| 106 | else { |
---|
| 107 | fs = boost::lexical_cast<boost::int64_t>(*beg); |
---|
| 108 | } |
---|
| 109 | #endif |
---|
| 110 | if(digits < precision){ |
---|
| 111 | // trailing zeros get dropped from the string, |
---|
| 112 | // "1:01:01.1" would yield .000001 instead of .100000 |
---|
| 113 | // the power() compensates for the missing decimal places |
---|
| 114 | fs *= power(10, precision - digits); |
---|
| 115 | } |
---|
| 116 | |
---|
| 117 | break; |
---|
| 118 | } |
---|
| 119 | }//switch |
---|
| 120 | pos++; |
---|
| 121 | } |
---|
| 122 | if(is_neg) { |
---|
| 123 | return -time_duration(hour, min, sec, fs); |
---|
| 124 | } |
---|
| 125 | else { |
---|
| 126 | return time_duration(hour, min, sec, fs); |
---|
| 127 | } |
---|
| 128 | } |
---|
| 129 | |
---|
| 130 | //! Creates a time_duration object from a delimited string |
---|
| 131 | /*! Expected format for string is "[-]h[h][:mm][:ss][.fff]". |
---|
| 132 | * If the number of fractional digits provided is greater than the |
---|
| 133 | * precision of the time duration type then the extra digits are |
---|
| 134 | * truncated. |
---|
| 135 | * |
---|
| 136 | * A negative duration will be created if the first character in |
---|
| 137 | * string is a '-', all other '-' will be treated as delimiters. |
---|
| 138 | * Accepted delimiters are "-:,.". |
---|
| 139 | */ |
---|
| 140 | template<class time_duration> |
---|
| 141 | inline |
---|
| 142 | time_duration |
---|
| 143 | parse_delimited_time_duration(const std::string& s) |
---|
| 144 | { |
---|
| 145 | return str_from_delimited_time_duration<time_duration,char>(s); |
---|
| 146 | } |
---|
| 147 | |
---|
| 148 | //! Utility function to split appart string |
---|
| 149 | inline |
---|
| 150 | bool |
---|
| 151 | split(const std::string& s, |
---|
| 152 | char sep, |
---|
| 153 | std::string& first, |
---|
| 154 | std::string& second) |
---|
| 155 | { |
---|
| 156 | int sep_pos = static_cast<int>(s.find(sep)); |
---|
| 157 | first = s.substr(0,sep_pos); |
---|
| 158 | second = s.substr(sep_pos+1); |
---|
| 159 | return true; |
---|
| 160 | } |
---|
| 161 | |
---|
| 162 | |
---|
| 163 | template<class time_type> |
---|
| 164 | inline |
---|
| 165 | time_type |
---|
| 166 | parse_delimited_time(const std::string& s, char sep) |
---|
| 167 | { |
---|
| 168 | typedef typename time_type::time_duration_type time_duration; |
---|
| 169 | typedef typename time_type::date_type date_type; |
---|
| 170 | |
---|
| 171 | //split date/time on a unique delimiter char such as ' ' or 'T' |
---|
| 172 | std::string date_string, tod_string; |
---|
| 173 | split(s, sep, date_string, tod_string); |
---|
| 174 | //call parse_date with first string |
---|
| 175 | date_type d = parse_date<date_type>(date_string); |
---|
| 176 | //call parse_time_duration with remaining string |
---|
| 177 | time_duration td = parse_delimited_time_duration<time_duration>(tod_string); |
---|
| 178 | //construct a time |
---|
| 179 | return time_type(d, td); |
---|
| 180 | |
---|
| 181 | } |
---|
| 182 | |
---|
| 183 | //! Parse time duration part of an iso time of form: [-]hhmmss[.fff...] (eg: 120259.123 is 12 hours, 2 min, 59 seconds, 123000 microseconds) |
---|
| 184 | template<class time_duration> |
---|
| 185 | inline |
---|
| 186 | time_duration |
---|
| 187 | parse_undelimited_time_duration(const std::string& s) |
---|
| 188 | { |
---|
| 189 | int precision = 0; |
---|
| 190 | { |
---|
| 191 | // msvc wouldn't compile 'time_duration::num_fractional_digits()' |
---|
| 192 | // (required template argument list) as a workaround, a temp |
---|
| 193 | // time_duration object was used |
---|
| 194 | time_duration tmp(0,0,0,1); |
---|
| 195 | precision = tmp.num_fractional_digits(); |
---|
| 196 | } |
---|
| 197 | // 'precision+1' is so we grab all digits, plus the decimal |
---|
| 198 | int offsets[] = {2,2,2, precision+1}; |
---|
| 199 | int pos = 0, sign = 0; |
---|
| 200 | int hours = 0; |
---|
| 201 | short min=0, sec=0; |
---|
| 202 | boost::int64_t fs=0; |
---|
| 203 | // increment one position if the string was "signed" |
---|
| 204 | if(s.at(sign) == '-') |
---|
| 205 | { |
---|
| 206 | ++sign; |
---|
| 207 | } |
---|
| 208 | // stlport choked when passing s.substr() to tokenizer |
---|
| 209 | // using a new string fixed the error |
---|
| 210 | std::string remain = s.substr(sign); |
---|
| 211 | /* We do not want the offset_separator to wrap the offsets, we |
---|
| 212 | * will never want to process more than: |
---|
| 213 | * 2 char, 2 char, 2 char, frac_sec length. |
---|
| 214 | * We *do* want the offset_separator to give us a partial for the |
---|
| 215 | * last characters if there were not enough provided in the input string. */ |
---|
| 216 | bool wrap_off = false; |
---|
| 217 | bool ret_part = true; |
---|
| 218 | boost::offset_separator osf(offsets, offsets+4, wrap_off, ret_part); |
---|
| 219 | typedef boost::tokenizer<boost::offset_separator, |
---|
| 220 | std::basic_string<char>::const_iterator, |
---|
| 221 | std::basic_string<char> > tokenizer; |
---|
| 222 | typedef boost::tokenizer<boost::offset_separator, |
---|
| 223 | std::basic_string<char>::const_iterator, |
---|
| 224 | std::basic_string<char> >::iterator tokenizer_iterator; |
---|
| 225 | tokenizer tok(remain, osf); |
---|
| 226 | for(tokenizer_iterator ti=tok.begin(); ti!=tok.end();++ti){ |
---|
| 227 | switch(pos) { |
---|
| 228 | case 0: |
---|
| 229 | { |
---|
| 230 | hours = boost::lexical_cast<int>(*ti); |
---|
| 231 | break; |
---|
| 232 | } |
---|
| 233 | case 1: |
---|
| 234 | { |
---|
| 235 | min = boost::lexical_cast<short>(*ti); |
---|
| 236 | break; |
---|
| 237 | } |
---|
| 238 | case 2: |
---|
| 239 | { |
---|
| 240 | sec = boost::lexical_cast<short>(*ti); |
---|
| 241 | break; |
---|
| 242 | } |
---|
| 243 | case 3: |
---|
| 244 | { |
---|
| 245 | std::string char_digits(ti->substr(1)); // digits w/no decimal |
---|
| 246 | int digits = static_cast<int>(char_digits.length()); |
---|
| 247 | |
---|
| 248 | //Works around a bug in MSVC 6 library that does not support |
---|
| 249 | //operator>> thus meaning lexical_cast will fail to compile. |
---|
| 250 | #if (defined(BOOST_MSVC) && (_MSC_VER <= 1200)) // 1200 == VC++ 6.0 |
---|
| 251 | // _atoi64 is an MS specific function |
---|
| 252 | if(digits >= precision) { |
---|
| 253 | // drop excess digits |
---|
| 254 | fs = _atoi64(char_digits.substr(0, precision).c_str()); |
---|
| 255 | } |
---|
| 256 | else if(digits == 0) { |
---|
| 257 | fs = 0; // just in case _atoi64 doesn't like an empty string |
---|
| 258 | } |
---|
| 259 | else { |
---|
| 260 | fs = _atoi64(char_digits.c_str()); |
---|
| 261 | } |
---|
| 262 | #else |
---|
| 263 | if(digits >= precision) { |
---|
| 264 | // drop excess digits |
---|
| 265 | fs = boost::lexical_cast<boost::int64_t>(char_digits.substr(0, precision)); |
---|
| 266 | } |
---|
| 267 | else if(digits == 0) { |
---|
| 268 | fs = 0; // lexical_cast doesn't like empty strings |
---|
| 269 | } |
---|
| 270 | else { |
---|
| 271 | fs = boost::lexical_cast<boost::int64_t>(char_digits); |
---|
| 272 | } |
---|
| 273 | #endif |
---|
| 274 | if(digits < precision){ |
---|
| 275 | // trailing zeros get dropped from the string, |
---|
| 276 | // "1:01:01.1" would yield .000001 instead of .100000 |
---|
| 277 | // the power() compensates for the missing decimal places |
---|
| 278 | fs *= power(10, precision - digits); |
---|
| 279 | } |
---|
| 280 | |
---|
| 281 | break; |
---|
| 282 | } |
---|
| 283 | }; |
---|
| 284 | pos++; |
---|
| 285 | } |
---|
| 286 | if(sign) { |
---|
| 287 | return -time_duration(hours, min, sec, fs); |
---|
| 288 | } |
---|
| 289 | else { |
---|
| 290 | return time_duration(hours, min, sec, fs); |
---|
| 291 | } |
---|
| 292 | } |
---|
| 293 | |
---|
| 294 | //! Parse time string of form YYYYMMDDThhmmss where T is delimeter between date and time |
---|
| 295 | template<class time_type> |
---|
| 296 | inline |
---|
| 297 | time_type |
---|
| 298 | parse_iso_time(const std::string& s, char sep) |
---|
| 299 | { |
---|
| 300 | typedef typename time_type::time_duration_type time_duration; |
---|
| 301 | typedef typename time_type::date_type date_type; |
---|
| 302 | |
---|
| 303 | //split date/time on a unique delimiter char such as ' ' or 'T' |
---|
| 304 | std::string date_string, tod_string; |
---|
| 305 | split(s, sep, date_string, tod_string); |
---|
| 306 | //call parse_date with first string |
---|
| 307 | date_type d = parse_undelimited_date<date_type>(date_string); |
---|
| 308 | //call parse_time_duration with remaining string |
---|
| 309 | time_duration td = parse_undelimited_time_duration<time_duration>(tod_string); |
---|
| 310 | //construct a time |
---|
| 311 | return time_type(d, td); |
---|
| 312 | } |
---|
| 313 | |
---|
| 314 | |
---|
| 315 | |
---|
| 316 | } }//namespace date_time |
---|
| 317 | |
---|
| 318 | |
---|
| 319 | |
---|
| 320 | |
---|
| 321 | #endif |
---|