RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
00001 /* date.cc 00002 Jeremy Barnes, 18 July 2010 00003 Copyright (c) 2010 Datacratic. All rights reserved. 00004 00005 */ 00006 00007 #include "date.h" 00008 #include <limits> 00009 #include "jml/utils/parse_context.h" 00010 #include "jml/arch/format.h" 00011 #include "soa/js/js_value.h" 00012 #include "soa/jsoncpp/json.h" 00013 #include <cmath> 00014 #include "ace/Time_Value.h" 00015 #include "jml/arch/exception.h" 00016 #include "jml/db/persistent.h" 00017 #include <boost/regex.hpp> 00018 00019 00020 using namespace std; 00021 using namespace ML; 00022 00023 00024 namespace Datacratic { 00025 00026 00027 /*****************************************************************************/ 00028 /* DATE */ 00029 /*****************************************************************************/ 00030 00031 Date:: 00032 Date(const ptime & date) 00033 : secondsSinceEpoch_((date - epoch).total_microseconds()/1000000.0) 00034 { 00035 } 00036 00037 Date:: 00038 Date(int year, int month, int day, 00039 int hour, int minute, int second, 00040 double fraction) 00041 : secondsSinceEpoch_((boost::posix_time::ptime 00042 (boost::gregorian::date(year, month, day)) - epoch) 00043 .total_seconds() 00044 + 3600 * hour + 60 * minute + second 00045 + fraction) 00046 { 00047 } 00048 00049 Date:: 00050 Date(JS::JSValue & value) 00051 { 00052 throw Exception("Date::Date(JSValue): not done"); 00053 } 00054 00055 Date:: 00056 Date(const Json::Value & value) 00057 { 00058 if (value.isConvertibleTo(Json::realValue)) { 00059 secondsSinceEpoch_ = value.asDouble(); 00060 } 00061 else if (value.isConvertibleTo(Json::stringValue)) { 00062 Date parsed = parse_date_time(value.asString(), "%y-%M-%d", "%H:%M:%S"); 00063 secondsSinceEpoch_ = parsed.secondsSinceEpoch_; 00064 } 00065 else throw Exception("Date::Date(Json): JSON value " 00066 + value.toStyledString() 00067 + "not convertible to date"); 00068 } 00069 00070 Date:: 00071 Date(const ACE_Time_Value & value) 00072 { 00073 uint64_t msec; 00074 value.msec(msec); 00075 secondsSinceEpoch_ = msec / 1000.0; 00076 } 00077 00078 Date 00079 Date:: 00080 parseSecondsSinceEpoch(const std::string & date) 00081 { 00082 errno = 0; 00083 char * end = 0; 00084 double seconds = strtod(date.c_str(), &end); 00085 if (errno != 0) 00086 throw ML::Exception(errno, "date parseSecondsSinceEpoch: " + date); 00087 if (end != date.c_str() + date.length()) 00088 throw ML::Exception("couldn't convert " + date + " to date"); 00089 return fromSecondsSinceEpoch(seconds); 00090 } 00091 00092 Date 00093 Date:: 00094 parseDefaultUtc(const std::string & date) 00095 { 00096 return parse_date_time(date, "%y-%M-%d", "%H:%M:%S"); 00097 } 00098 00099 Date 00100 Date:: 00101 parseIso8601(const std::string & date) 00102 { 00103 return parse_date_time(date, "%y-%m-%d", "T%H:%M:%SZ"); 00104 } 00105 00106 Date 00107 Date:: 00108 notADate() 00109 { 00110 Date result; 00111 result.secondsSinceEpoch_ = std::numeric_limits<double>::quiet_NaN(); 00112 return result; 00113 } 00114 00115 Date 00116 Date:: 00117 positiveInfinity() 00118 { 00119 Date result; 00120 result.secondsSinceEpoch_ = INFINITY; 00121 return result; 00122 } 00123 00124 Date 00125 Date:: 00126 negativeInfinity() 00127 { 00128 Date result; 00129 result.secondsSinceEpoch_ = -INFINITY; 00130 return result; 00131 } 00132 00133 Date 00134 Date:: 00135 now() 00136 { 00137 timespec time; 00138 int res = clock_gettime(CLOCK_REALTIME, &time); 00139 if (res == -1) 00140 throw ML::Exception(errno, "clock_gettime"); 00141 return fromSecondsSinceEpoch(time.tv_sec + time.tv_nsec * 0.000000001); 00142 } 00143 00144 Date 00145 Date:: 00146 nowOld() 00147 { 00148 boost::posix_time::ptime 00149 t(boost::posix_time::microsec_clock::universal_time()); 00150 Date result(t); 00151 return result; 00152 } 00153 00154 bool 00155 Date:: 00156 isADate() const 00157 { 00158 return std::isfinite(secondsSinceEpoch_); 00159 } 00160 00161 std::string 00162 Date:: 00163 print(unsigned seconds_digits) const 00164 { 00165 if (!std::isfinite(secondsSinceEpoch_)) { 00166 if (std::isnan(secondsSinceEpoch_)) { 00167 return "NaD"; 00168 } 00169 else if (secondsSinceEpoch_ > 0) { 00170 return "Inf"; 00171 } 00172 else return "-Inf"; 00173 } 00174 00175 string result = print("%Y-%b-%d %H:%M:%S"); 00176 if (seconds_digits == 0) return result; 00177 00178 double partial_seconds = fractionalSeconds(); 00179 string fractional = format("%0.*f", seconds_digits, partial_seconds); 00180 00181 result.append(fractional, 1, -1); 00182 return result; 00183 } 00184 00185 std::string 00186 Date:: 00187 printRfc2616() const 00188 { 00189 return print("%a, %d %b %Y %H:%M:%S GMT"); 00190 } 00191 00192 std::string 00193 Date:: 00194 printClassic() const 00195 { 00196 return print("%Y-%m-%d %H:%M:%S"); 00197 } 00198 00199 Date 00200 Date:: 00201 quantized(double fraction) const 00202 { 00203 Date result = *this; 00204 return result.quantize(fraction); 00205 } 00206 00207 Date & 00208 Date:: 00209 quantize(double fraction) 00210 { 00211 if (fraction <= 0.0) 00212 throw Exception("Date::quantize(): " 00213 "fraction cannot be zero or negative"); 00214 00215 if (fraction <= 1.0) { 00216 // Fractions of a second; split off to avoid loss of precision 00217 double whole_seconds, partial_seconds; 00218 partial_seconds = modf(secondsSinceEpoch_, &whole_seconds); 00219 00220 double periods_per_second = round(1.0 / fraction); 00221 partial_seconds = round(partial_seconds * periods_per_second) 00222 / periods_per_second; 00223 00224 secondsSinceEpoch_ = whole_seconds + partial_seconds; 00225 } 00226 else { 00227 // Fractions of a second; split off to avoid loss of precision 00228 double whole_seconds, partial_seconds; 00229 partial_seconds = modf(secondsSinceEpoch_, &whole_seconds); 00230 00231 uint64_t frac = fraction; 00232 if (frac != fraction) 00233 throw ML::Exception("non-integral numbers of seconds not supported"); 00234 uint64_t whole2 = whole_seconds; 00235 whole2 /= fraction; 00236 secondsSinceEpoch_ = whole2 * fraction; 00237 } 00238 00239 return *this; 00240 } 00241 00242 std::string 00243 Date:: 00244 print(const std::string & format) const 00245 { 00246 size_t buffer_size = format.size() + 1024; 00247 char buffer[buffer_size]; 00248 00249 time_t t = secondsSinceEpoch(); 00250 tm time; 00251 00252 if (!gmtime_r(&t, &time)) 00253 throw Exception("problem with gmtime_r"); 00254 size_t nchars = strftime(buffer, buffer_size, format.c_str(), 00255 &time); 00256 00257 if (nchars == 0) 00258 throw Exception("couldn't print date format " + format); 00259 00260 return string(buffer, buffer + nchars); 00261 } 00262 00263 int 00264 Date:: 00265 hour() const 00266 { 00267 throw Exception("Date: stub method"); 00268 } 00269 00270 int 00271 Date:: 00272 minute() const 00273 { 00274 throw Exception("Date: stub method"); 00275 } 00276 00277 int 00278 Date:: 00279 second() const 00280 { 00281 throw Exception("Date: stub method"); 00282 } 00283 00284 int 00285 Date:: 00286 weekday() const 00287 { 00288 using namespace boost::gregorian; 00289 00290 int day_of_week; 00291 try { 00292 day_of_week = from_string(print()).day_of_week(); 00293 } catch (...) { 00294 cerr << "order_date = " << *this << endl; 00295 throw; 00296 } 00297 00298 return day_of_week; 00299 } 00300 00301 int 00302 Date:: 00303 dayOfMonth() const 00304 { 00305 return boost::gregorian::from_string(print()).day(); 00306 } 00307 00308 int 00309 Date:: 00310 monthOfYear() const 00311 { 00312 return boost::gregorian::from_string(print()).month(); 00313 } 00314 00315 int 00316 Date:: 00317 year() const 00318 { 00319 return boost::gregorian::from_string(print()).year(); 00320 } 00321 00322 int 00323 Date:: 00324 hourOfWeek() const 00325 { 00326 time_t t = secondsSinceEpoch(); 00327 tm time; 00328 00329 if (!gmtime_r(&t, &time)) 00330 throw Exception("problem with gmtime_r"); 00331 00332 return time.tm_wday * 24 + time.tm_hour; 00333 } 00334 00335 std::string 00336 Date:: 00337 printMonth() const 00338 { 00339 throw Exception("Date: stub method"); 00340 } 00341 00342 std::string 00343 Date:: 00344 printWeekday() const 00345 { 00346 throw Exception("Date: stub method"); 00347 } 00348 00349 std::string 00350 Date:: 00351 printYearAndMonth() const 00352 { 00353 throw Exception("Date: stub method"); 00354 } 00355 00356 const boost::posix_time::ptime 00357 Date::epoch(boost::gregorian::date(1970, 1, 1)); 00358 00359 std::ostream & operator << (std::ostream & stream, const Date & date) 00360 { 00361 return stream << date.print(); 00362 } 00363 00364 std::istream & operator >> (std::istream & stream, Date & date) 00365 { 00366 throw Exception("date istream parsing doesn't work yet"); 00367 } 00368 00369 bool 00370 Date:: 00371 match_date(ML::Parse_Context & context, 00372 Date & result, 00373 const std::string & format) 00374 { 00375 Parse_Context::Revert_Token token(context); 00376 00377 int year = -1, month = 1, day = 1; 00378 00379 for (const char * f = format.c_str(); context && *f; ++f) { 00380 if (*f != '%') { 00381 if (!context.match_literal(*f)) return false; 00382 continue; 00383 } 00384 00385 ++f; 00386 switch (*f) { 00387 case '%': 00388 if (!context.match_literal('%')) return false; 00389 break; 00390 case 'd': 00391 if (!context.match_int(day, 1, 31)) return false; 00392 break; 00393 case 'm': 00394 if (!context.match_int(month, 1, 12)) return false; 00395 break; 00396 case 'M': 00397 switch(tolower(*context)) { 00398 case 'j': { 00399 ++context; 00400 if (context.match_literal("an")) { 00401 month = 1; 00402 break; 00403 } 00404 else if (context.match_literal("un")) { 00405 month = 6; 00406 break; 00407 } 00408 else if (context.match_literal("ul")) { 00409 month = 7; 00410 break; 00411 } 00412 else return false; 00413 } 00414 case 'f': { 00415 ++context; 00416 if (!context.match_literal("eb")) return false; 00417 month = 2; 00418 break; 00419 } 00420 case 'm': { 00421 ++context; 00422 if (context.match_literal("ar")) { 00423 month = 3; 00424 break; 00425 } 00426 else if (context.match_literal("ay")) { 00427 month = 5; 00428 break; 00429 } 00430 else return false; 00431 break; 00432 } 00433 case 'a': 00434 ++context; 00435 if (context.match_literal("pr")) { 00436 month = 4; 00437 break; 00438 } 00439 else if (context.match_literal("ug")) { 00440 month = 8; 00441 break; 00442 } 00443 else return false; 00444 break; 00445 case 's': 00446 ++context; 00447 if (!context.match_literal("ep")) return false; 00448 month = 9; 00449 break; 00450 case 'o': 00451 ++context; 00452 if (!context.match_literal("ct")) return false; 00453 month = 10; 00454 break; 00455 case 'n': { 00456 ++context; 00457 if (!context.match_literal("ov")) return false; 00458 month = 11; 00459 break; 00460 } 00461 case 'd': { 00462 ++context; 00463 if (!context.match_literal("ec")) return false; 00464 month = 12; 00465 break; 00466 } 00467 default: 00468 return false; 00469 } 00470 break; 00471 case 'y': 00472 if (!context.match_int(year, 1400, 9999)); 00473 break; 00474 default: 00475 throw Exception("expect_date: format " + string(1, *f) 00476 + " not implemented yet"); 00477 } 00478 } 00479 00480 try { 00481 boost::gregorian::date date(year, month, day); 00482 boost::posix_time::ptime time(date); 00483 result = Date(time); 00484 } catch (const std::exception & exc) { 00485 return false; 00486 } 00487 token.ignore(); 00488 return true; 00489 } 00490 00491 Date 00492 Date:: 00493 expect_date(ML::Parse_Context & context, const std::string & format) 00494 { 00495 int year = -1, month = 1, day = 1; 00496 00497 auto expectFixedWidthInt = [&] (int length, int min, int max, 00498 const char * message) 00499 { 00500 char buf[length + 1]; 00501 unsigned i = 0; 00502 for (; i < length && context; ++i, ++context) { 00503 char c = *context; 00504 if (c < '0' || c > '9') { 00505 break; 00506 } 00507 buf[i] = c; 00508 } 00509 if (i == 0) 00510 context.exception(message); 00511 buf[i] = 0; 00512 int result = boost::lexical_cast<int>((const char *)buf); 00513 00514 // This WILL bite us some time. 640k anyone? 00515 if (result < min || result > max) { 00516 context.exception(message); 00517 } 00518 return result; 00519 }; 00520 00521 for (const char * f = format.c_str(); context && *f; ++f) { 00522 if (*f != '%') { 00523 context.expect_literal(*f); 00524 continue; 00525 } 00526 00527 ++f; 00528 switch (*f) { 00529 case '%': 00530 context.expect_literal('%'); 00531 break; 00532 case 'd': 00533 day = expectFixedWidthInt(2, 1, 31, "expected day of month"); 00534 break; 00535 case 'm': 00536 month = expectFixedWidthInt(2, 1, 12, "expected month of year"); 00537 break; 00538 case 'M': 00539 switch(tolower(*context)) { 00540 case 'j': { 00541 ++context; 00542 if (context.match_literal("an")) { 00543 month = 1; 00544 break; 00545 } 00546 else if (context.match_literal("un")) { 00547 month = 6; 00548 break; 00549 } 00550 else if (context.match_literal("ul")) { 00551 month = 7; 00552 break; 00553 } 00554 else context.exception("expected month name"); 00555 } 00556 case 'f': { 00557 ++context; 00558 context.expect_literal("eb", "expected Feb"); 00559 month = 2; 00560 break; 00561 } 00562 case 'm': { 00563 ++context; 00564 if (context.match_literal("ar")) { 00565 month = 3; 00566 break; 00567 } 00568 else if (context.match_literal("ay")) { 00569 month = 5; 00570 break; 00571 } 00572 else context.exception("expected month name"); 00573 break; 00574 } 00575 case 'a': 00576 ++context; 00577 if (context.match_literal("pr")) { 00578 month = 4; 00579 break; 00580 } 00581 else if (context.match_literal("ug")) { 00582 month = 8; 00583 break; 00584 } 00585 else context.exception("expected month name"); 00586 break; 00587 case 's': 00588 ++context; 00589 context.expect_literal("ep", "expected Sep"); 00590 month = 9; 00591 break; 00592 case 'o': 00593 ++context; 00594 context.expect_literal("ct", "expected Oct"); 00595 month = 10; 00596 break; 00597 case 'n': { 00598 ++context; 00599 context.expect_literal("ov", "expected Nov"); 00600 month = 11; 00601 break; 00602 } 00603 case 'd': { 00604 ++context; 00605 context.expect_literal("ec", "expected Dec"); 00606 month = 12; 00607 break; 00608 } 00609 default: 00610 context.exception("expected month name for %M"); 00611 } 00612 break; 00613 case 'y': 00614 year = expectFixedWidthInt(4, 1400, 2999, "expected year"); 00615 break; 00616 default: 00617 throw Exception("expect_date: format " + string(1, *f) 00618 + " not implemented yet"); 00619 } 00620 } 00621 00622 //cerr << "year = " << year << " month = " << month 00623 // << " day = " << day << endl; 00624 00625 try { 00626 boost::gregorian::date date(year, month, day); 00627 boost::posix_time::ptime time(date); 00628 return Date(time); 00629 //result = (time - DateHandler::epoch).total_seconds(); 00630 //cerr << "result = " << result << endl; 00631 } catch (const std::exception & exc) { 00632 context.exception("error parsing date: " + string(exc.what())); 00633 throw Exception("not reached"); 00634 } 00635 //return result; 00636 } 00637 00638 #if 0 00639 Date 00640 Date:: 00641 expect_date(ML::Parse_Context & context, 00642 const std::string & format) 00643 { 00644 Date result; 00645 if (!match_date(context, format)) 00646 context.exception("expected date"); 00647 return result; 00648 } 00649 #endif 00650 00651 bool 00652 Date:: 00653 match_time(ML::Parse_Context & context, 00654 double & result, 00655 const std::string & format) 00656 { 00657 Parse_Context::Revert_Token token(context); 00658 00659 int hour = 0, minute = 0, offset = 0; 00660 double second = 0; 00661 bool twelve_hour = false; 00662 00663 for (const char * f = format.c_str(); context && *f; ++f) { 00664 if (*f != '%') { 00665 if (!context.match_literal(*f)) return false; 00666 continue; 00667 } 00668 00669 ++f; 00670 switch (*f) { 00671 case '%': 00672 if (!context.match_literal('%')) return false;; 00673 break; 00674 case 'h': 00675 twelve_hour = true; 00676 if (!context.match_int(hour, 1, 12)) 00677 return false; 00678 break; 00679 case 'H': 00680 twelve_hour = false; 00681 if (!context.match_int(hour, 0, 24)) 00682 return false; 00683 if (hour >= 24) return false; 00684 break; 00685 case 'M': 00686 if (!context.match_int(minute, 0, 60)) 00687 return false; 00688 break; 00689 case 'S': 00690 if (!context.match_double(second, 0, 60)) 00691 return false; 00692 if (second >= 60.0) return false; 00693 break; 00694 case 'p': 00695 twelve_hour = true; 00696 if (context.match_literal('A')) { 00697 if (!context.match_literal('M')) return false; 00698 offset = 0; 00699 } 00700 else if (context.match_literal('P')) { 00701 if (!context.match_literal('M')) return false; 00702 offset = 12; 00703 } 00704 else return false; 00705 break; 00706 00707 default: 00708 throw Exception("expect_time: format " + string(1, *f) 00709 + " not implemented yet"); 00710 } 00711 } 00712 00713 if (twelve_hour) { 00714 if (hour < 1 || hour > 12) 00715 return false; 00716 if (hour == 12) hour = 0; 00717 hour += offset; 00718 } 00719 00720 double fractional_sec, full_sec; 00721 fractional_sec = modf(second, &full_sec); 00722 00723 using namespace boost::posix_time; 00724 result = (hours(hour) + minutes(minute) + seconds(full_sec)).total_seconds() 00725 + fractional_sec; 00726 token.ignore(); 00727 return true; 00728 } 00729 00730 double 00731 Date:: 00732 expect_time(ML::Parse_Context & context, const std::string & format) 00733 { 00734 int hour = 0, minute = 0, offset = 0; 00735 double second = 0; 00736 bool twelve_hour = false; 00737 00738 for (const char * f = format.c_str(); context && *f; ++f) { 00739 if (*f != '%') { 00740 context.expect_literal(*f); 00741 continue; 00742 } 00743 00744 ++f; 00745 switch (*f) { 00746 case '%': 00747 context.expect_literal('%'); 00748 break; 00749 case 'h': 00750 twelve_hour = true; 00751 hour = context.expect_int(1, 12, "expected hours"); 00752 break; 00753 case 'H': 00754 twelve_hour = false; 00755 hour = context.expect_int(0, 24, "expected hours"); 00756 if (hour >= 24) 00757 context.exception("expected 24-hour hour"); 00758 break; 00759 case 'M': 00760 minute = context.expect_int(0, 60, "expected minutes"); 00761 break; 00762 case 'S': 00763 second = context.expect_double(0, 60, "expected seconds"); 00764 if (second >= 60.0) 00765 context.exception("seconds cannot be 60"); 00766 break; 00767 case 'p': 00768 twelve_hour = true; 00769 if (context.match_literal('A')) { 00770 context.expect_literal('M', "expected AM"); 00771 offset = 0; 00772 } 00773 else if (context.match_literal('P')) { 00774 context.expect_literal('M', "expected AM"); 00775 offset = 12; 00776 } 00777 else context.exception("expected AM or PM"); 00778 break; 00779 00780 default: 00781 throw Exception("expect_time: format " + string(1, *f) 00782 + " not implemented yet"); 00783 } 00784 } 00785 00786 if (twelve_hour) { 00787 if (hour < 1 || hour > 12) 00788 context.exception("invalid hour after 12 hours"); 00789 if (hour == 12) hour = 0; 00790 hour += offset; 00791 } 00792 00793 double fractional_sec, full_sec; 00794 fractional_sec = modf(second, &full_sec); 00795 00796 using namespace boost::posix_time; 00797 return (hours(hour) + minutes(minute) + seconds(full_sec)).total_seconds() 00798 + fractional_sec; 00799 } 00800 00801 #if 0 00802 double 00803 Date:: 00804 expect_time(ML::Parse_Context & context, 00805 const std::string & format) 00806 { 00807 double time; 00808 if (!context.match_time(context, time, format)) 00809 context.exception("expected time"); 00810 return time; 00811 } 00812 #endif 00813 00814 Date 00815 Date:: 00816 parse_date_time(const std::string & str, 00817 const std::string & date_format, 00818 const std::string & time_format) 00819 { 00820 using namespace boost::posix_time; 00821 00822 if (str == "") return Date::notADate(); 00823 00824 Date result; 00825 try { 00826 ML::Parse_Context context(str, 00827 str.c_str(), str.c_str() + str.length()); 00828 result = expect_date_time(context, date_format, time_format); 00829 00830 context.expect_eof(); 00831 } 00832 catch (const std::exception & exc) { 00833 cerr << "date was " << str << endl; 00834 throw; 00835 } 00836 00837 return result; 00838 } 00839 00840 Date 00841 Date:: 00842 expect_date_time(ML::Parse_Context & context, 00843 const std::string & date_format, 00844 const std::string & time_format) 00845 { 00846 Date date; 00847 double time = 0.0; 00848 00849 date = expect_date(context, date_format); 00850 00851 if (!context.eof()) { 00852 Parse_Context::Revert_Token token(context); 00853 context.match_whitespace(); 00854 if (match_time(context, time, time_format)) 00855 token.ignore(); 00856 } 00857 00858 return date.plusSeconds(time); 00859 } 00860 00861 bool 00862 Date:: 00863 match_date_time(ML::Parse_Context & context, 00864 Date & result, 00865 const std::string & date_format, 00866 const std::string & time_format) 00867 { 00868 Date date; 00869 double time = 0.0; 00870 00871 if (!match_date(context, date, date_format)) return false; 00872 context.match_whitespace(); 00873 match_time(context, time, time_format); 00874 result = date.plusSeconds(time); 00875 00876 return true; 00877 } 00878 00879 00880 ACE_Time_Value 00881 Date:: 00882 toAce() const 00883 { 00884 ACE_Time_Value result; 00885 result.set(secondsSinceEpoch_); 00886 return result; 00887 } 00888 00889 tm 00890 Date:: 00891 toTm() const 00892 { 00893 tm result; 00894 errno = 0; 00895 time_t t = toTimeT(); 00896 if (gmtime_r(&t, &result) == 0) 00897 throw ML::Exception("error converting time: t = %lld", 00898 (long long)t, 00899 strerror(errno)); 00900 return result; 00901 } 00902 00903 Date 00904 Date:: 00905 fromTm(const tm & t) 00906 { 00907 tm t2 = t; 00908 time_t t3 = mktime(&t2); 00909 if (t3 == (time_t)-1) 00910 throw ML::Exception("couldn't construct from invalid time"); 00911 return fromTimeT(t3); 00912 } 00913 00914 00915 void Date::addFromString(string str){ 00916 { 00917 using namespace boost; 00918 string format = "^[1-9][0-9]*[SMHd]$"; 00919 regex e(format); 00920 if(!regex_match(str, e)){ 00921 throw ML::Exception("String " + str + " did not match format " 00922 + format); 00923 } 00924 } 00925 char unit = str[str.length() - 1]; 00926 int length; 00927 { 00928 stringstream tmp; 00929 tmp << str.substr(0, -1); 00930 tmp >> length; 00931 } 00932 switch(unit){ 00933 case 'S': 00934 this->addSeconds(length); 00935 break; 00936 case 'M': 00937 this->addMinutes(length); 00938 break; 00939 case 'H': 00940 this->addHours(length); 00941 break; 00942 case 'd': 00943 this->addDays(length); 00944 break; 00945 default: 00946 throw ML::Exception("Should never get here with string: " + str); 00947 break; 00948 } 00949 } 00950 00951 DB::Store_Writer & operator << (ML::DB::Store_Writer & store, Date date) 00952 { 00953 return store << date.secondsSinceEpoch(); 00954 } 00955 00956 DB::Store_Reader & operator >> (ML::DB::Store_Reader & store, Date & date) 00957 { 00958 double d; 00959 store >> d; 00960 date = Date::fromSecondsSinceEpoch(d); 00961 return store; 00962 } 00963 00964 00965 } // namespace Datacratic