datetime.h

Go to the documentation of this file.
00001 00005 /* Copyright © 2001 Michael Geddes 00006 * 00007 * This class was originally based on ATL/MFC code, however the original 00008 * implementations have almost entirely been replaced with more efficient code. 00009 * The core date algorithms are from boost. 00010 * 00011 * This material is provided "as is", with absolutely no warranty 00012 * expressed or implied. Any use is at your own risk. Permission to 00013 * use or copy this software for any purpose is hereby granted without 00014 * fee, provided the above notices are retained on all copies. 00015 * Permission to modify the code and to distribute modified code is 00016 * granted, provided the above notices are retained, and a notice that 00017 * the code was modified is included with the above copyright notice. 00018 * 00019 * This header is part of comet. 00020 * http://www.lambdasoft.dk/comet 00021 */ 00022 #ifndef COMET_DATETIME_H 00023 #define COMET_DATETIME_H 00024 00025 #include <comet/error_fwd.h> 00026 #include <comet/bstr.h> 00027 #include <comet/auto_buffer.h> 00028 00029 #include <math.h> 00030 #include <time.h> 00031 00032 // The Platform SDK does not define VAR_FOURDIGITYEARS 00033 #ifndef VAR_FOURDIGITYEARS 00034 #define VAR_FOURDIGITYEARS ((DWORD)0x00000040) 00035 #endif 00036 00037 namespace comet 00038 { 00039 00040 #define COMET_DIVMOD_( quot,rem, val1, val2) quot = (val1)/(val2); rem = (val1)%(val2); 00041 00042 00046 00047 class datetime_exception : public std::exception 00048 { 00049 public: 00050 datetime_exception( const char *desc) : desc_(desc) 00051 {} 00052 00053 const char* what() const throw() 00054 { 00055 return desc_.c_str(); 00056 } 00057 00058 private: 00059 std::string desc_; 00060 }; 00061 00063 00067 00068 static struct dt_invalid_t {} dt_invalid; 00070 static struct dt_null_t {} dt_null; 00072 static struct dt_zero_t { 00073 operator double() const { return 0.;} 00074 operator long() const { return 0;} 00075 } dt_zero; 00076 00077 // timeperiod_t 00079 00083 class timeperiod_t 00084 { 00085 enum { 00086 dt_invalid_ = 2147483647L, 00087 }; 00089 double pd_; 00090 public: 00092 timeperiod_t() : pd_(0.){} 00093 00095 timeperiod_t( dt_invalid_t ) : pd_(dt_invalid_) {} 00097 timeperiod_t( dt_zero_t) : pd_(0.) {} 00098 00099 timeperiod_t( double period) :pd_(period){} 00100 timeperiod_t( float period) :pd_(period){} 00101 timeperiod_t( long period) :pd_(period){} 00102 timeperiod_t( int period) :pd_(period){} 00103 timeperiod_t( short period) :pd_(period){} 00104 timeperiod_t( unsigned long period) :pd_(period){} 00105 timeperiod_t( unsigned int period) :pd_(period){} 00106 timeperiod_t( unsigned short period) :pd_(period){} 00107 00108 timeperiod_t( long days, long hours) 00109 : pd_(days + hours/24){} 00110 timeperiod_t( long days, long hours, long minutes) 00111 : pd_(days + (hours*60+minutes)/(24.*60.)){} 00112 timeperiod_t( long days, long hours, long minutes, long seconds, long milliseconds=0 ) 00113 : pd_(days + ((hours*3600000L) + (minutes*60000L)+ (seconds*1000L)+ milliseconds)/86400000.){} 00114 00116 00117 timeperiod_t &operator =( const double &period){pd_=period; return *this;} 00118 timeperiod_t &operator =( float period){pd_=period; return *this;} 00119 timeperiod_t &operator =( long period){pd_=period; return *this;} 00120 timeperiod_t &operator =( int period){pd_=period; return *this;} 00121 timeperiod_t &operator =( short period){pd_=period; return *this;} 00123 00125 operator double() const{return pd_;} 00126 00128 00129 bool operator ==(const timeperiod_t &prd) const { return pd_ == prd.pd_; } 00130 bool operator !=(const timeperiod_t &prd) const { return pd_ != prd.pd_; } 00131 bool operator < (const timeperiod_t &prd) const { return pd_ < prd.pd_; } 00132 bool operator > (const timeperiod_t &prd) const { return pd_ > prd.pd_; } 00133 bool operator <=(const timeperiod_t &prd) const { return pd_ <= prd.pd_; } 00134 bool operator >=(const timeperiod_t &prd) const { return pd_ >= prd.pd_; } 00135 bool operator ==(dt_invalid_t) const { return pd_ == dt_invalid_; } 00136 bool operator !=(dt_invalid_t) const { return pd_ != dt_invalid_; } 00137 00138 // These shouldn't be needed. 00139 template<typename T> bool operator < (T prd) const { return pd_ < double(prd); } 00140 template<typename T> bool operator <= (T prd) const { return pd_ <= double(prd); } 00141 template<typename T> bool operator > (T prd) const { return pd_ > double(prd); } 00142 template<typename T> bool operator >= (T prd) const { return pd_ >= double(prd); } 00143 template<typename T> bool operator == (T prd) const { return pd_ == double(prd); } 00144 template<typename T> bool operator != (T prd) const { return pd_ != double(prd); } 00145 00147 00148 00149 timeperiod_t operator+(const timeperiod_t &prd) const { return pd_ + prd.pd_; } 00150 timeperiod_t operator-(const timeperiod_t &prd) const { return pd_ - prd.pd_; } 00151 timeperiod_t &operator+=(const timeperiod_t &prd) { pd_ += prd.pd_; return *this; } 00152 timeperiod_t &operator-=(const timeperiod_t &prd) { pd_ -= prd.pd_; return *this; } 00153 timeperiod_t operator-() const { return -pd_; } 00155 00157 00158 double as_days() { return pd_; } 00159 void as_days(double prd) { pd_=prd; } 00160 double as_hours() { return pd_*24; } 00161 void as_hours(double prd) { pd_= prd/24; } 00162 double as_minutes() { return pd_*24*60; } 00163 void as_minutes(double prd) { pd_= prd/(24*60); } 00164 double as_seconds() { return pd_*24*60*60; } 00165 void as_seconds(double prd) { pd_= prd/(24*60*60); } 00167 00169 00172 void split( long& days, long& hours, long& minutes, long& seconds ) 00173 { 00174 split(&days,&hours,&minutes,&seconds); 00175 } 00177 void split( long *days, long *hours, long *minutes, long *seconds, long *milliseconds = 0) 00178 { 00179 // Split into days and milliseconds. 00180 double int_part; 00181 long mspart = long(modf(pd_, &int_part) * 86400000); 00182 *days = long(int_part); 00183 // Optimise for integer. 00184 if (mspart == 0 ) 00185 { 00186 *days = *hours = *minutes = *seconds = 0; 00187 if (milliseconds!=NULL) *milliseconds =0; 00188 return; 00189 } 00190 // Split up parts. 00191 long ms, quot, quot2; 00192 COMET_DIVMOD_(quot, ms, mspart, 1000); 00193 COMET_DIVMOD_(quot2, *seconds, quot, 60); 00194 COMET_DIVMOD_( *hours, *minutes, quot2, 60); 00195 if( milliseconds != NULL) 00196 *milliseconds = ms; 00197 } 00198 00200 void set_period( long days, long hours, long minutes, long seconds, long milliseconds=0 ) 00201 { 00202 pd_ = days + ((hours*3600000L) + (minutes*60000L)+ (seconds*1000L)+ milliseconds)/86400000.; 00203 } 00204 00206 bool invalid() const 00207 { 00208 return pd_ == (double)(dt_invalid_); 00209 } 00211 bool good() const 00212 { 00213 return !invalid(); 00214 } 00218 bool valid() const { return !invalid(); } 00219 00221 static timeperiod_t invalid_period() { return timeperiod_t( (double)(dt_invalid_)); } 00222 00223 }; 00224 00225 00227 template< typename CHAR > 00228 inline size_t str_formattime( CHAR *strDest, size_t maxsize, const CHAR *format, const struct tm *timeptr ) 00229 { 00230 return -1; 00231 } 00233 template<> 00234 inline size_t str_formattime<char>( char *strDest, size_t maxsize, const char *format, const struct tm *timeptr ) 00235 { 00236 return strftime( strDest, maxsize, format, timeptr ); 00237 } 00238 00239 template<> 00240 inline size_t str_formattime<wchar_t>( wchar_t *strDest, size_t maxsize, const wchar_t *format, const struct tm *timeptr ) 00241 { 00242 return wcsftime( strDest, maxsize, format, timeptr ); 00243 } 00244 00245 namespace impl { 00246 // Internally used to group div/mod so optimiser is likely to pick it up. 00247 template<typename T> 00248 struct datetime_base 00249 { 00250 T dt_; 00251 00252 enum convert_mode { 00253 cmBoth, 00254 cmOnlyTime, 00255 cmOnlyDate 00256 }; 00257 00258 00261 static bool date_from_absdate_( long daysAbsolute, int *tm_year, int *tm_mon, int *tm_mday ); 00262 00265 static bool dow_from_absdate_( long daysAbsolute, int *tm_wday) 00266 { 00267 // Calculate the day of week (sun=0, mon=1...) 00268 // -1 because 1/1/0 is Sat. 00269 *tm_wday = (int)((daysAbsolute + 1) % 7L); 00270 return true; 00271 } 00272 00275 static bool absdate_from_date_( long *daysAbsolute, int tm_year, int tm_mon, int tm_mday); 00276 00279 static bool time_from_milliseconds_( long milliseconds, int *tm_hour, int *tm_min, int *tm_sec, int *tm_ms); 00282 static bool milliseconds_from_time_( long *milliseconds, unsigned short tm_hour, unsigned short tm_min, unsigned short tm_sec, unsigned short tm_ms); 00283 00286 static bool datetime_from_oledate_( DATE date, int *tm_year, int *tm_mon, int *tm_mday, int *tm_dow, int *tm_hour, int *tm_min, int *tm_sec, int *tm_ms, convert_mode mode); 00287 00290 static inline long split_oledate_as_absdate_( DATE date ) 00291 { 00292 double val = to_double(date)+ (693959 + 1721060) + half_millisecond; // Add days from 1/1/0 to 12/30/1899 00293 return long(floor(val)); 00294 } 00297 static inline long split_oledate_as_absdate_( DATE date, long *ms_part,bool need_ms_part ) 00298 { 00299 double val = to_double(date)+ (693959 + 1721060) + half_millisecond; // Add days from 1/1/0 to 12/30/1899 00300 if (!need_ms_part) return long(floor(val)); 00301 *ms_part = long(modf(val, &val) * 86400000); 00302 return long(val); 00303 } 00304 00307 static inline DATE join_absdate_as_oledate_( long absDate, long ms_part) 00308 { 00309 return to_date( (double(absDate) + (ms_part / 86400000.)) - 693959 - 1721060 ); 00310 } 00311 00312 00315 static bool oledate_from_datetime_( DATE *date, unsigned short tm_year, unsigned short tm_mon, unsigned short tm_mday, unsigned short tm_hour, unsigned short tm_min, unsigned short tm_sec, unsigned short tm_ms, convert_mode mode); 00316 00321 static bool from_tm_( const struct tm &src, DATE *dt, convert_mode mode); 00322 00326 static bool to_tm_( DATE dt, struct tm *dest, int *ms); 00327 00328 void set_invalid_() { dt_ = ((T) dt_invalid_); } 00329 void set_null_() { dt_ = ((T) dt_null_); } 00330 00332 static DATE to_date( double dbl) 00333 { 00334 if(dbl>0) return dbl; 00335 double t=floor(dbl); 00336 return t+(t-dbl); 00337 } 00339 static double to_double( DATE dt) 00340 { 00341 if(dt>=0) return dt; 00342 double t = ceil(dt); 00343 return t-(dt-t); 00344 } 00345 00347 bool set_check_range_( T dt) 00348 { 00349 bool result = (dt <= dt_max && dt >= dt_min); 00350 if (result) 00351 dt_ = dt; 00352 return result; 00353 } 00355 void set_invalid_check_range_(T dt) 00356 { 00357 if (!set_check_range_(dt) ) 00358 set_invalid_(); 00359 } 00360 00362 static bool is_leap_year( long year) 00363 { 00364 if ((year & 0x3) != 0) return false; 00365 // && ((year % 100) != 0 || (year % 400) == 0); 00366 long quot,rem; 00367 COMET_DIVMOD_(quot,rem, year, 100); 00368 if (rem != 0) return true; 00369 return ((quot & 0x3) == 0); 00370 } 00371 00372 enum { 00373 dt_max = 2958465L, // about year 9999 00374 dt_null_ = 2147483648L, 00375 dt_invalid_ = 2147483647L, 00376 dt_min = (-657434L) // about year 100 00377 }; 00378 00379 00380 static int days_in_month(int year, int month) 00381 { 00382 switch (month) 00383 { 00384 case 2: return (is_leap_year(year)?29:28); 00385 case 4: case 6: case 9: case 11: 00386 return 30; 00387 default:return 31; 00388 }; 00389 } 00390 00391 }; 00393 00394 const double half_millisecond = 1.0/172800000.0; 00395 00396 // Convert TM to OLE date 00397 template<typename T> 00398 bool 00399 datetime_base<T>::from_tm_( const struct tm &src, DATE *dt, convert_mode mode) 00400 { 00401 return oledate_from_datetime_( dt, unsigned short(src.tm_year + 1900),unsigned short( src.tm_mon+1),unsigned short( src.tm_mday),unsigned short( src.tm_hour),unsigned short( src.tm_min),unsigned short( src.tm_sec), 0U, mode); 00402 } 00403 00404 // Convert OLE date to TM. \retval true Successful conversion. 00405 template<typename T> 00406 bool 00407 datetime_base<T>::to_tm_( DATE dt, struct tm *dest, int *ms) 00408 { 00409 int y,m,d; 00410 if ( !datetime_from_oledate_( dt, &y, &m, &d, &dest->tm_wday, &dest->tm_hour, &dest->tm_min, &dest->tm_sec, NULL, cmBoth) ) 00411 return false; 00412 dest->tm_year = y; 00413 dest->tm_mon = m; 00414 dest->tm_mday = d; 00415 00416 00417 if (dest->tm_year != 0) 00418 { 00419 long firstday, thisday; 00420 absdate_from_date_( &thisday, y,m,d); 00421 absdate_from_date_(&firstday, y, 1, 1); 00422 dest->tm_yday = 1+ ( thisday - firstday); 00423 // Convert afx internal tm to format expected by runtimes (_tcsftime, etc) 00424 dest->tm_year -= 1900; // year is based on 1900 00425 dest->tm_mon -= 1; // month of year is 0-based 00426 dest->tm_isdst = -1; // Don't know DST status. 00427 } 00428 else 00429 dest->tm_yday = 0; 00430 return true; 00431 } 00432 00433 // Convert OLE date to date-parts. 00434 template<typename T> 00435 bool 00436 datetime_base<T>::date_from_absdate_(long daysAbsolute , int *tm_year, int *tm_mon, int *tm_mday) 00437 { 00438 // These algorithms are taken from the gregorian_calendar 00439 // calculations in boost. 00440 typedef long date_int_type; 00441 typedef int year_type; 00442 date_int_type dayNumber = daysAbsolute; 00443 date_int_type a = dayNumber + 32044 ; 00444 date_int_type b = (4*a + 3)/146097; 00445 date_int_type c = a-((146097*b)/4); 00446 date_int_type d = (4*c + 3)/1461; 00447 date_int_type e = c - (1461*d)/4; 00448 date_int_type m = (5*e + 2)/153; 00449 *tm_mday = static_cast<unsigned short>(e - ((153*m + 2)/5) + 1); 00450 *tm_mon = static_cast<unsigned short>(m + 3 - 12 * (m/10)); 00451 *tm_year = static_cast<unsigned short>(100*b + d - 4800 + (m/10)); 00452 return true; 00453 } 00454 00455 // Convert date parts to absolute date. 00456 template<typename T> 00457 bool 00458 datetime_base<T>::absdate_from_date_( long *daysAbsolute, int tm_year, int tm_month, int tm_mday) 00459 { 00460 // These algorithms are taken from the gregorian_calendar 00461 // calculations in boost. 00462 unsigned short a = static_cast<unsigned short>((14-tm_month)/12); 00463 unsigned short y = static_cast<unsigned short>(tm_year + 4800 - a); 00464 unsigned short m = static_cast<unsigned short>(tm_month + 12*a - 3); 00465 unsigned long d = tm_mday + ((153*m + 2)/5) + 365*y + (y/4) - (y/100) + (y/400) - 32045; 00466 00467 *daysAbsolute = d; 00468 00469 return true; 00470 } 00471 00472 // Convert OLE time to time of day parts. 00473 template<typename T> 00474 bool 00475 datetime_base<T>::time_from_milliseconds_( long milliseconds, int *tm_hour, int *tm_min, int *tm_sec, int *tm_ms) 00476 { 00477 if (milliseconds == 0 ) 00478 { 00479 *tm_hour = *tm_min = *tm_sec = 0; 00480 if (tm_ms!=NULL) *tm_ms =0; 00481 return true; 00482 } 00483 long ms, quot, quot2; 00484 COMET_DIVMOD_(quot, ms, milliseconds, 1000); 00485 COMET_DIVMOD_(quot2, *tm_sec, quot, 60); 00486 COMET_DIVMOD_( *tm_hour, *tm_min, quot2, 60); 00487 if( tm_ms != NULL) 00488 *tm_ms = ms; 00489 return true; 00490 } 00491 // Convert time-of-day parts to milliseconds. 00492 template<typename T> 00493 bool 00494 datetime_base<T>::milliseconds_from_time_( long *milliseconds, unsigned short tm_hour, unsigned short tm_min, unsigned short tm_sec, unsigned short tm_ms) 00495 { 00496 if ( tm_hour > 23 || tm_min > 59 || tm_sec> 59) return false; 00497 00498 *milliseconds = (tm_hour* 3600000L) + (tm_min*60000L)+ (tm_sec*1000)+ tm_ms; 00499 return true; 00500 } 00501 00502 // 00503 template<typename T> 00504 bool 00505 datetime_base<T>::datetime_from_oledate_( DATE date, int *tm_year, int *tm_mon, int *tm_mday, int *tm_wday, int *tm_hour, int *tm_min, int *tm_sec, int *tm_ms, convert_mode mode) 00506 { 00507 long datePart, msPart; 00508 datePart = split_oledate_as_absdate_(date, &msPart, mode != cmOnlyDate); 00509 if ( mode != cmOnlyDate && !time_from_milliseconds_( msPart, tm_hour, tm_min, tm_sec, tm_ms)) 00510 return false; 00511 return (mode == cmOnlyTime) || (date_from_absdate_( datePart, tm_year, tm_mon, tm_mday)) && ( (tm_wday==NULL) || dow_from_absdate_(datePart, tm_wday)); 00512 } 00513 // Convert datetime-parts to ole date. 00514 template<typename T> 00515 bool 00516 datetime_base<T>::oledate_from_datetime_( DATE *date, unsigned short tm_year, unsigned short tm_mon, unsigned short tm_mday, unsigned short tm_hour, unsigned short tm_min, unsigned short tm_sec, unsigned short tm_ms, convert_mode mode) 00517 { 00518 long datePart = 0, timePart = 0; 00519 if (mode != cmOnlyDate && !milliseconds_from_time_( &timePart, tm_hour, tm_min, tm_sec, tm_ms)) 00520 return false; 00521 if (mode != cmOnlyTime && !absdate_from_date_( &datePart, tm_year, tm_mon, tm_mday)) 00522 return false; 00523 *date = join_absdate_as_oledate_(datePart, timePart); 00524 return true; 00525 } 00526 }; 00527 00528 00533 class datetime_t : private impl::datetime_base<DATE> 00534 { 00535 public: 00536 00539 enum utc_convert_mode 00540 { 00541 ucm_none, 00542 ucm_local_to_utc, 00543 ucm_utc_to_local, 00544 }; 00546 enum tz_bias_mode 00547 { 00548 tbm_use_local_date, 00549 tbm_use_utc_date, 00550 tbm_force_standard, 00551 tbm_force_summer 00552 }; 00557 00558 datetime_t() { dt_ = 0.;} 00559 00561 explicit datetime_t(DATE date) 00562 { 00563 dt_ = date; 00564 } 00566 00568 explicit datetime_t(int year, int month, int day, int hours=-1, int minutes=0, int seconds=0, int milliseconds=0) 00569 { 00570 if (!oledate_from_datetime_( &dt_, (unsigned short)year, (unsigned short)month, 00571 (unsigned short)day, (unsigned short)hours, (unsigned short)minutes, (unsigned short)seconds, (unsigned short) milliseconds, 00572 (hours < 0)?cmOnlyDate:cmBoth )) 00573 set_invalid_(); 00574 } 00575 00577 datetime_t( dt_invalid_t) { dt_ = dt_invalid_; } 00579 datetime_t( dt_null_t) { dt_ = dt_null_; } 00581 datetime_t( dt_zero_t) { dt_ = 0.; } 00582 00584 static datetime_t get_null() { return datetime_t( DATE(dt_null_) ); } 00586 static datetime_t get_zero() { return datetime_t( DATE(0) ); } 00587 00588 00590 00593 explicit datetime_t(const SYSTEMTIME& systimeSrc) 00594 { 00595 if (!from_systemtime(systimeSrc)) 00596 set_invalid_(); 00597 } 00598 00600 00608 explicit datetime_t(const SYSTEMTIME& systimeSrc, utc_convert_mode utcMode, tz_bias_mode biasMode = tbm_use_local_date, const datetime_t &conversionTime = datetime_t()) 00609 { 00610 if (!from_systemtime(systimeSrc, utcMode, biasMode, conversionTime)) 00611 set_invalid_(); 00612 } 00613 00615 00627 explicit datetime_t(const FILETIME& filetimeSrc, utc_convert_mode utcMode =ucm_none, tz_bias_mode biasMode = tbm_use_local_date, const datetime_t &conversionTime = datetime_t()) 00628 { 00629 if (!from_filetime(filetimeSrc, utcMode, biasMode, conversionTime)) 00630 set_invalid_(); 00631 } 00632 00634 00643 explicit datetime_t(time_t timeSrc, utc_convert_mode utcMode = ucm_utc_to_local, tz_bias_mode biasMode = tbm_use_local_date, const datetime_t &conversionTime = datetime_t()) 00644 { 00645 if (!from_unixtime(timeSrc, utcMode, biasMode, conversionTime)) 00646 set_invalid_(); 00647 } 00648 00650 datetime_t(const datetime_t& date) 00651 { 00652 dt_ = date.dt_; 00653 } 00654 00656 00658 static const datetime_t& create_const_reference(const DATE& s) throw() 00659 { return *reinterpret_cast<const datetime_t*>(&s); } 00661 static datetime_t& create_reference(DATE& s) throw() 00662 { return *reinterpret_cast<datetime_t*>(&s); } 00663 00665 enum day_of_week { 00666 dow_sun=0, dow_mon, dow_tue, dow_wed, dow_thu, dow_fri, dow_sat 00667 }; 00668 00670 static datetime_t now() 00671 { 00672 SYSTEMTIME lt; 00673 ::GetLocalTime(&lt); 00674 return datetime_t(lt); 00675 } 00677 static datetime_t now_utc() 00678 { 00679 SYSTEMTIME lt; 00680 ::GetSystemTime(&lt); 00681 return datetime_t(lt); 00682 } 00683 00689 datetime_t &add_months(int inc_months) 00690 { 00691 int year,month,day; 00692 00693 split_date(&year,&month,&day); 00694 long months = (month-1)+(year*12)+inc_months; 00695 00696 long quot,rem; 00697 COMET_DIVMOD_(quot, rem, months, 12); 00698 if(!set_date( quot, rem+1, day)) 00699 throw datetime_exception("Invalid Date"); 00700 00701 return *this; 00702 } 00703 00705 datetime_t &add_years(int inc_years) 00706 { 00707 int year,month,day; 00708 split_date(&year,&month,&day); 00709 if(!set_date( year+inc_years, month, day)) 00710 throw datetime_exception("Invalid Date"); 00711 return *this; 00712 } 00713 00715 void split_date(int *year, int *month, int *day) const 00716 { 00717 if (good()) 00718 { 00719 long datePart = split_oledate_as_absdate_(dt_); 00720 if (date_from_absdate_( datePart, year, month, day) ) 00721 return; 00722 } 00723 throw datetime_exception("Invalid Date"); 00724 } 00725 00727 void split_time( int *hours, int *minutes, int *seconds, int *milliseconds=NULL) const 00728 { 00729 if(!good() || !datetime_from_oledate_(dt_, NULL, NULL, NULL, NULL, hours, minutes, seconds, milliseconds, cmOnlyTime)) 00730 throw datetime_exception("Invalid DateTime"); 00731 } 00733 void split(int *year, int *month, int *day, int *hours, int *minutes, int *seconds, int *milliseconds=NULL) 00734 { 00735 if(!good() || !datetime_from_oledate_(dt_, year, month, day, NULL, hours, minutes, seconds, milliseconds, cmBoth)) 00736 throw datetime_exception("Invalid DateTime"); 00737 } 00738 00739 00741 00742 00743 int year() const 00744 { 00745 int year,month,day; 00746 split_date(&year,&month,&day); 00747 return year; 00748 } 00750 int month() const 00751 { 00752 int year,month,day; 00753 split_date(&year,&month,&day); 00754 return month; 00755 } 00757 int day() const 00758 { 00759 int year,month,day; 00760 split_date(&year,&month,&day); 00761 return day; 00762 } 00764 int hour() const 00765 { 00766 int hours,minutes,seconds; 00767 split_time(&hours,&minutes,&seconds); 00768 return hours; 00769 } 00771 int minute() const 00772 { 00773 int hours,minutes,seconds; 00774 split_time(&hours,&minutes,&seconds); 00775 return minutes; 00776 } 00778 int second() const 00779 { 00780 int hours,minutes,seconds; 00781 split_time(&hours,&minutes,&seconds); 00782 return seconds; 00783 } 00785 int millisecond() const 00786 { 00787 int hours,minutes,seconds,ms; 00788 split_time(&hours,&minutes,&seconds,&ms); 00789 return ms; 00790 } 00791 00793 day_of_week dow() const 00794 { 00795 long datePart; 00796 datePart = split_oledate_as_absdate_(dt_); 00797 int wday; 00798 if(!good() || !dow_from_absdate_(datePart, &wday)) 00799 throw datetime_exception("Invalid Date"); 00800 return day_of_week(wday); 00801 } 00803 int year_day() const 00804 { 00805 if (good()) 00806 { 00807 long datepart = split_oledate_as_absdate_(dt_); 00808 int y,m,d; 00809 date_from_absdate_(datepart, &y,&m,&d); 00810 long firstday; 00811 if ( absdate_from_date_(&firstday, y, 1, 1)) 00812 return 1+ ( datepart - firstday); 00813 } 00814 throw datetime_exception("Invalid Date"); 00815 } 00817 int days_in_month() 00818 { 00819 int year,month,day; 00820 split_date(&year,&month,&day); 00821 return impl::datetime_base<DATE>::days_in_month(year,month); 00822 } 00824 static inline int days_in_month(int year, int month) 00825 { 00826 return impl::datetime_base<DATE>::days_in_month(year,month); 00827 } 00828 00830 00831 00832 datetime_t &operator=( const datetime_t& date) 00833 { 00834 dt_ = date.dt_; 00835 return *this; 00836 } 00837 00838 datetime_t &operator=( DATE date ) 00839 { 00840 set_invalid_check_range_(date); 00841 return *this; 00842 } 00844 00846 00847 bool operator==(const datetime_t& date) const{ return date.dt_==dt_; } 00848 bool operator!=(const datetime_t& date) const{ return date.dt_!=dt_; } 00849 bool operator<(const datetime_t& date) const { return to_double(dt_)<to_double(date.dt_); } 00850 bool operator>(const datetime_t& date) const{ return to_double(dt_)>to_double(date.dt_); } 00851 bool operator<=(const datetime_t& date) const{ return to_double(dt_)<=to_double(date.dt_); } 00852 bool operator>=(const datetime_t& date) const{ return to_double(dt_)>=to_double(date.dt_); } 00853 bool operator==(dt_invalid_t) const { return invalid(); } 00854 bool operator!=(dt_invalid_t) const { return !invalid(); } 00855 bool operator==(dt_zero_t) const { return dt_==0.; } 00856 bool operator!=(dt_zero_t) const { return dt_!=0.; } 00857 bool operator==(dt_null_t) const { return null(); } 00858 bool operator!=(dt_null_t) const { return !null(); } 00860 00862 00863 datetime_t operator+(const timeperiod_t& dateSpan) const 00864 { 00865 datetime_t dt(*this); 00866 dt+=dateSpan; 00867 return dt; 00868 } 00869 datetime_t operator-(const timeperiod_t& dateSpan) const 00870 { 00871 datetime_t dt(*this); 00872 dt-=dateSpan; 00873 return dt; 00874 } 00875 datetime_t& operator+=(const timeperiod_t &dateSpan) 00876 { 00877 COMET_ASSERT( good() ); 00878 if(!good()) 00879 set_invalid_(); 00880 else 00881 set_invalid_check_range_(to_date( to_double(dt_) + (double)dateSpan )); 00882 00883 return *this; 00884 } 00885 datetime_t& operator-=(const timeperiod_t &dateSpan) 00886 { 00887 COMET_ASSERT( good() ); 00888 if(!good()) 00889 set_invalid_(); 00890 else 00891 set_invalid_check_range_(to_date( to_double(dt_) - (double)dateSpan )); 00892 return *this; 00893 } 00894 timeperiod_t operator-(const datetime_t& date) const 00895 { 00896 COMET_ASSERT( good() && date.good() ); 00897 if( !good() || ! date.good()) 00898 return timeperiod_t::invalid_period(); 00899 return to_double(dt_) - to_double(date.dt_); 00900 } 00901 datetime_t &operator++() 00902 { 00903 (*this)+=1; 00904 return *this; 00905 } 00906 datetime_t operator++(int) 00907 { 00908 datetime_t t(*this); (*this)+=1; return t; 00909 } 00910 datetime_t &operator--() 00911 { 00912 (*this)-=1; return *this; 00913 } 00914 00915 datetime_t operator--(int) 00916 { 00917 datetime_t t(*this); (*this)-=1; return t; 00918 } 00920 00922 inline bool invalid() const { return dt_ == ((double) dt_invalid_); } 00924 inline bool null() const { return dt_ == ((double) dt_null_); } 00925 00927 inline bool zero() const { return dt_ == 0; } 00928 00932 inline bool valid() const { return !invalid(); } 00933 00935 inline bool good() const 00936 { 00937 switch ((long)dt_) 00938 { 00939 case dt_invalid_: case dt_null_: return false; 00940 default: return true; 00941 } 00942 } 00943 00945 00946 DATE get() const { if(invalid()) throw("Invalid Date"); return null()?0:dt_;} 00947 DATE in() const { return get(); } 00948 DATE *in_ptr() const { return const_cast<DATE *>(&dt_);} 00949 DATE *out() { return &dt_;} 00950 DATE *inout() { return &dt_;} 00952 00959 bool set_date( int year, int month, int day) 00960 { 00961 long datePart, timePart; 00962 datePart = split_oledate_as_absdate_(dt_, &timePart, true); 00963 if (!absdate_from_date_(&datePart, year,month, day)) 00964 return false; 00965 dt_ = join_absdate_as_oledate_( datePart, timePart); 00966 return true; 00967 } 00968 00977 bool set_time( int hours, int minutes, int seconds, int milliseconds =0) 00978 { 00979 long datePart, timePart; 00980 datePart = split_oledate_as_absdate_(dt_, &timePart, true); 00981 if (!milliseconds_from_time_(&timePart, (unsigned short)hours, (unsigned short)minutes, (unsigned short)seconds, (unsigned short)milliseconds)) 00982 return false; 00983 dt_ = join_absdate_as_oledate_( datePart, timePart); 00984 return true; 00985 } 00997 bool set_date_time(int year, int month, int day, int hours, int minutes, int seconds, int milliseconds = 0 ) 00998 { 00999 return oledate_from_datetime_(&dt_, (unsigned short)year, (unsigned short)month, (unsigned short)day, (unsigned short)hours, (unsigned short)minutes, (unsigned short)seconds, (unsigned short)milliseconds, cmBoth); 01000 } 01001 01003 enum format_flags{ 01004 ff_default = 0, 01005 ff_system_locale = LOCALE_NOUSEROVERRIDE, 01006 ff_hijri = VAR_CALENDAR_HIJRI, 01007 ff_thai = 0x10, /* VAR_CALENDAR_THAI, */ 01008 ff_gregorian = 0x20, /*VAR_CALENDAR_GREGORIAN*/ 01009 ff_four_digits = VAR_FOURDIGITYEARS, 01010 ff_time_only = VAR_TIMEVALUEONLY, 01011 ff_date_only = VAR_DATEVALUEONLY 01012 }; 01018 datetime_t &parse( const bstr_t &val, format_flags flags = ff_default, LCID locale = LOCALE_USER_DEFAULT) 01019 { 01020 VarDateFromStr( val.in(), locale, flags, &dt_) | raise_exception; 01021 return *this; 01022 } 01023 01027 double as_sortable_double() const { COMET_ASSERT( good() ); return to_double(dt_); } 01028 public: 01029 01030 01039 datetime_t local_to_utc( tz_bias_mode biasMode = tbm_use_local_date, datetime_t asOfDate = datetime_t() ) 01040 { 01041 if (asOfDate.invalid()) 01042 switch( biasMode) 01043 { 01044 case tbm_use_utc_date: biasMode = tbm_use_local_date; // no break 01045 case tbm_use_local_date: asOfDate = *this; 01046 } 01047 // if they didn't specify if the AS OF date is UTC, use the current date 01048 return datetime_t(to_date(to_double(dt_)+local_timezone_bias(asOfDate,biasMode))/(24.*60.)); 01049 } 01050 01061 datetime_t utc_to_local( tz_bias_mode biasMode = tbm_use_local_date,datetime_t asOfDate = datetime_t() ) 01062 { 01063 if ( asOfDate.invalid()) 01064 switch( biasMode) 01065 { 01066 case tbm_use_local_date: biasMode = tbm_use_utc_date; // no break! 01067 case tbm_use_utc_date: asOfDate = *this; 01068 } 01069 return datetime_t(to_date(to_double(dt_)-local_timezone_bias(asOfDate,biasMode))/(24.*60.)); 01070 } 01071 01074 bool to_systemtime( SYSTEMTIME *sysTime) 01075 { 01076 int year,month,day,dow,hour,minute,second,ms; 01077 if (!datetime_from_oledate_( dt_, &year, &month, &day, &dow, &hour, &minute, &second, &ms, cmBoth)) 01078 return false; 01079 sysTime->wYear = (short)year; 01080 sysTime->wMonth = (short)month; 01081 sysTime->wDay = (short)day; 01082 sysTime->wDayOfWeek = (short)dow; // Sunday=0 01083 sysTime->wHour = (short)hour; 01084 sysTime->wMinute = (short)minute; 01085 sysTime->wSecond = (short)second; 01086 sysTime->wMilliseconds = (short)ms; 01087 return true; 01088 } 01089 01092 bool from_systemtime(const SYSTEMTIME& src) 01093 { 01094 return oledate_from_datetime_( &dt_, src.wYear, src.wMonth, src.wDay, src.wHour, src.wMinute, src.wSecond, src.wMilliseconds, cmBoth); 01095 } 01104 bool from_systemtime(const SYSTEMTIME& src, utc_convert_mode utcMode, tz_bias_mode biasMode = tbm_use_local_date , const datetime_t &conversionTime = datetime_t()) 01105 { 01106 if (! from_systemtime( src)) 01107 return false; 01108 switch( utcMode) 01109 { 01110 case ucm_none: break; 01111 case ucm_local_to_utc: *this = local_to_utc( biasMode, conversionTime ); break; 01112 case ucm_utc_to_local: *this = utc_to_local( biasMode, conversionTime); break; 01113 } 01114 return true; 01115 } 01118 bool from_filetime(const FILETIME& src) 01119 { 01120 double ftd = (((__int64(src.dwHighDateTime) << 32 | src.dwLowDateTime)/(36000000000.)) - 2620920.)/24; 01121 return set_check_range_( to_date(ftd)); 01122 } 01131 bool from_filetime(const FILETIME& src, utc_convert_mode utcMode, tz_bias_mode biasMode = tbm_use_local_date, const datetime_t &conversionTime = datetime_t()) 01132 { 01133 if (! from_filetime(src)) 01134 return false; 01135 switch( utcMode) 01136 { 01137 case ucm_none: break; 01138 case ucm_local_to_utc: *this = local_to_utc( biasMode, conversionTime ); break; 01139 case ucm_utc_to_local: *this = utc_to_local( biasMode, conversionTime); break; 01140 } 01141 return true; 01142 } 01143 01144 01147 bool to_filetime( FILETIME *filetime) 01148 { 01149 double val = ((to_double(dt_) * 24.) + 2620920.)*(36000000000.) ; 01150 01151 __int64 llval = __int64(val); 01152 filetime->dwHighDateTime = long (llval >> 32); 01153 filetime->dwLowDateTime = long (llval); 01154 return val > 0; 01155 } 01156 01159 bool from_tm(const struct tm &tm_time) 01160 { 01161 return from_tm_( tm_time, &dt_, cmBoth); 01162 } 01171 bool from_tm(const struct tm &tm_time, utc_convert_mode utcMode, tz_bias_mode biasMode = tbm_use_local_date, datetime_t conversionTime = datetime_t()) 01172 { 01173 if(!from_tm(tm_time)) 01174 return false; 01175 switch( utcMode) 01176 { 01177 case ucm_none: 01178 break; 01179 case ucm_local_to_utc: 01180 // Take advantage of tm_isdst to work out dst mode! 01181 if( tm_time.tm_isdst >= 0) 01182 biasMode = (( tm_time.tm_isdst ==0)? tbm_force_standard: tbm_force_summer ); 01183 *this = local_to_utc( biasMode, conversionTime ); 01184 break; 01185 case ucm_utc_to_local: 01186 *this = utc_to_local( biasMode, conversionTime); 01187 break; 01188 } 01189 return true; 01190 } 01191 01194 bool from_unixtime( time_t val) 01195 { 01196 FILETIME ft; 01197 __int64 ll =(__int64(val) * 10000000L) + 116444736000000000L; 01198 ft.dwLowDateTime = (DWORD) ll; 01199 ft.dwHighDateTime = (DWORD)(ll >>32); 01200 return from_filetime(ft); 01201 } 01210 bool from_unixtime( time_t val, utc_convert_mode utcMode = ucm_utc_to_local, tz_bias_mode biasMode = tbm_use_local_date, const datetime_t &conversionTime = datetime_t()) 01211 { 01212 FILETIME ft; 01213 __int64 ll =(__int64(val) * 10000000L) + 116444736000000000L; 01214 ft.dwLowDateTime = (DWORD) ll; 01215 ft.dwHighDateTime = (DWORD)(ll >>32); 01216 return from_filetime( ft, utcMode, biasMode, conversionTime); 01217 } 01218 01227 bool to_unixtime( time_t *val, utc_convert_mode utcMode = ucm_local_to_utc, tz_bias_mode biasMode = tbm_use_local_date, const datetime_t &conversionTime = datetime_t()) 01228 { 01229 datetime_t dtval; 01230 switch( utcMode) 01231 { 01232 case ucm_none: dtval = *this; break; 01233 case ucm_local_to_utc: dtval = local_to_utc( biasMode, conversionTime ); break; 01234 case ucm_utc_to_local: dtval = utc_to_local( biasMode, conversionTime); break; 01235 } 01236 FILETIME ft; 01237 if( !dtval.to_filetime(&ft) ) 01238 return false; 01239 *val = time_t(((__int64(ft.dwHighDateTime) << 32 | ft.dwLowDateTime) - 116444736000000000L)/10000000L); 01240 return true; 01241 } 01242 01248 static long local_timezone_bias( datetime_t dt, tz_bias_mode biasMode) 01249 { 01250 TIME_ZONE_INFORMATION tzi; 01251 ::GetTimeZoneInformation(&tzi); 01252 01253 long baseBias= tzi.Bias; 01254 bool isUTC = false; 01255 switch ( biasMode ) 01256 { 01257 case tbm_force_standard: return baseBias + tzi.StandardBias; 01258 case tbm_force_summer: return baseBias + tzi.DaylightBias; 01259 case tbm_use_local_date: break; 01260 case tbm_use_utc_date: isUTC = true; break; 01261 } 01262 // if we've even got both time zones set, we have to choose which is active... 01263 if ((tzi.DaylightDate.wMonth != 0) && (tzi.StandardDate.wMonth != 0) ) 01264 { 01265 // all local standard time/daylight savings time rules are based on 01266 // local-time, so add the base bias FIRST 01267 if (isUTC) 01268 dt -= (baseBias/(24.*60.)); 01269 01270 SYSTEMTIME sysTime; 01271 if (!dt.to_systemtime(&sysTime)) 01272 throw datetime_exception("Invalid Date"); 01273 01274 bool DSTbeforeLST = tzi.DaylightDate.wMonth < tzi.StandardDate.wMonth; 01275 01276 bool afterDaylightStarts = tz_on_or_after_in_year(sysTime, tzi.DaylightDate); 01277 bool afterStandardStarts = tz_on_or_after_in_year(sysTime, tzi.StandardDate); 01278 01279 if( ((afterDaylightStarts== afterStandardStarts)!= DSTbeforeLST) ) 01280 return baseBias + tzi.DaylightBias; 01281 } 01282 return baseBias + tzi.StandardBias; 01283 } 01284 01285 protected: 01294 static bool tz_on_or_after_in_year(SYSTEMTIME testST, SYSTEMTIME tziST) 01295 { 01296 // assume month check first... 01297 long cmp = testST.wMonth - tziST.wMonth; 01298 if (cmp!=0) 01299 return cmp > 0; 01300 01301 SYSTEMTIME absST; 01302 01303 // if year is given, then the specified date is already exact... 01304 if (tziST.wYear != 0) 01305 { 01306 // first test the year... 01307 cmp = testST.wYear - tziST.wYear; 01308 if (cmp !=0) 01309 return cmp > 0; 01310 // carry on with the exact day known 01311 absST = tziST; 01312 } 01313 else 01314 { 01315 // compute the appropriate day from the specified instance of the set day-of-week 01316 // use the testST's year for the year in the calculation 01317 tz_convert_relative_dow_to_absolute(testST, tziST, &absST); 01318 } 01319 01320 // month same... check day/hour/minute/second/millisecond 01321 if ((cmp = testST.wDay - absST.wDay)==0) 01322 if ((cmp = testST.wHour - absST.wHour)==0) 01323 if ((cmp = testST.wMinute - absST.wMinute)==0) 01324 if ((cmp = testST.wSecond - absST.wSecond)==0) 01325 cmp = testST.wMilliseconds - absST.wMilliseconds; 01326 return cmp >= 0; 01327 } 01328 01329 // Computes the proper day-of-week instance (like last Sunday in October) for the 01330 // specified test year. See the encoding rules documented with TIME_ZONE_INFORMATION. 01331 // This ASSUMES that testST.wMonth == tziST.wMonth 01332 static void tz_convert_relative_dow_to_absolute(const SYSTEMTIME &testST , const SYSTEMTIME &tziST, SYSTEMTIME *absST) 01333 { 01334 COMET_ASSERT(testST.wMonth == tziST.wMonth); 01335 01336 // Set up the absolute date except for wDay, which we must find 01337 absST->wYear = testST.wYear; // year is only valid in the testST 01338 int month = absST->wMonth = tziST.wMonth; 01339 absST->wDayOfWeek = tziST.wDayOfWeek; 01340 01341 absST->wHour = tziST.wHour; 01342 absST->wMinute = tziST.wMinute; 01343 absST->wSecond = tziST.wSecond; 01344 absST->wMilliseconds = tziST.wMilliseconds; 01345 01346 // Find a day of the month that falls on the same day of the week as 01347 // the transition. 01348 01349 // If test day is the 29th of the month (testST.wDay = 29) and today 01350 // is a Tuesday (testST.wDayOfWeek = 2) and the transition occurs on 01351 // Sunday (testST.wDayOfWeek = 0) we compute absDay = 29 + 0 + 7 - 01352 // 2, giving the 34th 01353 01354 // then adjust that to a day of month adjustment 01355 long absDay = ((testST.wDay + tziST.wDayOfWeek + (7-1) - testST.wDayOfWeek) % 7) +1; 01356 01357 // now multiply this time the "which DOW" setting from the TZI 01358 // (1 = first, 5 = last) 01359 // add the requisite number of weeks to the base point 01360 absDay += (7 * (tziST.wDay - 1)); 01361 01362 // and if we exceeded the number of days in the month, back up by a 01363 // week (this handles the 5=last situation) 01364 01365 int daysInMonth = days_in_month( absST->wYear, month); 01366 01367 if (absDay > daysInMonth) 01368 absDay -= 7; 01369 01370 absST->wDay = (unsigned short)absDay; 01371 } 01372 public: 01373 01378 bstr_t format( format_flags flags = ff_default , LCID locale = LOCALE_USER_DEFAULT) const 01379 { 01380 bstr_t strDate; 01381 if (!good()) 01382 { 01383 return strDate; 01384 } 01385 VarBstrFromDate(dt_, locale, flags, strDate.out()) | raise_exception; 01386 return strDate; 01387 } 01388 01392 template<typename CHAR> 01393 std::basic_string<CHAR> format( const std::basic_string<CHAR> &fmt ) const 01394 { 01395 return format(fmt.c_str()); 01396 } 01397 01401 template<typename CHAR> 01402 std::basic_string<CHAR> format( const CHAR *fmt ) const 01403 { 01404 if (!good()) 01405 { 01406 return std::basic_string<CHAR>(); 01407 } 01408 struct tm src; 01409 if(!to_tm_( dt_, &src, NULL)) 01410 throw datetime_exception("Invalid Date"); 01411 01412 auto_buffer_t<CHAR>::size_type capacity = 50; 01413 auto_buffer_t<CHAR> buf(capacity); 01414 size_t ret; 01415 while( (ret = str_formattime( buf.get() , capacity, fmt, &src ))==0 && capacity < 1024) 01416 { 01417 capacity += 50; 01418 buf.resize(capacity); 01419 } 01420 if(ret == 0) 01421 buf.at(0)='\0'; 01422 01423 return std::basic_string<CHAR>(buf.get(), ret); 01424 } 01425 01427 DATE detach() 01428 { 01429 DATE val = dt_; 01430 dt_ = 0.; 01431 return val; 01432 } 01433 01435 static DATE detach( datetime_t &dt) 01436 { 01437 return dt.detach(); 01438 } 01439 01441 friend 01442 std::basic_ostream<char> &operator<<(std::basic_ostream<char> &os, const datetime_t &val) 01443 { 01444 os << val.format(); 01445 return os; 01446 } 01447 01449 friend 01450 std::basic_ostream<wchar_t> &operator<<(std::basic_ostream<wchar_t> &os, const datetime_t &val) 01451 { 01452 os << val.format(); 01453 return os; 01454 } 01455 01456 private: 01457 }; 01458 #undef COMET_DIVMOD_ 01459 01460 01461 }; 01462 #endif