1 /////////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     implementation of time/date related classes 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 //              parts of code taken from sndcal library by Scott E. Lee: 
  11 //               Copyright 1993-1995, Scott E. Lee, all rights reserved. 
  12 //               Permission granted to use, copy, modify, distribute and sell 
  13 //               so long as the above copyright and this permission statement 
  14 //               are retained in all copies. 
  16 // Licence:     wxWindows licence 
  17 /////////////////////////////////////////////////////////////////////////////// 
  20  * Implementation notes: 
  22  * 1. the time is stored as a 64bit integer containing the signed number of 
  23  *    milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always 
  26  * 2. the range is thus something about 580 million years, but due to current 
  27  *    algorithms limitations, only dates from Nov 24, 4714BC are handled 
  29  * 3. standard ANSI C functions are used to do time calculations whenever 
  30  *    possible, i.e. when the date is in the range Jan 1, 1970 to 2038 
  32  * 4. otherwise, the calculations are done by converting the date to/from JDN 
  33  *    first (the range limitation mentioned above comes from here: the 
  34  *    algorithm used by Scott E. Lee's code only works for positive JDNs, more 
  37  * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to 
  38  *    this moment in local time and may be converted to the object 
  39  *    corresponding to the same date/time in another time zone by using 
  42  * 6. the conversions to the current (or any other) timezone are done when the 
  43  *    internal time representation is converted to the broken-down one in 
  47 // ============================================================================ 
  49 // ============================================================================ 
  51 // ---------------------------------------------------------------------------- 
  53 // ---------------------------------------------------------------------------- 
  55 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  56     #pragma implementation "datetime.h" 
  59 // For compilers that support precompilation, includes "wx.h". 
  60 #include "wx/wxprec.h" 
  66 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME 
  69     #include "wx/string.h" 
  74 #include "wx/thread.h" 
  75 #include "wx/tokenzr.h" 
  76 #include "wx/module.h" 
  80 #include "wx/datetime.h" 
  81 #include "wx/stopwatch.h"           // for wxGetLocalTimeMillis() 
  83 const long wxDateTime::TIME_T_FACTOR 
= 1000l; 
  85 // ---------------------------------------------------------------------------- 
  86 // conditional compilation 
  87 // ---------------------------------------------------------------------------- 
  89 #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \ 
  90         ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0)) 
  91     // glibc 2.0.7 strptime() is broken - the following snippet causes it to 
  92     // crash (instead of just failing): 
  94     //      strncpy(buf, "Tue Dec 21 20:25:40 1999", 128); 
  95     //      strptime(buf, "%x", &tm); 
  99 #endif // broken strptime() 
 101 #if defined(__MWERKS__) && wxUSE_UNICODE 
 105 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM) 
 106     #if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__) 
 107         #define WX_TIMEZONE _timezone 
 108     #elif defined(__MWERKS__) 
 109         long wxmw_timezone 
= 28800; 
 110         #define WX_TIMEZONE wxmw_timezone 
 111     #elif defined(__DJGPP__) || defined(__WINE__) 
 112         #include <sys/timeb.h> 
 114         static long wxGetTimeZone() 
 116             static long timezone 
= MAXLONG
; // invalid timezone 
 117             if (timezone 
== MAXLONG
) 
 121                 timezone 
= tb
.timezone
; 
 125         #define WX_TIMEZONE wxGetTimeZone() 
 126     #elif defined(__DARWIN__) 
 127         #define WX_GMTOFF_IN_TM 
 128     #else // unknown platform - try timezone 
 129         #define WX_TIMEZONE timezone 
 131 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM 
 133 // ---------------------------------------------------------------------------- 
 135 // ---------------------------------------------------------------------------- 
 137 // debugging helper: just a convenient replacement of wxCHECK() 
 138 #define wxDATETIME_CHECK(expr, msg)     \ 
 142             *this = wxInvalidDateTime;  \ 
 146 // ---------------------------------------------------------------------------- 
 148 // ---------------------------------------------------------------------------- 
 150 class wxDateTimeHolidaysModule 
: public wxModule
 
 153     virtual bool OnInit() 
 155         wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
); 
 160     virtual void OnExit() 
 162         wxDateTimeHolidayAuthority::ClearAllAuthorities(); 
 163         wxDateTimeHolidayAuthority::ms_authorities
.clear(); 
 167     DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
) 
 170 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
) 
 172 // ---------------------------------------------------------------------------- 
 174 // ---------------------------------------------------------------------------- 
 177 static const int MONTHS_IN_YEAR 
= 12; 
 179 static const int SEC_PER_MIN 
= 60; 
 181 static const int MIN_PER_HOUR 
= 60; 
 183 static const int HOURS_PER_DAY 
= 24; 
 185 static const long SECONDS_PER_DAY 
= 86400l; 
 187 static const int DAYS_PER_WEEK 
= 7; 
 189 static const long MILLISECONDS_PER_DAY 
= 86400000l; 
 191 // this is the integral part of JDN of the midnight of Jan 1, 1970 
 192 // (i.e. JDN(Jan 1, 1970) = 2440587.5) 
 193 static const long EPOCH_JDN 
= 2440587l; 
 195 // the date of JDN -0.5 (as we don't work with fractional parts, this is the 
 196 // reference date for us) is Nov 24, 4714BC 
 197 static const int JDN_0_YEAR 
= -4713; 
 198 static const int JDN_0_MONTH 
= wxDateTime::Nov
; 
 199 static const int JDN_0_DAY 
= 24; 
 201 // the constants used for JDN calculations 
 202 static const long JDN_OFFSET         
= 32046l; 
 203 static const long DAYS_PER_5_MONTHS  
= 153l; 
 204 static const long DAYS_PER_4_YEARS   
= 1461l; 
 205 static const long DAYS_PER_400_YEARS 
= 146097l; 
 207 // this array contains the cumulated number of days in all previous months for 
 208 // normal and leap years 
 209 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] = 
 211     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, 
 212     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } 
 215 // ---------------------------------------------------------------------------- 
 217 // ---------------------------------------------------------------------------- 
 219 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to 
 220 // indicate an invalid wxDateTime object 
 221 const wxDateTime wxDefaultDateTime
; 
 223 wxDateTime::Country 
wxDateTime::ms_country 
= wxDateTime::Country_Unknown
; 
 225 // ---------------------------------------------------------------------------- 
 227 // ---------------------------------------------------------------------------- 
 229 // debugger helper: shows what the date really is 
 231 extern const wxChar 
*wxDumpDate(const wxDateTime
* dt
) 
 233     static wxChar buf
[128]; 
 235     wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S"))); 
 241 // get the number of days in the given month of the given year 
 243 wxDateTime::wxDateTime_t 
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
) 
 245     // the number of days in month in Julian/Gregorian calendar: the first line 
 246     // is for normal years, the second one is for the leap ones 
 247     static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] = 
 249         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 
 250         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 
 253     return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
]; 
 256 // returns the time zone in the C sense, i.e. the difference UTC - local 
 258 static int GetTimeZone() 
 260 #ifdef WX_GMTOFF_IN_TM 
 261     // set to TRUE when the timezone is set 
 262     static bool s_timezoneSet 
= FALSE
; 
 263     static long gmtoffset 
= LONG_MAX
; // invalid timezone 
 265     // ensure that the timezone variable is set by calling localtime 
 266     if ( !s_timezoneSet 
) 
 268         // just call localtime() instead of figuring out whether this system 
 269         // supports tzset(), _tzset() or something else 
 274         s_timezoneSet 
= TRUE
; 
 276         // note that GMT offset is the opposite of time zone and so to return 
 277         // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM 
 278         // cases we have to negate it 
 279         gmtoffset 
= -tm
->tm_gmtoff
; 
 282     return (int)gmtoffset
; 
 283 #else // !WX_GMTOFF_IN_TM 
 284     return (int)WX_TIMEZONE
; 
 285 #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM 
 288 // return the integral part of the JDN for the midnight of the given date (to 
 289 // get the real JDN you need to add 0.5, this is, in fact, JDN of the 
 290 // noon of the previous day) 
 291 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
, 
 292                             wxDateTime::Month mon
, 
 295     // CREDIT: code below is by Scott E. Lee (but bugs are mine) 
 297     // check the date validity 
 299       (year 
> JDN_0_YEAR
) || 
 300       ((year 
== JDN_0_YEAR
) && (mon 
> JDN_0_MONTH
)) || 
 301       ((year 
== JDN_0_YEAR
) && (mon 
== JDN_0_MONTH
) && (day 
>= JDN_0_DAY
)), 
 302       _T("date out of range - can't convert to JDN") 
 305     // make the year positive to avoid problems with negative numbers division 
 308     // months are counted from March here 
 310     if ( mon 
>= wxDateTime::Mar 
) 
 320     // now we can simply add all the contributions together 
 321     return ((year 
/ 100) * DAYS_PER_400_YEARS
) / 4 
 322             + ((year 
% 100) * DAYS_PER_4_YEARS
) / 4 
 323             + (month 
* DAYS_PER_5_MONTHS 
+ 2) / 5 
 328 // this function is a wrapper around strftime(3) adding error checking 
 329 static wxString 
CallStrftime(const wxChar 
*format
, const tm
* tm
) 
 332     if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) ) 
 334         // buffer is too small? 
 335         wxFAIL_MSG(_T("strftime() failed")); 
 338     return wxString(buf
); 
 343 // glibc2 doesn't define this in the headers unless _XOPEN_SOURCE is defined 
 344 // which, unfortunately, wreaks havoc elsewhere 
 345 #if defined(__GLIBC__) && (__GLIBC__ == 2) 
 346     extern "C" char *strptime(const char *, const char *, struct tm 
*); 
 349 // Unicode-friendly strptime() wrapper 
 350 static const wxChar 
* 
 351 CallStrptime(const wxChar 
*input
, const char *fmt
, tm 
*tm
) 
 353     // the problem here is that strptime() returns pointer into the string we 
 354     // passed to it while we're really interested in the pointer into the 
 355     // original, Unicode, string so we try to transform the pointer back 
 357     wxCharBuffer 
inputMB(wxConvertWX2MB(input
)); 
 359     const char * const inputMB 
= input
; 
 360 #endif // Unicode/Ascii 
 362     const char *result 
= strptime(inputMB
, fmt
, tm
); 
 367     // FIXME: this is wrong in presence of surrogates &c 
 368     return input 
+ (result 
- inputMB
.data()); 
 371 #endif // Unicode/Ascii 
 374 #endif // HAVE_STRPTIME 
 376 // if year and/or month have invalid values, replace them with the current ones 
 377 static void ReplaceDefaultYearMonthWithCurrent(int *year
, 
 378                                                wxDateTime::Month 
*month
) 
 380     struct tm 
*tmNow 
= NULL
; 
 382     if ( *year 
== wxDateTime::Inv_Year 
) 
 384         tmNow 
= wxDateTime::GetTmNow(); 
 386         *year 
= 1900 + tmNow
->tm_year
; 
 389     if ( *month 
== wxDateTime::Inv_Month 
) 
 392             tmNow 
= wxDateTime::GetTmNow(); 
 394         *month 
= (wxDateTime::Month
)tmNow
->tm_mon
; 
 398 // fll the struct tm with default values 
 399 static void InitTm(struct tm
& tm
) 
 401     // struct tm may have etxra fields (undocumented and with unportable 
 402     // names) which, nevertheless, must be set to 0 
 403     memset(&tm
, 0, sizeof(struct tm
)); 
 405     tm
.tm_mday 
= 1;   // mday 0 is invalid 
 406     tm
.tm_year 
= 76;  // any valid year 
 407     tm
.tm_isdst 
= -1; // auto determine 
 413 // return the month if the string is a month name or Inv_Month otherwise 
 414 static wxDateTime::Month 
GetMonthFromName(const wxString
& name
, int flags
) 
 416     wxDateTime::Month mon
; 
 417     for ( mon 
= wxDateTime::Jan
; mon 
< wxDateTime::Inv_Month
; wxNextMonth(mon
) ) 
 419         // case-insensitive comparison either one of or with both abbreviated 
 421         if ( flags 
& wxDateTime::Name_Full 
) 
 423             if ( name
.CmpNoCase(wxDateTime:: 
 424                         GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 ) 
 430         if ( flags 
& wxDateTime::Name_Abbr 
) 
 432             if ( name
.CmpNoCase(wxDateTime:: 
 433                         GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 ) 
 443 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise 
 444 static wxDateTime::WeekDay 
GetWeekDayFromName(const wxString
& name
, int flags
) 
 446     wxDateTime::WeekDay wd
; 
 447     for ( wd 
= wxDateTime::Sun
; wd 
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) ) 
 449         // case-insensitive comparison either one of or with both abbreviated 
 451         if ( flags 
& wxDateTime::Name_Full 
) 
 453             if ( name
.CmpNoCase(wxDateTime:: 
 454                         GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 ) 
 460         if ( flags 
& wxDateTime::Name_Abbr 
) 
 462             if ( name
.CmpNoCase(wxDateTime:: 
 463                         GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 ) 
 473 // scans all digits (but no more than len) and returns the resulting number 
 474 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
) 
 478     while ( wxIsdigit(*p
) ) 
 482         if ( len 
&& ++n 
> len 
) 
 486     return !!s 
&& s
.ToULong(number
); 
 489 // scans all alphabetic characters and returns the resulting string 
 490 static wxString 
GetAlphaToken(const wxChar
*& p
) 
 493     while ( wxIsalpha(*p
) ) 
 501 // ============================================================================ 
 502 // implementation of wxDateTime 
 503 // ============================================================================ 
 505 // ---------------------------------------------------------------------------- 
 507 // ---------------------------------------------------------------------------- 
 511     year 
= (wxDateTime_t
)wxDateTime::Inv_Year
; 
 512     mon 
= wxDateTime::Inv_Month
; 
 514     hour 
= min 
= sec 
= msec 
= 0; 
 515     wday 
= wxDateTime::Inv_WeekDay
; 
 518 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
) 
 526     mon 
= (wxDateTime::Month
)tm
.tm_mon
; 
 527     year 
= 1900 + tm
.tm_year
; 
 532 bool wxDateTime::Tm::IsValid() const 
 534     // we allow for the leap seconds, although we don't use them (yet) 
 535     return (year 
!= wxDateTime::Inv_Year
) && (mon 
!= wxDateTime::Inv_Month
) && 
 536            (mday 
<= GetNumOfDaysInMonth(year
, mon
)) && 
 537            (hour 
< 24) && (min 
< 60) && (sec 
< 62) && (msec 
< 1000); 
 540 void wxDateTime::Tm::ComputeWeekDay() 
 542     // compute the week day from day/month/year: we use the dumbest algorithm 
 543     // possible: just compute our JDN and then use the (simple to derive) 
 544     // formula: weekday = (JDN + 1.5) % 7 
 545     wday 
= (wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7; 
 548 void wxDateTime::Tm::AddMonths(int monDiff
) 
 550     // normalize the months field 
 551     while ( monDiff 
< -mon 
) 
 555         monDiff 
+= MONTHS_IN_YEAR
; 
 558     while ( monDiff 
+ mon 
>= MONTHS_IN_YEAR 
) 
 562         monDiff 
-= MONTHS_IN_YEAR
; 
 565     mon 
= (wxDateTime::Month
)(mon 
+ monDiff
); 
 567     wxASSERT_MSG( mon 
>= 0 && mon 
< MONTHS_IN_YEAR
, _T("logic error") ); 
 569     // NB: we don't check here that the resulting date is valid, this function 
 570     //     is private and the caller must check it if needed 
 573 void wxDateTime::Tm::AddDays(int dayDiff
) 
 575     // normalize the days field 
 576     while ( dayDiff 
+ mday 
< 1 ) 
 580         dayDiff 
+= GetNumOfDaysInMonth(year
, mon
); 
 584     while ( mday 
> GetNumOfDaysInMonth(year
, mon
) ) 
 586         mday 
-= GetNumOfDaysInMonth(year
, mon
); 
 591     wxASSERT_MSG( mday 
> 0 && mday 
<= GetNumOfDaysInMonth(year
, mon
), 
 595 // ---------------------------------------------------------------------------- 
 597 // ---------------------------------------------------------------------------- 
 599 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
) 
 603         case wxDateTime::Local
: 
 604             // get the offset from C RTL: it returns the difference GMT-local 
 605             // while we want to have the offset _from_ GMT, hence the '-' 
 606             m_offset 
= -GetTimeZone(); 
 609         case wxDateTime::GMT_12
: 
 610         case wxDateTime::GMT_11
: 
 611         case wxDateTime::GMT_10
: 
 612         case wxDateTime::GMT_9
: 
 613         case wxDateTime::GMT_8
: 
 614         case wxDateTime::GMT_7
: 
 615         case wxDateTime::GMT_6
: 
 616         case wxDateTime::GMT_5
: 
 617         case wxDateTime::GMT_4
: 
 618         case wxDateTime::GMT_3
: 
 619         case wxDateTime::GMT_2
: 
 620         case wxDateTime::GMT_1
: 
 621             m_offset 
= -3600*(wxDateTime::GMT0 
- tz
); 
 624         case wxDateTime::GMT0
: 
 625         case wxDateTime::GMT1
: 
 626         case wxDateTime::GMT2
: 
 627         case wxDateTime::GMT3
: 
 628         case wxDateTime::GMT4
: 
 629         case wxDateTime::GMT5
: 
 630         case wxDateTime::GMT6
: 
 631         case wxDateTime::GMT7
: 
 632         case wxDateTime::GMT8
: 
 633         case wxDateTime::GMT9
: 
 634         case wxDateTime::GMT10
: 
 635         case wxDateTime::GMT11
: 
 636         case wxDateTime::GMT12
: 
 637             m_offset 
= 3600*(tz 
- wxDateTime::GMT0
); 
 640         case wxDateTime::A_CST
: 
 641             // Central Standard Time in use in Australia = UTC + 9.5 
 642             m_offset 
= 60l*(9*60 + 30); 
 646             wxFAIL_MSG( _T("unknown time zone") ); 
 650 // ---------------------------------------------------------------------------- 
 652 // ---------------------------------------------------------------------------- 
 655 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
) 
 657     if ( year 
== Inv_Year 
) 
 658         year 
= GetCurrentYear(); 
 660     if ( cal 
== Gregorian 
) 
 662         // in Gregorian calendar leap years are those divisible by 4 except 
 663         // those divisible by 100 unless they're also divisible by 400 
 664         // (in some countries, like Russia and Greece, additional corrections 
 665         // exist, but they won't manifest themselves until 2700) 
 666         return (year 
% 4 == 0) && ((year 
% 100 != 0) || (year 
% 400 == 0)); 
 668     else if ( cal 
== Julian 
) 
 670         // in Julian calendar the rule is simpler 
 671         return year 
% 4 == 0; 
 675         wxFAIL_MSG(_T("unknown calendar")); 
 682 int wxDateTime::GetCentury(int year
) 
 684     return year 
> 0 ? year 
/ 100 : year 
/ 100 - 1; 
 688 int wxDateTime::ConvertYearToBC(int year
) 
 691     return year 
> 0 ? year 
: year 
- 1; 
 695 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
) 
 700             return Now().GetYear(); 
 703             wxFAIL_MSG(_T("TODO")); 
 707             wxFAIL_MSG(_T("unsupported calendar")); 
 715 wxDateTime::Month 
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
) 
 720             return Now().GetMonth(); 
 723             wxFAIL_MSG(_T("TODO")); 
 727             wxFAIL_MSG(_T("unsupported calendar")); 
 735 wxDateTime::wxDateTime_t 
wxDateTime::GetNumberOfDays(int year
, Calendar cal
) 
 737     if ( year 
== Inv_Year 
) 
 739         // take the current year if none given 
 740         year 
= GetCurrentYear(); 
 747             return IsLeapYear(year
) ? 366 : 365; 
 750             wxFAIL_MSG(_T("unsupported calendar")); 
 758 wxDateTime::wxDateTime_t 
wxDateTime::GetNumberOfDays(wxDateTime::Month month
, 
 760                                                      wxDateTime::Calendar cal
) 
 762     wxCHECK_MSG( month 
< MONTHS_IN_YEAR
, 0, _T("invalid month") ); 
 764     if ( cal 
== Gregorian 
|| cal 
== Julian 
) 
 766         if ( year 
== Inv_Year 
) 
 768             // take the current year if none given 
 769             year 
= GetCurrentYear(); 
 772         return GetNumOfDaysInMonth(year
, month
); 
 776         wxFAIL_MSG(_T("unsupported calendar")); 
 783 wxString 
wxDateTime::GetMonthName(wxDateTime::Month month
, 
 784                                   wxDateTime::NameFlags flags
) 
 786     wxCHECK_MSG( month 
!= Inv_Month
, _T(""), _T("invalid month") ); 
 788     // notice that we must set all the fields to avoid confusing libc (GNU one 
 789     // gets confused to a crash if we don't do this) 
 794     return CallStrftime(flags 
== Name_Abbr 
? _T("%b") : _T("%B"), &tm
); 
 798 wxString 
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
, 
 799                                     wxDateTime::NameFlags flags
) 
 801     wxCHECK_MSG( wday 
!= Inv_WeekDay
, _T(""), _T("invalid weekday") ); 
 803     // take some arbitrary Sunday 
 810     // and offset it by the number of days needed to get the correct wday 
 813     // call mktime() to normalize it... 
 816     // ... and call strftime() 
 817     return CallStrftime(flags 
== Name_Abbr 
? _T("%a") : _T("%A"), &tm
); 
 821 void wxDateTime::GetAmPmStrings(wxString 
*am
, wxString 
*pm
) 
 827         *am 
= CallStrftime(_T("%p"), &tm
); 
 832         *pm 
= CallStrftime(_T("%p"), &tm
); 
 836 // ---------------------------------------------------------------------------- 
 837 // Country stuff: date calculations depend on the country (DST, work days, 
 838 // ...), so we need to know which rules to follow. 
 839 // ---------------------------------------------------------------------------- 
 842 wxDateTime::Country 
wxDateTime::GetCountry() 
 844     // TODO use LOCALE_ICOUNTRY setting under Win32 
 846     if ( ms_country 
== Country_Unknown 
) 
 848         // try to guess from the time zone name 
 849         time_t t 
= time(NULL
); 
 850         struct tm 
*tm 
= localtime(&t
); 
 852         wxString tz 
= CallStrftime(_T("%Z"), tm
); 
 853         if ( tz 
== _T("WET") || tz 
== _T("WEST") ) 
 857         else if ( tz 
== _T("CET") || tz 
== _T("CEST") ) 
 859             ms_country 
= Country_EEC
; 
 861         else if ( tz 
== _T("MSK") || tz 
== _T("MSD") ) 
 865         else if ( tz 
== _T("AST") || tz 
== _T("ADT") || 
 866                   tz 
== _T("EST") || tz 
== _T("EDT") || 
 867                   tz 
== _T("CST") || tz 
== _T("CDT") || 
 868                   tz 
== _T("MST") || tz 
== _T("MDT") || 
 869                   tz 
== _T("PST") || tz 
== _T("PDT") ) 
 875             // well, choose a default one 
 884 void wxDateTime::SetCountry(wxDateTime::Country country
) 
 886     ms_country 
= country
; 
 890 bool wxDateTime::IsWestEuropeanCountry(Country country
) 
 892     if ( country 
== Country_Default 
) 
 894         country 
= GetCountry(); 
 897     return (Country_WesternEurope_Start 
<= country
) && 
 898            (country 
<= Country_WesternEurope_End
); 
 901 // ---------------------------------------------------------------------------- 
 902 // DST calculations: we use 3 different rules for the West European countries, 
 903 // USA and for the rest of the world. This is undoubtedly false for many 
 904 // countries, but I lack the necessary info (and the time to gather it), 
 905 // please add the other rules here! 
 906 // ---------------------------------------------------------------------------- 
 909 bool wxDateTime::IsDSTApplicable(int year
, Country country
) 
 911     if ( year 
== Inv_Year 
) 
 913         // take the current year if none given 
 914         year 
= GetCurrentYear(); 
 917     if ( country 
== Country_Default 
) 
 919         country 
= GetCountry(); 
 926             // DST was first observed in the US and UK during WWI, reused 
 927             // during WWII and used again since 1966 
 928             return year 
>= 1966 || 
 929                    (year 
>= 1942 && year 
<= 1945) || 
 930                    (year 
== 1918 || year 
== 1919); 
 933             // assume that it started after WWII 
 939 wxDateTime 
wxDateTime::GetBeginDST(int year
, Country country
) 
 941     if ( year 
== Inv_Year 
) 
 943         // take the current year if none given 
 944         year 
= GetCurrentYear(); 
 947     if ( country 
== Country_Default 
) 
 949         country 
= GetCountry(); 
 952     if ( !IsDSTApplicable(year
, country
) ) 
 954         return wxInvalidDateTime
; 
 959     if ( IsWestEuropeanCountry(country
) || (country 
== Russia
) ) 
 961         // DST begins at 1 a.m. GMT on the last Sunday of March 
 962         if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) ) 
 965             wxFAIL_MSG( _T("no last Sunday in March?") ); 
 968         dt 
+= wxTimeSpan::Hours(1); 
 970         // disable DST tests because it could result in an infinite recursion! 
 973     else switch ( country 
) 
 980                     // don't know for sure - assume it was in effect all year 
 985                     dt
.Set(1, Jan
, year
); 
 989                     // DST was installed Feb 2, 1942 by the Congress 
 990                     dt
.Set(2, Feb
, year
); 
 993                     // Oil embargo changed the DST period in the US 
 995                     dt
.Set(6, Jan
, 1974); 
 999                     dt
.Set(23, Feb
, 1975); 
1003                     // before 1986, DST begun on the last Sunday of April, but 
1004                     // in 1986 Reagan changed it to begin at 2 a.m. of the 
1005                     // first Sunday in April 
1008                         if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) ) 
1011                             wxFAIL_MSG( _T("no first Sunday in April?") ); 
1016                         if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) ) 
1019                             wxFAIL_MSG( _T("no first Sunday in April?") ); 
1023                     dt 
+= wxTimeSpan::Hours(2); 
1025                     // TODO what about timezone?? 
1031             // assume Mar 30 as the start of the DST for the rest of the world 
1032             // - totally bogus, of course 
1033             dt
.Set(30, Mar
, year
); 
1040 wxDateTime 
wxDateTime::GetEndDST(int year
, Country country
) 
1042     if ( year 
== Inv_Year 
) 
1044         // take the current year if none given 
1045         year 
= GetCurrentYear(); 
1048     if ( country 
== Country_Default 
) 
1050         country 
= GetCountry(); 
1053     if ( !IsDSTApplicable(year
, country
) ) 
1055         return wxInvalidDateTime
; 
1060     if ( IsWestEuropeanCountry(country
) || (country 
== Russia
) ) 
1062         // DST ends at 1 a.m. GMT on the last Sunday of October 
1063         if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) ) 
1065             // weirder and weirder... 
1066             wxFAIL_MSG( _T("no last Sunday in October?") ); 
1069         dt 
+= wxTimeSpan::Hours(1); 
1071         // disable DST tests because it could result in an infinite recursion! 
1074     else switch ( country 
) 
1081                     // don't know for sure - assume it was in effect all year 
1085                     dt
.Set(31, Dec
, year
); 
1089                     // the time was reset after the end of the WWII 
1090                     dt
.Set(30, Sep
, year
); 
1094                     // DST ends at 2 a.m. on the last Sunday of October 
1095                     if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) ) 
1097                         // weirder and weirder... 
1098                         wxFAIL_MSG( _T("no last Sunday in October?") ); 
1101                     dt 
+= wxTimeSpan::Hours(2); 
1103                     // TODO what about timezone?? 
1108             // assume October 26th as the end of the DST - totally bogus too 
1109             dt
.Set(26, Oct
, year
); 
1115 // ---------------------------------------------------------------------------- 
1116 // constructors and assignment operators 
1117 // ---------------------------------------------------------------------------- 
1119 // return the current time with ms precision 
1120 /* static */ wxDateTime 
wxDateTime::UNow() 
1122     return wxDateTime(wxGetLocalTimeMillis()); 
1125 // the values in the tm structure contain the local time 
1126 wxDateTime
& wxDateTime::Set(const struct tm
& tm
) 
1129     time_t timet 
= mktime(&tm2
); 
1131     if ( timet 
== (time_t)-1 ) 
1133         // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is 
1134         // less than timezone - try to make it work for this case 
1135         if ( tm2
.tm_year 
== 70 && tm2
.tm_mon 
== 0 && tm2
.tm_mday 
== 1 ) 
1137             // add timezone to make sure that date is in range 
1138             tm2
.tm_sec 
-= GetTimeZone(); 
1140             timet 
= mktime(&tm2
); 
1141             if ( timet 
!= (time_t)-1 ) 
1143                 timet 
+= GetTimeZone(); 
1149         wxFAIL_MSG( _T("mktime() failed") ); 
1151         *this = wxInvalidDateTime
; 
1161 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
, 
1162                             wxDateTime_t minute
, 
1163                             wxDateTime_t second
, 
1164                             wxDateTime_t millisec
) 
1166     // we allow seconds to be 61 to account for the leap seconds, even if we 
1167     // don't use them really 
1168     wxDATETIME_CHECK( hour 
< 24 && 
1172                       _T("Invalid time in wxDateTime::Set()") ); 
1174     // get the current date from system 
1175     struct tm 
*tm 
= GetTmNow(); 
1177     wxDATETIME_CHECK( tm
, _T("localtime() failed") ); 
1181     tm
->tm_min 
= minute
; 
1182     tm
->tm_sec 
= second
; 
1186     // and finally adjust milliseconds 
1187     return SetMillisecond(millisec
); 
1190 wxDateTime
& wxDateTime::Set(wxDateTime_t day
, 
1194                             wxDateTime_t minute
, 
1195                             wxDateTime_t second
, 
1196                             wxDateTime_t millisec
) 
1198     wxDATETIME_CHECK( hour 
< 24 && 
1202                       _T("Invalid time in wxDateTime::Set()") ); 
1204     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
1206     wxDATETIME_CHECK( (0 < day
) && (day 
<= GetNumberOfDays(month
, year
)), 
1207                       _T("Invalid date in wxDateTime::Set()") ); 
1209     // the range of time_t type (inclusive) 
1210     static const int yearMinInRange 
= 1970; 
1211     static const int yearMaxInRange 
= 2037; 
1213     // test only the year instead of testing for the exact end of the Unix 
1214     // time_t range - it doesn't bring anything to do more precise checks 
1215     if ( year 
>= yearMinInRange 
&& year 
<= yearMaxInRange 
) 
1217         // use the standard library version if the date is in range - this is 
1218         // probably more efficient than our code 
1220         tm
.tm_year 
= year 
- 1900; 
1226         tm
.tm_isdst 
= -1;       // mktime() will guess it 
1230         // and finally adjust milliseconds 
1231         return SetMillisecond(millisec
); 
1235         // do time calculations ourselves: we want to calculate the number of 
1236         // milliseconds between the given date and the epoch 
1238         // get the JDN for the midnight of this day 
1239         m_time 
= GetTruncatedJDN(day
, month
, year
); 
1240         m_time 
-= EPOCH_JDN
; 
1241         m_time 
*= SECONDS_PER_DAY 
* TIME_T_FACTOR
; 
1243         // JDN corresponds to GMT, we take localtime 
1244         Add(wxTimeSpan(hour
, minute
, second 
+ GetTimeZone(), millisec
)); 
1250 wxDateTime
& wxDateTime::Set(double jdn
) 
1252     // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn 
1254     jdn 
-= EPOCH_JDN 
+ 0.5; 
1256     jdn 
*= MILLISECONDS_PER_DAY
; 
1263 wxDateTime
& wxDateTime::ResetTime() 
1267     if ( tm
.hour 
|| tm
.min 
|| tm
.sec 
|| tm
.msec 
) 
1280 // ---------------------------------------------------------------------------- 
1281 // DOS Date and Time Format functions 
1282 // ---------------------------------------------------------------------------- 
1283 // the dos date and time value is an unsigned 32 bit value in the format: 
1284 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss 
1286 // Y = year offset from 1980 (0-127) 
1288 // D = day of month (1-31) 
1290 // m = minute (0-59) 
1291 // s = bisecond (0-29) each bisecond indicates two seconds 
1292 // ---------------------------------------------------------------------------- 
1294 wxDateTime
& wxDateTime::SetFromDOS(unsigned long ddt
) 
1298     long year 
= ddt 
& 0xFE000000; 
1303     long month 
= ddt 
& 0x1E00000; 
1308     long day 
= ddt 
& 0x1F0000; 
1312     long hour 
= ddt 
& 0xF800; 
1316     long minute 
= ddt 
& 0x7E0; 
1320     long second 
= ddt 
& 0x1F; 
1321     tm
.tm_sec 
= second 
* 2; 
1323     return Set(mktime(&tm
)); 
1326 unsigned long wxDateTime::GetAsDOS() const 
1329     time_t ticks 
= GetTicks(); 
1330     struct tm 
*tm 
= localtime(&ticks
); 
1332     long year 
= tm
->tm_year
; 
1336     long month 
= tm
->tm_mon
; 
1340     long day 
= tm
->tm_mday
; 
1343     long hour 
= tm
->tm_hour
; 
1346     long minute 
= tm
->tm_min
; 
1349     long second 
= tm
->tm_sec
; 
1352     ddt 
= year 
| month 
| day 
| hour 
| minute 
| second
; 
1356 // ---------------------------------------------------------------------------- 
1357 // time_t <-> broken down time conversions 
1358 // ---------------------------------------------------------------------------- 
1360 wxDateTime::Tm 
wxDateTime::GetTm(const TimeZone
& tz
) const 
1362     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1364     time_t time 
= GetTicks(); 
1365     if ( time 
!= (time_t)-1 ) 
1367         // use C RTL functions 
1369         if ( tz
.GetOffset() == -GetTimeZone() ) 
1371             // we are working with local time 
1372             tm 
= localtime(&time
); 
1374             // should never happen 
1375             wxCHECK_MSG( tm
, Tm(), _T("localtime() failed") ); 
1379             time 
+= (time_t)tz
.GetOffset(); 
1380 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning 
1381             int time2 
= (int) time
; 
1389                 // should never happen 
1390                 wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") ); 
1394                 tm 
= (struct tm 
*)NULL
; 
1400             // adjust the milliseconds 
1402             long timeOnly 
= (m_time 
% MILLISECONDS_PER_DAY
).ToLong(); 
1403             tm2
.msec 
= (wxDateTime_t
)(timeOnly 
% 1000); 
1406         //else: use generic code below 
1409     // remember the time and do the calculations with the date only - this 
1410     // eliminates rounding errors of the floating point arithmetics 
1412     wxLongLong timeMidnight 
= m_time 
+ tz
.GetOffset() * 1000; 
1414     long timeOnly 
= (timeMidnight 
% MILLISECONDS_PER_DAY
).ToLong(); 
1416     // we want to always have positive time and timeMidnight to be really 
1417     // the midnight before it 
1420         timeOnly 
= MILLISECONDS_PER_DAY 
+ timeOnly
; 
1423     timeMidnight 
-= timeOnly
; 
1425     // calculate the Gregorian date from JDN for the midnight of our date: 
1426     // this will yield day, month (in 1..12 range) and year 
1428     // actually, this is the JDN for the noon of the previous day 
1429     long jdn 
= (timeMidnight 
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
; 
1431     // CREDIT: code below is by Scott E. Lee (but bugs are mine) 
1433     wxASSERT_MSG( jdn 
> -2, _T("JDN out of range") ); 
1435     // calculate the century 
1436     long temp 
= (jdn 
+ JDN_OFFSET
) * 4 - 1; 
1437     long century 
= temp 
/ DAYS_PER_400_YEARS
; 
1439     // then the year and day of year (1 <= dayOfYear <= 366) 
1440     temp 
= ((temp 
% DAYS_PER_400_YEARS
) / 4) * 4 + 3; 
1441     long year 
= (century 
* 100) + (temp 
/ DAYS_PER_4_YEARS
); 
1442     long dayOfYear 
= (temp 
% DAYS_PER_4_YEARS
) / 4 + 1; 
1444     // and finally the month and day of the month 
1445     temp 
= dayOfYear 
* 5 - 3; 
1446     long month 
= temp 
/ DAYS_PER_5_MONTHS
; 
1447     long day 
= (temp 
% DAYS_PER_5_MONTHS
) / 5 + 1; 
1449     // month is counted from March - convert to normal 
1460     // year is offset by 4800 
1463     // check that the algorithm gave us something reasonable 
1464     wxASSERT_MSG( (0 < month
) && (month 
<= 12), _T("invalid month") ); 
1465     wxASSERT_MSG( (1 <= day
) && (day 
< 32), _T("invalid day") ); 
1467     // construct Tm from these values 
1469     tm
.year 
= (int)year
; 
1470     tm
.mon 
= (Month
)(month 
- 1); // algorithm yields 1 for January, not 0 
1471     tm
.mday 
= (wxDateTime_t
)day
; 
1472     tm
.msec 
= (wxDateTime_t
)(timeOnly 
% 1000); 
1473     timeOnly 
-= tm
.msec
; 
1474     timeOnly 
/= 1000;               // now we have time in seconds 
1476     tm
.sec 
= (wxDateTime_t
)(timeOnly 
% 60); 
1478     timeOnly 
/= 60;                 // now we have time in minutes 
1480     tm
.min 
= (wxDateTime_t
)(timeOnly 
% 60); 
1483     tm
.hour 
= (wxDateTime_t
)(timeOnly 
/ 60); 
1488 wxDateTime
& wxDateTime::SetYear(int year
) 
1490     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1499 wxDateTime
& wxDateTime::SetMonth(Month month
) 
1501     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1510 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
) 
1512     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1521 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
) 
1523     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1532 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
) 
1534     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1543 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
) 
1545     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1554 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
) 
1556     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1558     // we don't need to use GetTm() for this one 
1559     m_time 
-= m_time 
% 1000l; 
1560     m_time 
+= millisecond
; 
1565 // ---------------------------------------------------------------------------- 
1566 // wxDateTime arithmetics 
1567 // ---------------------------------------------------------------------------- 
1569 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
) 
1573     tm
.year 
+= diff
.GetYears(); 
1574     tm
.AddMonths(diff
.GetMonths()); 
1576     // check that the resulting date is valid 
1577     if ( tm
.mday 
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) ) 
1579         // We suppose that when adding one month to Jan 31 we want to get Feb 
1580         // 28 (or 29), i.e. adding a month to the last day of the month should 
1581         // give the last day of the next month which is quite logical. 
1583         // Unfortunately, there is no logic way to understand what should 
1584         // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)? 
1585         // We make it Feb 28 (last day too), but it is highly questionable. 
1586         tm
.mday 
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
); 
1589     tm
.AddDays(diff
.GetTotalDays()); 
1593     wxASSERT_MSG( IsSameTime(tm
), 
1594                   _T("Add(wxDateSpan) shouldn't modify time") ); 
1599 // ---------------------------------------------------------------------------- 
1600 // Weekday and monthday stuff 
1601 // ---------------------------------------------------------------------------- 
1603 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
, 
1607     wxASSERT_MSG( numWeek 
> 0, 
1608                   _T("invalid week number: weeks are counted from 1") ); 
1610     int year 
= GetYear(); 
1612     // Jan 4 always lies in the 1st week of the year 
1614     SetToWeekDayInSameWeek(weekday
, flags
) += wxDateSpan::Weeks(numWeek 
- 1); 
1616     if ( GetYear() != year 
) 
1618         // oops... numWeek was too big 
1625 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
, 
1628     // take the current month/year if none specified 
1629     if ( year 
== Inv_Year 
) 
1631     if ( month 
== Inv_Month 
) 
1634     return Set(GetNumOfDaysInMonth(year
, month
), month
, year
); 
1637 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
, WeekFlags flags
) 
1639     wxDATETIME_CHECK( weekday 
!= Inv_WeekDay
, _T("invalid weekday") ); 
1641     int wdayThis 
= GetWeekDay(); 
1642     if ( weekday 
== wdayThis 
) 
1648     if ( flags 
== Default_First 
) 
1650         flags 
= GetCountry() == USA 
? Sunday_First 
: Monday_First
; 
1653     // the logic below based on comparing weekday and wdayThis works if Sun (0) 
1654     // is the first day in the week, but breaks down for Monday_First case so 
1655     // we adjust the week days in this case 
1656     if( flags 
== Monday_First 
) 
1658         if ( wdayThis 
== Sun 
) 
1661     //else: Sunday_First, nothing to do 
1663     // go forward or back in time to the day we want 
1664     if ( weekday 
< wdayThis 
) 
1666         return Subtract(wxDateSpan::Days(wdayThis 
- weekday
)); 
1668     else // weekday > wdayThis 
1670         return Add(wxDateSpan::Days(weekday 
- wdayThis
)); 
1674 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
) 
1676     wxDATETIME_CHECK( weekday 
!= Inv_WeekDay
, _T("invalid weekday") ); 
1679     WeekDay wdayThis 
= GetWeekDay(); 
1680     if ( weekday 
== wdayThis 
) 
1685     else if ( weekday 
< wdayThis 
) 
1687         // need to advance a week 
1688         diff 
= 7 - (wdayThis 
- weekday
); 
1690     else // weekday > wdayThis 
1692         diff 
= weekday 
- wdayThis
; 
1695     return Add(wxDateSpan::Days(diff
)); 
1698 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
) 
1700     wxDATETIME_CHECK( weekday 
!= Inv_WeekDay
, _T("invalid weekday") ); 
1703     WeekDay wdayThis 
= GetWeekDay(); 
1704     if ( weekday 
== wdayThis 
) 
1709     else if ( weekday 
> wdayThis 
) 
1711         // need to go to previous week 
1712         diff 
= 7 - (weekday 
- wdayThis
); 
1714     else // weekday < wdayThis 
1716         diff 
= wdayThis 
- weekday
; 
1719     return Subtract(wxDateSpan::Days(diff
)); 
1722 bool wxDateTime::SetToWeekDay(WeekDay weekday
, 
1727     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, FALSE
, _T("invalid weekday") ); 
1729     // we don't check explicitly that -5 <= n <= 5 because we will return FALSE 
1730     // anyhow in such case - but may be should still give an assert for it? 
1732     // take the current month/year if none specified 
1733     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
1737     // TODO this probably could be optimised somehow... 
1741         // get the first day of the month 
1742         dt
.Set(1, month
, year
); 
1745         WeekDay wdayFirst 
= dt
.GetWeekDay(); 
1747         // go to the first weekday of the month 
1748         int diff 
= weekday 
- wdayFirst
; 
1752         // add advance n-1 weeks more 
1755         dt 
+= wxDateSpan::Days(diff
); 
1757     else // count from the end of the month 
1759         // get the last day of the month 
1760         dt
.SetToLastMonthDay(month
, year
); 
1763         WeekDay wdayLast 
= dt
.GetWeekDay(); 
1765         // go to the last weekday of the month 
1766         int diff 
= wdayLast 
- weekday
; 
1770         // and rewind n-1 weeks from there 
1773         dt 
-= wxDateSpan::Days(diff
); 
1776     // check that it is still in the same month 
1777     if ( dt
.GetMonth() == month 
) 
1785         // no such day in this month 
1790 wxDateTime::wxDateTime_t 
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const 
1794     return gs_cumulatedDays
[IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
; 
1797 wxDateTime::wxDateTime_t 
wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, 
1798                                                    const TimeZone
& tz
) const 
1800     if ( flags 
== Default_First 
) 
1802         flags 
= GetCountry() == USA 
? Sunday_First 
: Monday_First
; 
1805     wxDateTime_t nDayInYear 
= GetDayOfYear(tz
); 
1808     WeekDay wd 
= GetWeekDay(tz
); 
1809     if ( flags 
== Sunday_First 
) 
1811         week 
= (nDayInYear 
- wd 
+ 7) / 7; 
1815         // have to shift the week days values 
1816         week 
= (nDayInYear 
- (wd 
- 1 + 7) % 7 + 7) / 7; 
1819     // FIXME some more elegant way?? 
1820     WeekDay wdYearStart 
= wxDateTime(1, Jan
, GetYear()).GetWeekDay(); 
1821     if ( wdYearStart 
== Wed 
|| wdYearStart 
== Thu 
) 
1829 wxDateTime::wxDateTime_t 
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
, 
1830                                                     const TimeZone
& tz
) const 
1833     wxDateTime dtMonthStart 
= wxDateTime(1, tm
.mon
, tm
.year
); 
1834     int nWeek 
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1; 
1837         // this may happen for January when Jan, 1 is the last week of the 
1839         nWeek 
+= IsLeapYear(tm
.year 
- 1) ? 53 : 52; 
1842     return (wxDateTime::wxDateTime_t
)nWeek
; 
1845 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
) 
1847     int year 
= GetYear(); 
1848     wxDATETIME_CHECK( (0 < yday
) && (yday 
<= GetNumberOfDays(year
)), 
1849                       _T("invalid year day") ); 
1851     bool isLeap 
= IsLeapYear(year
); 
1852     for ( Month mon 
= Jan
; mon 
< Inv_Month
; wxNextMonth(mon
) ) 
1854         // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we 
1855         // don't need it neither - because of the CHECK above we know that 
1856         // yday lies in December then 
1857         if ( (mon 
== Dec
) || (yday 
< gs_cumulatedDays
[isLeap
][mon 
+ 1]) ) 
1859             Set(yday 
- gs_cumulatedDays
[isLeap
][mon
], mon
, year
); 
1868 // ---------------------------------------------------------------------------- 
1869 // Julian day number conversion and related stuff 
1870 // ---------------------------------------------------------------------------- 
1872 double wxDateTime::GetJulianDayNumber() const 
1874     // JDN are always expressed for the GMT dates 
1875     Tm 
tm(ToTimezone(GMT0
).GetTm(GMT0
)); 
1877     double result 
= GetTruncatedJDN(tm
.mday
, tm
.mon
, tm
.year
); 
1879     // add the part GetTruncatedJDN() neglected 
1882     // and now add the time: 86400 sec = 1 JDN 
1883     return result 
+ ((double)(60*(60*tm
.hour 
+ tm
.min
) + tm
.sec
)) / 86400; 
1886 double wxDateTime::GetRataDie() const 
1888     // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5 
1889     return GetJulianDayNumber() - 1721119.5 - 306; 
1892 // ---------------------------------------------------------------------------- 
1893 // timezone and DST stuff 
1894 // ---------------------------------------------------------------------------- 
1896 int wxDateTime::IsDST(wxDateTime::Country country
) const 
1898     wxCHECK_MSG( country 
== Country_Default
, -1, 
1899                  _T("country support not implemented") ); 
1901     // use the C RTL for the dates in the standard range 
1902     time_t timet 
= GetTicks(); 
1903     if ( timet 
!= (time_t)-1 ) 
1905         tm 
*tm 
= localtime(&timet
); 
1907         wxCHECK_MSG( tm
, -1, _T("localtime() failed") ); 
1909         return tm
->tm_isdst
; 
1913         int year 
= GetYear(); 
1915         if ( !IsDSTApplicable(year
, country
) ) 
1917             // no DST time in this year in this country 
1921         return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
)); 
1925 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
) 
1927     long secDiff 
= GetTimeZone() + tz
.GetOffset(); 
1929     // we need to know whether DST is or not in effect for this date unless 
1930     // the test disabled by the caller 
1931     if ( !noDST 
&& (IsDST() == 1) ) 
1933         // FIXME we assume that the DST is always shifted by 1 hour 
1937     return Subtract(wxTimeSpan::Seconds(secDiff
)); 
1940 // ---------------------------------------------------------------------------- 
1941 // wxDateTime to/from text representations 
1942 // ---------------------------------------------------------------------------- 
1944 wxString 
wxDateTime::Format(const wxChar 
*format
, const TimeZone
& tz
) const 
1946     wxCHECK_MSG( format
, _T(""), _T("NULL format in wxDateTime::Format") ); 
1948     // we have to use our own implementation if the date is out of range of 
1949     // strftime() or if we use non standard specificators 
1950     time_t time 
= GetTicks(); 
1951     if ( (time 
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) ) 
1955         if ( tz
.GetOffset() == -GetTimeZone() ) 
1957             // we are working with local time 
1958             tm 
= localtime(&time
); 
1960             // should never happen 
1961             wxCHECK_MSG( tm
, wxEmptyString
, _T("localtime() failed") ); 
1965             time 
+= (int)tz
.GetOffset(); 
1967 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning 
1968             int time2 
= (int) time
; 
1976                 // should never happen 
1977                 wxCHECK_MSG( tm
, wxEmptyString
, _T("gmtime() failed") ); 
1981                 tm 
= (struct tm 
*)NULL
; 
1987             return CallStrftime(format
, tm
); 
1989         //else: use generic code below 
1992     // we only parse ANSI C format specifications here, no POSIX 2 
1993     // complications, no GNU extensions but we do add support for a "%l" format 
1994     // specifier allowing to get the number of milliseconds 
1997     // used for calls to strftime() when we only deal with time 
1998     struct tm tmTimeOnly
; 
1999     tmTimeOnly
.tm_hour 
= tm
.hour
; 
2000     tmTimeOnly
.tm_min 
= tm
.min
; 
2001     tmTimeOnly
.tm_sec 
= tm
.sec
; 
2002     tmTimeOnly
.tm_wday 
= 0; 
2003     tmTimeOnly
.tm_yday 
= 0; 
2004     tmTimeOnly
.tm_mday 
= 1;         // any date will do 
2005     tmTimeOnly
.tm_mon 
= 0; 
2006     tmTimeOnly
.tm_year 
= 76; 
2007     tmTimeOnly
.tm_isdst 
= 0;        // no DST, we adjust for tz ourselves 
2009     wxString tmp
, res
, fmt
; 
2010     for ( const wxChar 
*p 
= format
; *p
; p
++ ) 
2012         if ( *p 
!= _T('%') ) 
2020         // set the default format 
2023             case _T('Y'):               // year has 4 digits 
2027             case _T('j'):               // day of year has 3 digits 
2028             case _T('l'):               // milliseconds have 3 digits 
2032             case _T('w'):               // week day as number has only one 
2037                 // it's either another valid format specifier in which case 
2038                 // the format is "%02d" (for all the rest) or we have the 
2039                 // field width preceding the format in which case it will 
2040                 // override the default format anyhow 
2044         bool restart 
= TRUE
; 
2049             // start of the format specification 
2052                 case _T('a'):       // a weekday name 
2054                     // second parameter should be TRUE for abbreviated names 
2055                     res 
+= GetWeekDayName(tm
.GetWeekDay(), 
2056                                           *p 
== _T('a') ? Name_Abbr 
: Name_Full
); 
2059                 case _T('b'):       // a month name 
2061                     res 
+= GetMonthName(tm
.mon
, 
2062                                         *p 
== _T('b') ? Name_Abbr 
: Name_Full
); 
2065                 case _T('c'):       // locale default date and time  representation 
2066                 case _T('x'):       // locale default date representation 
2068                     // the problem: there is no way to know what do these format 
2069                     // specifications correspond to for the current locale. 
2071                     // the solution: use a hack and still use strftime(): first 
2072                     // find the YEAR which is a year in the strftime() range (1970 
2073                     // - 2038) whose Jan 1 falls on the same week day as the Jan 1 
2074                     // of the real year. Then make a copy of the format and 
2075                     // replace all occurences of YEAR in it with some unique 
2076                     // string not appearing anywhere else in it, then use 
2077                     // strftime() to format the date in year YEAR and then replace 
2078                     // YEAR back by the real year and the unique replacement 
2079                     // string back with YEAR. Notice that "all occurences of YEAR" 
2080                     // means all occurences of 4 digit as well as 2 digit form! 
2082                     // the bugs: we assume that neither of %c nor %x contains any 
2083                     // fields which may change between the YEAR and real year. For 
2084                     // example, the week number (%U, %W) and the day number (%j) 
2085                     // will change if one of these years is leap and the other one 
2088                         // find the YEAR: normally, for any year X, Jan 1 or the 
2089                         // year X + 28 is the same weekday as Jan 1 of X (because 
2090                         // the weekday advances by 1 for each normal X and by 2 
2091                         // for each leap X, hence by 5 every 4 years or by 35 
2092                         // which is 0 mod 7 every 28 years) but this rule breaks 
2093                         // down if there are years between X and Y which are 
2094                         // divisible by 4 but not leap (i.e. divisible by 100 but 
2095                         // not 400), hence the correction. 
2097                         int yearReal 
= GetYear(tz
); 
2098                         int mod28 
= yearReal 
% 28; 
2100                         // be careful to not go too far - we risk to leave the 
2105                             year 
= 1988 + mod28
;      // 1988 == 0 (mod 28) 
2109                             year 
= 1970 + mod28 
- 10; // 1970 == 10 (mod 28) 
2112                         int nCentury 
= year 
/ 100, 
2113                             nCenturyReal 
= yearReal 
/ 100; 
2115                         // need to adjust for the years divisble by 400 which are 
2116                         // not leap but are counted like leap ones if we just take 
2117                         // the number of centuries in between for nLostWeekDays 
2118                         int nLostWeekDays 
= (nCentury 
- nCenturyReal
) - 
2119                                             (nCentury 
/ 4 - nCenturyReal 
/ 4); 
2121                         // we have to gain back the "lost" weekdays: note that the 
2122                         // effect of this loop is to not do anything to 
2123                         // nLostWeekDays (which we won't use any more), but to 
2124                         // (indirectly) set the year correctly 
2125                         while ( (nLostWeekDays 
% 7) != 0 ) 
2127                             nLostWeekDays 
+= year
++ % 4 ? 1 : 2; 
2130                         // at any rate, we couldn't go further than 1988 + 9 + 28! 
2131                         wxASSERT_MSG( year 
< 2030, 
2132                                       _T("logic error in wxDateTime::Format") ); 
2134                         wxString strYear
, strYear2
; 
2135                         strYear
.Printf(_T("%d"), year
); 
2136                         strYear2
.Printf(_T("%d"), year 
% 100); 
2138                         // find two strings not occuring in format (this is surely 
2139                         // not optimal way of doing it... improvements welcome!) 
2140                         wxString fmt 
= format
; 
2141                         wxString replacement 
= (wxChar
)-1; 
2142                         while ( fmt
.Find(replacement
) != wxNOT_FOUND 
) 
2144                             replacement 
<< (wxChar
)-1; 
2147                         wxString replacement2 
= (wxChar
)-2; 
2148                         while ( fmt
.Find(replacement
) != wxNOT_FOUND 
) 
2150                             replacement 
<< (wxChar
)-2; 
2153                         // replace all occurences of year with it 
2154                         bool wasReplaced 
= fmt
.Replace(strYear
, replacement
) > 0; 
2156                             wasReplaced 
= fmt
.Replace(strYear2
, replacement2
) > 0; 
2158                         // use strftime() to format the same date but in supported 
2161                         // NB: we assume that strftime() doesn't check for the 
2162                         //     date validity and will happily format the date 
2163                         //     corresponding to Feb 29 of a non leap year (which 
2164                         //     may happen if yearReal was leap and year is not) 
2165                         struct tm tmAdjusted
; 
2167                         tmAdjusted
.tm_hour 
= tm
.hour
; 
2168                         tmAdjusted
.tm_min 
= tm
.min
; 
2169                         tmAdjusted
.tm_sec 
= tm
.sec
; 
2170                         tmAdjusted
.tm_wday 
= tm
.GetWeekDay(); 
2171                         tmAdjusted
.tm_yday 
= GetDayOfYear(); 
2172                         tmAdjusted
.tm_mday 
= tm
.mday
; 
2173                         tmAdjusted
.tm_mon 
= tm
.mon
; 
2174                         tmAdjusted
.tm_year 
= year 
- 1900; 
2175                         tmAdjusted
.tm_isdst 
= 0; // no DST, already adjusted 
2176                         wxString str 
= CallStrftime(*p 
== _T('c') ? _T("%c") 
2180                         // now replace the occurence of 1999 with the real year 
2181                         wxString strYearReal
, strYearReal2
; 
2182                         strYearReal
.Printf(_T("%04d"), yearReal
); 
2183                         strYearReal2
.Printf(_T("%02d"), yearReal 
% 100); 
2184                         str
.Replace(strYear
, strYearReal
); 
2185                         str
.Replace(strYear2
, strYearReal2
); 
2187                         // and replace back all occurences of replacement string 
2190                             str
.Replace(replacement2
, strYear2
); 
2191                             str
.Replace(replacement
, strYear
); 
2198                 case _T('d'):       // day of a month (01-31) 
2199                     res 
+= wxString::Format(fmt
, tm
.mday
); 
2202                 case _T('H'):       // hour in 24h format (00-23) 
2203                     res 
+= wxString::Format(fmt
, tm
.hour
); 
2206                 case _T('I'):       // hour in 12h format (01-12) 
2208                         // 24h -> 12h, 0h -> 12h too 
2209                         int hour12 
= tm
.hour 
> 12 ? tm
.hour 
- 12 
2210                                                   : tm
.hour 
? tm
.hour 
: 12; 
2211                         res 
+= wxString::Format(fmt
, hour12
); 
2215                 case _T('j'):       // day of the year 
2216                     res 
+= wxString::Format(fmt
, GetDayOfYear(tz
)); 
2219                 case _T('l'):       // milliseconds (NOT STANDARD) 
2220                     res 
+= wxString::Format(fmt
, GetMillisecond(tz
)); 
2223                 case _T('m'):       // month as a number (01-12) 
2224                     res 
+= wxString::Format(fmt
, tm
.mon 
+ 1); 
2227                 case _T('M'):       // minute as a decimal number (00-59) 
2228                     res 
+= wxString::Format(fmt
, tm
.min
); 
2231                 case _T('p'):       // AM or PM string 
2232                     res 
+= CallStrftime(_T("%p"), &tmTimeOnly
); 
2235                 case _T('S'):       // second as a decimal number (00-61) 
2236                     res 
+= wxString::Format(fmt
, tm
.sec
); 
2239                 case _T('U'):       // week number in the year (Sunday 1st week day) 
2240                     res 
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
)); 
2243                 case _T('W'):       // week number in the year (Monday 1st week day) 
2244                     res 
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
)); 
2247                 case _T('w'):       // weekday as a number (0-6), Sunday = 0 
2248                     res 
+= wxString::Format(fmt
, tm
.GetWeekDay()); 
2251                 // case _T('x'): -- handled with "%c" 
2253                 case _T('X'):       // locale default time representation 
2254                     // just use strftime() to format the time for us 
2255                     res 
+= CallStrftime(_T("%X"), &tmTimeOnly
); 
2258                 case _T('y'):       // year without century (00-99) 
2259                     res 
+= wxString::Format(fmt
, tm
.year 
% 100); 
2262                 case _T('Y'):       // year with century 
2263                     res 
+= wxString::Format(fmt
, tm
.year
); 
2266                 case _T('Z'):       // timezone name 
2267                     res 
+= CallStrftime(_T("%Z"), &tmTimeOnly
); 
2271                     // is it the format width? 
2273                     while ( *p 
== _T('-') || *p 
== _T('+') || 
2274                             *p 
== _T(' ') || wxIsdigit(*p
) ) 
2279                     if ( !fmt
.IsEmpty() ) 
2281                         // we've only got the flags and width so far in fmt 
2282                         fmt
.Prepend(_T('%')); 
2283                         fmt
.Append(_T('d')); 
2290                     // no, it wasn't the width 
2291                     wxFAIL_MSG(_T("unknown format specificator")); 
2293                     // fall through and just copy it nevertheless 
2295                 case _T('%'):       // a percent sign 
2299                 case 0:             // the end of string 
2300                     wxFAIL_MSG(_T("missing format at the end of string")); 
2302                     // just put the '%' which was the last char in format 
2312 // this function parses a string in (strict) RFC 822 format: see the section 5 
2313 // of the RFC for the detailed description, but briefly it's something of the 
2314 // form "Sat, 18 Dec 1999 00:48:30 +0100" 
2316 // this function is "strict" by design - it must reject anything except true 
2317 // RFC822 time specs. 
2319 // TODO a great candidate for using reg exps 
2320 const wxChar 
*wxDateTime::ParseRfc822Date(const wxChar
* date
) 
2322     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
2324     const wxChar 
*p 
= date
; 
2325     const wxChar 
*comma 
= wxStrchr(p
, _T(',')); 
2328         // the part before comma is the weekday 
2330         // skip it for now - we don't use but might check that it really 
2331         // corresponds to the specfied date 
2334         if ( *p 
!= _T(' ') ) 
2336             wxLogDebug(_T("no space after weekday in RFC822 time spec")); 
2338             return (wxChar 
*)NULL
; 
2344     // the following 1 or 2 digits are the day number 
2345     if ( !wxIsdigit(*p
) ) 
2347         wxLogDebug(_T("day number expected in RFC822 time spec, none found")); 
2349         return (wxChar 
*)NULL
; 
2352     wxDateTime_t day 
= *p
++ - _T('0'); 
2353     if ( wxIsdigit(*p
) ) 
2356         day 
+= *p
++ - _T('0'); 
2359     if ( *p
++ != _T(' ') ) 
2361         return (wxChar 
*)NULL
; 
2364     // the following 3 letters specify the month 
2365     wxString 
monName(p
, 3); 
2367     if ( monName 
== _T("Jan") ) 
2369     else if ( monName 
== _T("Feb") ) 
2371     else if ( monName 
== _T("Mar") ) 
2373     else if ( monName 
== _T("Apr") ) 
2375     else if ( monName 
== _T("May") ) 
2377     else if ( monName 
== _T("Jun") ) 
2379     else if ( monName 
== _T("Jul") ) 
2381     else if ( monName 
== _T("Aug") ) 
2383     else if ( monName 
== _T("Sep") ) 
2385     else if ( monName 
== _T("Oct") ) 
2387     else if ( monName 
== _T("Nov") ) 
2389     else if ( monName 
== _T("Dec") ) 
2393         wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str()); 
2395         return (wxChar 
*)NULL
; 
2400     if ( *p
++ != _T(' ') ) 
2402         return (wxChar 
*)NULL
; 
2406     if ( !wxIsdigit(*p
) ) 
2409         return (wxChar 
*)NULL
; 
2412     int year 
= *p
++ - _T('0'); 
2414     if ( !wxIsdigit(*p
) ) 
2416         // should have at least 2 digits in the year 
2417         return (wxChar 
*)NULL
; 
2421     year 
+= *p
++ - _T('0'); 
2423     // is it a 2 digit year (as per original RFC 822) or a 4 digit one? 
2424     if ( wxIsdigit(*p
) ) 
2427         year 
+= *p
++ - _T('0'); 
2429         if ( !wxIsdigit(*p
) ) 
2431             // no 3 digit years please 
2432             return (wxChar 
*)NULL
; 
2436         year 
+= *p
++ - _T('0'); 
2439     if ( *p
++ != _T(' ') ) 
2441         return (wxChar 
*)NULL
; 
2444     // time is in the format hh:mm:ss and seconds are optional 
2445     if ( !wxIsdigit(*p
) ) 
2447         return (wxChar 
*)NULL
; 
2450     wxDateTime_t hour 
= *p
++ - _T('0'); 
2452     if ( !wxIsdigit(*p
) ) 
2454         return (wxChar 
*)NULL
; 
2458     hour 
+= *p
++ - _T('0'); 
2460     if ( *p
++ != _T(':') ) 
2462         return (wxChar 
*)NULL
; 
2465     if ( !wxIsdigit(*p
) ) 
2467         return (wxChar 
*)NULL
; 
2470     wxDateTime_t min 
= *p
++ - _T('0'); 
2472     if ( !wxIsdigit(*p
) ) 
2474         return (wxChar 
*)NULL
; 
2478     min 
+= *p
++ - _T('0'); 
2480     wxDateTime_t sec 
= 0; 
2481     if ( *p
++ == _T(':') ) 
2483         if ( !wxIsdigit(*p
) ) 
2485             return (wxChar 
*)NULL
; 
2488         sec 
= *p
++ - _T('0'); 
2490         if ( !wxIsdigit(*p
) ) 
2492             return (wxChar 
*)NULL
; 
2496         sec 
+= *p
++ - _T('0'); 
2499     if ( *p
++ != _T(' ') ) 
2501         return (wxChar 
*)NULL
; 
2504     // and now the interesting part: the timezone 
2506     if ( *p 
== _T('-') || *p 
== _T('+') ) 
2508         // the explicit offset given: it has the form of hhmm 
2509         bool plus 
= *p
++ == _T('+'); 
2511         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
2513             return (wxChar 
*)NULL
; 
2517         offset 
= 60*(10*(*p 
- _T('0')) + (*(p 
+ 1) - _T('0'))); 
2521         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
2523             return (wxChar 
*)NULL
; 
2527         offset 
+= 10*(*p 
- _T('0')) + (*(p 
+ 1) - _T('0')); 
2538         // the symbolic timezone given: may be either military timezone or one 
2539         // of standard abbreviations 
2542             // military: Z = UTC, J unused, A = -1, ..., Y = +12 
2543             static const int offsets
[26] = 
2545                 //A  B   C   D   E   F   G   H   I    J    K    L    M 
2546                 -1, -2, -3, -4, -5, -6, -7, -8, -9,   0, -10, -11, -12, 
2547                 //N  O   P   R   Q   S   T   U   V    W    Z    Y    Z 
2548                 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0 
2551             if ( *p 
< _T('A') || *p 
> _T('Z') || *p 
== _T('J') ) 
2553                 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
); 
2555                 return (wxChar 
*)NULL
; 
2558             offset 
= offsets
[*p
++ - _T('A')]; 
2564             if ( tz 
== _T("UT") || tz 
== _T("UTC") || tz 
== _T("GMT") ) 
2566             else if ( tz 
== _T("AST") ) 
2567                 offset 
= AST 
- GMT0
; 
2568             else if ( tz 
== _T("ADT") ) 
2569                 offset 
= ADT 
- GMT0
; 
2570             else if ( tz 
== _T("EST") ) 
2571                 offset 
= EST 
- GMT0
; 
2572             else if ( tz 
== _T("EDT") ) 
2573                 offset 
= EDT 
- GMT0
; 
2574             else if ( tz 
== _T("CST") ) 
2575                 offset 
= CST 
- GMT0
; 
2576             else if ( tz 
== _T("CDT") ) 
2577                 offset 
= CDT 
- GMT0
; 
2578             else if ( tz 
== _T("MST") ) 
2579                 offset 
= MST 
- GMT0
; 
2580             else if ( tz 
== _T("MDT") ) 
2581                 offset 
= MDT 
- GMT0
; 
2582             else if ( tz 
== _T("PST") ) 
2583                 offset 
= PST 
- GMT0
; 
2584             else if ( tz 
== _T("PDT") ) 
2585                 offset 
= PDT 
- GMT0
; 
2588                 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
); 
2590                 return (wxChar 
*)NULL
; 
2600     // the spec was correct 
2601     Set(day
, mon
, year
, hour
, min
, sec
); 
2602     MakeTimezone((wxDateTime_t
)(60*offset
)); 
2607 const wxChar 
*wxDateTime::ParseFormat(const wxChar 
*date
, 
2608                                       const wxChar 
*format
, 
2609                                       const wxDateTime
& dateDef
) 
2611     wxCHECK_MSG( date 
&& format
, (wxChar 
*)NULL
, 
2612                  _T("NULL pointer in wxDateTime::ParseFormat()") ); 
2617     // what fields have we found? 
2618     bool haveWDay 
= FALSE
, 
2627     bool hourIsIn12hFormat 
= FALSE
, // or in 24h one? 
2628          isPM 
= FALSE
;              // AM by default 
2630     // and the value of the items we have (init them to get rid of warnings) 
2631     wxDateTime_t sec 
= 0, 
2634     WeekDay wday 
= Inv_WeekDay
; 
2635     wxDateTime_t yday 
= 0, 
2637     wxDateTime::Month mon 
= Inv_Month
; 
2640     const wxChar 
*input 
= date
; 
2641     for ( const wxChar 
*fmt 
= format
; *fmt
; fmt
++ ) 
2643         if ( *fmt 
!= _T('%') ) 
2645             if ( wxIsspace(*fmt
) ) 
2647                 // a white space in the format string matches 0 or more white 
2648                 // spaces in the input 
2649                 while ( wxIsspace(*input
) ) 
2656                 // any other character (not whitespace, not '%') must be 
2657                 // matched by itself in the input 
2658                 if ( *input
++ != *fmt 
) 
2661                     return (wxChar 
*)NULL
; 
2665             // done with this format char 
2669         // start of a format specification 
2671         // parse the optional width 
2673         while ( wxIsdigit(*++fmt
) ) 
2676             width 
+= *fmt 
- _T('0'); 
2679         // the default widths for the various fields 
2684                 case _T('Y'):               // year has 4 digits 
2688                 case _T('j'):               // day of year has 3 digits 
2689                 case _T('l'):               // milliseconds have 3 digits 
2693                 case _T('w'):               // week day as number has only one 
2698                     // default for all other fields 
2703         // then the format itself 
2706             case _T('a'):       // a weekday name 
2709                     int flag 
= *fmt 
== _T('a') ? Name_Abbr 
: Name_Full
; 
2710                     wday 
= GetWeekDayFromName(GetAlphaToken(input
), flag
); 
2711                     if ( wday 
== Inv_WeekDay 
) 
2714                         return (wxChar 
*)NULL
; 
2720             case _T('b'):       // a month name 
2723                     int flag 
= *fmt 
== _T('b') ? Name_Abbr 
: Name_Full
; 
2724                     mon 
= GetMonthFromName(GetAlphaToken(input
), flag
); 
2725                     if ( mon 
== Inv_Month 
) 
2728                         return (wxChar 
*)NULL
; 
2734             case _T('c'):       // locale default date and time  representation 
2738                     // this is the format which corresponds to ctime() output 
2739                     // and strptime("%c") should parse it, so try it first 
2740                     static const wxChar 
*fmtCtime 
= _T("%a %b %d %H:%M:%S %Y"); 
2742                     const wxChar 
*result 
= dt
.ParseFormat(input
, fmtCtime
); 
2745                         result 
= dt
.ParseFormat(input
, _T("%x %X")); 
2750                         result 
= dt
.ParseFormat(input
, _T("%X %x")); 
2755                         // we've tried everything and still no match 
2756                         return (wxChar 
*)NULL
; 
2761                     haveDay 
= haveMon 
= haveYear 
= 
2762                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2776             case _T('d'):       // day of a month (01-31) 
2777                 if ( !GetNumericToken(width
, input
, &num
) || 
2778                         (num 
> 31) || (num 
< 1) ) 
2781                     return (wxChar 
*)NULL
; 
2784                 // we can't check whether the day range is correct yet, will 
2785                 // do it later - assume ok for now 
2787                 mday 
= (wxDateTime_t
)num
; 
2790             case _T('H'):       // hour in 24h format (00-23) 
2791                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 23) ) 
2794                     return (wxChar 
*)NULL
; 
2798                 hour 
= (wxDateTime_t
)num
; 
2801             case _T('I'):       // hour in 12h format (01-12) 
2802                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 12) ) 
2805                     return (wxChar 
*)NULL
; 
2809                 hourIsIn12hFormat 
= TRUE
; 
2810                 hour 
= (wxDateTime_t
)(num 
% 12);        // 12 should be 0 
2813             case _T('j'):       // day of the year 
2814                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 366) ) 
2817                     return (wxChar 
*)NULL
; 
2821                 yday 
= (wxDateTime_t
)num
; 
2824             case _T('m'):       // month as a number (01-12) 
2825                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 12) ) 
2828                     return (wxChar 
*)NULL
; 
2832                 mon 
= (Month
)(num 
- 1); 
2835             case _T('M'):       // minute as a decimal number (00-59) 
2836                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 59) ) 
2839                     return (wxChar 
*)NULL
; 
2843                 min 
= (wxDateTime_t
)num
; 
2846             case _T('p'):       // AM or PM string 
2848                     wxString am
, pm
, token 
= GetAlphaToken(input
); 
2850                     GetAmPmStrings(&am
, &pm
); 
2851                     if ( token
.CmpNoCase(pm
) == 0 ) 
2855                     else if ( token
.CmpNoCase(am
) != 0 ) 
2858                         return (wxChar 
*)NULL
; 
2863             case _T('r'):       // time as %I:%M:%S %p 
2866                     input 
= dt
.ParseFormat(input
, _T("%I:%M:%S %p")); 
2870                         return (wxChar 
*)NULL
; 
2873                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2882             case _T('R'):       // time as %H:%M 
2885                     input 
= dt
.ParseFormat(input
, _T("%H:%M")); 
2889                         return (wxChar 
*)NULL
; 
2892                     haveHour 
= haveMin 
= TRUE
; 
2899             case _T('S'):       // second as a decimal number (00-61) 
2900                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 61) ) 
2903                     return (wxChar 
*)NULL
; 
2907                 sec 
= (wxDateTime_t
)num
; 
2910             case _T('T'):       // time as %H:%M:%S 
2913                     input 
= dt
.ParseFormat(input
, _T("%H:%M:%S")); 
2917                         return (wxChar 
*)NULL
; 
2920                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2929             case _T('w'):       // weekday as a number (0-6), Sunday = 0 
2930                 if ( !GetNumericToken(width
, input
, &num
) || (wday 
> 6) ) 
2933                     return (wxChar 
*)NULL
; 
2937                 wday 
= (WeekDay
)num
; 
2940             case _T('x'):       // locale default date representation 
2941 #ifdef HAVE_STRPTIME 
2942                 // try using strptime() -- it may fail even if the input is 
2943                 // correct but the date is out of range, so we will fall back 
2944                 // to our generic code anyhow 
2948                     const wxChar 
*result 
= CallStrptime(input
, "%x", &tm
); 
2953                         haveDay 
= haveMon 
= haveYear 
= TRUE
; 
2955                         year 
= 1900 + tm
.tm_year
; 
2956                         mon 
= (Month
)tm
.tm_mon
; 
2962 #endif // HAVE_STRPTIME 
2964                 // TODO query the LOCALE_IDATE setting under Win32 
2968                     wxString fmtDate
, fmtDateAlt
; 
2969                     if ( IsWestEuropeanCountry(GetCountry()) || 
2970                          GetCountry() == Russia 
) 
2972                         fmtDate 
= _T("%d/%m/%y"); 
2973                         fmtDateAlt 
= _T("%m/%d/%y"); 
2977                         fmtDate 
= _T("%m/%d/%y"); 
2978                         fmtDateAlt 
= _T("%d/%m/%y"); 
2981                     const wxChar 
*result 
= dt
.ParseFormat(input
, fmtDate
); 
2985                         // ok, be nice and try another one 
2986                         result 
= dt
.ParseFormat(input
, fmtDateAlt
); 
2992                         return (wxChar 
*)NULL
; 
2997                     haveDay 
= haveMon 
= haveYear 
= TRUE
; 
3008             case _T('X'):       // locale default time representation 
3009 #ifdef HAVE_STRPTIME 
3011                     // use strptime() to do it for us (FIXME !Unicode friendly) 
3013                     input 
= CallStrptime(input
, "%X", &tm
); 
3016                         return (wxChar 
*)NULL
; 
3019                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
3025 #else // !HAVE_STRPTIME 
3026                 // TODO under Win32 we can query the LOCALE_ITIME system 
3027                 //      setting which says whether the default time format is 
3030                     // try to parse what follows as "%H:%M:%S" and, if this 
3031                     // fails, as "%I:%M:%S %p" - this should catch the most 
3035                     const wxChar 
*result 
= dt
.ParseFormat(input
, _T("%T")); 
3038                         result 
= dt
.ParseFormat(input
, _T("%r")); 
3044                         return (wxChar 
*)NULL
; 
3047                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
3056 #endif // HAVE_STRPTIME/!HAVE_STRPTIME 
3059             case _T('y'):       // year without century (00-99) 
3060                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 99) ) 
3063                     return (wxChar 
*)NULL
; 
3068                 // TODO should have an option for roll over date instead of 
3069                 //      hard coding it here 
3070                 year 
= (num 
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
; 
3073             case _T('Y'):       // year with century 
3074                 if ( !GetNumericToken(width
, input
, &num
) ) 
3077                     return (wxChar 
*)NULL
; 
3081                 year 
= (wxDateTime_t
)num
; 
3084             case _T('Z'):       // timezone name 
3085                 wxFAIL_MSG(_T("TODO")); 
3088             case _T('%'):       // a percent sign 
3089                 if ( *input
++ != _T('%') ) 
3092                     return (wxChar 
*)NULL
; 
3096             case 0:             // the end of string 
3097                 wxFAIL_MSG(_T("unexpected format end")); 
3101             default:            // not a known format spec 
3102                 return (wxChar 
*)NULL
; 
3106     // format matched, try to construct a date from what we have now 
3108     if ( dateDef
.IsValid() ) 
3110         // take this date as default 
3111         tmDef 
= dateDef
.GetTm(); 
3113     else if ( IsValid() ) 
3115         // if this date is valid, don't change it 
3120         // no default and this date is invalid - fall back to Today() 
3121         tmDef 
= Today().GetTm(); 
3132     // TODO we don't check here that the values are consistent, if both year 
3133     //      day and month/day were found, we just ignore the year day and we 
3134     //      also always ignore the week day 
3135     if ( haveMon 
&& haveDay 
) 
3137         if ( mday 
> GetNumOfDaysInMonth(tm
.year
, mon
) ) 
3139             wxLogDebug(_T("bad month day in wxDateTime::ParseFormat")); 
3141             return (wxChar 
*)NULL
; 
3147     else if ( haveYDay 
) 
3149         if ( yday 
> GetNumberOfDays(tm
.year
) ) 
3151             wxLogDebug(_T("bad year day in wxDateTime::ParseFormat")); 
3153             return (wxChar 
*)NULL
; 
3156         Tm tm2 
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm(); 
3163     if ( haveHour 
&& hourIsIn12hFormat 
&& isPM 
) 
3165         // translate to 24hour format 
3168     //else: either already in 24h format or no translation needed 
3188     // finally check that the week day is consistent -- if we had it 
3189     if ( haveWDay 
&& GetWeekDay() != wday 
) 
3191         wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()")); 
3199 const wxChar 
*wxDateTime::ParseDateTime(const wxChar 
*date
) 
3201     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
3203     // there is a public domain version of getdate.y, but it only works for 
3205     wxFAIL_MSG(_T("TODO")); 
3207     return (wxChar 
*)NULL
; 
3210 const wxChar 
*wxDateTime::ParseDate(const wxChar 
*date
) 
3212     // this is a simplified version of ParseDateTime() which understands only 
3213     // "today" (for wxDate compatibility) and digits only otherwise (and not 
3214     // all esoteric constructions ParseDateTime() knows about) 
3216     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
3218     const wxChar 
*p 
= date
; 
3219     while ( wxIsspace(*p
) ) 
3222     // some special cases 
3226         int dayDiffFromToday
; 
3229         { wxTRANSLATE("today"),             0 }, 
3230         { wxTRANSLATE("yesterday"),        -1 }, 
3231         { wxTRANSLATE("tomorrow"),          1 }, 
3234     for ( size_t n 
= 0; n 
< WXSIZEOF(literalDates
); n
++ ) 
3236         wxString date 
= wxGetTranslation(literalDates
[n
].str
); 
3237         size_t len 
= date
.length(); 
3238         if ( wxStrlen(p
) >= len 
&& (wxString(p
, len
).CmpNoCase(date
) == 0) ) 
3240             // nothing can follow this, so stop here 
3243             int dayDiffFromToday 
= literalDates
[n
].dayDiffFromToday
; 
3245             if ( dayDiffFromToday 
) 
3247                 *this += wxDateSpan::Days(dayDiffFromToday
); 
3254     // We try to guess what we have here: for each new (numeric) token, we 
3255     // determine if it can be a month, day or a year. Of course, there is an 
3256     // ambiguity as some numbers may be days as well as months, so we also 
3257     // have the ability to back track. 
3260     bool haveDay 
= FALSE
,       // the months day? 
3261          haveWDay 
= FALSE
,      // the day of week? 
3262          haveMon 
= FALSE
,       // the month? 
3263          haveYear 
= FALSE
;      // the year? 
3265     // and the value of the items we have (init them to get rid of warnings) 
3266     WeekDay wday 
= Inv_WeekDay
; 
3267     wxDateTime_t day 
= 0; 
3268     wxDateTime::Month mon 
= Inv_Month
; 
3271     // tokenize the string 
3273     static const wxChar 
*dateDelimiters 
= _T(".,/-\t\r\n "); 
3274     wxStringTokenizer 
tok(p
, dateDelimiters
); 
3275     while ( tok
.HasMoreTokens() ) 
3277         wxString token 
= tok
.GetNextToken(); 
3283         if ( token
.ToULong(&val
) ) 
3285             // guess what this number is 
3291             if ( !haveMon 
&& val 
> 0 && val 
<= 12 ) 
3293                 // assume it is month 
3296             else // not the month 
3298                 wxDateTime_t maxDays 
= haveMon
 
3299                     ? GetNumOfDaysInMonth(haveYear 
? year 
: Inv_Year
, mon
) 
3303                 if ( (val 
== 0) || (val 
> (unsigned long)maxDays
) )  // cast to shut up compiler warning in BCC 
3320                 year 
= (wxDateTime_t
)val
; 
3329                 day 
= (wxDateTime_t
)val
; 
3335                 mon 
= (Month
)(val 
- 1); 
3338         else // not a number 
3340             // be careful not to overwrite the current mon value 
3341             Month mon2 
= GetMonthFromName(token
, Name_Full 
| Name_Abbr
); 
3342             if ( mon2 
!= Inv_Month 
) 
3347                     // but we already have a month - maybe we guessed wrong? 
3350                         // no need to check in month range as always < 12, but 
3351                         // the days are counted from 1 unlike the months 
3352                         day 
= (wxDateTime_t
)mon 
+ 1; 
3357                         // could possible be the year (doesn't the year come 
3358                         // before the month in the japanese format?) (FIXME) 
3367             else // not a valid month name 
3369                 wday 
= GetWeekDayFromName(token
, Name_Full 
| Name_Abbr
); 
3370                 if ( wday 
!= Inv_WeekDay 
) 
3380                 else // not a valid weekday name 
3383                     static const wxChar 
*ordinals
[] = 
3385                         wxTRANSLATE("first"), 
3386                         wxTRANSLATE("second"), 
3387                         wxTRANSLATE("third"), 
3388                         wxTRANSLATE("fourth"), 
3389                         wxTRANSLATE("fifth"), 
3390                         wxTRANSLATE("sixth"), 
3391                         wxTRANSLATE("seventh"), 
3392                         wxTRANSLATE("eighth"), 
3393                         wxTRANSLATE("ninth"), 
3394                         wxTRANSLATE("tenth"), 
3395                         wxTRANSLATE("eleventh"), 
3396                         wxTRANSLATE("twelfth"), 
3397                         wxTRANSLATE("thirteenth"), 
3398                         wxTRANSLATE("fourteenth"), 
3399                         wxTRANSLATE("fifteenth"), 
3400                         wxTRANSLATE("sixteenth"), 
3401                         wxTRANSLATE("seventeenth"), 
3402                         wxTRANSLATE("eighteenth"), 
3403                         wxTRANSLATE("nineteenth"), 
3404                         wxTRANSLATE("twentieth"), 
3405                         // that's enough - otherwise we'd have problems with 
3406                         // composite (or not) ordinals 
3410                     for ( n 
= 0; n 
< WXSIZEOF(ordinals
); n
++ ) 
3412                         if ( token
.CmpNoCase(ordinals
[n
]) == 0 ) 
3418                     if ( n 
== WXSIZEOF(ordinals
) ) 
3420                         // stop here - something unknown 
3427                         // don't try anything here (as in case of numeric day 
3428                         // above) - the symbolic day spec should always 
3429                         // precede the month/year 
3435                     day 
= (wxDateTime_t
)(n 
+ 1); 
3440         nPosCur 
= tok
.GetPosition(); 
3443     // either no more tokens or the scan was stopped by something we couldn't 
3444     // parse - in any case, see if we can construct a date from what we have 
3445     if ( !haveDay 
&& !haveWDay 
) 
3447         wxLogDebug(_T("ParseDate: no day, no weekday hence no date.")); 
3449         return (wxChar 
*)NULL
; 
3452     if ( haveWDay 
&& (haveMon 
|| haveYear 
|| haveDay
) && 
3453          !(haveDay 
&& haveMon 
&& haveYear
) ) 
3455         // without adjectives (which we don't support here) the week day only 
3456         // makes sense completely separately or with the full date 
3457         // specification (what would "Wed 1999" mean?) 
3458         return (wxChar 
*)NULL
; 
3461     if ( !haveWDay 
&& haveYear 
&& !(haveDay 
&& haveMon
) ) 
3463         // may be we have month and day instead of day and year? 
3464         if ( haveDay 
&& !haveMon 
) 
3468                 // exchange day and month 
3469                 mon 
= (wxDateTime::Month
)(day 
- 1); 
3471                 // we're in the current year then 
3472                 if ( (year 
> 0) && (year 
<= (int)GetNumOfDaysInMonth(Inv_Year
, mon
)) ) 
3479                 //else: no, can't exchange, leave haveMon == FALSE 
3485             // if we give the year, month and day must be given too 
3486             wxLogDebug(_T("ParseDate: day and month should be specified if year is.")); 
3488             return (wxChar 
*)NULL
; 
3494         mon 
= GetCurrentMonth(); 
3499         year 
= GetCurrentYear(); 
3504         Set(day
, mon
, year
); 
3508             // check that it is really the same 
3509             if ( GetWeekDay() != wday 
) 
3511                 // inconsistency detected 
3512                 wxLogDebug(_T("ParseDate: inconsistent day/weekday.")); 
3514                 return (wxChar 
*)NULL
; 
3522         SetToWeekDayInSameWeek(wday
); 
3525     // return the pointer to the first unparsed char 
3527     if ( nPosCur 
&& wxStrchr(dateDelimiters
, *(p 
- 1)) ) 
3529         // if we couldn't parse the token after the delimiter, put back the 
3530         // delimiter as well 
3537 const wxChar 
*wxDateTime::ParseTime(const wxChar 
*time
) 
3539     wxCHECK_MSG( time
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
3541     // first try some extra things 
3548         { wxTRANSLATE("noon"),      12 }, 
3549         { wxTRANSLATE("midnight"),  00 }, 
3553     for ( size_t n 
= 0; n 
< WXSIZEOF(stdTimes
); n
++ ) 
3555         wxString timeString 
= wxGetTranslation(stdTimes
[n
].name
); 
3556         size_t len 
= timeString
.length(); 
3557         if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 ) 
3559             // casts required by DigitalMars 
3560             Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0)); 
3566     // try all time formats we may think about in the order from longest to 
3569     // 12hour with AM/PM? 
3570     const wxChar 
*result 
= ParseFormat(time
, _T("%I:%M:%S %p")); 
3574         // normally, it's the same, but why not try it? 
3575         result 
= ParseFormat(time
, _T("%H:%M:%S")); 
3580         // 12hour with AM/PM but without seconds? 
3581         result 
= ParseFormat(time
, _T("%I:%M %p")); 
3587         result 
= ParseFormat(time
, _T("%H:%M")); 
3592         // just the hour and AM/PM? 
3593         result 
= ParseFormat(time
, _T("%I %p")); 
3599         result 
= ParseFormat(time
, _T("%H")); 
3604         // parse the standard format: normally it is one of the formats above 
3605         // but it may be set to something completely different by the user 
3606         result 
= ParseFormat(time
, _T("%X")); 
3609     // TODO: parse timezones 
3614 // ---------------------------------------------------------------------------- 
3615 // Workdays and holidays support 
3616 // ---------------------------------------------------------------------------- 
3618 bool wxDateTime::IsWorkDay(Country 
WXUNUSED(country
)) const 
3620     return !wxDateTimeHolidayAuthority::IsHoliday(*this); 
3623 // ============================================================================ 
3625 // ============================================================================ 
3627 // this enum is only used in wxTimeSpan::Format() below but we can't declare 
3628 // it locally to the method as it provokes an internal compiler error in egcs 
3629 // 2.91.60 when building with -O2 
3640 // not all strftime(3) format specifiers make sense here because, for example, 
3641 // a time span doesn't have a year nor a timezone 
3643 // Here are the ones which are supported (all of them are supported by strftime 
3645 //  %H          hour in 24 hour format 
3646 //  %M          minute (00 - 59) 
3647 //  %S          second (00 - 59) 
3650 // Also, for MFC CTimeSpan compatibility, we support 
3651 //  %D          number of days 
3653 // And, to be better than MFC :-), we also have 
3654 //  %E          number of wEeks 
3655 //  %l          milliseconds (000 - 999) 
3656 wxString 
wxTimeSpan::Format(const wxChar 
*format
) const 
3658     wxCHECK_MSG( format
, _T(""), _T("NULL format in wxTimeSpan::Format") ); 
3661     str
.Alloc(wxStrlen(format
)); 
3663     // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */) 
3665     // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the 
3666     // question is what should ts.Format("%S") do? The code here returns "3273" 
3667     // in this case (i.e. the total number of seconds, not just seconds % 60) 
3668     // because, for me, this call means "give me entire time interval in 
3669     // seconds" and not "give me the seconds part of the time interval" 
3671     // If we agree that it should behave like this, it is clear that the 
3672     // interpretation of each format specifier depends on the presence of the 
3673     // other format specs in the string: if there was "%H" before "%M", we 
3674     // should use GetMinutes() % 60, otherwise just GetMinutes() &c 
3676     // we remember the most important unit found so far 
3677     TimeSpanPart partBiggest 
= Part_MSec
; 
3679     for ( const wxChar 
*pch 
= format
; *pch
; pch
++ ) 
3683         if ( ch 
== _T('%') ) 
3685             // the start of the format specification of the printf() below 
3686             wxString fmtPrefix 
= _T('%'); 
3691             ch 
= *++pch
;    // get the format spec char 
3695                     wxFAIL_MSG( _T("invalid format character") ); 
3701                     // skip the part below switch 
3706                     if ( partBiggest 
< Part_Day 
) 
3712                         partBiggest 
= Part_Day
; 
3717                     partBiggest 
= Part_Week
; 
3723                     if ( partBiggest 
< Part_Hour 
) 
3729                         partBiggest 
= Part_Hour
; 
3732                     fmtPrefix 
+= _T("02"); 
3736                     n 
= GetMilliseconds().ToLong(); 
3737                     if ( partBiggest 
< Part_MSec 
) 
3741                     //else: no need to reset partBiggest to Part_MSec, it is 
3742                     //      the least significant one anyhow 
3744                     fmtPrefix 
+= _T("03"); 
3749                     if ( partBiggest 
< Part_Min 
) 
3755                         partBiggest 
= Part_Min
; 
3758                     fmtPrefix 
+= _T("02"); 
3762                     n 
= GetSeconds().ToLong(); 
3763                     if ( partBiggest 
< Part_Sec 
) 
3769                         partBiggest 
= Part_Sec
; 
3772                     fmtPrefix 
+= _T("02"); 
3776             str 
+= wxString::Format(fmtPrefix 
+ _T("ld"), n
); 
3780             // normal character, just copy 
3788 // ============================================================================ 
3789 // wxDateTimeHolidayAuthority and related classes 
3790 // ============================================================================ 
3792 #include "wx/arrimpl.cpp" 
3794 WX_DEFINE_OBJARRAY(wxDateTimeArray
); 
3796 static int wxCMPFUNC_CONV
 
3797 wxDateTimeCompareFunc(wxDateTime 
**first
, wxDateTime 
**second
) 
3799     wxDateTime dt1 
= **first
, 
3802     return dt1 
== dt2 
? 0 : dt1 
< dt2 
? -1 : +1; 
3805 // ---------------------------------------------------------------------------- 
3806 // wxDateTimeHolidayAuthority 
3807 // ---------------------------------------------------------------------------- 
3809 wxHolidayAuthoritiesArray 
wxDateTimeHolidayAuthority::ms_authorities
; 
3812 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
) 
3814     size_t count 
= ms_authorities
.size(); 
3815     for ( size_t n 
= 0; n 
< count
; n
++ ) 
3817         if ( ms_authorities
[n
]->DoIsHoliday(dt
) ) 
3828 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
, 
3829                                                const wxDateTime
& dtEnd
, 
3830                                                wxDateTimeArray
& holidays
) 
3832     wxDateTimeArray hol
; 
3836     size_t count 
= ms_authorities
.size(); 
3837     for ( size_t nAuth 
= 0; nAuth 
< count
; nAuth
++ ) 
3839         ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
); 
3841         WX_APPEND_ARRAY(holidays
, hol
); 
3844     holidays
.Sort(wxDateTimeCompareFunc
); 
3846     return holidays
.size(); 
3850 void wxDateTimeHolidayAuthority::ClearAllAuthorities() 
3852     WX_CLEAR_ARRAY(ms_authorities
); 
3856 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority 
*auth
) 
3858     ms_authorities
.push_back(auth
); 
3861 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority() 
3863     // nothing to do here 
3866 // ---------------------------------------------------------------------------- 
3867 // wxDateTimeWorkDays 
3868 // ---------------------------------------------------------------------------- 
3870 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const 
3872     wxDateTime::WeekDay wd 
= dt
.GetWeekDay(); 
3874     return (wd 
== wxDateTime::Sun
) || (wd 
== wxDateTime::Sat
); 
3877 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
, 
3878                                                 const wxDateTime
& dtEnd
, 
3879                                                 wxDateTimeArray
& holidays
) const 
3881     if ( dtStart 
> dtEnd 
) 
3883         wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") ); 
3890     // instead of checking all days, start with the first Sat after dtStart and 
3891     // end with the last Sun before dtEnd 
3892     wxDateTime dtSatFirst 
= dtStart
.GetNextWeekDay(wxDateTime::Sat
), 
3893                dtSatLast 
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
), 
3894                dtSunFirst 
= dtStart
.GetNextWeekDay(wxDateTime::Sun
), 
3895                dtSunLast 
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
), 
3898     for ( dt 
= dtSatFirst
; dt 
<= dtSatLast
; dt 
+= wxDateSpan::Week() ) 
3903     for ( dt 
= dtSunFirst
; dt 
<= dtSunLast
; dt 
+= wxDateSpan::Week() ) 
3908     return holidays
.GetCount(); 
3911 #endif // wxUSE_DATETIME