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" 
  67     #include "wx/string.h" 
  72 #include "wx/thread.h" 
  73 #include "wx/tokenzr.h" 
  74 #include "wx/module.h" 
  76 #define wxDEFINE_TIME_CONSTANTS // before including datetime.h 
  80 #include "wx/datetime.h" 
  81 #include "wx/timer.h"           // for wxGetLocalTimeMillis() 
  83 // ---------------------------------------------------------------------------- 
  84 // conditional compilation 
  85 // ---------------------------------------------------------------------------- 
  87 #if defined(HAVE_STRPTIME) && defined(__LINUX__) 
  88     // glibc 2.0.7 strptime() is broken - the following snippet causes it to 
  89     // crash (instead of just failing): 
  91     //      strncpy(buf, "Tue Dec 21 20:25:40 1999", 128); 
  92     //      strptime(buf, "%x", &tm); 
  96 #endif // broken strptime() 
  99     #if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__) 
 100         #define WX_TIMEZONE _timezone 
 101     #elif defined(__MWERKS__) 
 102         long wxmw_timezone 
= 28800; 
 103         #define WX_TIMEZONE wxmw_timezone; 
 104     #else // unknown platform - try timezone 
 105         #define WX_TIMEZONE timezone 
 107 #endif // !WX_TIMEZONE 
 109 // ---------------------------------------------------------------------------- 
 111 // ---------------------------------------------------------------------------- 
 113 class wxDateTimeHolidaysModule 
: public wxModule
 
 116     virtual bool OnInit() 
 118         wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays
); 
 123     virtual void OnExit() 
 125         wxDateTimeHolidayAuthority::ClearAllAuthorities(); 
 126         wxDateTimeHolidayAuthority::ms_authorities
.Clear(); 
 130     DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule
) 
 133 IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule
, wxModule
) 
 135 // ---------------------------------------------------------------------------- 
 137 // ---------------------------------------------------------------------------- 
 140 static const int MONTHS_IN_YEAR 
= 12; 
 142 static const int SECONDS_IN_MINUTE 
= 60; 
 144 static const long SECONDS_PER_DAY 
= 86400l; 
 146 static const long MILLISECONDS_PER_DAY 
= 86400000l; 
 148 // this is the integral part of JDN of the midnight of Jan 1, 1970 
 149 // (i.e. JDN(Jan 1, 1970) = 2440587.5) 
 150 static const long EPOCH_JDN 
= 2440587l; 
 152 // the date of JDN -0.5 (as we don't work with fractional parts, this is the 
 153 // reference date for us) is Nov 24, 4714BC 
 154 static const int JDN_0_YEAR 
= -4713; 
 155 static const int JDN_0_MONTH 
= wxDateTime::Nov
; 
 156 static const int JDN_0_DAY 
= 24; 
 158 // the constants used for JDN calculations 
 159 static const long JDN_OFFSET         
= 32046l; 
 160 static const long DAYS_PER_5_MONTHS  
= 153l; 
 161 static const long DAYS_PER_4_YEARS   
= 1461l; 
 162 static const long DAYS_PER_400_YEARS 
= 146097l; 
 164 // this array contains the cumulated number of days in all previous months for 
 165 // normal and leap years 
 166 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] = 
 168     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, 
 169     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } 
 172 // ---------------------------------------------------------------------------- 
 174 // ---------------------------------------------------------------------------- 
 176 static wxDateTime gs_dtDefault
; 
 178 wxDateTime
& wxDefaultDateTime 
= gs_dtDefault
; 
 180 wxDateTime::Country 
wxDateTime::ms_country 
= wxDateTime::Country_Unknown
; 
 182 // ---------------------------------------------------------------------------- 
 184 // ---------------------------------------------------------------------------- 
 186 // a critical section is needed to protect GetTimeZone() static 
 187 // variable in MT case 
 189     static wxCriticalSection gs_critsectTimezone
; 
 190 #endif // wxUSE_THREADS 
 192 // ---------------------------------------------------------------------------- 
 194 // ---------------------------------------------------------------------------- 
 196 // debugger helper: shows what the date really is 
 198 extern const wxChar 
*wxDumpDate(const wxDateTime
* dt
) 
 200     static wxChar buf
[128]; 
 202     wxStrcpy(buf
, dt
->Format(_T("%Y-%m-%d (%a) %H:%M:%S"))); 
 208 // get the number of days in the given month of the given year 
 210 wxDateTime::wxDateTime_t 
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
) 
 212     // the number of days in month in Julian/Gregorian calendar: the first line 
 213     // is for normal years, the second one is for the leap ones 
 214     static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] = 
 216         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 
 217         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 
 220     return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
]; 
 223 // ensure that the timezone variable is set by calling localtime 
 224 static int GetTimeZone() 
 226     // set to TRUE when the timezone is set 
 227     static bool s_timezoneSet 
= FALSE
; 
 229     wxCRIT_SECT_LOCKER(lock
, gs_critsectTimezone
); 
 231     if ( !s_timezoneSet 
) 
 233         // just call localtime() instead of figuring out whether this system 
 234         // supports tzset(), _tzset() or something else 
 238         s_timezoneSet 
= TRUE
; 
 241     return (int)WX_TIMEZONE
; 
 244 // return the integral part of the JDN for the midnight of the given date (to 
 245 // get the real JDN you need to add 0.5, this is, in fact, JDN of the 
 246 // noon of the previous day) 
 247 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
, 
 248                             wxDateTime::Month mon
, 
 251     // CREDIT: code below is by Scott E. Lee (but bugs are mine) 
 253     // check the date validity 
 255       (year 
> JDN_0_YEAR
) || 
 256       ((year 
== JDN_0_YEAR
) && (mon 
> JDN_0_MONTH
)) || 
 257       ((year 
== JDN_0_YEAR
) && (mon 
== JDN_0_MONTH
) && (day 
>= JDN_0_DAY
)), 
 258       _T("date out of range - can't convert to JDN") 
 261     // make the year positive to avoid problems with negative numbers division 
 264     // months are counted from March here 
 266     if ( mon 
>= wxDateTime::Mar 
) 
 276     // now we can simply add all the contributions together 
 277     return ((year 
/ 100) * DAYS_PER_400_YEARS
) / 4 
 278             + ((year 
% 100) * DAYS_PER_4_YEARS
) / 4 
 279             + (month 
* DAYS_PER_5_MONTHS 
+ 2) / 5 
 284 // this function is a wrapper around strftime(3) 
 285 static wxString 
CallStrftime(const wxChar 
*format
, const tm
* tm
) 
 288     if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) ) 
 290         // buffer is too small? 
 291         wxFAIL_MSG(_T("strftime() failed")); 
 294     return wxString(buf
); 
 297 // if year and/or month have invalid values, replace them with the current ones 
 298 static void ReplaceDefaultYearMonthWithCurrent(int *year
, 
 299                                                wxDateTime::Month 
*month
) 
 301     struct tm 
*tmNow 
= NULL
; 
 303     if ( *year 
== wxDateTime::Inv_Year 
) 
 305         tmNow 
= wxDateTime::GetTmNow(); 
 307         *year 
= 1900 + tmNow
->tm_year
; 
 310     if ( *month 
== wxDateTime::Inv_Month 
) 
 313             tmNow 
= wxDateTime::GetTmNow(); 
 315         *month 
= (wxDateTime::Month
)tmNow
->tm_mon
; 
 319 // fll the struct tm with default values 
 320 static void InitTm(struct tm
& tm
) 
 322     // struct tm may have etxra fields (undocumented and with unportable 
 323     // names) which, nevertheless, must be set to 0 
 324     memset(&tm
, 0, sizeof(struct tm
)); 
 326     tm
.tm_mday 
= 1;   // mday 0 is invalid 
 327     tm
.tm_year 
= 76;  // any valid year 
 328     tm
.tm_isdst 
= -1; // auto determine 
 334 // return the month if the string is a month name or Inv_Month otherwise 
 335 static wxDateTime::Month 
GetMonthFromName(const wxString
& name
, int flags
) 
 337     wxDateTime::Month mon
; 
 338     for ( mon 
= wxDateTime::Jan
; mon 
< wxDateTime::Inv_Month
; wxNextMonth(mon
) ) 
 340         // case-insensitive comparison either one of or with both abbreviated 
 342         if ( flags 
& wxDateTime::Name_Full 
) 
 344             if ( name
.CmpNoCase(wxDateTime:: 
 345                         GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 ) 
 351         if ( flags 
& wxDateTime::Name_Abbr 
) 
 353             if ( name
.CmpNoCase(wxDateTime:: 
 354                         GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 ) 
 364 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise 
 365 static wxDateTime::WeekDay 
GetWeekDayFromName(const wxString
& name
, int flags
) 
 367     wxDateTime::WeekDay wd
; 
 368     for ( wd 
= wxDateTime::Sun
; wd 
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) ) 
 370         // case-insensitive comparison either one of or with both abbreviated 
 372         if ( flags 
& wxDateTime::Name_Full 
) 
 374             if ( name
.CmpNoCase(wxDateTime:: 
 375                         GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 ) 
 381         if ( flags 
& wxDateTime::Name_Abbr 
) 
 383             if ( name
.CmpNoCase(wxDateTime:: 
 384                         GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 ) 
 394 // scans all digits (but no more than len) and returns the resulting number 
 395 static bool GetNumericToken(size_t len
, const wxChar
*& p
, unsigned long *number
) 
 399     while ( wxIsdigit(*p
) ) 
 403         if ( len 
&& ++n 
> len 
) 
 407     return !!s 
&& s
.ToULong(number
); 
 410 // scans all alphabetic characters and returns the resulting string 
 411 static wxString 
GetAlphaToken(const wxChar
*& p
) 
 414     while ( wxIsalpha(*p
) ) 
 422 // ============================================================================ 
 423 // implementation of wxDateTime 
 424 // ============================================================================ 
 426 // ---------------------------------------------------------------------------- 
 428 // ---------------------------------------------------------------------------- 
 432     year 
= (wxDateTime_t
)wxDateTime::Inv_Year
; 
 433     mon 
= wxDateTime::Inv_Month
; 
 435     hour 
= min 
= sec 
= msec 
= 0; 
 436     wday 
= wxDateTime::Inv_WeekDay
; 
 439 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
) 
 447     mon 
= (wxDateTime::Month
)tm
.tm_mon
; 
 448     year 
= 1900 + tm
.tm_year
; 
 453 bool wxDateTime::Tm::IsValid() const 
 455     // we allow for the leap seconds, although we don't use them (yet) 
 456     return (year 
!= wxDateTime::Inv_Year
) && (mon 
!= wxDateTime::Inv_Month
) && 
 457            (mday 
<= GetNumOfDaysInMonth(year
, mon
)) && 
 458            (hour 
< 24) && (min 
< 60) && (sec 
< 62) && (msec 
< 1000); 
 461 void wxDateTime::Tm::ComputeWeekDay() 
 463     // compute the week day from day/month/year: we use the dumbest algorithm 
 464     // possible: just compute our JDN and then use the (simple to derive) 
 465     // formula: weekday = (JDN + 1.5) % 7 
 466     wday 
= (wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7; 
 469 void wxDateTime::Tm::AddMonths(int monDiff
) 
 471     // normalize the months field 
 472     while ( monDiff 
< -mon 
) 
 476         monDiff 
+= MONTHS_IN_YEAR
; 
 479     while ( monDiff 
+ mon 
>= MONTHS_IN_YEAR 
) 
 483         monDiff 
-= MONTHS_IN_YEAR
; 
 486     mon 
= (wxDateTime::Month
)(mon 
+ monDiff
); 
 488     wxASSERT_MSG( mon 
>= 0 && mon 
< MONTHS_IN_YEAR
, _T("logic error") ); 
 490     // NB: we don't check here that the resulting date is valid, this function 
 491     //     is private and the caller must check it if needed 
 494 void wxDateTime::Tm::AddDays(int dayDiff
) 
 496     // normalize the days field 
 497     while ( dayDiff 
+ mday 
< 1 ) 
 501         dayDiff 
+= GetNumOfDaysInMonth(year
, mon
); 
 505     while ( mday 
> GetNumOfDaysInMonth(year
, mon
) ) 
 507         mday 
-= GetNumOfDaysInMonth(year
, mon
); 
 512     wxASSERT_MSG( mday 
> 0 && mday 
<= GetNumOfDaysInMonth(year
, mon
), 
 516 // ---------------------------------------------------------------------------- 
 518 // ---------------------------------------------------------------------------- 
 520 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
) 
 524         case wxDateTime::Local
: 
 525             // get the offset from C RTL: it returns the difference GMT-local 
 526             // while we want to have the offset _from_ GMT, hence the '-' 
 527             m_offset 
= -GetTimeZone(); 
 530         case wxDateTime::GMT_12
: 
 531         case wxDateTime::GMT_11
: 
 532         case wxDateTime::GMT_10
: 
 533         case wxDateTime::GMT_9
: 
 534         case wxDateTime::GMT_8
: 
 535         case wxDateTime::GMT_7
: 
 536         case wxDateTime::GMT_6
: 
 537         case wxDateTime::GMT_5
: 
 538         case wxDateTime::GMT_4
: 
 539         case wxDateTime::GMT_3
: 
 540         case wxDateTime::GMT_2
: 
 541         case wxDateTime::GMT_1
: 
 542             m_offset 
= -3600*(wxDateTime::GMT0 
- tz
); 
 545         case wxDateTime::GMT0
: 
 546         case wxDateTime::GMT1
: 
 547         case wxDateTime::GMT2
: 
 548         case wxDateTime::GMT3
: 
 549         case wxDateTime::GMT4
: 
 550         case wxDateTime::GMT5
: 
 551         case wxDateTime::GMT6
: 
 552         case wxDateTime::GMT7
: 
 553         case wxDateTime::GMT8
: 
 554         case wxDateTime::GMT9
: 
 555         case wxDateTime::GMT10
: 
 556         case wxDateTime::GMT11
: 
 557         case wxDateTime::GMT12
: 
 558             m_offset 
= 3600*(tz 
- wxDateTime::GMT0
); 
 561         case wxDateTime::A_CST
: 
 562             // Central Standard Time in use in Australia = UTC + 9.5 
 563             m_offset 
= 60l*(9*60 + 30); 
 567             wxFAIL_MSG( _T("unknown time zone") ); 
 571 // ---------------------------------------------------------------------------- 
 573 // ---------------------------------------------------------------------------- 
 576 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
) 
 578     if ( year 
== Inv_Year 
) 
 579         year 
= GetCurrentYear(); 
 581     if ( cal 
== Gregorian 
) 
 583         // in Gregorian calendar leap years are those divisible by 4 except 
 584         // those divisible by 100 unless they're also divisible by 400 
 585         // (in some countries, like Russia and Greece, additional corrections 
 586         // exist, but they won't manifest themselves until 2700) 
 587         return (year 
% 4 == 0) && ((year 
% 100 != 0) || (year 
% 400 == 0)); 
 589     else if ( cal 
== Julian 
) 
 591         // in Julian calendar the rule is simpler 
 592         return year 
% 4 == 0; 
 596         wxFAIL_MSG(_T("unknown calendar")); 
 603 int wxDateTime::GetCentury(int year
) 
 605     return year 
> 0 ? year 
/ 100 : year 
/ 100 - 1; 
 609 int wxDateTime::ConvertYearToBC(int year
) 
 612     return year 
> 0 ? year 
: year 
- 1; 
 616 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
) 
 621             return Now().GetYear(); 
 624             wxFAIL_MSG(_T("TODO")); 
 628             wxFAIL_MSG(_T("unsupported calendar")); 
 636 wxDateTime::Month 
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
) 
 641             return Now().GetMonth(); 
 644             wxFAIL_MSG(_T("TODO")); 
 648             wxFAIL_MSG(_T("unsupported calendar")); 
 656 wxDateTime::wxDateTime_t 
wxDateTime::GetNumberOfDays(int year
, Calendar cal
) 
 658     if ( year 
== Inv_Year 
) 
 660         // take the current year if none given 
 661         year 
= GetCurrentYear(); 
 668             return IsLeapYear(year
) ? 366 : 365; 
 671             wxFAIL_MSG(_T("unsupported calendar")); 
 679 wxDateTime::wxDateTime_t 
wxDateTime::GetNumberOfDays(wxDateTime::Month month
, 
 681                                                      wxDateTime::Calendar cal
) 
 683     wxCHECK_MSG( month 
< MONTHS_IN_YEAR
, 0, _T("invalid month") ); 
 685     if ( cal 
== Gregorian 
|| cal 
== Julian 
) 
 687         if ( year 
== Inv_Year 
) 
 689             // take the current year if none given 
 690             year 
= GetCurrentYear(); 
 693         return GetNumOfDaysInMonth(year
, month
); 
 697         wxFAIL_MSG(_T("unsupported calendar")); 
 704 wxString 
wxDateTime::GetMonthName(wxDateTime::Month month
, 
 705                                   wxDateTime::NameFlags flags
) 
 707     wxCHECK_MSG( month 
!= Inv_Month
, _T(""), _T("invalid month") ); 
 709     // notice that we must set all the fields to avoid confusing libc (GNU one 
 710     // gets confused to a crash if we don't do this) 
 715     return CallStrftime(flags 
== Name_Abbr 
? _T("%b") : _T("%B"), &tm
); 
 719 wxString 
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
, 
 720                                     wxDateTime::NameFlags flags
) 
 722     wxCHECK_MSG( wday 
!= Inv_WeekDay
, _T(""), _T("invalid weekday") ); 
 724     // take some arbitrary Sunday 
 731     // and offset it by the number of days needed to get the correct wday 
 734     // call mktime() to normalize it... 
 737     // ... and call strftime() 
 738     return CallStrftime(flags 
== Name_Abbr 
? _T("%a") : _T("%A"), &tm
); 
 742 void wxDateTime::GetAmPmStrings(wxString 
*am
, wxString 
*pm
) 
 748         *am 
= CallStrftime(_T("%p"), &tm
); 
 753         *pm 
= CallStrftime(_T("%p"), &tm
); 
 757 // ---------------------------------------------------------------------------- 
 758 // Country stuff: date calculations depend on the country (DST, work days, 
 759 // ...), so we need to know which rules to follow. 
 760 // ---------------------------------------------------------------------------- 
 763 wxDateTime::Country 
wxDateTime::GetCountry() 
 765     // TODO use LOCALE_ICOUNTRY setting under Win32 
 767     if ( ms_country 
== Country_Unknown 
) 
 769         // try to guess from the time zone name 
 770         time_t t 
= time(NULL
); 
 771         struct tm 
*tm 
= localtime(&t
); 
 773         wxString tz 
= CallStrftime(_T("%Z"), tm
); 
 774         if ( tz 
== _T("WET") || tz 
== _T("WEST") ) 
 778         else if ( tz 
== _T("CET") || tz 
== _T("CEST") ) 
 780             ms_country 
= Country_EEC
; 
 782         else if ( tz 
== _T("MSK") || tz 
== _T("MSD") ) 
 786         else if ( tz 
== _T("AST") || tz 
== _T("ADT") || 
 787                   tz 
== _T("EST") || tz 
== _T("EDT") || 
 788                   tz 
== _T("CST") || tz 
== _T("CDT") || 
 789                   tz 
== _T("MST") || tz 
== _T("MDT") || 
 790                   tz 
== _T("PST") || tz 
== _T("PDT") ) 
 796             // well, choose a default one 
 805 void wxDateTime::SetCountry(wxDateTime::Country country
) 
 807     ms_country 
= country
; 
 811 bool wxDateTime::IsWestEuropeanCountry(Country country
) 
 813     if ( country 
== Country_Default 
) 
 815         country 
= GetCountry(); 
 818     return (Country_WesternEurope_Start 
<= country
) && 
 819            (country 
<= Country_WesternEurope_End
); 
 822 // ---------------------------------------------------------------------------- 
 823 // DST calculations: we use 3 different rules for the West European countries, 
 824 // USA and for the rest of the world. This is undoubtedly false for many 
 825 // countries, but I lack the necessary info (and the time to gather it), 
 826 // please add the other rules here! 
 827 // ---------------------------------------------------------------------------- 
 830 bool wxDateTime::IsDSTApplicable(int year
, Country country
) 
 832     if ( year 
== Inv_Year 
) 
 834         // take the current year if none given 
 835         year 
= GetCurrentYear(); 
 838     if ( country 
== Country_Default 
) 
 840         country 
= GetCountry(); 
 847             // DST was first observed in the US and UK during WWI, reused 
 848             // during WWII and used again since 1966 
 849             return year 
>= 1966 || 
 850                    (year 
>= 1942 && year 
<= 1945) || 
 851                    (year 
== 1918 || year 
== 1919); 
 854             // assume that it started after WWII 
 860 wxDateTime 
wxDateTime::GetBeginDST(int year
, Country country
) 
 862     if ( year 
== Inv_Year 
) 
 864         // take the current year if none given 
 865         year 
= GetCurrentYear(); 
 868     if ( country 
== Country_Default 
) 
 870         country 
= GetCountry(); 
 873     if ( !IsDSTApplicable(year
, country
) ) 
 875         return wxInvalidDateTime
; 
 880     if ( IsWestEuropeanCountry(country
) || (country 
== Russia
) ) 
 882         // DST begins at 1 a.m. GMT on the last Sunday of March 
 883         if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) ) 
 886             wxFAIL_MSG( _T("no last Sunday in March?") ); 
 889         dt 
+= wxTimeSpan::Hours(1); 
 891         // disable DST tests because it could result in an infinite recursion! 
 894     else switch ( country 
) 
 901                     // don't know for sure - assume it was in effect all year 
 906                     dt
.Set(1, Jan
, year
); 
 910                     // DST was installed Feb 2, 1942 by the Congress 
 911                     dt
.Set(2, Feb
, year
); 
 914                     // Oil embargo changed the DST period in the US 
 916                     dt
.Set(6, Jan
, 1974); 
 920                     dt
.Set(23, Feb
, 1975); 
 924                     // before 1986, DST begun on the last Sunday of April, but 
 925                     // in 1986 Reagan changed it to begin at 2 a.m. of the 
 926                     // first Sunday in April 
 929                         if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) ) 
 932                             wxFAIL_MSG( _T("no first Sunday in April?") ); 
 937                         if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) ) 
 940                             wxFAIL_MSG( _T("no first Sunday in April?") ); 
 944                     dt 
+= wxTimeSpan::Hours(2); 
 946                     // TODO what about timezone?? 
 952             // assume Mar 30 as the start of the DST for the rest of the world 
 953             // - totally bogus, of course 
 954             dt
.Set(30, Mar
, year
); 
 961 wxDateTime 
wxDateTime::GetEndDST(int year
, Country country
) 
 963     if ( year 
== Inv_Year 
) 
 965         // take the current year if none given 
 966         year 
= GetCurrentYear(); 
 969     if ( country 
== Country_Default 
) 
 971         country 
= GetCountry(); 
 974     if ( !IsDSTApplicable(year
, country
) ) 
 976         return wxInvalidDateTime
; 
 981     if ( IsWestEuropeanCountry(country
) || (country 
== Russia
) ) 
 983         // DST ends at 1 a.m. GMT on the last Sunday of October 
 984         if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) ) 
 986             // weirder and weirder... 
 987             wxFAIL_MSG( _T("no last Sunday in October?") ); 
 990         dt 
+= wxTimeSpan::Hours(1); 
 992         // disable DST tests because it could result in an infinite recursion! 
 995     else switch ( country 
) 
1002                     // don't know for sure - assume it was in effect all year 
1006                     dt
.Set(31, Dec
, year
); 
1010                     // the time was reset after the end of the WWII 
1011                     dt
.Set(30, Sep
, year
); 
1015                     // DST ends at 2 a.m. on the last Sunday of October 
1016                     if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) ) 
1018                         // weirder and weirder... 
1019                         wxFAIL_MSG( _T("no last Sunday in October?") ); 
1022                     dt 
+= wxTimeSpan::Hours(2); 
1024                     // TODO what about timezone?? 
1029             // assume October 26th as the end of the DST - totally bogus too 
1030             dt
.Set(26, Oct
, year
); 
1036 // ---------------------------------------------------------------------------- 
1037 // constructors and assignment operators 
1038 // ---------------------------------------------------------------------------- 
1040 // return the current time with ms precision 
1041 /* static */ wxDateTime 
wxDateTime::UNow() 
1043     return wxDateTime(wxGetLocalTimeMillis()); 
1046 // the values in the tm structure contain the local time 
1047 wxDateTime
& wxDateTime::Set(const struct tm
& tm
) 
1049     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1052     time_t timet 
= mktime(&tm2
); 
1054     if ( timet 
== (time_t)-1 ) 
1056         // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is 
1057         // less than timezone - try to make it work for this case 
1058         if ( tm2
.tm_year 
== 70 && tm2
.tm_mon 
== 0 && tm2
.tm_mday 
== 1 ) 
1060             // add timezone to make sure that date is in range 
1061             tm2
.tm_sec 
-= GetTimeZone(); 
1063             timet 
= mktime(&tm2
); 
1064             if ( timet 
!= (time_t)-1 ) 
1066                 timet 
+= GetTimeZone(); 
1072         wxFAIL_MSG( _T("mktime() failed") ); 
1074         return wxInvalidDateTime
; 
1082 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
, 
1083                             wxDateTime_t minute
, 
1084                             wxDateTime_t second
, 
1085                             wxDateTime_t millisec
) 
1087     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1089     // we allow seconds to be 61 to account for the leap seconds, even if we 
1090     // don't use them really 
1091     wxCHECK_MSG( hour 
< 24 && second 
< 62 && minute 
< 60 && millisec 
< 1000, 
1093                  _T("Invalid time in wxDateTime::Set()") ); 
1095     // get the current date from system 
1096     struct tm 
*tm 
= GetTmNow(); 
1098     wxCHECK_MSG( tm
, wxInvalidDateTime
, _T("localtime() failed") ); 
1102     tm
->tm_min 
= minute
; 
1103     tm
->tm_sec 
= second
; 
1107     // and finally adjust milliseconds 
1108     return SetMillisecond(millisec
); 
1111 wxDateTime
& wxDateTime::Set(wxDateTime_t day
, 
1115                             wxDateTime_t minute
, 
1116                             wxDateTime_t second
, 
1117                             wxDateTime_t millisec
) 
1119     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1121     wxCHECK_MSG( hour 
< 24 && second 
< 62 && minute 
< 60 && millisec 
< 1000, 
1123                  _T("Invalid time in wxDateTime::Set()") ); 
1125     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
1127     wxCHECK_MSG( (0 < day
) && (day 
<= GetNumberOfDays(month
, year
)), 
1129                  _T("Invalid date in wxDateTime::Set()") ); 
1131     // the range of time_t type (inclusive) 
1132     static const int yearMinInRange 
= 1970; 
1133     static const int yearMaxInRange 
= 2037; 
1135     // test only the year instead of testing for the exact end of the Unix 
1136     // time_t range - it doesn't bring anything to do more precise checks 
1137     if ( year 
>= yearMinInRange 
&& year 
<= yearMaxInRange 
) 
1139         // use the standard library version if the date is in range - this is 
1140         // probably more efficient than our code 
1142         tm
.tm_year 
= year 
- 1900; 
1148         tm
.tm_isdst 
= -1;       // mktime() will guess it 
1152         // and finally adjust milliseconds 
1153         return SetMillisecond(millisec
); 
1157         // do time calculations ourselves: we want to calculate the number of 
1158         // milliseconds between the given date and the epoch 
1160         // get the JDN for the midnight of this day 
1161         m_time 
= GetTruncatedJDN(day
, month
, year
); 
1162         m_time 
-= EPOCH_JDN
; 
1163         m_time 
*= SECONDS_PER_DAY 
* TIME_T_FACTOR
; 
1165         // JDN corresponds to GMT, we take localtime 
1166         Add(wxTimeSpan(hour
, minute
, second 
+ GetTimeZone(), millisec
)); 
1172 wxDateTime
& wxDateTime::Set(double jdn
) 
1174     // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn 
1176     jdn 
-= EPOCH_JDN 
+ 0.5; 
1178     jdn 
*= MILLISECONDS_PER_DAY
; 
1185 wxDateTime
& wxDateTime::ResetTime() 
1189     if ( tm
.hour 
|| tm
.min 
|| tm
.sec 
|| tm
.msec 
) 
1202 // ---------------------------------------------------------------------------- 
1203 // time_t <-> broken down time conversions 
1204 // ---------------------------------------------------------------------------- 
1206 wxDateTime::Tm 
wxDateTime::GetTm(const TimeZone
& tz
) const 
1208     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1210     time_t time 
= GetTicks(); 
1211     if ( time 
!= (time_t)-1 ) 
1213         // use C RTL functions 
1215         if ( tz
.GetOffset() == -GetTimeZone() ) 
1217             // we are working with local time 
1218             tm 
= localtime(&time
); 
1220             // should never happen 
1221             wxCHECK_MSG( tm
, Tm(), _T("localtime() failed") ); 
1225             time 
+= (time_t)tz
.GetOffset(); 
1226 #ifdef __VMS__ // time is unsigned so avoid warning 
1227             int time2 
= (int) time
; 
1235                 // should never happen 
1236                 wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") ); 
1240                 tm 
= (struct tm 
*)NULL
; 
1246             // adjust the milliseconds 
1248             long timeOnly 
= (m_time 
% MILLISECONDS_PER_DAY
).ToLong(); 
1249             tm2
.msec 
= (wxDateTime_t
)(timeOnly 
% 1000); 
1252         //else: use generic code below 
1255     // remember the time and do the calculations with the date only - this 
1256     // eliminates rounding errors of the floating point arithmetics 
1258     wxLongLong timeMidnight 
= m_time 
+ tz
.GetOffset() * 1000; 
1260     long timeOnly 
= (timeMidnight 
% MILLISECONDS_PER_DAY
).ToLong(); 
1262     // we want to always have positive time and timeMidnight to be really 
1263     // the midnight before it 
1266         timeOnly 
= MILLISECONDS_PER_DAY 
+ timeOnly
; 
1269     timeMidnight 
-= timeOnly
; 
1271     // calculate the Gregorian date from JDN for the midnight of our date: 
1272     // this will yield day, month (in 1..12 range) and year 
1274     // actually, this is the JDN for the noon of the previous day 
1275     long jdn 
= (timeMidnight 
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
; 
1277     // CREDIT: code below is by Scott E. Lee (but bugs are mine) 
1279     wxASSERT_MSG( jdn 
> -2, _T("JDN out of range") ); 
1281     // calculate the century 
1282     long temp 
= (jdn 
+ JDN_OFFSET
) * 4 - 1; 
1283     long century 
= temp 
/ DAYS_PER_400_YEARS
; 
1285     // then the year and day of year (1 <= dayOfYear <= 366) 
1286     temp 
= ((temp 
% DAYS_PER_400_YEARS
) / 4) * 4 + 3; 
1287     long year 
= (century 
* 100) + (temp 
/ DAYS_PER_4_YEARS
); 
1288     long dayOfYear 
= (temp 
% DAYS_PER_4_YEARS
) / 4 + 1; 
1290     // and finally the month and day of the month 
1291     temp 
= dayOfYear 
* 5 - 3; 
1292     long month 
= temp 
/ DAYS_PER_5_MONTHS
; 
1293     long day 
= (temp 
% DAYS_PER_5_MONTHS
) / 5 + 1; 
1295     // month is counted from March - convert to normal 
1306     // year is offset by 4800 
1309     // check that the algorithm gave us something reasonable 
1310     wxASSERT_MSG( (0 < month
) && (month 
<= 12), _T("invalid month") ); 
1311     wxASSERT_MSG( (1 <= day
) && (day 
< 32), _T("invalid day") ); 
1312     wxASSERT_MSG( (INT_MIN 
<= year
) && (year 
<= INT_MAX
), 
1313                   _T("year range overflow") ); 
1315     // construct Tm from these values 
1317     tm
.year 
= (int)year
; 
1318     tm
.mon 
= (Month
)(month 
- 1); // algorithm yields 1 for January, not 0 
1319     tm
.mday 
= (wxDateTime_t
)day
; 
1320     tm
.msec 
= (wxDateTime_t
)(timeOnly 
% 1000); 
1321     timeOnly 
-= tm
.msec
; 
1322     timeOnly 
/= 1000;               // now we have time in seconds 
1324     tm
.sec 
= (wxDateTime_t
)(timeOnly 
% 60); 
1326     timeOnly 
/= 60;                 // now we have time in minutes 
1328     tm
.min 
= (wxDateTime_t
)(timeOnly 
% 60); 
1331     tm
.hour 
= (wxDateTime_t
)(timeOnly 
/ 60); 
1336 wxDateTime
& wxDateTime::SetYear(int year
) 
1338     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1347 wxDateTime
& wxDateTime::SetMonth(Month month
) 
1349     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1358 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
) 
1360     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1369 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
) 
1371     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1380 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
) 
1382     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1391 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
) 
1393     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1402 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
) 
1404     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1406     // we don't need to use GetTm() for this one 
1407     m_time 
-= m_time 
% 1000l; 
1408     m_time 
+= millisecond
; 
1413 // ---------------------------------------------------------------------------- 
1414 // wxDateTime arithmetics 
1415 // ---------------------------------------------------------------------------- 
1417 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
) 
1421     tm
.year 
+= diff
.GetYears(); 
1422     tm
.AddMonths(diff
.GetMonths()); 
1424     // check that the resulting date is valid 
1425     if ( tm
.mday 
> GetNumOfDaysInMonth(tm
.year
, tm
.mon
) ) 
1427         // We suppose that when adding one month to Jan 31 we want to get Feb 
1428         // 28 (or 29), i.e. adding a month to the last day of the month should 
1429         // give the last day of the next month which is quite logical. 
1431         // Unfortunately, there is no logic way to understand what should 
1432         // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)? 
1433         // We make it Feb 28 (last day too), but it is highly questionable. 
1434         tm
.mday 
= GetNumOfDaysInMonth(tm
.year
, tm
.mon
); 
1437     tm
.AddDays(diff
.GetTotalDays()); 
1441     wxASSERT_MSG( IsSameTime(tm
), 
1442                   _T("Add(wxDateSpan) shouldn't modify time") ); 
1447 // ---------------------------------------------------------------------------- 
1448 // Weekday and monthday stuff 
1449 // ---------------------------------------------------------------------------- 
1451 bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek
, WeekDay weekday
) 
1453     int year 
= GetYear(); 
1455     // Jan 4 always lies in the 1st week of the year 
1457     SetToWeekDayInSameWeek(weekday
) += wxDateSpan::Weeks(numWeek
); 
1459     if ( GetYear() != year 
) 
1461         // oops... numWeek was too big 
1468 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
, 
1471     // take the current month/year if none specified 
1472     if ( year 
== Inv_Year 
) 
1474     if ( month 
== Inv_Month 
) 
1477     return Set(GetNumOfDaysInMonth(year
, month
), month
, year
); 
1480 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
) 
1482     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, wxInvalidDateTime
, _T("invalid weekday") ); 
1484     WeekDay wdayThis 
= GetWeekDay(); 
1485     if ( weekday 
== wdayThis 
) 
1490     else if ( weekday 
< wdayThis 
) 
1492         return Subtract(wxDateSpan::Days(wdayThis 
- weekday
)); 
1494     else // weekday > wdayThis 
1496         return Add(wxDateSpan::Days(weekday 
- wdayThis
)); 
1500 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
) 
1502     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, wxInvalidDateTime
, _T("invalid weekday") ); 
1505     WeekDay wdayThis 
= GetWeekDay(); 
1506     if ( weekday 
== wdayThis 
) 
1511     else if ( weekday 
< wdayThis 
) 
1513         // need to advance a week 
1514         diff 
= 7 - (wdayThis 
- weekday
); 
1516     else // weekday > wdayThis 
1518         diff 
= weekday 
- wdayThis
; 
1521     return Add(wxDateSpan::Days(diff
)); 
1524 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
) 
1526     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, wxInvalidDateTime
, _T("invalid weekday") ); 
1529     WeekDay wdayThis 
= GetWeekDay(); 
1530     if ( weekday 
== wdayThis 
) 
1535     else if ( weekday 
> wdayThis 
) 
1537         // need to go to previous week 
1538         diff 
= 7 - (weekday 
- wdayThis
); 
1540     else // weekday < wdayThis 
1542         diff 
= wdayThis 
- weekday
; 
1545     return Subtract(wxDateSpan::Days(diff
)); 
1548 bool wxDateTime::SetToWeekDay(WeekDay weekday
, 
1553     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, FALSE
, _T("invalid weekday") ); 
1555     // we don't check explicitly that -5 <= n <= 5 because we will return FALSE 
1556     // anyhow in such case - but may be should still give an assert for it? 
1558     // take the current month/year if none specified 
1559     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
1563     // TODO this probably could be optimised somehow... 
1567         // get the first day of the month 
1568         dt
.Set(1, month
, year
); 
1571         WeekDay wdayFirst 
= dt
.GetWeekDay(); 
1573         // go to the first weekday of the month 
1574         int diff 
= weekday 
- wdayFirst
; 
1578         // add advance n-1 weeks more 
1581         dt 
+= wxDateSpan::Days(diff
); 
1583     else // count from the end of the month 
1585         // get the last day of the month 
1586         dt
.SetToLastMonthDay(month
, year
); 
1589         WeekDay wdayLast 
= dt
.GetWeekDay(); 
1591         // go to the last weekday of the month 
1592         int diff 
= wdayLast 
- weekday
; 
1596         // and rewind n-1 weeks from there 
1599         dt 
-= wxDateSpan::Days(diff
); 
1602     // check that it is still in the same month 
1603     if ( dt
.GetMonth() == month 
) 
1611         // no such day in this month 
1616 wxDateTime::wxDateTime_t 
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const 
1620     return gs_cumulatedDays
[IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
; 
1623 wxDateTime::wxDateTime_t 
wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags
, 
1624                                                    const TimeZone
& tz
) const 
1626     if ( flags 
== Default_First 
) 
1628         flags 
= GetCountry() == USA 
? Sunday_First 
: Monday_First
; 
1631     wxDateTime_t nDayInYear 
= GetDayOfYear(tz
); 
1634     WeekDay wd 
= GetWeekDay(tz
); 
1635     if ( flags 
== Sunday_First 
) 
1637         week 
= (nDayInYear 
- wd 
+ 7) / 7; 
1641         // have to shift the week days values 
1642         week 
= (nDayInYear 
- (wd 
- 1 + 7) % 7 + 7) / 7; 
1645     // FIXME some more elegant way?? 
1646     WeekDay wdYearStart 
= wxDateTime(1, Jan
, GetYear()).GetWeekDay(); 
1647     if ( wdYearStart 
== Wed 
|| wdYearStart 
== Thu 
) 
1655 wxDateTime::wxDateTime_t 
wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags
, 
1656                                                     const TimeZone
& tz
) const 
1659     wxDateTime dtMonthStart 
= wxDateTime(1, tm
.mon
, tm
.year
); 
1660     int nWeek 
= GetWeekOfYear(flags
) - dtMonthStart
.GetWeekOfYear(flags
) + 1; 
1663         // this may happen for January when Jan, 1 is the last week of the 
1665         nWeek 
+= IsLeapYear(tm
.year 
- 1) ? 53 : 52; 
1668     return (wxDateTime::wxDateTime_t
)nWeek
; 
1671 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
) 
1673     int year 
= GetYear(); 
1674     wxCHECK_MSG( (0 < yday
) && (yday 
<= GetNumberOfDays(year
)), 
1675                  wxInvalidDateTime
, _T("invalid year day") ); 
1677     bool isLeap 
= IsLeapYear(year
); 
1678     for ( Month mon 
= Jan
; mon 
< Inv_Month
; wxNextMonth(mon
) ) 
1680         // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we 
1681         // don't need it neither - because of the CHECK above we know that 
1682         // yday lies in December then 
1683         if ( (mon 
== Dec
) || (yday 
< gs_cumulatedDays
[isLeap
][mon 
+ 1]) ) 
1685             Set(yday 
- gs_cumulatedDays
[isLeap
][mon
], mon
, year
); 
1694 // ---------------------------------------------------------------------------- 
1695 // Julian day number conversion and related stuff 
1696 // ---------------------------------------------------------------------------- 
1698 double wxDateTime::GetJulianDayNumber() const 
1700     // JDN are always expressed for the GMT dates 
1701     Tm 
tm(ToTimezone(GMT0
).GetTm(GMT0
)); 
1703     double result 
= GetTruncatedJDN(tm
.mday
, tm
.mon
, tm
.year
); 
1705     // add the part GetTruncatedJDN() neglected 
1708     // and now add the time: 86400 sec = 1 JDN 
1709     return result 
+ ((double)(60*(60*tm
.hour 
+ tm
.min
) + tm
.sec
)) / 86400; 
1712 double wxDateTime::GetRataDie() const 
1714     // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5 
1715     return GetJulianDayNumber() - 1721119.5 - 306; 
1718 // ---------------------------------------------------------------------------- 
1719 // timezone and DST stuff 
1720 // ---------------------------------------------------------------------------- 
1722 int wxDateTime::IsDST(wxDateTime::Country country
) const 
1724     wxCHECK_MSG( country 
== Country_Default
, -1, 
1725                  _T("country support not implemented") ); 
1727     // use the C RTL for the dates in the standard range 
1728     time_t timet 
= GetTicks(); 
1729     if ( timet 
!= (time_t)-1 ) 
1731         tm 
*tm 
= localtime(&timet
); 
1733         wxCHECK_MSG( tm
, -1, _T("localtime() failed") ); 
1735         return tm
->tm_isdst
; 
1739         int year 
= GetYear(); 
1741         if ( !IsDSTApplicable(year
, country
) ) 
1743             // no DST time in this year in this country 
1747         return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
)); 
1751 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
) 
1753     long secDiff 
= GetTimeZone() + tz
.GetOffset(); 
1755     // we need to know whether DST is or not in effect for this date unless 
1756     // the test disabled by the caller 
1757     if ( !noDST 
&& (IsDST() == 1) ) 
1759         // FIXME we assume that the DST is always shifted by 1 hour 
1763     return Subtract(wxTimeSpan::Seconds(secDiff
)); 
1766 // ---------------------------------------------------------------------------- 
1767 // wxDateTime to/from text representations 
1768 // ---------------------------------------------------------------------------- 
1770 wxString 
wxDateTime::Format(const wxChar 
*format
, const TimeZone
& tz
) const 
1772     wxCHECK_MSG( format
, _T(""), _T("NULL format in wxDateTime::Format") ); 
1774     // we have to use our own implementation if the date is out of range of 
1775     // strftime() or if we use non standard specificators 
1776     time_t time 
= GetTicks(); 
1777     if ( (time 
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) ) 
1781         if ( tz
.GetOffset() == -GetTimeZone() ) 
1783             // we are working with local time 
1784             tm 
= localtime(&time
); 
1786             // should never happen 
1787             wxCHECK_MSG( tm
, wxEmptyString
, _T("localtime() failed") ); 
1791             time 
+= (int)tz
.GetOffset(); 
1793 #ifdef __VMS__ // time is unsigned so avoid the warning 
1794             int time2 
= (int) time
; 
1802                 // should never happen 
1803                 wxCHECK_MSG( tm
, wxEmptyString
, _T("gmtime() failed") ); 
1807                 tm 
= (struct tm 
*)NULL
; 
1813             return CallStrftime(format
, tm
); 
1815         //else: use generic code below 
1818     // we only parse ANSI C format specifications here, no POSIX 2 
1819     // complications, no GNU extensions but we do add support for a "%l" format 
1820     // specifier allowing to get the number of milliseconds 
1823     // used for calls to strftime() when we only deal with time 
1824     struct tm tmTimeOnly
; 
1825     tmTimeOnly
.tm_hour 
= tm
.hour
; 
1826     tmTimeOnly
.tm_min 
= tm
.min
; 
1827     tmTimeOnly
.tm_sec 
= tm
.sec
; 
1828     tmTimeOnly
.tm_wday 
= 0; 
1829     tmTimeOnly
.tm_yday 
= 0; 
1830     tmTimeOnly
.tm_mday 
= 1;         // any date will do 
1831     tmTimeOnly
.tm_mon 
= 0; 
1832     tmTimeOnly
.tm_year 
= 76; 
1833     tmTimeOnly
.tm_isdst 
= 0;        // no DST, we adjust for tz ourselves 
1835     wxString tmp
, res
, fmt
; 
1836     for ( const wxChar 
*p 
= format
; *p
; p
++ ) 
1838         if ( *p 
!= _T('%') ) 
1846         // set the default format 
1849             case _T('Y'):               // year has 4 digits 
1853             case _T('j'):               // day of year has 3 digits 
1854             case _T('l'):               // milliseconds have 3 digits 
1859                 // it's either another valid format specifier in which case 
1860                 // the format is "%02d" (for all the rest) or we have the 
1861                 // field width preceding the format in which case it will 
1862                 // override the default format anyhow 
1866         bool restart 
= TRUE
; 
1871             // start of the format specification 
1874                 case _T('a'):       // a weekday name 
1876                     // second parameter should be TRUE for abbreviated names 
1877                     res 
+= GetWeekDayName(tm
.GetWeekDay(), 
1878                                           *p 
== _T('a') ? Name_Abbr 
: Name_Full
); 
1881                 case _T('b'):       // a month name 
1883                     res 
+= GetMonthName(tm
.mon
, 
1884                                         *p 
== _T('b') ? Name_Abbr 
: Name_Full
); 
1887                 case _T('c'):       // locale default date and time  representation 
1888                 case _T('x'):       // locale default date representation 
1890                     // the problem: there is no way to know what do these format 
1891                     // specifications correspond to for the current locale. 
1893                     // the solution: use a hack and still use strftime(): first 
1894                     // find the YEAR which is a year in the strftime() range (1970 
1895                     // - 2038) whose Jan 1 falls on the same week day as the Jan 1 
1896                     // of the real year. Then make a copy of the format and 
1897                     // replace all occurences of YEAR in it with some unique 
1898                     // string not appearing anywhere else in it, then use 
1899                     // strftime() to format the date in year YEAR and then replace 
1900                     // YEAR back by the real year and the unique replacement 
1901                     // string back with YEAR. Notice that "all occurences of YEAR" 
1902                     // means all occurences of 4 digit as well as 2 digit form! 
1904                     // the bugs: we assume that neither of %c nor %x contains any 
1905                     // fields which may change between the YEAR and real year. For 
1906                     // example, the week number (%U, %W) and the day number (%j) 
1907                     // will change if one of these years is leap and the other one 
1910                         // find the YEAR: normally, for any year X, Jan 1 or the 
1911                         // year X + 28 is the same weekday as Jan 1 of X (because 
1912                         // the weekday advances by 1 for each normal X and by 2 
1913                         // for each leap X, hence by 5 every 4 years or by 35 
1914                         // which is 0 mod 7 every 28 years) but this rule breaks 
1915                         // down if there are years between X and Y which are 
1916                         // divisible by 4 but not leap (i.e. divisible by 100 but 
1917                         // not 400), hence the correction. 
1919                         int yearReal 
= GetYear(tz
); 
1920                         int mod28 
= yearReal 
% 28; 
1922                         // be careful to not go too far - we risk to leave the 
1927                             year 
= 1988 + mod28
;      // 1988 == 0 (mod 28) 
1931                             year 
= 1970 + mod28 
- 10; // 1970 == 10 (mod 28) 
1934                         int nCentury 
= year 
/ 100, 
1935                             nCenturyReal 
= yearReal 
/ 100; 
1937                         // need to adjust for the years divisble by 400 which are 
1938                         // not leap but are counted like leap ones if we just take 
1939                         // the number of centuries in between for nLostWeekDays 
1940                         int nLostWeekDays 
= (nCentury 
- nCenturyReal
) - 
1941                                             (nCentury 
/ 4 - nCenturyReal 
/ 4); 
1943                         // we have to gain back the "lost" weekdays: note that the 
1944                         // effect of this loop is to not do anything to 
1945                         // nLostWeekDays (which we won't use any more), but to 
1946                         // (indirectly) set the year correctly 
1947                         while ( (nLostWeekDays 
% 7) != 0 ) 
1949                             nLostWeekDays 
+= year
++ % 4 ? 1 : 2; 
1952                         // at any rate, we couldn't go further than 1988 + 9 + 28! 
1953                         wxASSERT_MSG( year 
< 2030, 
1954                                       _T("logic error in wxDateTime::Format") ); 
1956                         wxString strYear
, strYear2
; 
1957                         strYear
.Printf(_T("%d"), year
); 
1958                         strYear2
.Printf(_T("%d"), year 
% 100); 
1960                         // find two strings not occuring in format (this is surely 
1961                         // not optimal way of doing it... improvements welcome!) 
1962                         wxString fmt 
= format
; 
1963                         wxString replacement 
= (wxChar
)-1; 
1964                         while ( fmt
.Find(replacement
) != wxNOT_FOUND 
) 
1966                             replacement 
<< (wxChar
)-1; 
1969                         wxString replacement2 
= (wxChar
)-2; 
1970                         while ( fmt
.Find(replacement
) != wxNOT_FOUND 
) 
1972                             replacement 
<< (wxChar
)-2; 
1975                         // replace all occurences of year with it 
1976                         bool wasReplaced 
= fmt
.Replace(strYear
, replacement
) > 0; 
1978                             wasReplaced 
= fmt
.Replace(strYear2
, replacement2
) > 0; 
1980                         // use strftime() to format the same date but in supported 
1983                         // NB: we assume that strftime() doesn't check for the 
1984                         //     date validity and will happily format the date 
1985                         //     corresponding to Feb 29 of a non leap year (which 
1986                         //     may happen if yearReal was leap and year is not) 
1987                         struct tm tmAdjusted
; 
1989                         tmAdjusted
.tm_hour 
= tm
.hour
; 
1990                         tmAdjusted
.tm_min 
= tm
.min
; 
1991                         tmAdjusted
.tm_sec 
= tm
.sec
; 
1992                         tmAdjusted
.tm_wday 
= tm
.GetWeekDay(); 
1993                         tmAdjusted
.tm_yday 
= GetDayOfYear(); 
1994                         tmAdjusted
.tm_mday 
= tm
.mday
; 
1995                         tmAdjusted
.tm_mon 
= tm
.mon
; 
1996                         tmAdjusted
.tm_year 
= year 
- 1900; 
1997                         tmAdjusted
.tm_isdst 
= 0; // no DST, already adjusted 
1998                         wxString str 
= CallStrftime(*p 
== _T('c') ? _T("%c") 
2002                         // now replace the occurence of 1999 with the real year 
2003                         wxString strYearReal
, strYearReal2
; 
2004                         strYearReal
.Printf(_T("%04d"), yearReal
); 
2005                         strYearReal2
.Printf(_T("%02d"), yearReal 
% 100); 
2006                         str
.Replace(strYear
, strYearReal
); 
2007                         str
.Replace(strYear2
, strYearReal2
); 
2009                         // and replace back all occurences of replacement string 
2012                             str
.Replace(replacement2
, strYear2
); 
2013                             str
.Replace(replacement
, strYear
); 
2020                 case _T('d'):       // day of a month (01-31) 
2021                     res 
+= wxString::Format(fmt
, tm
.mday
); 
2024                 case _T('H'):       // hour in 24h format (00-23) 
2025                     res 
+= wxString::Format(fmt
, tm
.hour
); 
2028                 case _T('I'):       // hour in 12h format (01-12) 
2030                         // 24h -> 12h, 0h -> 12h too 
2031                         int hour12 
= tm
.hour 
> 12 ? tm
.hour 
- 12 
2032                                                   : tm
.hour 
? tm
.hour 
: 12; 
2033                         res 
+= wxString::Format(fmt
, hour12
); 
2037                 case _T('j'):       // day of the year 
2038                     res 
+= wxString::Format(fmt
, GetDayOfYear(tz
)); 
2041                 case _T('l'):       // milliseconds (NOT STANDARD) 
2042                     res 
+= wxString::Format(fmt
, GetMillisecond(tz
)); 
2045                 case _T('m'):       // month as a number (01-12) 
2046                     res 
+= wxString::Format(fmt
, tm
.mon 
+ 1); 
2049                 case _T('M'):       // minute as a decimal number (00-59) 
2050                     res 
+= wxString::Format(fmt
, tm
.min
); 
2053                 case _T('p'):       // AM or PM string 
2054                     res 
+= CallStrftime(_T("%p"), &tmTimeOnly
); 
2057                 case _T('S'):       // second as a decimal number (00-61) 
2058                     res 
+= wxString::Format(fmt
, tm
.sec
); 
2061                 case _T('U'):       // week number in the year (Sunday 1st week day) 
2062                     res 
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
)); 
2065                 case _T('W'):       // week number in the year (Monday 1st week day) 
2066                     res 
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
)); 
2069                 case _T('w'):       // weekday as a number (0-6), Sunday = 0 
2070                     res 
+= wxString::Format(fmt
, tm
.GetWeekDay()); 
2073                 // case _T('x'): -- handled with "%c" 
2075                 case _T('X'):       // locale default time representation 
2076                     // just use strftime() to format the time for us 
2077                     res 
+= CallStrftime(_T("%X"), &tmTimeOnly
); 
2080                 case _T('y'):       // year without century (00-99) 
2081                     res 
+= wxString::Format(fmt
, tm
.year 
% 100); 
2084                 case _T('Y'):       // year with century 
2085                     res 
+= wxString::Format(fmt
, tm
.year
); 
2088                 case _T('Z'):       // timezone name 
2089                     res 
+= CallStrftime(_T("%Z"), &tmTimeOnly
); 
2093                     // is it the format width? 
2095                     while ( *p 
== _T('-') || *p 
== _T('+') || 
2096                             *p 
== _T(' ') || wxIsdigit(*p
) ) 
2101                     if ( !fmt
.IsEmpty() ) 
2103                         // we've only got the flags and width so far in fmt 
2104                         fmt
.Prepend(_T('%')); 
2105                         fmt
.Append(_T('d')); 
2112                     // no, it wasn't the width 
2113                     wxFAIL_MSG(_T("unknown format specificator")); 
2115                     // fall through and just copy it nevertheless 
2117                 case _T('%'):       // a percent sign 
2121                 case 0:             // the end of string 
2122                     wxFAIL_MSG(_T("missing format at the end of string")); 
2124                     // just put the '%' which was the last char in format 
2134 // this function parses a string in (strict) RFC 822 format: see the section 5 
2135 // of the RFC for the detailed description, but briefly it's something of the 
2136 // form "Sat, 18 Dec 1999 00:48:30 +0100" 
2138 // this function is "strict" by design - it must reject anything except true 
2139 // RFC822 time specs. 
2141 // TODO a great candidate for using reg exps 
2142 const wxChar 
*wxDateTime::ParseRfc822Date(const wxChar
* date
) 
2144     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
2146     const wxChar 
*p 
= date
; 
2147     const wxChar 
*comma 
= wxStrchr(p
, _T(',')); 
2150         // the part before comma is the weekday 
2152         // skip it for now - we don't use but might check that it really 
2153         // corresponds to the specfied date 
2156         if ( *p 
!= _T(' ') ) 
2158             wxLogDebug(_T("no space after weekday in RFC822 time spec")); 
2160             return (wxChar 
*)NULL
; 
2166     // the following 1 or 2 digits are the day number 
2167     if ( !wxIsdigit(*p
) ) 
2169         wxLogDebug(_T("day number expected in RFC822 time spec, none found")); 
2171         return (wxChar 
*)NULL
; 
2174     wxDateTime_t day 
= *p
++ - _T('0'); 
2175     if ( wxIsdigit(*p
) ) 
2178         day 
+= *p
++ - _T('0'); 
2181     if ( *p
++ != _T(' ') ) 
2183         return (wxChar 
*)NULL
; 
2186     // the following 3 letters specify the month 
2187     wxString 
monName(p
, 3); 
2189     if ( monName 
== _T("Jan") ) 
2191     else if ( monName 
== _T("Feb") ) 
2193     else if ( monName 
== _T("Mar") ) 
2195     else if ( monName 
== _T("Apr") ) 
2197     else if ( monName 
== _T("May") ) 
2199     else if ( monName 
== _T("Jun") ) 
2201     else if ( monName 
== _T("Jul") ) 
2203     else if ( monName 
== _T("Aug") ) 
2205     else if ( monName 
== _T("Sep") ) 
2207     else if ( monName 
== _T("Oct") ) 
2209     else if ( monName 
== _T("Nov") ) 
2211     else if ( monName 
== _T("Dec") ) 
2215         wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str()); 
2217         return (wxChar 
*)NULL
; 
2222     if ( *p
++ != _T(' ') ) 
2224         return (wxChar 
*)NULL
; 
2228     if ( !wxIsdigit(*p
) ) 
2231         return (wxChar 
*)NULL
; 
2234     int year 
= *p
++ - _T('0'); 
2236     if ( !wxIsdigit(*p
) ) 
2238         // should have at least 2 digits in the year 
2239         return (wxChar 
*)NULL
; 
2243     year 
+= *p
++ - _T('0'); 
2245     // is it a 2 digit year (as per original RFC 822) or a 4 digit one? 
2246     if ( wxIsdigit(*p
) ) 
2249         year 
+= *p
++ - _T('0'); 
2251         if ( !wxIsdigit(*p
) ) 
2253             // no 3 digit years please 
2254             return (wxChar 
*)NULL
; 
2258         year 
+= *p
++ - _T('0'); 
2261     if ( *p
++ != _T(' ') ) 
2263         return (wxChar 
*)NULL
; 
2266     // time is in the format hh:mm:ss and seconds are optional 
2267     if ( !wxIsdigit(*p
) ) 
2269         return (wxChar 
*)NULL
; 
2272     wxDateTime_t hour 
= *p
++ - _T('0'); 
2274     if ( !wxIsdigit(*p
) ) 
2276         return (wxChar 
*)NULL
; 
2280     hour 
+= *p
++ - _T('0'); 
2282     if ( *p
++ != _T(':') ) 
2284         return (wxChar 
*)NULL
; 
2287     if ( !wxIsdigit(*p
) ) 
2289         return (wxChar 
*)NULL
; 
2292     wxDateTime_t min 
= *p
++ - _T('0'); 
2294     if ( !wxIsdigit(*p
) ) 
2296         return (wxChar 
*)NULL
; 
2300     min 
+= *p
++ - _T('0'); 
2302     wxDateTime_t sec 
= 0; 
2303     if ( *p
++ == _T(':') ) 
2305         if ( !wxIsdigit(*p
) ) 
2307             return (wxChar 
*)NULL
; 
2310         sec 
= *p
++ - _T('0'); 
2312         if ( !wxIsdigit(*p
) ) 
2314             return (wxChar 
*)NULL
; 
2318         sec 
+= *p
++ - _T('0'); 
2321     if ( *p
++ != _T(' ') ) 
2323         return (wxChar 
*)NULL
; 
2326     // and now the interesting part: the timezone 
2328     if ( *p 
== _T('-') || *p 
== _T('+') ) 
2330         // the explicit offset given: it has the form of hhmm 
2331         bool plus 
= *p
++ == _T('+'); 
2333         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
2335             return (wxChar 
*)NULL
; 
2339         offset 
= 60*(10*(*p 
- _T('0')) + (*(p 
+ 1) - _T('0'))); 
2343         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
2345             return (wxChar 
*)NULL
; 
2349         offset 
+= 10*(*p 
- _T('0')) + (*(p 
+ 1) - _T('0')); 
2360         // the symbolic timezone given: may be either military timezone or one 
2361         // of standard abbreviations 
2364             // military: Z = UTC, J unused, A = -1, ..., Y = +12 
2365             static const int offsets
[26] = 
2367                 //A  B   C   D   E   F   G   H   I    J    K    L    M 
2368                 -1, -2, -3, -4, -5, -6, -7, -8, -9,   0, -10, -11, -12, 
2369                 //N  O   P   R   Q   S   T   U   V    W    Z    Y    Z 
2370                 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0 
2373             if ( *p 
< _T('A') || *p 
> _T('Z') || *p 
== _T('J') ) 
2375                 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
); 
2377                 return (wxChar 
*)NULL
; 
2380             offset 
= offsets
[*p
++ - _T('A')]; 
2386             if ( tz 
== _T("UT") || tz 
== _T("UTC") || tz 
== _T("GMT") ) 
2388             else if ( tz 
== _T("AST") ) 
2389                 offset 
= AST 
- GMT0
; 
2390             else if ( tz 
== _T("ADT") ) 
2391                 offset 
= ADT 
- GMT0
; 
2392             else if ( tz 
== _T("EST") ) 
2393                 offset 
= EST 
- GMT0
; 
2394             else if ( tz 
== _T("EDT") ) 
2395                 offset 
= EDT 
- GMT0
; 
2396             else if ( tz 
== _T("CST") ) 
2397                 offset 
= CST 
- GMT0
; 
2398             else if ( tz 
== _T("CDT") ) 
2399                 offset 
= CDT 
- GMT0
; 
2400             else if ( tz 
== _T("MST") ) 
2401                 offset 
= MST 
- GMT0
; 
2402             else if ( tz 
== _T("MDT") ) 
2403                 offset 
= MDT 
- GMT0
; 
2404             else if ( tz 
== _T("PST") ) 
2405                 offset 
= PST 
- GMT0
; 
2406             else if ( tz 
== _T("PDT") ) 
2407                 offset 
= PDT 
- GMT0
; 
2410                 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
); 
2412                 return (wxChar 
*)NULL
; 
2422     // the spec was correct 
2423     Set(day
, mon
, year
, hour
, min
, sec
); 
2424     MakeTimezone((wxDateTime_t
)(60*offset
)); 
2429 const wxChar 
*wxDateTime::ParseFormat(const wxChar 
*date
, 
2430                                       const wxChar 
*format
, 
2431                                       const wxDateTime
& dateDef
) 
2433     wxCHECK_MSG( date 
&& format
, (wxChar 
*)NULL
, 
2434                  _T("NULL pointer in wxDateTime::ParseFormat()") ); 
2439     // what fields have we found? 
2440     bool haveWDay 
= FALSE
, 
2449     bool hourIsIn12hFormat 
= FALSE
, // or in 24h one? 
2450          isPM 
= FALSE
;              // AM by default 
2452     // and the value of the items we have (init them to get rid of warnings) 
2453     wxDateTime_t sec 
= 0, 
2456     WeekDay wday 
= Inv_WeekDay
; 
2457     wxDateTime_t yday 
= 0, 
2459     wxDateTime::Month mon 
= Inv_Month
; 
2462     const wxChar 
*input 
= date
; 
2463     for ( const wxChar 
*fmt 
= format
; *fmt
; fmt
++ ) 
2465         if ( *fmt 
!= _T('%') ) 
2467             if ( wxIsspace(*fmt
) ) 
2469                 // a white space in the format string matches 0 or more white 
2470                 // spaces in the input 
2471                 while ( wxIsspace(*input
) ) 
2478                 // any other character (not whitespace, not '%') must be 
2479                 // matched by itself in the input 
2480                 if ( *input
++ != *fmt 
) 
2483                     return (wxChar 
*)NULL
; 
2487             // done with this format char 
2491         // start of a format specification 
2493         // parse the optional width 
2495         while ( isdigit(*++fmt
) ) 
2498             width 
+= *fmt 
- _T('0'); 
2501         // then the format itself 
2504             case _T('a'):       // a weekday name 
2507                     int flag 
= *fmt 
== _T('a') ? Name_Abbr 
: Name_Full
; 
2508                     wday 
= GetWeekDayFromName(GetAlphaToken(input
), flag
); 
2509                     if ( wday 
== Inv_WeekDay 
) 
2512                         return (wxChar 
*)NULL
; 
2518             case _T('b'):       // a month name 
2521                     int flag 
= *fmt 
== _T('b') ? Name_Abbr 
: Name_Full
; 
2522                     mon 
= GetMonthFromName(GetAlphaToken(input
), flag
); 
2523                     if ( mon 
== Inv_Month 
) 
2526                         return (wxChar 
*)NULL
; 
2532             case _T('c'):       // locale default date and time  representation 
2536                     // this is the format which corresponds to ctime() output 
2537                     // and strptime("%c") should parse it, so try it first 
2538                     static const wxChar 
*fmtCtime 
= _T("%a %b %d %H:%M:%S %Y"); 
2540                     const wxChar 
*result 
= dt
.ParseFormat(input
, fmtCtime
); 
2543                         result 
= dt
.ParseFormat(input
, _T("%x %X")); 
2548                         result 
= dt
.ParseFormat(input
, _T("%X %x")); 
2553                         // we've tried everything and still no match 
2554                         return (wxChar 
*)NULL
; 
2559                     haveDay 
= haveMon 
= haveYear 
= 
2560                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2574             case _T('d'):       // day of a month (01-31) 
2575                 if ( !GetNumericToken(width
, input
, &num
) || 
2576                         (num 
> 31) || (num 
< 1) ) 
2579                     return (wxChar 
*)NULL
; 
2582                 // we can't check whether the day range is correct yet, will 
2583                 // do it later - assume ok for now 
2585                 mday 
= (wxDateTime_t
)num
; 
2588             case _T('H'):       // hour in 24h format (00-23) 
2589                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 23) ) 
2592                     return (wxChar 
*)NULL
; 
2596                 hour 
= (wxDateTime_t
)num
; 
2599             case _T('I'):       // hour in 12h format (01-12) 
2600                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 12) ) 
2603                     return (wxChar 
*)NULL
; 
2607                 hourIsIn12hFormat 
= TRUE
; 
2608                 hour 
= (wxDateTime_t
)(num 
% 12);        // 12 should be 0 
2611             case _T('j'):       // day of the year 
2612                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 366) ) 
2615                     return (wxChar 
*)NULL
; 
2619                 yday 
= (wxDateTime_t
)num
; 
2622             case _T('m'):       // month as a number (01-12) 
2623                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 12) ) 
2626                     return (wxChar 
*)NULL
; 
2630                 mon 
= (Month
)(num 
- 1); 
2633             case _T('M'):       // minute as a decimal number (00-59) 
2634                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 59) ) 
2637                     return (wxChar 
*)NULL
; 
2641                 min 
= (wxDateTime_t
)num
; 
2644             case _T('p'):       // AM or PM string 
2646                     wxString am
, pm
, token 
= GetAlphaToken(input
); 
2648                     GetAmPmStrings(&am
, &pm
); 
2649                     if ( token
.CmpNoCase(pm
) == 0 ) 
2653                     else if ( token
.CmpNoCase(am
) != 0 ) 
2656                         return (wxChar 
*)NULL
; 
2661             case _T('r'):       // time as %I:%M:%S %p 
2664                     input 
= dt
.ParseFormat(input
, _T("%I:%M:%S %p")); 
2668                         return (wxChar 
*)NULL
; 
2671                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2680             case _T('R'):       // time as %H:%M 
2683                     input 
= dt
.ParseFormat(input
, _T("%H:%M")); 
2687                         return (wxChar 
*)NULL
; 
2690                     haveHour 
= haveMin 
= TRUE
; 
2697             case _T('S'):       // second as a decimal number (00-61) 
2698                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 61) ) 
2701                     return (wxChar 
*)NULL
; 
2705                 sec 
= (wxDateTime_t
)num
; 
2708             case _T('T'):       // time as %H:%M:%S 
2711                     input 
= dt
.ParseFormat(input
, _T("%H:%M:%S")); 
2715                         return (wxChar 
*)NULL
; 
2718                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2727             case _T('w'):       // weekday as a number (0-6), Sunday = 0 
2728                 if ( !GetNumericToken(width
, input
, &num
) || (wday 
> 6) ) 
2731                     return (wxChar 
*)NULL
; 
2735                 wday 
= (WeekDay
)num
; 
2738             case _T('x'):       // locale default date representation 
2739 #ifdef HAVE_STRPTIME 
2740                 // try using strptime() - it may fail even if the input is 
2741                 // correct but the date is out of range, so we will fall back 
2742                 // to our generic code anyhow (FIXME !Unicode friendly) 
2745                     const wxChar 
*result 
= strptime(input
, "%x", &tm
); 
2750                         haveDay 
= haveMon 
= haveYear 
= TRUE
; 
2752                         year 
= 1900 + tm
.tm_year
; 
2753                         mon 
= (Month
)tm
.tm_mon
; 
2759 #endif // HAVE_STRPTIME 
2761                 // TODO query the LOCALE_IDATE setting under Win32 
2765                     wxString fmtDate
, fmtDateAlt
; 
2766                     if ( IsWestEuropeanCountry(GetCountry()) || 
2767                          GetCountry() == Russia 
) 
2769                         fmtDate 
= _T("%d/%m/%y"); 
2770                         fmtDateAlt 
= _T("%m/%d/%y"); 
2774                         fmtDate 
= _T("%m/%d/%y"); 
2775                         fmtDateAlt 
= _T("%d/%m/%y"); 
2778                     const wxChar 
*result 
= dt
.ParseFormat(input
, fmtDate
); 
2782                         // ok, be nice and try another one 
2783                         result 
= dt
.ParseFormat(input
, fmtDateAlt
); 
2789                         return (wxChar 
*)NULL
; 
2794                     haveDay 
= haveMon 
= haveYear 
= TRUE
; 
2805             case _T('X'):       // locale default time representation 
2806 #ifdef HAVE_STRPTIME 
2808                     // use strptime() to do it for us (FIXME !Unicode friendly) 
2810                     input 
= strptime(input
, "%X", &tm
); 
2813                         return (wxChar 
*)NULL
; 
2816                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2822 #else // !HAVE_STRPTIME 
2823                 // TODO under Win32 we can query the LOCALE_ITIME system 
2824                 //      setting which says whether the default time format is 
2827                     // try to parse what follows as "%H:%M:%S" and, if this 
2828                     // fails, as "%I:%M:%S %p" - this should catch the most 
2832                     const wxChar 
*result 
= dt
.ParseFormat(input
, _T("%T")); 
2835                         result 
= dt
.ParseFormat(input
, _T("%r")); 
2841                         return (wxChar 
*)NULL
; 
2844                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2853 #endif // HAVE_STRPTIME/!HAVE_STRPTIME 
2856             case _T('y'):       // year without century (00-99) 
2857                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 99) ) 
2860                     return (wxChar 
*)NULL
; 
2865                 // TODO should have an option for roll over date instead of 
2866                 //      hard coding it here 
2867                 year 
= (num 
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
; 
2870             case _T('Y'):       // year with century 
2871                 if ( !GetNumericToken(width
, input
, &num
) ) 
2874                     return (wxChar 
*)NULL
; 
2878                 year 
= (wxDateTime_t
)num
; 
2881             case _T('Z'):       // timezone name 
2882                 wxFAIL_MSG(_T("TODO")); 
2885             case _T('%'):       // a percent sign 
2886                 if ( *input
++ != _T('%') ) 
2889                     return (wxChar 
*)NULL
; 
2893             case 0:             // the end of string 
2894                 wxFAIL_MSG(_T("unexpected format end")); 
2898             default:            // not a known format spec 
2899                 return (wxChar 
*)NULL
; 
2903     // format matched, try to construct a date from what we have now 
2905     if ( dateDef
.IsValid() ) 
2907         // take this date as default 
2908         tmDef 
= dateDef
.GetTm(); 
2911     else if ( m_time 
!= 0 ) 
2913     else if ( m_time 
!= wxLongLong(0) ) 
2916         // if this date is valid, don't change it 
2921         // no default and this date is invalid - fall back to Today() 
2922         tmDef 
= Today().GetTm(); 
2933     // TODO we don't check here that the values are consistent, if both year 
2934     //      day and month/day were found, we just ignore the year day and we 
2935     //      also always ignore the week day 
2936     if ( haveMon 
&& haveDay 
) 
2938         if ( mday 
> GetNumOfDaysInMonth(tm
.year
, mon
) ) 
2940             wxLogDebug(_T("bad month day in wxDateTime::ParseFormat")); 
2942             return (wxChar 
*)NULL
; 
2948     else if ( haveYDay 
) 
2950         if ( yday 
> GetNumberOfDays(tm
.year
) ) 
2952             wxLogDebug(_T("bad year day in wxDateTime::ParseFormat")); 
2954             return (wxChar 
*)NULL
; 
2957         Tm tm2 
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm(); 
2964     if ( haveHour 
&& hourIsIn12hFormat 
&& isPM 
) 
2966         // translate to 24hour format 
2969     //else: either already in 24h format or no translation needed 
2992 const wxChar 
*wxDateTime::ParseDateTime(const wxChar 
*date
) 
2994     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
2996     // there is a public domain version of getdate.y, but it only works for 
2998     wxFAIL_MSG(_T("TODO")); 
3000     return (wxChar 
*)NULL
; 
3003 const wxChar 
*wxDateTime::ParseDate(const wxChar 
*date
) 
3005     // this is a simplified version of ParseDateTime() which understands only 
3006     // "today" (for wxDate compatibility) and digits only otherwise (and not 
3007     // all esoteric constructions ParseDateTime() knows about) 
3009     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
3011     const wxChar 
*p 
= date
; 
3012     while ( wxIsspace(*p
) ) 
3015     // some special cases 
3019         int dayDiffFromToday
; 
3022         { wxTRANSLATE("today"),             0 }, 
3023         { wxTRANSLATE("yesterday"),        -1 }, 
3024         { wxTRANSLATE("tomorrow"),          1 }, 
3027     for ( size_t n 
= 0; n 
< WXSIZEOF(literalDates
); n
++ ) 
3029         wxString date 
= wxGetTranslation(literalDates
[n
].str
); 
3030         size_t len 
= date
.length(); 
3031         if ( wxStrlen(p
) >= len 
&& (wxString(p
, len
).CmpNoCase(date
) == 0) ) 
3033             // nothing can follow this, so stop here 
3036             int dayDiffFromToday 
= literalDates
[n
].dayDiffFromToday
; 
3038             if ( dayDiffFromToday 
) 
3040                 *this += wxDateSpan::Days(dayDiffFromToday
); 
3048     bool haveDay 
= FALSE
,       // the months day? 
3049          haveWDay 
= FALSE
,      // the day of week? 
3050          haveMon 
= FALSE
,       // the month? 
3051          haveYear 
= FALSE
;      // the year? 
3053     // and the value of the items we have (init them to get rid of warnings) 
3054     WeekDay wday 
= Inv_WeekDay
; 
3055     wxDateTime_t day 
= 0; 
3056     wxDateTime::Month mon 
= Inv_Month
; 
3059     // tokenize the string 
3061     static const wxChar 
*dateDelimiters 
= _T(".,/-\t\n "); 
3062     wxStringTokenizer 
tok(p
, dateDelimiters
); 
3063     while ( tok
.HasMoreTokens() ) 
3065         wxString token 
= tok
.GetNextToken(); 
3069         if ( token
.ToULong(&val
) ) 
3071             // guess what this number is 
3075                  // only years are counted from 0 
3076                  isYear 
= (val 
== 0) || (val 
> 31); 
3079                 // may be the month or month day or the year, assume the 
3080                 // month day by default and fallback to month if the range 
3081                 // allow it or to the year if our assumption doesn't work 
3084                     // we already have the day, so may only be a month or year 
3094                 else // it may be day 
3101                         if ( val 
> GetNumOfDaysInMonth(haveYear 
? year
 
3105                             // oops, it can't be a day finally 
3121             // remember that we have this and stop the scan if it's the second 
3122             // time we find this, except for the day logic (see there) 
3132                 // no roll over - 99 means 99, not 1999 for us 
3133                 year 
= (wxDateTime_t
)val
; 
3145                 mon 
= (wxDateTime::Month
)(val 
- 1); 
3149                 wxASSERT_MSG( isDay
, _T("logic error") ); 
3153                     // may be were mistaken when we found it for the first 
3154                     // time? may be it was a month or year instead? 
3156                     // this ability to "backtrack" allows us to correctly parse 
3157                     // both things like 01/13 and 13/01 - but, of course, we 
3158                     // still can't resolve the ambiguity in 01/02 (it will be 
3159                     // Feb 1 for us, not Jan 2 as americans might expect!) 
3160                     if ( (day 
<= 12) && !haveMon 
) 
3162                         // exchange day and month 
3163                         mon 
= (wxDateTime::Month
)(day 
- 1); 
3167                     else if ( !haveYear 
) 
3169                         // exchange day and year 
3178                 day 
= (wxDateTime_t
)val
; 
3181         else // not a number 
3183             // be careful not to overwrite the current mon value 
3184             Month mon2 
= GetMonthFromName(token
, Name_Full 
| Name_Abbr
); 
3185             if ( mon2 
!= Inv_Month 
) 
3199                 wday 
= GetWeekDayFromName(token
, Name_Full 
| Name_Abbr
); 
3200                 if ( wday 
!= Inv_WeekDay 
) 
3213                     static const wxChar 
*ordinals
[] = 
3215                         wxTRANSLATE("first"), 
3216                         wxTRANSLATE("second"), 
3217                         wxTRANSLATE("third"), 
3218                         wxTRANSLATE("fourth"), 
3219                         wxTRANSLATE("fifth"), 
3220                         wxTRANSLATE("sixth"), 
3221                         wxTRANSLATE("seventh"), 
3222                         wxTRANSLATE("eighth"), 
3223                         wxTRANSLATE("ninth"), 
3224                         wxTRANSLATE("tenth"), 
3225                         wxTRANSLATE("eleventh"), 
3226                         wxTRANSLATE("twelfth"), 
3227                         wxTRANSLATE("thirteenth"), 
3228                         wxTRANSLATE("fourteenth"), 
3229                         wxTRANSLATE("fifteenth"), 
3230                         wxTRANSLATE("sixteenth"), 
3231                         wxTRANSLATE("seventeenth"), 
3232                         wxTRANSLATE("eighteenth"), 
3233                         wxTRANSLATE("nineteenth"), 
3234                         wxTRANSLATE("twentieth"), 
3235                         // that's enough - otherwise we'd have problems with 
3236                         // composite (or not) ordinals otherwise 
3240                     for ( n 
= 0; n 
< WXSIZEOF(ordinals
); n
++ ) 
3242                         if ( token
.CmpNoCase(ordinals
[n
]) == 0 ) 
3248                     if ( n 
== WXSIZEOF(ordinals
) ) 
3250                         // stop here - something unknown 
3257                         // don't try anything here (as in case of numeric day 
3258                         // above) - the symbolic day spec should always 
3259                         // precede the month/year 
3270         nPosCur 
= tok
.GetPosition(); 
3273     // either no more tokens or the scan was stopped by something we couldn't 
3274     // parse - in any case, see if we can construct a date from what we have 
3275     if ( !haveDay 
&& !haveWDay 
) 
3277         wxLogDebug(_T("ParseDate: no day, no weekday hence no date.")); 
3279         return (wxChar 
*)NULL
; 
3282     if ( haveWDay 
&& (haveMon 
|| haveYear 
|| haveDay
) && 
3283          !(haveDay 
&& haveMon 
&& haveYear
) ) 
3285         // without adjectives (which we don't support here) the week day only 
3286         // makes sense completely separately or with the full date 
3287         // specification (what would "Wed 1999" mean?) 
3288         return (wxChar 
*)NULL
; 
3291     if ( !haveWDay 
&& haveYear 
&& !(haveDay 
&& haveMon
) ) 
3293         // may be we have month and day instead of day and year? 
3294         if ( haveDay 
&& !haveMon 
) 
3298                 // exchange day and month 
3299                 mon 
= (wxDateTime::Month
)(day 
- 1); 
3301                 // we're in the current year then 
3302                 if ( year 
<= GetNumOfDaysInMonth(Inv_Year
, mon
) ) 
3309                 //else: no, can't exchange, leave haveMon == FALSE 
3315             // if we give the year, month and day must be given too 
3316             wxLogDebug(_T("ParseDate: day and month should be specified if year is.")); 
3318             return (wxChar 
*)NULL
; 
3324         mon 
= GetCurrentMonth(); 
3329         year 
= GetCurrentYear(); 
3334         Set(day
, mon
, year
); 
3338             // check that it is really the same 
3339             if ( GetWeekDay() != wday 
) 
3341                 // inconsistency detected 
3342                 wxLogDebug(_T("ParseDate: inconsistent day/weekday.")); 
3344                 return (wxChar 
*)NULL
; 
3352         SetToWeekDayInSameWeek(wday
); 
3355     // return the pointer to the first unparsed char 
3357     if ( nPosCur 
&& wxStrchr(dateDelimiters
, *(p 
- 1)) ) 
3359         // if we couldn't parse the token after the delimiter, put back the 
3360         // delimiter as well 
3367 const wxChar 
*wxDateTime::ParseTime(const wxChar 
*time
) 
3369     wxCHECK_MSG( time
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
3371     // first try some extra things 
3378         { wxTRANSLATE("noon"),      12 }, 
3379         { wxTRANSLATE("midnight"),  00 }, 
3383     for ( size_t n 
= 0; n 
< WXSIZEOF(stdTimes
); n
++ ) 
3385         wxString timeString 
= wxGetTranslation(stdTimes
[n
].name
); 
3386         size_t len 
= timeString
.length(); 
3387         if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 ) 
3389             Set(stdTimes
[n
].hour
, 0, 0); 
3395     // try all time formats we may think about starting with the standard one 
3396     const wxChar 
*result 
= ParseFormat(time
, _T("%X")); 
3399         // normally, it's the same, but why not try it? 
3400         result 
= ParseFormat(time
, _T("%H:%M:%S")); 
3405         // 12hour with AM/PM? 
3406         result 
= ParseFormat(time
, _T("%I:%M:%S %p")); 
3412         result 
= ParseFormat(time
, _T("%H:%M")); 
3417         // 12hour with AM/PM but without seconds? 
3418         result 
= ParseFormat(time
, _T("%I:%M %p")); 
3424         result 
= ParseFormat(time
, _T("%H")); 
3429         // just the hour and AM/PM? 
3430         result 
= ParseFormat(time
, _T("%I %p")); 
3433     // TODO: parse timezones 
3438 // ---------------------------------------------------------------------------- 
3439 // Workdays and holidays support 
3440 // ---------------------------------------------------------------------------- 
3442 bool wxDateTime::IsWorkDay(Country 
WXUNUSED(country
)) const 
3444     return !wxDateTimeHolidayAuthority::IsHoliday(*this); 
3447 // ============================================================================ 
3449 // ============================================================================ 
3451 // not all strftime(3) format specifiers make sense here because, for example, 
3452 // a time span doesn't have a year nor a timezone 
3454 // Here are the ones which are supported (all of them are supported by strftime 
3456 //  %H          hour in 24 hour format 
3457 //  %M          minute (00 - 59) 
3458 //  %S          second (00 - 59) 
3461 // Also, for MFC CTimeSpan compatibility, we support 
3462 //  %D          number of days 
3464 // And, to be better than MFC :-), we also have 
3465 //  %E          number of wEeks 
3466 //  %l          milliseconds (000 - 999) 
3467 wxString 
wxTimeSpan::Format(const wxChar 
*format
) const 
3469     wxCHECK_MSG( format
, _T(""), _T("NULL format in wxTimeSpan::Format") ); 
3472     str
.Alloc(wxStrlen(format
)); 
3474     for ( const wxChar 
*pch 
= format
; *pch
; pch
++ ) 
3478         if ( ch 
== _T('%') ) 
3482             ch 
= *++pch
;    // get the format spec char 
3486                     wxFAIL_MSG( _T("invalid format character") ); 
3490                     // will get to str << ch below 
3494                     tmp
.Printf(_T("%d"), GetDays()); 
3498                     tmp
.Printf(_T("%d"), GetWeeks()); 
3502                     tmp
.Printf(_T("%02d"), GetHours()); 
3506                     tmp
.Printf(_T("%03ld"), GetMilliseconds().ToLong()); 
3510                     tmp
.Printf(_T("%02d"), GetMinutes()); 
3514                     tmp
.Printf(_T("%02ld"), GetSeconds().ToLong()); 
3522                 // skip str += ch below 
3533 // ============================================================================ 
3534 // wxDateTimeHolidayAuthority and related classes 
3535 // ============================================================================ 
3537 #include "wx/arrimpl.cpp" 
3539 WX_DEFINE_OBJARRAY(wxDateTimeArray
); 
3541 static int wxCMPFUNC_CONV
 
3542 wxDateTimeCompareFunc(wxDateTime 
**first
, wxDateTime 
**second
) 
3544     wxDateTime dt1 
= **first
, 
3547     return dt1 
== dt2 
? 0 : dt1 
< dt2 
? -1 : +1; 
3550 // ---------------------------------------------------------------------------- 
3551 // wxDateTimeHolidayAuthority 
3552 // ---------------------------------------------------------------------------- 
3554 wxHolidayAuthoritiesArray 
wxDateTimeHolidayAuthority::ms_authorities
; 
3557 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime
& dt
) 
3559     size_t count 
= ms_authorities
.GetCount(); 
3560     for ( size_t n 
= 0; n 
< count
; n
++ ) 
3562         if ( ms_authorities
[n
]->DoIsHoliday(dt
) ) 
3573 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime
& dtStart
, 
3574                                                const wxDateTime
& dtEnd
, 
3575                                                wxDateTimeArray
& holidays
) 
3577     wxDateTimeArray hol
; 
3581     size_t count 
= ms_authorities
.GetCount(); 
3582     for ( size_t nAuth 
= 0; nAuth 
< count
; nAuth
++ ) 
3584         ms_authorities
[nAuth
]->DoGetHolidaysInRange(dtStart
, dtEnd
, hol
); 
3586         WX_APPEND_ARRAY(holidays
, hol
); 
3589     holidays
.Sort(wxDateTimeCompareFunc
); 
3591     return holidays
.GetCount(); 
3595 void wxDateTimeHolidayAuthority::ClearAllAuthorities() 
3597     WX_CLEAR_ARRAY(ms_authorities
); 
3601 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority 
*auth
) 
3603     ms_authorities
.Add(auth
); 
3606 // ---------------------------------------------------------------------------- 
3607 // wxDateTimeWorkDays 
3608 // ---------------------------------------------------------------------------- 
3610 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime
& dt
) const 
3612     wxDateTime::WeekDay wd 
= dt
.GetWeekDay(); 
3614     return (wd 
== wxDateTime::Sun
) || (wd 
== wxDateTime::Sat
); 
3617 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime
& dtStart
, 
3618                                                 const wxDateTime
& dtEnd
, 
3619                                                 wxDateTimeArray
& holidays
) const 
3621     if ( dtStart 
> dtEnd 
) 
3623         wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") ); 
3630     // instead of checking all days, start with the first Sat after dtStart and 
3631     // end with the last Sun before dtEnd 
3632     wxDateTime dtSatFirst 
= dtStart
.GetNextWeekDay(wxDateTime::Sat
), 
3633                dtSatLast 
= dtEnd
.GetPrevWeekDay(wxDateTime::Sat
), 
3634                dtSunFirst 
= dtStart
.GetNextWeekDay(wxDateTime::Sun
), 
3635                dtSunLast 
= dtEnd
.GetPrevWeekDay(wxDateTime::Sun
), 
3638     for ( dt 
= dtSatFirst
; dt 
<= dtSatLast
; dt 
+= wxDateSpan::Week() ) 
3643     for ( dt 
= dtSunFirst
; dt 
<= dtSunLast
; dt 
+= wxDateSpan::Week() ) 
3648     return holidays
.GetCount();