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 license 
  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 // ---------------------------------------------------------------------------- 
  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" 
  78 #define wxDEFINE_TIME_CONSTANTS // before including datetime.h 
  82 #include "wx/datetime.h" 
  83 #include "wx/timer.h"           // for wxGetLocalTimeMillis() 
  85 // ---------------------------------------------------------------------------- 
  86 // conditional compilation 
  87 // ---------------------------------------------------------------------------- 
  89 #if defined(HAVE_STRPTIME) && defined(__LINUX__) 
  90     // glibc 2.0.7 strptime() is broken - the following snippet causes it to 
  91     // crash (instead of just failing): 
  93     //      strncpy(buf, "Tue Dec 21 20:25:40 1999", 128); 
  94     //      strptime(buf, "%x", &tm); 
  98 #endif // broken strptime() 
 100 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM) 
 101     #if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__) 
 102         #define WX_TIMEZONE _timezone 
 103     #elif defined(__MWERKS__) 
 104         long wxmw_timezone 
= 28800; 
 105         #define WX_TIMEZONE wxmw_timezone 
 106     #elif defined(__DJGPP__) 
 107         #include <sys/timeb.h> 
 109         static long wxGetTimeZone() 
 111             static long timezone 
= MAXLONG
; // invalid timezone 
 112             if (timezone 
== MAXLONG
) 
 116                 timezone 
= tb
.timezone
; 
 120         #define WX_TIMEZONE wxGetTimeZone() 
 121     #elif defined(__DARWIN__) 
 122         #define WX_GMTOFF_IN_TM 
 123     #else // unknown platform - try timezone 
 124         #define WX_TIMEZONE timezone 
 126 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM 
 128 // ---------------------------------------------------------------------------- 
 130 // ---------------------------------------------------------------------------- 
 132 // debugging helper: just a convenient replacement of wxCHECK() 
 133 #define wxDATETIME_CHECK(expr, msg)     \ 
 137             *this = wxInvalidDateTime;  \ 
 141 // ---------------------------------------------------------------------------- 
 143 // ---------------------------------------------------------------------------- 
 145 class wxDateTimeHolidaysModule 
: public wxModule
 
 148     virtual bool OnInit() 
 150         wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
); 
 155     virtual void OnExit() 
 157         wxDateTimeHolidayAuthority::ClearAllAuthorities(); 
 158         wxDateTimeHolidayAuthority::ms_authorities
.Clear(); 
 162     DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
) 
 165 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
) 
 167 // ---------------------------------------------------------------------------- 
 169 // ---------------------------------------------------------------------------- 
 172 static const int MONTHS_IN_YEAR 
= 12; 
 174 static const int SEC_PER_MIN 
= 60; 
 176 static const int MIN_PER_HOUR 
= 60; 
 178 static const int HOURS_PER_DAY 
= 24; 
 180 static const long SECONDS_PER_DAY 
= 86400l; 
 182 static const int DAYS_PER_WEEK 
= 7; 
 184 static const long MILLISECONDS_PER_DAY 
= 86400000l; 
 186 // this is the integral part of JDN of the midnight of Jan 1, 1970 
 187 // (i.e. JDN(Jan 1, 1970) = 2440587.5) 
 188 static const long EPOCH_JDN 
= 2440587l; 
 190 // the date of JDN -0.5 (as we don't work with fractional parts, this is the 
 191 // reference date for us) is Nov 24, 4714BC 
 192 static const int JDN_0_YEAR 
= -4713; 
 193 static const int JDN_0_MONTH 
= wxDateTime::Nov
; 
 194 static const int JDN_0_DAY 
= 24; 
 196 // the constants used for JDN calculations 
 197 static const long JDN_OFFSET         
= 32046l; 
 198 static const long DAYS_PER_5_MONTHS  
= 153l; 
 199 static const long DAYS_PER_4_YEARS   
= 1461l; 
 200 static const long DAYS_PER_400_YEARS 
= 146097l; 
 202 // this array contains the cumulated number of days in all previous months for 
 203 // normal and leap years 
 204 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] = 
 206     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, 
 207     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } 
 210 // ---------------------------------------------------------------------------- 
 212 // ---------------------------------------------------------------------------- 
 214 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to 
 215 // indicate an invalid wxDateTime object 
 216 const wxDateTime wxDefaultDateTime
; 
 218 wxDateTime::Country 
wxDateTime::ms_country 
= wxDateTime::Country_Unknown
; 
 220 // ---------------------------------------------------------------------------- 
 222 // ---------------------------------------------------------------------------- 
 224 // a critical section is needed to protect GetTimeZone() static 
 225 // variable in MT case 
 227     static wxCriticalSection gs_critsectTimezone
; 
 228 #endif // wxUSE_THREADS 
 230 // ---------------------------------------------------------------------------- 
 232 // ---------------------------------------------------------------------------- 
 234 // debugger helper: shows what the date really is 
 236 extern const wxChar 
*wxDumpDate(const wxDateTime
* dt
) 
 238     static wxChar buf
[128]; 
 240     wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S"))); 
 246 // get the number of days in the given month of the given year 
 248 wxDateTime::wxDateTime_t 
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
) 
 250     // the number of days in month in Julian/Gregorian calendar: the first line 
 251     // is for normal years, the second one is for the leap ones 
 252     static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] = 
 254         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 
 255         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 
 258     return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
]; 
 261 // ensure that the timezone variable is set by calling localtime 
 262 static int GetTimeZone() 
 264     // set to TRUE when the timezone is set 
 265     static bool s_timezoneSet 
= FALSE
; 
 266 #ifdef WX_GMTOFF_IN_TM 
 267     static long gmtoffset 
= LONG_MAX
; // invalid timezone 
 270     wxCRIT_SECT_LOCKER(lock
, gs_critsectTimezone
); 
 272     if ( !s_timezoneSet 
) 
 274         // just call localtime() instead of figuring out whether this system 
 275         // supports tzset(), _tzset() or something else 
 280         s_timezoneSet 
= TRUE
; 
 281 #ifdef WX_GMTOFF_IN_TM 
 282         gmtoffset 
= tm
->tm_gmtoff
; 
 286 #ifdef WX_GMTOFF_IN_TM 
 287     return (int)gmtoffset
; 
 289     return (int)WX_TIMEZONE
; 
 293 // return the integral part of the JDN for the midnight of the given date (to 
 294 // get the real JDN you need to add 0.5, this is, in fact, JDN of the 
 295 // noon of the previous day) 
 296 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
, 
 297                             wxDateTime::Month mon
, 
 300     // CREDIT: code below is by Scott E. Lee (but bugs are mine) 
 302     // check the date validity 
 304       (year 
> JDN_0_YEAR
) || 
 305       ((year 
== JDN_0_YEAR
) && (mon 
> JDN_0_MONTH
)) || 
 306       ((year 
== JDN_0_YEAR
) && (mon 
== JDN_0_MONTH
) && (day 
>= JDN_0_DAY
)), 
 307       _T("date out of range - can't convert to JDN") 
 310     // make the year positive to avoid problems with negative numbers division 
 313     // months are counted from March here 
 315     if ( mon 
>= wxDateTime::Mar 
) 
 325     // now we can simply add all the contributions together 
 326     return ((year 
/ 100) * DAYS_PER_400_YEARS
) / 4 
 327             + ((year 
% 100) * DAYS_PER_4_YEARS
) / 4 
 328             + (month 
* DAYS_PER_5_MONTHS 
+ 2) / 5 
 333 // this function is a wrapper around strftime(3) 
 334 static wxString 
CallStrftime(const wxChar 
*format
, const tm
* tm
) 
 337     if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) ) 
 339         // buffer is too small? 
 340         wxFAIL_MSG(_T("strftime() failed")); 
 343     return wxString(buf
); 
 346 // if year and/or month have invalid values, replace them with the current ones 
 347 static void ReplaceDefaultYearMonthWithCurrent(int *year
, 
 348                                                wxDateTime::Month 
*month
) 
 350     struct tm 
*tmNow 
= NULL
; 
 352     if ( *year 
== wxDateTime::Inv_Year 
) 
 354         tmNow 
= wxDateTime::GetTmNow(); 
 356         *year 
= 1900 + tmNow
->tm_year
; 
 359     if ( *month 
== wxDateTime::Inv_Month 
) 
 362             tmNow 
= wxDateTime::GetTmNow(); 
 364         *month 
= (wxDateTime::Month
)tmNow
->tm_mon
; 
 368 // fll the struct tm with default values 
 369 static void InitTm(struct tm
& tm
) 
 371     // struct tm may have etxra fields (undocumented and with unportable 
 372     // names) which, nevertheless, must be set to 0 
 373     memset(&tm
, 0, sizeof(struct tm
)); 
 375     tm
.tm_mday 
= 1;   // mday 0 is invalid 
 376     tm
.tm_year 
= 76;  // any valid year 
 377     tm
.tm_isdst 
= -1; // auto determine 
 383 // return the month if the string is a month name or Inv_Month otherwise 
 384 static wxDateTime::Month 
GetMonthFromName(const wxString
& name
, int flags
) 
 386     wxDateTime::Month mon
; 
 387     for ( mon 
= wxDateTime::Jan
; mon 
< wxDateTime::Inv_Month
; wxNextMonth(mon
) ) 
 389         // case-insensitive comparison either one of or with both abbreviated 
 391         if ( flags 
& wxDateTime::Name_Full 
) 
 393             if ( name
.CmpNoCase(wxDateTime:: 
 394                         GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 ) 
 400         if ( flags 
& wxDateTime::Name_Abbr 
) 
 402             if ( name
.CmpNoCase(wxDateTime:: 
 403                         GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 ) 
 413 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise 
 414 static wxDateTime::WeekDay 
GetWeekDayFromName(const wxString
& name
, int flags
) 
 416     wxDateTime::WeekDay wd
; 
 417     for ( wd 
= wxDateTime::Sun
; wd 
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) ) 
 419         // case-insensitive comparison either one of or with both abbreviated 
 421         if ( flags 
& wxDateTime::Name_Full 
) 
 423             if ( name
.CmpNoCase(wxDateTime:: 
 424                         GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 ) 
 430         if ( flags 
& wxDateTime::Name_Abbr 
) 
 432             if ( name
.CmpNoCase(wxDateTime:: 
 433                         GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 ) 
 443 // scans all digits (but no more than len) and returns the resulting number 
 444 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
) 
 448     while ( wxIsdigit(*p
) ) 
 452         if ( len 
&& ++n 
> len 
) 
 456     return !!s 
&& s
.ToULong(number
); 
 459 // scans all alphabetic characters and returns the resulting string 
 460 static wxString 
GetAlphaToken(const wxChar
*& p
) 
 463     while ( wxIsalpha(*p
) ) 
 471 // ============================================================================ 
 472 // implementation of wxDateTime 
 473 // ============================================================================ 
 475 // ---------------------------------------------------------------------------- 
 477 // ---------------------------------------------------------------------------- 
 481     year 
= (wxDateTime_t
)wxDateTime::Inv_Year
; 
 482     mon 
= wxDateTime::Inv_Month
; 
 484     hour 
= min 
= sec 
= msec 
= 0; 
 485     wday 
= wxDateTime::Inv_WeekDay
; 
 488 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
) 
 496     mon 
= (wxDateTime::Month
)tm
.tm_mon
; 
 497     year 
= 1900 + tm
.tm_year
; 
 502 bool wxDateTime::Tm::IsValid() const 
 504     // we allow for the leap seconds, although we don't use them (yet) 
 505     return (year 
!= wxDateTime::Inv_Year
) && (mon 
!= wxDateTime::Inv_Month
) && 
 506            (mday 
<= GetNumOfDaysInMonth(year
, mon
)) && 
 507            (hour 
< 24) && (min 
< 60) && (sec 
< 62) && (msec 
< 1000); 
 510 void wxDateTime::Tm::ComputeWeekDay() 
 512     // compute the week day from day/month/year: we use the dumbest algorithm 
 513     // possible: just compute our JDN and then use the (simple to derive) 
 514     // formula: weekday = (JDN + 1.5) % 7 
 515     wday 
= (wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7; 
 518 void wxDateTime::Tm::AddMonths(int monDiff
) 
 520     // normalize the months field 
 521     while ( monDiff 
< -mon 
) 
 525         monDiff 
+= MONTHS_IN_YEAR
; 
 528     while ( monDiff 
+ mon 
>= MONTHS_IN_YEAR 
) 
 532         monDiff 
-= MONTHS_IN_YEAR
; 
 535     mon 
= (wxDateTime::Month
)(mon 
+ monDiff
); 
 537     wxASSERT_MSG( mon 
>= 0 && mon 
< MONTHS_IN_YEAR
, _T("logic error") ); 
 539     // NB: we don't check here that the resulting date is valid, this function 
 540     //     is private and the caller must check it if needed 
 543 void wxDateTime::Tm::AddDays(int dayDiff
) 
 545     // normalize the days field 
 546     while ( dayDiff 
+ mday 
< 1 ) 
 550         dayDiff 
+= GetNumOfDaysInMonth(year
, mon
); 
 554     while ( mday 
> GetNumOfDaysInMonth(year
, mon
) ) 
 556         mday 
-= GetNumOfDaysInMonth(year
, mon
); 
 561     wxASSERT_MSG( mday 
> 0 && mday 
<= GetNumOfDaysInMonth(year
, mon
), 
 565 // ---------------------------------------------------------------------------- 
 567 // ---------------------------------------------------------------------------- 
 569 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
) 
 573         case wxDateTime::Local
: 
 574             // get the offset from C RTL: it returns the difference GMT-local 
 575             // while we want to have the offset _from_ GMT, hence the '-' 
 576             m_offset 
= -GetTimeZone(); 
 579         case wxDateTime::GMT_12
: 
 580         case wxDateTime::GMT_11
: 
 581         case wxDateTime::GMT_10
: 
 582         case wxDateTime::GMT_9
: 
 583         case wxDateTime::GMT_8
: 
 584         case wxDateTime::GMT_7
: 
 585         case wxDateTime::GMT_6
: 
 586         case wxDateTime::GMT_5
: 
 587         case wxDateTime::GMT_4
: 
 588         case wxDateTime::GMT_3
: 
 589         case wxDateTime::GMT_2
: 
 590         case wxDateTime::GMT_1
: 
 591             m_offset 
= -3600*(wxDateTime::GMT0 
- tz
); 
 594         case wxDateTime::GMT0
: 
 595         case wxDateTime::GMT1
: 
 596         case wxDateTime::GMT2
: 
 597         case wxDateTime::GMT3
: 
 598         case wxDateTime::GMT4
: 
 599         case wxDateTime::GMT5
: 
 600         case wxDateTime::GMT6
: 
 601         case wxDateTime::GMT7
: 
 602         case wxDateTime::GMT8
: 
 603         case wxDateTime::GMT9
: 
 604         case wxDateTime::GMT10
: 
 605         case wxDateTime::GMT11
: 
 606         case wxDateTime::GMT12
: 
 607             m_offset 
= 3600*(tz 
- wxDateTime::GMT0
); 
 610         case wxDateTime::A_CST
: 
 611             // Central Standard Time in use in Australia = UTC + 9.5 
 612             m_offset 
= 60l*(9*60 + 30); 
 616             wxFAIL_MSG( _T("unknown time zone") ); 
 620 // ---------------------------------------------------------------------------- 
 622 // ---------------------------------------------------------------------------- 
 625 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
) 
 627     if ( year 
== Inv_Year 
) 
 628         year 
= GetCurrentYear(); 
 630     if ( cal 
== Gregorian 
) 
 632         // in Gregorian calendar leap years are those divisible by 4 except 
 633         // those divisible by 100 unless they're also divisible by 400 
 634         // (in some countries, like Russia and Greece, additional corrections 
 635         // exist, but they won't manifest themselves until 2700) 
 636         return (year 
% 4 == 0) && ((year 
% 100 != 0) || (year 
% 400 == 0)); 
 638     else if ( cal 
== Julian 
) 
 640         // in Julian calendar the rule is simpler 
 641         return year 
% 4 == 0; 
 645         wxFAIL_MSG(_T("unknown calendar")); 
 652 int wxDateTime::GetCentury(int year
) 
 654     return year 
> 0 ? year 
/ 100 : year 
/ 100 - 1; 
 658 int wxDateTime::ConvertYearToBC(int year
) 
 661     return year 
> 0 ? year 
: year 
- 1; 
 665 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
) 
 670             return Now().GetYear(); 
 673             wxFAIL_MSG(_T("TODO")); 
 677             wxFAIL_MSG(_T("unsupported calendar")); 
 685 wxDateTime::Month 
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
) 
 690             return Now().GetMonth(); 
 693             wxFAIL_MSG(_T("TODO")); 
 697             wxFAIL_MSG(_T("unsupported calendar")); 
 705 wxDateTime::wxDateTime_t 
wxDateTime::GetNumberOfDays(int year
, Calendar cal
) 
 707     if ( year 
== Inv_Year 
) 
 709         // take the current year if none given 
 710         year 
= GetCurrentYear(); 
 717             return IsLeapYear(year
) ? 366 : 365; 
 720             wxFAIL_MSG(_T("unsupported calendar")); 
 728 wxDateTime::wxDateTime_t 
wxDateTime::GetNumberOfDays(wxDateTime::Month month
, 
 730                                                      wxDateTime::Calendar cal
) 
 732     wxCHECK_MSG( month 
< MONTHS_IN_YEAR
, 0, _T("invalid month") ); 
 734     if ( cal 
== Gregorian 
|| cal 
== Julian 
) 
 736         if ( year 
== Inv_Year 
) 
 738             // take the current year if none given 
 739             year 
= GetCurrentYear(); 
 742         return GetNumOfDaysInMonth(year
, month
); 
 746         wxFAIL_MSG(_T("unsupported calendar")); 
 753 wxString 
wxDateTime::GetMonthName(wxDateTime::Month month
, 
 754                                   wxDateTime::NameFlags flags
) 
 756     wxCHECK_MSG( month 
!= Inv_Month
, _T(""), _T("invalid month") ); 
 758     // notice that we must set all the fields to avoid confusing libc (GNU one 
 759     // gets confused to a crash if we don't do this) 
 764     return CallStrftime(flags 
== Name_Abbr 
? _T("%b") : _T("%B"), &tm
); 
 768 wxString 
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
, 
 769                                     wxDateTime::NameFlags flags
) 
 771     wxCHECK_MSG( wday 
!= Inv_WeekDay
, _T(""), _T("invalid weekday") ); 
 773     // take some arbitrary Sunday 
 780     // and offset it by the number of days needed to get the correct wday 
 783     // call mktime() to normalize it... 
 786     // ... and call strftime() 
 787     return CallStrftime(flags 
== Name_Abbr 
? _T("%a") : _T("%A"), &tm
); 
 791 void wxDateTime::GetAmPmStrings(wxString 
*am
, wxString 
*pm
) 
 797         *am 
= CallStrftime(_T("%p"), &tm
); 
 802         *pm 
= CallStrftime(_T("%p"), &tm
); 
 806 // ---------------------------------------------------------------------------- 
 807 // Country stuff: date calculations depend on the country (DST, work days, 
 808 // ...), so we need to know which rules to follow. 
 809 // ---------------------------------------------------------------------------- 
 812 wxDateTime::Country 
wxDateTime::GetCountry() 
 814     // TODO use LOCALE_ICOUNTRY setting under Win32 
 816     if ( ms_country 
== Country_Unknown 
) 
 818         // try to guess from the time zone name 
 819         time_t t 
= time(NULL
); 
 820         struct tm 
*tm 
= localtime(&t
); 
 822         wxString tz 
= CallStrftime(_T("%Z"), tm
); 
 823         if ( tz 
== _T("WET") || tz 
== _T("WEST") ) 
 827         else if ( tz 
== _T("CET") || tz 
== _T("CEST") ) 
 829             ms_country 
= Country_EEC
; 
 831         else if ( tz 
== _T("MSK") || tz 
== _T("MSD") ) 
 835         else if ( tz 
== _T("AST") || tz 
== _T("ADT") || 
 836                   tz 
== _T("EST") || tz 
== _T("EDT") || 
 837                   tz 
== _T("CST") || tz 
== _T("CDT") || 
 838                   tz 
== _T("MST") || tz 
== _T("MDT") || 
 839                   tz 
== _T("PST") || tz 
== _T("PDT") ) 
 845             // well, choose a default one 
 854 void wxDateTime::SetCountry(wxDateTime::Country country
) 
 856     ms_country 
= country
; 
 860 bool wxDateTime::IsWestEuropeanCountry(Country country
) 
 862     if ( country 
== Country_Default 
) 
 864         country 
= GetCountry(); 
 867     return (Country_WesternEurope_Start 
<= country
) && 
 868            (country 
<= Country_WesternEurope_End
); 
 871 // ---------------------------------------------------------------------------- 
 872 // DST calculations: we use 3 different rules for the West European countries, 
 873 // USA and for the rest of the world. This is undoubtedly false for many 
 874 // countries, but I lack the necessary info (and the time to gather it), 
 875 // please add the other rules here! 
 876 // ---------------------------------------------------------------------------- 
 879 bool wxDateTime::IsDSTApplicable(int year
, Country country
) 
 881     if ( year 
== Inv_Year 
) 
 883         // take the current year if none given 
 884         year 
= GetCurrentYear(); 
 887     if ( country 
== Country_Default 
) 
 889         country 
= GetCountry(); 
 896             // DST was first observed in the US and UK during WWI, reused 
 897             // during WWII and used again since 1966 
 898             return year 
>= 1966 || 
 899                    (year 
>= 1942 && year 
<= 1945) || 
 900                    (year 
== 1918 || year 
== 1919); 
 903             // assume that it started after WWII 
 909 wxDateTime 
wxDateTime::GetBeginDST(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(); 
 922     if ( !IsDSTApplicable(year
, country
) ) 
 924         return wxInvalidDateTime
; 
 929     if ( IsWestEuropeanCountry(country
) || (country 
== Russia
) ) 
 931         // DST begins at 1 a.m. GMT on the last Sunday of March 
 932         if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) ) 
 935             wxFAIL_MSG( _T("no last Sunday in March?") ); 
 938         dt 
+= wxTimeSpan::Hours(1); 
 940         // disable DST tests because it could result in an infinite recursion! 
 943     else switch ( country 
) 
 950                     // don't know for sure - assume it was in effect all year 
 955                     dt
.Set(1, Jan
, year
); 
 959                     // DST was installed Feb 2, 1942 by the Congress 
 960                     dt
.Set(2, Feb
, year
); 
 963                     // Oil embargo changed the DST period in the US 
 965                     dt
.Set(6, Jan
, 1974); 
 969                     dt
.Set(23, Feb
, 1975); 
 973                     // before 1986, DST begun on the last Sunday of April, but 
 974                     // in 1986 Reagan changed it to begin at 2 a.m. of the 
 975                     // first Sunday in April 
 978                         if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) ) 
 981                             wxFAIL_MSG( _T("no first Sunday in April?") ); 
 986                         if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) ) 
 989                             wxFAIL_MSG( _T("no first Sunday in April?") ); 
 993                     dt 
+= wxTimeSpan::Hours(2); 
 995                     // TODO what about timezone?? 
1001             // assume Mar 30 as the start of the DST for the rest of the world 
1002             // - totally bogus, of course 
1003             dt
.Set(30, Mar
, year
); 
1010 wxDateTime 
wxDateTime::GetEndDST(int year
, Country country
) 
1012     if ( year 
== Inv_Year 
) 
1014         // take the current year if none given 
1015         year 
= GetCurrentYear(); 
1018     if ( country 
== Country_Default 
) 
1020         country 
= GetCountry(); 
1023     if ( !IsDSTApplicable(year
, country
) ) 
1025         return wxInvalidDateTime
; 
1030     if ( IsWestEuropeanCountry(country
) || (country 
== Russia
) ) 
1032         // DST ends at 1 a.m. GMT on the last Sunday of October 
1033         if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) ) 
1035             // weirder and weirder... 
1036             wxFAIL_MSG( _T("no last Sunday in October?") ); 
1039         dt 
+= wxTimeSpan::Hours(1); 
1041         // disable DST tests because it could result in an infinite recursion! 
1044     else switch ( country 
) 
1051                     // don't know for sure - assume it was in effect all year 
1055                     dt
.Set(31, Dec
, year
); 
1059                     // the time was reset after the end of the WWII 
1060                     dt
.Set(30, Sep
, year
); 
1064                     // DST ends at 2 a.m. on the last Sunday of October 
1065                     if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) ) 
1067                         // weirder and weirder... 
1068                         wxFAIL_MSG( _T("no last Sunday in October?") ); 
1071                     dt 
+= wxTimeSpan::Hours(2); 
1073                     // TODO what about timezone?? 
1078             // assume October 26th as the end of the DST - totally bogus too 
1079             dt
.Set(26, Oct
, year
); 
1085 // ---------------------------------------------------------------------------- 
1086 // constructors and assignment operators 
1087 // ---------------------------------------------------------------------------- 
1089 // return the current time with ms precision 
1090 /* static */ wxDateTime 
wxDateTime::UNow() 
1092     return wxDateTime(wxGetLocalTimeMillis()); 
1095 // the values in the tm structure contain the local time 
1096 wxDateTime
& wxDateTime::Set(const struct tm
& tm
) 
1099     time_t timet 
= mktime(&tm2
); 
1101     if ( timet 
== (time_t)-1 ) 
1103         // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is 
1104         // less than timezone - try to make it work for this case 
1105         if ( tm2
.tm_year 
== 70 && tm2
.tm_mon 
== 0 && tm2
.tm_mday 
== 1 ) 
1107             // add timezone to make sure that date is in range 
1108             tm2
.tm_sec 
-= GetTimeZone(); 
1110             timet 
= mktime(&tm2
); 
1111             if ( timet 
!= (time_t)-1 ) 
1113                 timet 
+= GetTimeZone(); 
1119         wxFAIL_MSG( _T("mktime() failed") ); 
1121         *this = wxInvalidDateTime
; 
1131 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
, 
1132                             wxDateTime_t minute
, 
1133                             wxDateTime_t second
, 
1134                             wxDateTime_t millisec
) 
1136     // we allow seconds to be 61 to account for the leap seconds, even if we 
1137     // don't use them really 
1138     wxDATETIME_CHECK( hour 
< 24 && 
1142                       _T("Invalid time in wxDateTime::Set()") ); 
1144     // get the current date from system 
1145     struct tm 
*tm 
= GetTmNow(); 
1147     wxDATETIME_CHECK( tm
, _T("localtime() failed") ); 
1151     tm
->tm_min 
= minute
; 
1152     tm
->tm_sec 
= second
; 
1156     // and finally adjust milliseconds 
1157     return SetMillisecond(millisec
); 
1160 wxDateTime
& wxDateTime::Set(wxDateTime_t day
, 
1164                             wxDateTime_t minute
, 
1165                             wxDateTime_t second
, 
1166                             wxDateTime_t millisec
) 
1168     wxDATETIME_CHECK( hour 
< 24 && 
1172                       _T("Invalid time in wxDateTime::Set()") ); 
1174     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
1176     wxDATETIME_CHECK( (0 < day
) && (day 
<= GetNumberOfDays(month
, year
)), 
1177                       _T("Invalid date in wxDateTime::Set()") ); 
1179     // the range of time_t type (inclusive) 
1180     static const int yearMinInRange 
= 1970; 
1181     static const int yearMaxInRange 
= 2037; 
1183     // test only the year instead of testing for the exact end of the Unix 
1184     // time_t range - it doesn't bring anything to do more precise checks 
1185     if ( year 
>= yearMinInRange 
&& year 
<= yearMaxInRange 
) 
1187         // use the standard library version if the date is in range - this is 
1188         // probably more efficient than our code 
1190         tm
.tm_year 
= year 
- 1900; 
1196         tm
.tm_isdst 
= -1;       // mktime() will guess it 
1200         // and finally adjust milliseconds 
1201         return SetMillisecond(millisec
); 
1205         // do time calculations ourselves: we want to calculate the number of 
1206         // milliseconds between the given date and the epoch 
1208         // get the JDN for the midnight of this day 
1209         m_time 
= GetTruncatedJDN(day
, month
, year
); 
1210         m_time 
-= EPOCH_JDN
; 
1211         m_time 
*= SECONDS_PER_DAY 
* TIME_T_FACTOR
; 
1213         // JDN corresponds to GMT, we take localtime 
1214         Add(wxTimeSpan(hour
, minute
, second 
+ GetTimeZone(), millisec
)); 
1220 wxDateTime
& wxDateTime::Set(double jdn
) 
1222     // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn 
1224     jdn 
-= EPOCH_JDN 
+ 0.5; 
1226     jdn 
*= MILLISECONDS_PER_DAY
; 
1233 wxDateTime
& wxDateTime::ResetTime() 
1237     if ( tm
.hour 
|| tm
.min 
|| tm
.sec 
|| tm
.msec 
) 
1250 // ---------------------------------------------------------------------------- 
1251 // time_t <-> broken down time conversions 
1252 // ---------------------------------------------------------------------------- 
1254 wxDateTime::Tm 
wxDateTime::GetTm(const TimeZone
& tz
) const 
1256     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1258     time_t time 
= GetTicks(); 
1259     if ( time 
!= (time_t)-1 ) 
1261         // use C RTL functions 
1263         if ( tz
.GetOffset() == -GetTimeZone() ) 
1265             // we are working with local time 
1266             tm 
= localtime(&time
); 
1268             // should never happen 
1269             wxCHECK_MSG( tm
, Tm(), _T("localtime() failed") ); 
1273             time 
+= (time_t)tz
.GetOffset(); 
1274 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning 
1275             int time2 
= (int) time
; 
1283                 // should never happen 
1284                 wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") ); 
1288                 tm 
= (struct tm 
*)NULL
; 
1294             // adjust the milliseconds 
1296             long timeOnly 
= (m_time 
% MILLISECONDS_PER_DAY
).ToLong(); 
1297             tm2
.msec 
= (wxDateTime_t
)(timeOnly 
% 1000); 
1300         //else: use generic code below 
1303     // remember the time and do the calculations with the date only - this 
1304     // eliminates rounding errors of the floating point arithmetics 
1306     wxLongLong timeMidnight 
= m_time 
+ tz
.GetOffset() * 1000; 
1308     long timeOnly 
= (timeMidnight 
% MILLISECONDS_PER_DAY
).ToLong(); 
1310     // we want to always have positive time and timeMidnight to be really 
1311     // the midnight before it 
1314         timeOnly 
= MILLISECONDS_PER_DAY 
+ timeOnly
; 
1317     timeMidnight 
-= timeOnly
; 
1319     // calculate the Gregorian date from JDN for the midnight of our date: 
1320     // this will yield day, month (in 1..12 range) and year 
1322     // actually, this is the JDN for the noon of the previous day 
1323     long jdn 
= (timeMidnight 
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
; 
1325     // CREDIT: code below is by Scott E. Lee (but bugs are mine) 
1327     wxASSERT_MSG( jdn 
> -2, _T("JDN out of range") ); 
1329     // calculate the century 
1330     long temp 
= (jdn 
+ JDN_OFFSET
) * 4 - 1; 
1331     long century 
= temp 
/ DAYS_PER_400_YEARS
; 
1333     // then the year and day of year (1 <= dayOfYear <= 366) 
1334     temp 
= ((temp 
% DAYS_PER_400_YEARS
) / 4) * 4 + 3; 
1335     long year 
= (century 
* 100) + (temp 
/ DAYS_PER_4_YEARS
); 
1336     long dayOfYear 
= (temp 
% DAYS_PER_4_YEARS
) / 4 + 1; 
1338     // and finally the month and day of the month 
1339     temp 
= dayOfYear 
* 5 - 3; 
1340     long month 
= temp 
/ DAYS_PER_5_MONTHS
; 
1341     long day 
= (temp 
% DAYS_PER_5_MONTHS
) / 5 + 1; 
1343     // month is counted from March - convert to normal 
1354     // year is offset by 4800 
1357     // check that the algorithm gave us something reasonable 
1358     wxASSERT_MSG( (0 < month
) && (month 
<= 12), _T("invalid month") ); 
1359     wxASSERT_MSG( (1 <= day
) && (day 
< 32), _T("invalid day") ); 
1361     // construct Tm from these values 
1363     tm
.year 
= (int)year
; 
1364     tm
.mon 
= (Month
)(month 
- 1); // algorithm yields 1 for January, not 0 
1365     tm
.mday 
= (wxDateTime_t
)day
; 
1366     tm
.msec 
= (wxDateTime_t
)(timeOnly 
% 1000); 
1367     timeOnly 
-= tm
.msec
; 
1368     timeOnly 
/= 1000;               // now we have time in seconds 
1370     tm
.sec 
= (wxDateTime_t
)(timeOnly 
% 60); 
1372     timeOnly 
/= 60;                 // now we have time in minutes 
1374     tm
.min 
= (wxDateTime_t
)(timeOnly 
% 60); 
1377     tm
.hour 
= (wxDateTime_t
)(timeOnly 
/ 60); 
1382 wxDateTime
& wxDateTime::SetYear(int year
) 
1384     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1393 wxDateTime
& wxDateTime::SetMonth(Month month
) 
1395     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1404 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
) 
1406     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1415 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
) 
1417     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1426 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
) 
1428     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1437 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
) 
1439     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1448 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
) 
1450     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1452     // we don't need to use GetTm() for this one 
1453     m_time 
-= m_time 
% 1000l; 
1454     m_time 
+= millisecond
; 
1459 // ---------------------------------------------------------------------------- 
1460 // wxDateTime arithmetics 
1461 // ---------------------------------------------------------------------------- 
1463 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
) 
1467     tm
.year 
+= diff
.GetYears(); 
1468     tm
.AddMonths(diff
.GetMonths()); 
1470     // check that the resulting date is valid 
1471     if ( tm
.mday 
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) ) 
1473         // We suppose that when adding one month to Jan 31 we want to get Feb 
1474         // 28 (or 29), i.e. adding a month to the last day of the month should 
1475         // give the last day of the next month which is quite logical. 
1477         // Unfortunately, there is no logic way to understand what should 
1478         // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)? 
1479         // We make it Feb 28 (last day too), but it is highly questionable. 
1480         tm
.mday 
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
); 
1483     tm
.AddDays(diff
.GetTotalDays()); 
1487     wxASSERT_MSG( IsSameTime(tm
), 
1488                   _T("Add(wxDateSpan) shouldn't modify time") ); 
1493 // ---------------------------------------------------------------------------- 
1494 // Weekday and monthday stuff 
1495 // ---------------------------------------------------------------------------- 
1497 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
, WeekDay weekday
) 
1499     int year 
= GetYear(); 
1501     // Jan 4 always lies in the 1st week of the year 
1503     SetToWeekDayInSameWeek(weekday
) += wxDateSpan::Weeks(numWeek
); 
1505     if ( GetYear() != year 
) 
1507         // oops... numWeek was too big 
1514 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
, 
1517     // take the current month/year if none specified 
1518     if ( year 
== Inv_Year 
) 
1520     if ( month 
== Inv_Month 
) 
1523     return Set(GetNumOfDaysInMonth(year
, month
), month
, year
); 
1526 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
) 
1528     wxDATETIME_CHECK( weekday 
!= Inv_WeekDay
, _T("invalid weekday") ); 
1530     WeekDay wdayThis 
= GetWeekDay(); 
1531     if ( weekday 
== wdayThis 
) 
1536     else if ( weekday 
< wdayThis 
) 
1538         return Subtract(wxDateSpan::Days(wdayThis 
- weekday
)); 
1540     else // weekday > wdayThis 
1542         return Add(wxDateSpan::Days(weekday 
- wdayThis
)); 
1546 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
) 
1548     wxDATETIME_CHECK( weekday 
!= Inv_WeekDay
, _T("invalid weekday") ); 
1551     WeekDay wdayThis 
= GetWeekDay(); 
1552     if ( weekday 
== wdayThis 
) 
1557     else if ( weekday 
< wdayThis 
) 
1559         // need to advance a week 
1560         diff 
= 7 - (wdayThis 
- weekday
); 
1562     else // weekday > wdayThis 
1564         diff 
= weekday 
- wdayThis
; 
1567     return Add(wxDateSpan::Days(diff
)); 
1570 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
) 
1572     wxDATETIME_CHECK( weekday 
!= Inv_WeekDay
, _T("invalid weekday") ); 
1575     WeekDay wdayThis 
= GetWeekDay(); 
1576     if ( weekday 
== wdayThis 
) 
1581     else if ( weekday 
> wdayThis 
) 
1583         // need to go to previous week 
1584         diff 
= 7 - (weekday 
- wdayThis
); 
1586     else // weekday < wdayThis 
1588         diff 
= wdayThis 
- weekday
; 
1591     return Subtract(wxDateSpan::Days(diff
)); 
1594 bool wxDateTime::SetToWeekDay(WeekDay weekday
, 
1599     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, FALSE
, _T("invalid weekday") ); 
1601     // we don't check explicitly that -5 <= n <= 5 because we will return FALSE 
1602     // anyhow in such case - but may be should still give an assert for it? 
1604     // take the current month/year if none specified 
1605     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
1609     // TODO this probably could be optimised somehow... 
1613         // get the first day of the month 
1614         dt
.Set(1, month
, year
); 
1617         WeekDay wdayFirst 
= dt
.GetWeekDay(); 
1619         // go to the first weekday of the month 
1620         int diff 
= weekday 
- wdayFirst
; 
1624         // add advance n-1 weeks more 
1627         dt 
+= wxDateSpan::Days(diff
); 
1629     else // count from the end of the month 
1631         // get the last day of the month 
1632         dt
.SetToLastMonthDay(month
, year
); 
1635         WeekDay wdayLast 
= dt
.GetWeekDay(); 
1637         // go to the last weekday of the month 
1638         int diff 
= wdayLast 
- weekday
; 
1642         // and rewind n-1 weeks from there 
1645         dt 
-= wxDateSpan::Days(diff
); 
1648     // check that it is still in the same month 
1649     if ( dt
.GetMonth() == month 
) 
1657         // no such day in this month 
1662 wxDateTime::wxDateTime_t 
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const 
1666     return gs_cumulatedDays
[IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
; 
1669 wxDateTime::wxDateTime_t 
wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, 
1670                                                    const TimeZone
& tz
) const 
1672     if ( flags 
== Default_First 
) 
1674         flags 
= GetCountry() == USA 
? Sunday_First 
: Monday_First
; 
1677     wxDateTime_t nDayInYear 
= GetDayOfYear(tz
); 
1680     WeekDay wd 
= GetWeekDay(tz
); 
1681     if ( flags 
== Sunday_First 
) 
1683         week 
= (nDayInYear 
- wd 
+ 7) / 7; 
1687         // have to shift the week days values 
1688         week 
= (nDayInYear 
- (wd 
- 1 + 7) % 7 + 7) / 7; 
1691     // FIXME some more elegant way?? 
1692     WeekDay wdYearStart 
= wxDateTime(1, Jan
, GetYear()).GetWeekDay(); 
1693     if ( wdYearStart 
== Wed 
|| wdYearStart 
== Thu 
) 
1701 wxDateTime::wxDateTime_t 
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
, 
1702                                                     const TimeZone
& tz
) const 
1705     wxDateTime dtMonthStart 
= wxDateTime(1, tm
.mon
, tm
.year
); 
1706     int nWeek 
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1; 
1709         // this may happen for January when Jan, 1 is the last week of the 
1711         nWeek 
+= IsLeapYear(tm
.year 
- 1) ? 53 : 52; 
1714     return (wxDateTime::wxDateTime_t
)nWeek
; 
1717 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
) 
1719     int year 
= GetYear(); 
1720     wxDATETIME_CHECK( (0 < yday
) && (yday 
<= GetNumberOfDays(year
)), 
1721                       _T("invalid year day") ); 
1723     bool isLeap 
= IsLeapYear(year
); 
1724     for ( Month mon 
= Jan
; mon 
< Inv_Month
; wxNextMonth(mon
) ) 
1726         // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we 
1727         // don't need it neither - because of the CHECK above we know that 
1728         // yday lies in December then 
1729         if ( (mon 
== Dec
) || (yday 
< gs_cumulatedDays
[isLeap
][mon 
+ 1]) ) 
1731             Set(yday 
- gs_cumulatedDays
[isLeap
][mon
], mon
, year
); 
1740 // ---------------------------------------------------------------------------- 
1741 // Julian day number conversion and related stuff 
1742 // ---------------------------------------------------------------------------- 
1744 double wxDateTime::GetJulianDayNumber() const 
1746     // JDN are always expressed for the GMT dates 
1747     Tm 
tm(ToTimezone(GMT0
).GetTm(GMT0
)); 
1749     double result 
= GetTruncatedJDN(tm
.mday
, tm
.mon
, tm
.year
); 
1751     // add the part GetTruncatedJDN() neglected 
1754     // and now add the time: 86400 sec = 1 JDN 
1755     return result 
+ ((double)(60*(60*tm
.hour 
+ tm
.min
) + tm
.sec
)) / 86400; 
1758 double wxDateTime::GetRataDie() const 
1760     // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5 
1761     return GetJulianDayNumber() - 1721119.5 - 306; 
1764 // ---------------------------------------------------------------------------- 
1765 // timezone and DST stuff 
1766 // ---------------------------------------------------------------------------- 
1768 int wxDateTime::IsDST(wxDateTime::Country country
) const 
1770     wxCHECK_MSG( country 
== Country_Default
, -1, 
1771                  _T("country support not implemented") ); 
1773     // use the C RTL for the dates in the standard range 
1774     time_t timet 
= GetTicks(); 
1775     if ( timet 
!= (time_t)-1 ) 
1777         tm 
*tm 
= localtime(&timet
); 
1779         wxCHECK_MSG( tm
, -1, _T("localtime() failed") ); 
1781         return tm
->tm_isdst
; 
1785         int year 
= GetYear(); 
1787         if ( !IsDSTApplicable(year
, country
) ) 
1789             // no DST time in this year in this country 
1793         return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
)); 
1797 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
) 
1799     long secDiff 
= GetTimeZone() + tz
.GetOffset(); 
1801     // we need to know whether DST is or not in effect for this date unless 
1802     // the test disabled by the caller 
1803     if ( !noDST 
&& (IsDST() == 1) ) 
1805         // FIXME we assume that the DST is always shifted by 1 hour 
1809     return Subtract(wxTimeSpan::Seconds(secDiff
)); 
1812 // ---------------------------------------------------------------------------- 
1813 // wxDateTime to/from text representations 
1814 // ---------------------------------------------------------------------------- 
1816 wxString 
wxDateTime::Format(const wxChar 
*format
, const TimeZone
& tz
) const 
1818     wxCHECK_MSG( format
, _T(""), _T("NULL format in wxDateTime::Format") ); 
1820     // we have to use our own implementation if the date is out of range of 
1821     // strftime() or if we use non standard specificators 
1822     time_t time 
= GetTicks(); 
1823     if ( (time 
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) ) 
1827         if ( tz
.GetOffset() == -GetTimeZone() ) 
1829             // we are working with local time 
1830             tm 
= localtime(&time
); 
1832             // should never happen 
1833             wxCHECK_MSG( tm
, wxEmptyString
, _T("localtime() failed") ); 
1837             time 
+= (int)tz
.GetOffset(); 
1839 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning 
1840             int time2 
= (int) time
; 
1848                 // should never happen 
1849                 wxCHECK_MSG( tm
, wxEmptyString
, _T("gmtime() failed") ); 
1853                 tm 
= (struct tm 
*)NULL
; 
1859             return CallStrftime(format
, tm
); 
1861         //else: use generic code below 
1864     // we only parse ANSI C format specifications here, no POSIX 2 
1865     // complications, no GNU extensions but we do add support for a "%l" format 
1866     // specifier allowing to get the number of milliseconds 
1869     // used for calls to strftime() when we only deal with time 
1870     struct tm tmTimeOnly
; 
1871     tmTimeOnly
.tm_hour 
= tm
.hour
; 
1872     tmTimeOnly
.tm_min 
= tm
.min
; 
1873     tmTimeOnly
.tm_sec 
= tm
.sec
; 
1874     tmTimeOnly
.tm_wday 
= 0; 
1875     tmTimeOnly
.tm_yday 
= 0; 
1876     tmTimeOnly
.tm_mday 
= 1;         // any date will do 
1877     tmTimeOnly
.tm_mon 
= 0; 
1878     tmTimeOnly
.tm_year 
= 76; 
1879     tmTimeOnly
.tm_isdst 
= 0;        // no DST, we adjust for tz ourselves 
1881     wxString tmp
, res
, fmt
; 
1882     for ( const wxChar 
*p 
= format
; *p
; p
++ ) 
1884         if ( *p 
!= _T('%') ) 
1892         // set the default format 
1895             case _T('Y'):               // year has 4 digits 
1899             case _T('j'):               // day of year has 3 digits 
1900             case _T('l'):               // milliseconds have 3 digits 
1905                 // it's either another valid format specifier in which case 
1906                 // the format is "%02d" (for all the rest) or we have the 
1907                 // field width preceding the format in which case it will 
1908                 // override the default format anyhow 
1912         bool restart 
= TRUE
; 
1917             // start of the format specification 
1920                 case _T('a'):       // a weekday name 
1922                     // second parameter should be TRUE for abbreviated names 
1923                     res 
+= GetWeekDayName(tm
.GetWeekDay(), 
1924                                           *p 
== _T('a') ? Name_Abbr 
: Name_Full
); 
1927                 case _T('b'):       // a month name 
1929                     res 
+= GetMonthName(tm
.mon
, 
1930                                         *p 
== _T('b') ? Name_Abbr 
: Name_Full
); 
1933                 case _T('c'):       // locale default date and time  representation 
1934                 case _T('x'):       // locale default date representation 
1936                     // the problem: there is no way to know what do these format 
1937                     // specifications correspond to for the current locale. 
1939                     // the solution: use a hack and still use strftime(): first 
1940                     // find the YEAR which is a year in the strftime() range (1970 
1941                     // - 2038) whose Jan 1 falls on the same week day as the Jan 1 
1942                     // of the real year. Then make a copy of the format and 
1943                     // replace all occurences of YEAR in it with some unique 
1944                     // string not appearing anywhere else in it, then use 
1945                     // strftime() to format the date in year YEAR and then replace 
1946                     // YEAR back by the real year and the unique replacement 
1947                     // string back with YEAR. Notice that "all occurences of YEAR" 
1948                     // means all occurences of 4 digit as well as 2 digit form! 
1950                     // the bugs: we assume that neither of %c nor %x contains any 
1951                     // fields which may change between the YEAR and real year. For 
1952                     // example, the week number (%U, %W) and the day number (%j) 
1953                     // will change if one of these years is leap and the other one 
1956                         // find the YEAR: normally, for any year X, Jan 1 or the 
1957                         // year X + 28 is the same weekday as Jan 1 of X (because 
1958                         // the weekday advances by 1 for each normal X and by 2 
1959                         // for each leap X, hence by 5 every 4 years or by 35 
1960                         // which is 0 mod 7 every 28 years) but this rule breaks 
1961                         // down if there are years between X and Y which are 
1962                         // divisible by 4 but not leap (i.e. divisible by 100 but 
1963                         // not 400), hence the correction. 
1965                         int yearReal 
= GetYear(tz
); 
1966                         int mod28 
= yearReal 
% 28; 
1968                         // be careful to not go too far - we risk to leave the 
1973                             year 
= 1988 + mod28
;      // 1988 == 0 (mod 28) 
1977                             year 
= 1970 + mod28 
- 10; // 1970 == 10 (mod 28) 
1980                         int nCentury 
= year 
/ 100, 
1981                             nCenturyReal 
= yearReal 
/ 100; 
1983                         // need to adjust for the years divisble by 400 which are 
1984                         // not leap but are counted like leap ones if we just take 
1985                         // the number of centuries in between for nLostWeekDays 
1986                         int nLostWeekDays 
= (nCentury 
- nCenturyReal
) - 
1987                                             (nCentury 
/ 4 - nCenturyReal 
/ 4); 
1989                         // we have to gain back the "lost" weekdays: note that the 
1990                         // effect of this loop is to not do anything to 
1991                         // nLostWeekDays (which we won't use any more), but to 
1992                         // (indirectly) set the year correctly 
1993                         while ( (nLostWeekDays 
% 7) != 0 ) 
1995                             nLostWeekDays 
+= year
++ % 4 ? 1 : 2; 
1998                         // at any rate, we couldn't go further than 1988 + 9 + 28! 
1999                         wxASSERT_MSG( year 
< 2030, 
2000                                       _T("logic error in wxDateTime::Format") ); 
2002                         wxString strYear
, strYear2
; 
2003                         strYear
.Printf(_T("%d"), year
); 
2004                         strYear2
.Printf(_T("%d"), year 
% 100); 
2006                         // find two strings not occuring in format (this is surely 
2007                         // not optimal way of doing it... improvements welcome!) 
2008                         wxString fmt 
= format
; 
2009                         wxString replacement 
= (wxChar
)-1; 
2010                         while ( fmt
.Find(replacement
) != wxNOT_FOUND 
) 
2012                             replacement 
<< (wxChar
)-1; 
2015                         wxString replacement2 
= (wxChar
)-2; 
2016                         while ( fmt
.Find(replacement
) != wxNOT_FOUND 
) 
2018                             replacement 
<< (wxChar
)-2; 
2021                         // replace all occurences of year with it 
2022                         bool wasReplaced 
= fmt
.Replace(strYear
, replacement
) > 0; 
2024                             wasReplaced 
= fmt
.Replace(strYear2
, replacement2
) > 0; 
2026                         // use strftime() to format the same date but in supported 
2029                         // NB: we assume that strftime() doesn't check for the 
2030                         //     date validity and will happily format the date 
2031                         //     corresponding to Feb 29 of a non leap year (which 
2032                         //     may happen if yearReal was leap and year is not) 
2033                         struct tm tmAdjusted
; 
2035                         tmAdjusted
.tm_hour 
= tm
.hour
; 
2036                         tmAdjusted
.tm_min 
= tm
.min
; 
2037                         tmAdjusted
.tm_sec 
= tm
.sec
; 
2038                         tmAdjusted
.tm_wday 
= tm
.GetWeekDay(); 
2039                         tmAdjusted
.tm_yday 
= GetDayOfYear(); 
2040                         tmAdjusted
.tm_mday 
= tm
.mday
; 
2041                         tmAdjusted
.tm_mon 
= tm
.mon
; 
2042                         tmAdjusted
.tm_year 
= year 
- 1900; 
2043                         tmAdjusted
.tm_isdst 
= 0; // no DST, already adjusted 
2044                         wxString str 
= CallStrftime(*p 
== _T('c') ? _T("%c") 
2048                         // now replace the occurence of 1999 with the real year 
2049                         wxString strYearReal
, strYearReal2
; 
2050                         strYearReal
.Printf(_T("%04d"), yearReal
); 
2051                         strYearReal2
.Printf(_T("%02d"), yearReal 
% 100); 
2052                         str
.Replace(strYear
, strYearReal
); 
2053                         str
.Replace(strYear2
, strYearReal2
); 
2055                         // and replace back all occurences of replacement string 
2058                             str
.Replace(replacement2
, strYear2
); 
2059                             str
.Replace(replacement
, strYear
); 
2066                 case _T('d'):       // day of a month (01-31) 
2067                     res 
+= wxString::Format(fmt
, tm
.mday
); 
2070                 case _T('H'):       // hour in 24h format (00-23) 
2071                     res 
+= wxString::Format(fmt
, tm
.hour
); 
2074                 case _T('I'):       // hour in 12h format (01-12) 
2076                         // 24h -> 12h, 0h -> 12h too 
2077                         int hour12 
= tm
.hour 
> 12 ? tm
.hour 
- 12 
2078                                                   : tm
.hour 
? tm
.hour 
: 12; 
2079                         res 
+= wxString::Format(fmt
, hour12
); 
2083                 case _T('j'):       // day of the year 
2084                     res 
+= wxString::Format(fmt
, GetDayOfYear(tz
)); 
2087                 case _T('l'):       // milliseconds (NOT STANDARD) 
2088                     res 
+= wxString::Format(fmt
, GetMillisecond(tz
)); 
2091                 case _T('m'):       // month as a number (01-12) 
2092                     res 
+= wxString::Format(fmt
, tm
.mon 
+ 1); 
2095                 case _T('M'):       // minute as a decimal number (00-59) 
2096                     res 
+= wxString::Format(fmt
, tm
.min
); 
2099                 case _T('p'):       // AM or PM string 
2100                     res 
+= CallStrftime(_T("%p"), &tmTimeOnly
); 
2103                 case _T('S'):       // second as a decimal number (00-61) 
2104                     res 
+= wxString::Format(fmt
, tm
.sec
); 
2107                 case _T('U'):       // week number in the year (Sunday 1st week day) 
2108                     res 
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
)); 
2111                 case _T('W'):       // week number in the year (Monday 1st week day) 
2112                     res 
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
)); 
2115                 case _T('w'):       // weekday as a number (0-6), Sunday = 0 
2116                     res 
+= wxString::Format(fmt
, tm
.GetWeekDay()); 
2119                 // case _T('x'): -- handled with "%c" 
2121                 case _T('X'):       // locale default time representation 
2122                     // just use strftime() to format the time for us 
2123                     res 
+= CallStrftime(_T("%X"), &tmTimeOnly
); 
2126                 case _T('y'):       // year without century (00-99) 
2127                     res 
+= wxString::Format(fmt
, tm
.year 
% 100); 
2130                 case _T('Y'):       // year with century 
2131                     res 
+= wxString::Format(fmt
, tm
.year
); 
2134                 case _T('Z'):       // timezone name 
2135                     res 
+= CallStrftime(_T("%Z"), &tmTimeOnly
); 
2139                     // is it the format width? 
2141                     while ( *p 
== _T('-') || *p 
== _T('+') || 
2142                             *p 
== _T(' ') || wxIsdigit(*p
) ) 
2147                     if ( !fmt
.IsEmpty() ) 
2149                         // we've only got the flags and width so far in fmt 
2150                         fmt
.Prepend(_T('%')); 
2151                         fmt
.Append(_T('d')); 
2158                     // no, it wasn't the width 
2159                     wxFAIL_MSG(_T("unknown format specificator")); 
2161                     // fall through and just copy it nevertheless 
2163                 case _T('%'):       // a percent sign 
2167                 case 0:             // the end of string 
2168                     wxFAIL_MSG(_T("missing format at the end of string")); 
2170                     // just put the '%' which was the last char in format 
2180 // this function parses a string in (strict) RFC 822 format: see the section 5 
2181 // of the RFC for the detailed description, but briefly it's something of the 
2182 // form "Sat, 18 Dec 1999 00:48:30 +0100" 
2184 // this function is "strict" by design - it must reject anything except true 
2185 // RFC822 time specs. 
2187 // TODO a great candidate for using reg exps 
2188 const wxChar 
*wxDateTime::ParseRfc822Date(const wxChar
* date
) 
2190     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
2192     const wxChar 
*p 
= date
; 
2193     const wxChar 
*comma 
= wxStrchr(p
, _T(',')); 
2196         // the part before comma is the weekday 
2198         // skip it for now - we don't use but might check that it really 
2199         // corresponds to the specfied date 
2202         if ( *p 
!= _T(' ') ) 
2204             wxLogDebug(_T("no space after weekday in RFC822 time spec")); 
2206             return (wxChar 
*)NULL
; 
2212     // the following 1 or 2 digits are the day number 
2213     if ( !wxIsdigit(*p
) ) 
2215         wxLogDebug(_T("day number expected in RFC822 time spec, none found")); 
2217         return (wxChar 
*)NULL
; 
2220     wxDateTime_t day 
= *p
++ - _T('0'); 
2221     if ( wxIsdigit(*p
) ) 
2224         day 
+= *p
++ - _T('0'); 
2227     if ( *p
++ != _T(' ') ) 
2229         return (wxChar 
*)NULL
; 
2232     // the following 3 letters specify the month 
2233     wxString 
monName(p
, 3); 
2235     if ( monName 
== _T("Jan") ) 
2237     else if ( monName 
== _T("Feb") ) 
2239     else if ( monName 
== _T("Mar") ) 
2241     else if ( monName 
== _T("Apr") ) 
2243     else if ( monName 
== _T("May") ) 
2245     else if ( monName 
== _T("Jun") ) 
2247     else if ( monName 
== _T("Jul") ) 
2249     else if ( monName 
== _T("Aug") ) 
2251     else if ( monName 
== _T("Sep") ) 
2253     else if ( monName 
== _T("Oct") ) 
2255     else if ( monName 
== _T("Nov") ) 
2257     else if ( monName 
== _T("Dec") ) 
2261         wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str()); 
2263         return (wxChar 
*)NULL
; 
2268     if ( *p
++ != _T(' ') ) 
2270         return (wxChar 
*)NULL
; 
2274     if ( !wxIsdigit(*p
) ) 
2277         return (wxChar 
*)NULL
; 
2280     int year 
= *p
++ - _T('0'); 
2282     if ( !wxIsdigit(*p
) ) 
2284         // should have at least 2 digits in the year 
2285         return (wxChar 
*)NULL
; 
2289     year 
+= *p
++ - _T('0'); 
2291     // is it a 2 digit year (as per original RFC 822) or a 4 digit one? 
2292     if ( wxIsdigit(*p
) ) 
2295         year 
+= *p
++ - _T('0'); 
2297         if ( !wxIsdigit(*p
) ) 
2299             // no 3 digit years please 
2300             return (wxChar 
*)NULL
; 
2304         year 
+= *p
++ - _T('0'); 
2307     if ( *p
++ != _T(' ') ) 
2309         return (wxChar 
*)NULL
; 
2312     // time is in the format hh:mm:ss and seconds are optional 
2313     if ( !wxIsdigit(*p
) ) 
2315         return (wxChar 
*)NULL
; 
2318     wxDateTime_t hour 
= *p
++ - _T('0'); 
2320     if ( !wxIsdigit(*p
) ) 
2322         return (wxChar 
*)NULL
; 
2326     hour 
+= *p
++ - _T('0'); 
2328     if ( *p
++ != _T(':') ) 
2330         return (wxChar 
*)NULL
; 
2333     if ( !wxIsdigit(*p
) ) 
2335         return (wxChar 
*)NULL
; 
2338     wxDateTime_t min 
= *p
++ - _T('0'); 
2340     if ( !wxIsdigit(*p
) ) 
2342         return (wxChar 
*)NULL
; 
2346     min 
+= *p
++ - _T('0'); 
2348     wxDateTime_t sec 
= 0; 
2349     if ( *p
++ == _T(':') ) 
2351         if ( !wxIsdigit(*p
) ) 
2353             return (wxChar 
*)NULL
; 
2356         sec 
= *p
++ - _T('0'); 
2358         if ( !wxIsdigit(*p
) ) 
2360             return (wxChar 
*)NULL
; 
2364         sec 
+= *p
++ - _T('0'); 
2367     if ( *p
++ != _T(' ') ) 
2369         return (wxChar 
*)NULL
; 
2372     // and now the interesting part: the timezone 
2374     if ( *p 
== _T('-') || *p 
== _T('+') ) 
2376         // the explicit offset given: it has the form of hhmm 
2377         bool plus 
= *p
++ == _T('+'); 
2379         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
2381             return (wxChar 
*)NULL
; 
2385         offset 
= 60*(10*(*p 
- _T('0')) + (*(p 
+ 1) - _T('0'))); 
2389         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
2391             return (wxChar 
*)NULL
; 
2395         offset 
+= 10*(*p 
- _T('0')) + (*(p 
+ 1) - _T('0')); 
2406         // the symbolic timezone given: may be either military timezone or one 
2407         // of standard abbreviations 
2410             // military: Z = UTC, J unused, A = -1, ..., Y = +12 
2411             static const int offsets
[26] = 
2413                 //A  B   C   D   E   F   G   H   I    J    K    L    M 
2414                 -1, -2, -3, -4, -5, -6, -7, -8, -9,   0, -10, -11, -12, 
2415                 //N  O   P   R   Q   S   T   U   V    W    Z    Y    Z 
2416                 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0 
2419             if ( *p 
< _T('A') || *p 
> _T('Z') || *p 
== _T('J') ) 
2421                 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
); 
2423                 return (wxChar 
*)NULL
; 
2426             offset 
= offsets
[*p
++ - _T('A')]; 
2432             if ( tz 
== _T("UT") || tz 
== _T("UTC") || tz 
== _T("GMT") ) 
2434             else if ( tz 
== _T("AST") ) 
2435                 offset 
= AST 
- GMT0
; 
2436             else if ( tz 
== _T("ADT") ) 
2437                 offset 
= ADT 
- GMT0
; 
2438             else if ( tz 
== _T("EST") ) 
2439                 offset 
= EST 
- GMT0
; 
2440             else if ( tz 
== _T("EDT") ) 
2441                 offset 
= EDT 
- GMT0
; 
2442             else if ( tz 
== _T("CST") ) 
2443                 offset 
= CST 
- GMT0
; 
2444             else if ( tz 
== _T("CDT") ) 
2445                 offset 
= CDT 
- GMT0
; 
2446             else if ( tz 
== _T("MST") ) 
2447                 offset 
= MST 
- GMT0
; 
2448             else if ( tz 
== _T("MDT") ) 
2449                 offset 
= MDT 
- GMT0
; 
2450             else if ( tz 
== _T("PST") ) 
2451                 offset 
= PST 
- GMT0
; 
2452             else if ( tz 
== _T("PDT") ) 
2453                 offset 
= PDT 
- GMT0
; 
2456                 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
); 
2458                 return (wxChar 
*)NULL
; 
2468     // the spec was correct 
2469     Set(day
, mon
, year
, hour
, min
, sec
); 
2470     MakeTimezone((wxDateTime_t
)(60*offset
)); 
2475 const wxChar 
*wxDateTime::ParseFormat(const wxChar 
*date
, 
2476                                       const wxChar 
*format
, 
2477                                       const wxDateTime
& dateDef
) 
2479     wxCHECK_MSG( date 
&& format
, (wxChar 
*)NULL
, 
2480                  _T("NULL pointer in wxDateTime::ParseFormat()") ); 
2485     // what fields have we found? 
2486     bool haveWDay 
= FALSE
, 
2495     bool hourIsIn12hFormat 
= FALSE
, // or in 24h one? 
2496          isPM 
= FALSE
;              // AM by default 
2498     // and the value of the items we have (init them to get rid of warnings) 
2499     wxDateTime_t sec 
= 0, 
2502     WeekDay wday 
= Inv_WeekDay
; 
2503     wxDateTime_t yday 
= 0, 
2505     wxDateTime::Month mon 
= Inv_Month
; 
2508     const wxChar 
*input 
= date
; 
2509     for ( const wxChar 
*fmt 
= format
; *fmt
; fmt
++ ) 
2511         if ( *fmt 
!= _T('%') ) 
2513             if ( wxIsspace(*fmt
) ) 
2515                 // a white space in the format string matches 0 or more white 
2516                 // spaces in the input 
2517                 while ( wxIsspace(*input
) ) 
2524                 // any other character (not whitespace, not '%') must be 
2525                 // matched by itself in the input 
2526                 if ( *input
++ != *fmt 
) 
2529                     return (wxChar 
*)NULL
; 
2533             // done with this format char 
2537         // start of a format specification 
2539         // parse the optional width 
2541         while ( isdigit(*++fmt
) ) 
2544             width 
+= *fmt 
- _T('0'); 
2547         // then the format itself 
2550             case _T('a'):       // a weekday name 
2553                     int flag 
= *fmt 
== _T('a') ? Name_Abbr 
: Name_Full
; 
2554                     wday 
= GetWeekDayFromName(GetAlphaToken(input
), flag
); 
2555                     if ( wday 
== Inv_WeekDay 
) 
2558                         return (wxChar 
*)NULL
; 
2564             case _T('b'):       // a month name 
2567                     int flag 
= *fmt 
== _T('b') ? Name_Abbr 
: Name_Full
; 
2568                     mon 
= GetMonthFromName(GetAlphaToken(input
), flag
); 
2569                     if ( mon 
== Inv_Month 
) 
2572                         return (wxChar 
*)NULL
; 
2578             case _T('c'):       // locale default date and time  representation 
2582                     // this is the format which corresponds to ctime() output 
2583                     // and strptime("%c") should parse it, so try it first 
2584                     static const wxChar 
*fmtCtime 
= _T("%a %b %d %H:%M:%S %Y"); 
2586                     const wxChar 
*result 
= dt
.ParseFormat(input
, fmtCtime
); 
2589                         result 
= dt
.ParseFormat(input
, _T("%x %X")); 
2594                         result 
= dt
.ParseFormat(input
, _T("%X %x")); 
2599                         // we've tried everything and still no match 
2600                         return (wxChar 
*)NULL
; 
2605                     haveDay 
= haveMon 
= haveYear 
= 
2606                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2620             case _T('d'):       // day of a month (01-31) 
2621                 if ( !GetNumericToken(width
, input
, &num
) || 
2622                         (num 
> 31) || (num 
< 1) ) 
2625                     return (wxChar 
*)NULL
; 
2628                 // we can't check whether the day range is correct yet, will 
2629                 // do it later - assume ok for now 
2631                 mday 
= (wxDateTime_t
)num
; 
2634             case _T('H'):       // hour in 24h format (00-23) 
2635                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 23) ) 
2638                     return (wxChar 
*)NULL
; 
2642                 hour 
= (wxDateTime_t
)num
; 
2645             case _T('I'):       // hour in 12h format (01-12) 
2646                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 12) ) 
2649                     return (wxChar 
*)NULL
; 
2653                 hourIsIn12hFormat 
= TRUE
; 
2654                 hour 
= (wxDateTime_t
)(num 
% 12);        // 12 should be 0 
2657             case _T('j'):       // day of the year 
2658                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 366) ) 
2661                     return (wxChar 
*)NULL
; 
2665                 yday 
= (wxDateTime_t
)num
; 
2668             case _T('m'):       // month as a number (01-12) 
2669                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 12) ) 
2672                     return (wxChar 
*)NULL
; 
2676                 mon 
= (Month
)(num 
- 1); 
2679             case _T('M'):       // minute as a decimal number (00-59) 
2680                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 59) ) 
2683                     return (wxChar 
*)NULL
; 
2687                 min 
= (wxDateTime_t
)num
; 
2690             case _T('p'):       // AM or PM string 
2692                     wxString am
, pm
, token 
= GetAlphaToken(input
); 
2694                     GetAmPmStrings(&am
, &pm
); 
2695                     if ( token
.CmpNoCase(pm
) == 0 ) 
2699                     else if ( token
.CmpNoCase(am
) != 0 ) 
2702                         return (wxChar 
*)NULL
; 
2707             case _T('r'):       // time as %I:%M:%S %p 
2710                     input 
= dt
.ParseFormat(input
, _T("%I:%M:%S %p")); 
2714                         return (wxChar 
*)NULL
; 
2717                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2726             case _T('R'):       // time as %H:%M 
2729                     input 
= dt
.ParseFormat(input
, _T("%H:%M")); 
2733                         return (wxChar 
*)NULL
; 
2736                     haveHour 
= haveMin 
= TRUE
; 
2743             case _T('S'):       // second as a decimal number (00-61) 
2744                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 61) ) 
2747                     return (wxChar 
*)NULL
; 
2751                 sec 
= (wxDateTime_t
)num
; 
2754             case _T('T'):       // time as %H:%M:%S 
2757                     input 
= dt
.ParseFormat(input
, _T("%H:%M:%S")); 
2761                         return (wxChar 
*)NULL
; 
2764                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2773             case _T('w'):       // weekday as a number (0-6), Sunday = 0 
2774                 if ( !GetNumericToken(width
, input
, &num
) || (wday 
> 6) ) 
2777                     return (wxChar 
*)NULL
; 
2781                 wday 
= (WeekDay
)num
; 
2784             case _T('x'):       // locale default date representation 
2785 #ifdef HAVE_STRPTIME 
2786                 // try using strptime() - it may fail even if the input is 
2787                 // correct but the date is out of range, so we will fall back 
2788                 // to our generic code anyhow (FIXME !Unicode friendly) 
2791                     const wxChar 
*result 
= strptime(input
, "%x", &tm
); 
2796                         haveDay 
= haveMon 
= haveYear 
= TRUE
; 
2798                         year 
= 1900 + tm
.tm_year
; 
2799                         mon 
= (Month
)tm
.tm_mon
; 
2805 #endif // HAVE_STRPTIME 
2807                 // TODO query the LOCALE_IDATE setting under Win32 
2811                     wxString fmtDate
, fmtDateAlt
; 
2812                     if ( IsWestEuropeanCountry(GetCountry()) || 
2813                          GetCountry() == Russia 
) 
2815                         fmtDate 
= _T("%d/%m/%y"); 
2816                         fmtDateAlt 
= _T("%m/%d/%y"); 
2820                         fmtDate 
= _T("%m/%d/%y"); 
2821                         fmtDateAlt 
= _T("%d/%m/%y"); 
2824                     const wxChar 
*result 
= dt
.ParseFormat(input
, fmtDate
); 
2828                         // ok, be nice and try another one 
2829                         result 
= dt
.ParseFormat(input
, fmtDateAlt
); 
2835                         return (wxChar 
*)NULL
; 
2840                     haveDay 
= haveMon 
= haveYear 
= TRUE
; 
2851             case _T('X'):       // locale default time representation 
2852 #ifdef HAVE_STRPTIME 
2854                     // use strptime() to do it for us (FIXME !Unicode friendly) 
2856                     input 
= strptime(input
, "%X", &tm
); 
2859                         return (wxChar 
*)NULL
; 
2862                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2868 #else // !HAVE_STRPTIME 
2869                 // TODO under Win32 we can query the LOCALE_ITIME system 
2870                 //      setting which says whether the default time format is 
2873                     // try to parse what follows as "%H:%M:%S" and, if this 
2874                     // fails, as "%I:%M:%S %p" - this should catch the most 
2878                     const wxChar 
*result 
= dt
.ParseFormat(input
, _T("%T")); 
2881                         result 
= dt
.ParseFormat(input
, _T("%r")); 
2887                         return (wxChar 
*)NULL
; 
2890                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2899 #endif // HAVE_STRPTIME/!HAVE_STRPTIME 
2902             case _T('y'):       // year without century (00-99) 
2903                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 99) ) 
2906                     return (wxChar 
*)NULL
; 
2911                 // TODO should have an option for roll over date instead of 
2912                 //      hard coding it here 
2913                 year 
= (num 
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
; 
2916             case _T('Y'):       // year with century 
2917                 if ( !GetNumericToken(width
, input
, &num
) ) 
2920                     return (wxChar 
*)NULL
; 
2924                 year 
= (wxDateTime_t
)num
; 
2927             case _T('Z'):       // timezone name 
2928                 wxFAIL_MSG(_T("TODO")); 
2931             case _T('%'):       // a percent sign 
2932                 if ( *input
++ != _T('%') ) 
2935                     return (wxChar 
*)NULL
; 
2939             case 0:             // the end of string 
2940                 wxFAIL_MSG(_T("unexpected format end")); 
2944             default:            // not a known format spec 
2945                 return (wxChar 
*)NULL
; 
2949     // format matched, try to construct a date from what we have now 
2951     if ( dateDef
.IsValid() ) 
2953         // take this date as default 
2954         tmDef 
= dateDef
.GetTm(); 
2956     else if ( IsValid() ) 
2958         // if this date is valid, don't change it 
2963         // no default and this date is invalid - fall back to Today() 
2964         tmDef 
= Today().GetTm(); 
2975     // TODO we don't check here that the values are consistent, if both year 
2976     //      day and month/day were found, we just ignore the year day and we 
2977     //      also always ignore the week day 
2978     if ( haveMon 
&& haveDay 
) 
2980         if ( mday 
> GetNumOfDaysInMonth(tm
.year
, mon
) ) 
2982             wxLogDebug(_T("bad month day in wxDateTime::ParseFormat")); 
2984             return (wxChar 
*)NULL
; 
2990     else if ( haveYDay 
) 
2992         if ( yday 
> GetNumberOfDays(tm
.year
) ) 
2994             wxLogDebug(_T("bad year day in wxDateTime::ParseFormat")); 
2996             return (wxChar 
*)NULL
; 
2999         Tm tm2 
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm(); 
3006     if ( haveHour 
&& hourIsIn12hFormat 
&& isPM 
) 
3008         // translate to 24hour format 
3011     //else: either already in 24h format or no translation needed 
3034 const wxChar 
*wxDateTime::ParseDateTime(const wxChar 
*date
) 
3036     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
3038     // there is a public domain version of getdate.y, but it only works for 
3040     wxFAIL_MSG(_T("TODO")); 
3042     return (wxChar 
*)NULL
; 
3045 const wxChar 
*wxDateTime::ParseDate(const wxChar 
*date
) 
3047     // this is a simplified version of ParseDateTime() which understands only 
3048     // "today" (for wxDate compatibility) and digits only otherwise (and not 
3049     // all esoteric constructions ParseDateTime() knows about) 
3051     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
3053     const wxChar 
*p 
= date
; 
3054     while ( wxIsspace(*p
) ) 
3057     // some special cases 
3061         int dayDiffFromToday
; 
3064         { wxTRANSLATE("today"),             0 }, 
3065         { wxTRANSLATE("yesterday"),        -1 }, 
3066         { wxTRANSLATE("tomorrow"),          1 }, 
3069     for ( size_t n 
= 0; n 
< WXSIZEOF(literalDates
); n
++ ) 
3071         wxString date 
= wxGetTranslation(literalDates
[n
].str
); 
3072         size_t len 
= date
.length(); 
3073         if ( wxStrlen(p
) >= len 
&& (wxString(p
, len
).CmpNoCase(date
) == 0) ) 
3075             // nothing can follow this, so stop here 
3078             int dayDiffFromToday 
= literalDates
[n
].dayDiffFromToday
; 
3080             if ( dayDiffFromToday 
) 
3082                 *this += wxDateSpan::Days(dayDiffFromToday
); 
3089     // We try to guess what we have here: for each new (numeric) token, we 
3090     // determine if it can be a month, day or a year. Of course, there is an 
3091     // ambiguity as some numbers may be days as well as months, so we also 
3092     // have the ability to back track. 
3095     bool haveDay 
= FALSE
,       // the months day? 
3096          haveWDay 
= FALSE
,      // the day of week? 
3097          haveMon 
= FALSE
,       // the month? 
3098          haveYear 
= FALSE
;      // the year? 
3100     // and the value of the items we have (init them to get rid of warnings) 
3101     WeekDay wday 
= Inv_WeekDay
; 
3102     wxDateTime_t day 
= 0; 
3103     wxDateTime::Month mon 
= Inv_Month
; 
3106     // tokenize the string 
3108     static const wxChar 
*dateDelimiters 
= _T(".,/-\t\n "); 
3109     wxStringTokenizer 
tok(p
, dateDelimiters
); 
3110     while ( tok
.HasMoreTokens() ) 
3112         wxString token 
= tok
.GetNextToken(); 
3118         if ( token
.ToULong(&val
) ) 
3120             // guess what this number is 
3126             if ( !haveMon 
&& val 
> 0 && val 
<= 12 ) 
3128                 // assume it is month 
3131             else // not the month 
3133                 wxDateTime_t maxDays 
= haveMon
 
3134                     ? GetNumOfDaysInMonth(haveYear 
? year 
: Inv_Year
, mon
) 
3138                 if ( (val 
== 0) || (val 
> (unsigned long)maxDays
) )  // cast to shut up compiler warning in BCC 
3155                 year 
= (wxDateTime_t
)val
; 
3164                 day 
= (wxDateTime_t
)val
; 
3170                 mon 
= (Month
)(val 
- 1); 
3173         else // not a number 
3175             // be careful not to overwrite the current mon value 
3176             Month mon2 
= GetMonthFromName(token
, Name_Full 
| Name_Abbr
); 
3177             if ( mon2 
!= Inv_Month 
) 
3182                     // but we already have a month - maybe we guessed wrong? 
3185                         // no need to check in month range as always < 12, but 
3186                         // the days are counted from 1 unlike the months 
3187                         day 
= (wxDateTime_t
)mon 
+ 1; 
3192                         // could possible be the year (doesn't the year come 
3193                         // before the month in the japanese format?) (FIXME) 
3202             else // not a valid month name 
3204                 wday 
= GetWeekDayFromName(token
, Name_Full 
| Name_Abbr
); 
3205                 if ( wday 
!= Inv_WeekDay 
) 
3215                 else // not a valid weekday name 
3218                     static const wxChar 
*ordinals
[] = 
3220                         wxTRANSLATE("first"), 
3221                         wxTRANSLATE("second"), 
3222                         wxTRANSLATE("third"), 
3223                         wxTRANSLATE("fourth"), 
3224                         wxTRANSLATE("fifth"), 
3225                         wxTRANSLATE("sixth"), 
3226                         wxTRANSLATE("seventh"), 
3227                         wxTRANSLATE("eighth"), 
3228                         wxTRANSLATE("ninth"), 
3229                         wxTRANSLATE("tenth"), 
3230                         wxTRANSLATE("eleventh"), 
3231                         wxTRANSLATE("twelfth"), 
3232                         wxTRANSLATE("thirteenth"), 
3233                         wxTRANSLATE("fourteenth"), 
3234                         wxTRANSLATE("fifteenth"), 
3235                         wxTRANSLATE("sixteenth"), 
3236                         wxTRANSLATE("seventeenth"), 
3237                         wxTRANSLATE("eighteenth"), 
3238                         wxTRANSLATE("nineteenth"), 
3239                         wxTRANSLATE("twentieth"), 
3240                         // that's enough - otherwise we'd have problems with 
3241                         // composite (or not) ordinals 
3245                     for ( n 
= 0; n 
< WXSIZEOF(ordinals
); n
++ ) 
3247                         if ( token
.CmpNoCase(ordinals
[n
]) == 0 ) 
3253                     if ( n 
== WXSIZEOF(ordinals
) ) 
3255                         // stop here - something unknown 
3262                         // don't try anything here (as in case of numeric day 
3263                         // above) - the symbolic day spec should always 
3264                         // precede the month/year 
3270                     day 
= (wxDateTime_t
)(n 
+ 1); 
3275         nPosCur 
= tok
.GetPosition(); 
3278     // either no more tokens or the scan was stopped by something we couldn't 
3279     // parse - in any case, see if we can construct a date from what we have 
3280     if ( !haveDay 
&& !haveWDay 
) 
3282         wxLogDebug(_T("ParseDate: no day, no weekday hence no date.")); 
3284         return (wxChar 
*)NULL
; 
3287     if ( haveWDay 
&& (haveMon 
|| haveYear 
|| haveDay
) && 
3288          !(haveDay 
&& haveMon 
&& haveYear
) ) 
3290         // without adjectives (which we don't support here) the week day only 
3291         // makes sense completely separately or with the full date 
3292         // specification (what would "Wed 1999" mean?) 
3293         return (wxChar 
*)NULL
; 
3296     if ( !haveWDay 
&& haveYear 
&& !(haveDay 
&& haveMon
) ) 
3298         // may be we have month and day instead of day and year? 
3299         if ( haveDay 
&& !haveMon 
) 
3303                 // exchange day and month 
3304                 mon 
= (wxDateTime::Month
)(day 
- 1); 
3306                 // we're in the current year then 
3308                         (unsigned)year 
<= GetNumOfDaysInMonth(Inv_Year
, mon
) ) 
3315                 //else: no, can't exchange, leave haveMon == FALSE 
3321             // if we give the year, month and day must be given too 
3322             wxLogDebug(_T("ParseDate: day and month should be specified if year is.")); 
3324             return (wxChar 
*)NULL
; 
3330         mon 
= GetCurrentMonth(); 
3335         year 
= GetCurrentYear(); 
3340         Set(day
, mon
, year
); 
3344             // check that it is really the same 
3345             if ( GetWeekDay() != wday 
) 
3347                 // inconsistency detected 
3348                 wxLogDebug(_T("ParseDate: inconsistent day/weekday.")); 
3350                 return (wxChar 
*)NULL
; 
3358         SetToWeekDayInSameWeek(wday
); 
3361     // return the pointer to the first unparsed char 
3363     if ( nPosCur 
&& wxStrchr(dateDelimiters
, *(p 
- 1)) ) 
3365         // if we couldn't parse the token after the delimiter, put back the 
3366         // delimiter as well 
3373 const wxChar 
*wxDateTime::ParseTime(const wxChar 
*time
) 
3375     wxCHECK_MSG( time
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
3377     // first try some extra things 
3384         { wxTRANSLATE("noon"),      12 }, 
3385         { wxTRANSLATE("midnight"),  00 }, 
3389     for ( size_t n 
= 0; n 
< WXSIZEOF(stdTimes
); n
++ ) 
3391         wxString timeString 
= wxGetTranslation(stdTimes
[n
].name
); 
3392         size_t len 
= timeString
.length(); 
3393         if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 ) 
3395             Set(stdTimes
[n
].hour
, 0, 0); 
3401     // try all time formats we may think about in the order from longest to 
3404     // 12hour with AM/PM? 
3405     const wxChar 
*result 
= ParseFormat(time
, _T("%I:%M:%S %p")); 
3409         // normally, it's the same, but why not try it? 
3410         result 
= ParseFormat(time
, _T("%H:%M:%S")); 
3415         // 12hour with AM/PM but without seconds? 
3416         result 
= ParseFormat(time
, _T("%I:%M %p")); 
3422         result 
= ParseFormat(time
, _T("%H:%M")); 
3427         // just the hour and AM/PM? 
3428         result 
= ParseFormat(time
, _T("%I %p")); 
3434         result 
= ParseFormat(time
, _T("%H")); 
3439         // parse the standard format: normally it is one of the formats above 
3440         // but it may be set to something completely different by the user 
3441         result 
= ParseFormat(time
, _T("%X")); 
3444     // TODO: parse timezones 
3449 // ---------------------------------------------------------------------------- 
3450 // Workdays and holidays support 
3451 // ---------------------------------------------------------------------------- 
3453 bool wxDateTime::IsWorkDay(Country 
WXUNUSED(country
)) const 
3455     return !wxDateTimeHolidayAuthority::IsHoliday(*this); 
3458 // ============================================================================ 
3460 // ============================================================================ 
3462 // this enum is only used in wxTimeSpan::Format() below but we can't declare 
3463 // it locally to the method as it provokes an internal compiler error in egcs 
3464 // 2.91.60 when building with -O2 
3475 // not all strftime(3) format specifiers make sense here because, for example, 
3476 // a time span doesn't have a year nor a timezone 
3478 // Here are the ones which are supported (all of them are supported by strftime 
3480 //  %H          hour in 24 hour format 
3481 //  %M          minute (00 - 59) 
3482 //  %S          second (00 - 59) 
3485 // Also, for MFC CTimeSpan compatibility, we support 
3486 //  %D          number of days 
3488 // And, to be better than MFC :-), we also have 
3489 //  %E          number of wEeks 
3490 //  %l          milliseconds (000 - 999) 
3491 wxString 
wxTimeSpan::Format(const wxChar 
*format
) const 
3493     wxCHECK_MSG( format
, _T(""), _T("NULL format in wxTimeSpan::Format") ); 
3496     str
.Alloc(wxStrlen(format
)); 
3498     // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */) 
3500     // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the 
3501     // question is what should ts.Format("%S") do? The code here returns "3273" 
3502     // in this case (i.e. the total number of seconds, not just seconds % 60) 
3503     // because, for me, this call means "give me entire time interval in 
3504     // seconds" and not "give me the seconds part of the time interval" 
3506     // If we agree that it should behave like this, it is clear that the 
3507     // interpretation of each format specifier depends on the presence of the 
3508     // other format specs in the string: if there was "%H" before "%M", we 
3509     // should use GetMinutes() % 60, otherwise just GetMinutes() &c 
3511     // we remember the most important unit found so far 
3512     TimeSpanPart partBiggest 
= Part_MSec
; 
3514     for ( const wxChar 
*pch 
= format
; *pch
; pch
++ ) 
3518         if ( ch 
== _T('%') ) 
3520             // the start of the format specification of the printf() below 
3521             wxString fmtPrefix 
= _T('%'); 
3526             ch 
= *++pch
;    // get the format spec char 
3530                     wxFAIL_MSG( _T("invalid format character") ); 
3536                     // skip the part below switch 
3541                     if ( partBiggest 
< Part_Day 
) 
3547                         partBiggest 
= Part_Day
; 
3552                     partBiggest 
= Part_Week
; 
3558                     if ( partBiggest 
< Part_Hour 
) 
3564                         partBiggest 
= Part_Hour
; 
3567                     fmtPrefix 
+= _T("02"); 
3571                     n 
= GetMilliseconds().ToLong(); 
3572                     if ( partBiggest 
< Part_MSec 
) 
3576                     //else: no need to reset partBiggest to Part_MSec, it is 
3577                     //      the least significant one anyhow 
3579                     fmtPrefix 
+= _T("03"); 
3584                     if ( partBiggest 
< Part_Min 
) 
3590                         partBiggest 
= Part_Min
; 
3593                     fmtPrefix 
+= _T("02"); 
3597                     n 
= GetSeconds().ToLong(); 
3598                     if ( partBiggest 
< Part_Sec 
) 
3604                         partBiggest 
= Part_Sec
; 
3607                     fmtPrefix 
+= _T("02"); 
3611             str 
+= wxString::Format(fmtPrefix 
+ _T("ld"), n
); 
3615             // normal character, just copy 
3623 // ============================================================================ 
3624 // wxDateTimeHolidayAuthority and related classes 
3625 // ============================================================================ 
3627 #include "wx/arrimpl.cpp" 
3629 WX_DEFINE_OBJARRAY(wxDateTimeArray
); 
3631 static int wxCMPFUNC_CONV
 
3632 wxDateTimeCompareFunc(wxDateTime 
**first
, wxDateTime 
**second
) 
3634     wxDateTime dt1 
= **first
, 
3637     return dt1 
== dt2 
? 0 : dt1 
< dt2 
? -1 : +1; 
3640 // ---------------------------------------------------------------------------- 
3641 // wxDateTimeHolidayAuthority 
3642 // ---------------------------------------------------------------------------- 
3644 wxHolidayAuthoritiesArray 
wxDateTimeHolidayAuthority::ms_authorities
; 
3647 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
) 
3649     size_t count 
= ms_authorities
.GetCount(); 
3650     for ( size_t n 
= 0; n 
< count
; n
++ ) 
3652         if ( ms_authorities
[n
]->DoIsHoliday(dt
) ) 
3663 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
, 
3664                                                const wxDateTime
& dtEnd
, 
3665                                                wxDateTimeArray
& holidays
) 
3667     wxDateTimeArray hol
; 
3671     size_t count 
= ms_authorities
.GetCount(); 
3672     for ( size_t nAuth 
= 0; nAuth 
< count
; nAuth
++ ) 
3674         ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
); 
3676         WX_APPEND_ARRAY(holidays
, hol
); 
3679     holidays
.Sort(wxDateTimeCompareFunc
); 
3681     return holidays
.GetCount(); 
3685 void wxDateTimeHolidayAuthority::ClearAllAuthorities() 
3687     WX_CLEAR_ARRAY(ms_authorities
); 
3691 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority 
*auth
) 
3693     ms_authorities
.Add(auth
); 
3696 // ---------------------------------------------------------------------------- 
3697 // wxDateTimeWorkDays 
3698 // ---------------------------------------------------------------------------- 
3700 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const 
3702     wxDateTime::WeekDay wd 
= dt
.GetWeekDay(); 
3704     return (wd 
== wxDateTime::Sun
) || (wd 
== wxDateTime::Sat
); 
3707 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
, 
3708                                                 const wxDateTime
& dtEnd
, 
3709                                                 wxDateTimeArray
& holidays
) const 
3711     if ( dtStart 
> dtEnd 
) 
3713         wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") ); 
3720     // instead of checking all days, start with the first Sat after dtStart and 
3721     // end with the last Sun before dtEnd 
3722     wxDateTime dtSatFirst 
= dtStart
.GetNextWeekDay(wxDateTime::Sat
), 
3723                dtSatLast 
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
), 
3724                dtSunFirst 
= dtStart
.GetNextWeekDay(wxDateTime::Sun
), 
3725                dtSunLast 
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
), 
3728     for ( dt 
= dtSatFirst
; dt 
<= dtSatLast
; dt 
+= wxDateSpan::Week() ) 
3733     for ( dt 
= dtSunFirst
; dt 
<= dtSunLast
; dt 
+= wxDateSpan::Week() ) 
3738     return holidays
.GetCount(); 
3741 #endif // wxUSE_DATETIME