RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
soa/types/date.cc
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
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator