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" 
  75 #define wxDEFINE_TIME_CONSTANTS 
  77 #include "wx/datetime.h" 
  80     #define WX_TIMEZONE timezone 
  83 // Is this right? Just a guess. (JACS) 
  85 #define timezone _timezone 
  88 // ---------------------------------------------------------------------------- 
  90 // ---------------------------------------------------------------------------- 
  93 static const int MONTHS_IN_YEAR 
= 12; 
  95 static const int SECONDS_IN_MINUTE 
= 60; 
  97 static const long SECONDS_PER_DAY 
= 86400l; 
  99 static const long MILLISECONDS_PER_DAY 
= 86400000l; 
 101 // this is the integral part of JDN of the midnight of Jan 1, 1970 
 102 // (i.e. JDN(Jan 1, 1970) = 2440587.5) 
 103 static const long EPOCH_JDN 
= 2440587l; 
 105 // the date of JDN -0.5 (as we don't work with fractional parts, this is the 
 106 // reference date for us) is Nov 24, 4714BC 
 107 static const int JDN_0_YEAR 
= -4713; 
 108 static const int JDN_0_MONTH 
= wxDateTime::Nov
; 
 109 static const int JDN_0_DAY 
= 24; 
 111 // the constants used for JDN calculations 
 112 static const long JDN_OFFSET         
= 32046l; 
 113 static const long DAYS_PER_5_MONTHS  
= 153l; 
 114 static const long DAYS_PER_4_YEARS   
= 1461l; 
 115 static const long DAYS_PER_400_YEARS 
= 146097l; 
 117 // ---------------------------------------------------------------------------- 
 119 // ---------------------------------------------------------------------------- 
 121 // a critical section is needed to protect GetTimeZone() static 
 122 // variable in MT case 
 124     wxCriticalSection gs_critsectTimezone
; 
 125 #endif // wxUSE_THREADS 
 127 // the symbolic names for date spans 
 128 wxDateSpan wxYear  
= wxDateSpan(1, 0, 0, 0); 
 129 wxDateSpan wxMonth 
= wxDateSpan(0, 1, 0, 0); 
 130 wxDateSpan wxWeek  
= wxDateSpan(0, 0, 1, 0); 
 131 wxDateSpan wxDay   
= wxDateSpan(0, 0, 0, 1); 
 133 // ---------------------------------------------------------------------------- 
 135 // ---------------------------------------------------------------------------- 
 137 // get the number of days in the given month of the given year 
 139 wxDateTime::wxDateTime_t 
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
) 
 141     // the number of days in month in Julian/Gregorian calendar: the first line 
 142     // is for normal years, the second one is for the leap ones 
 143     static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] = 
 145         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 
 146         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 
 149     return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
]; 
 152 // ensure that the timezone variable is set by calling localtime 
 153 static int GetTimeZone() 
 155     // set to TRUE when the timezone is set 
 156     static bool s_timezoneSet 
= FALSE
; 
 158     wxCRIT_SECT_LOCKER(lock
, gs_critsectTimezone
); 
 160     if ( !s_timezoneSet 
) 
 162         // just call localtime() instead of figuring out whether this system 
 163         // supports tzset(), _tzset() or something else 
 167         s_timezoneSet 
= TRUE
; 
 170     return (int)WX_TIMEZONE
; 
 173 // return the integral part of the JDN for the midnight of the given date (to 
 174 // get the real JDN you need to add 0.5, this is, in fact, JDN of the 
 175 // noon of the previous day) 
 176 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
, 
 177                             wxDateTime::Month mon
, 
 180     // CREDIT: code below is by Scott E. Lee (but bugs are mine) 
 182     // check the date validity 
 184       (year 
> JDN_0_YEAR
) || 
 185       ((year 
== JDN_0_YEAR
) && (mon 
> JDN_0_MONTH
)) || 
 186       ((year 
== JDN_0_YEAR
) && (mon 
== JDN_0_MONTH
) && (day 
>= JDN_0_DAY
)), 
 187       _T("date out of range - can't convert to JDN") 
 190     // make the year positive to avoid problems with negative numbers division 
 193     // months are counted from March here 
 195     if ( mon 
>= wxDateTime::Mar 
) 
 205     // now we can simply add all the contributions together 
 206     return ((year 
/ 100) * DAYS_PER_400_YEARS
) / 4 
 207             + ((year 
% 100) * DAYS_PER_4_YEARS
) / 4 
 208             + (month 
* DAYS_PER_5_MONTHS 
+ 2) / 5 
 213 // this function is a wrapper around strftime(3) 
 214 static wxString 
CallStrftime(const wxChar 
*format
, const tm
* tm
) 
 217     if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) ) 
 219         // buffer is too small? 
 220         wxFAIL_MSG(_T("strftime() failed")); 
 223     return wxString(buf
); 
 226 // if year and/or month have invalid values, replace them with the current ones 
 227 static void ReplaceDefaultYearMonthWithCurrent(int *year
, 
 228                                                wxDateTime::Month 
*month
) 
 230     struct tm 
*tmNow 
= NULL
; 
 232     if ( *year 
== wxDateTime::Inv_Year 
) 
 234         tmNow 
= wxDateTime::GetTmNow(); 
 236         *year 
= 1900 + tmNow
->tm_year
; 
 239     if ( *month 
== wxDateTime::Inv_Month 
) 
 242             tmNow 
= wxDateTime::GetTmNow(); 
 244         *month 
= (wxDateTime::Month
)tmNow
->tm_mon
; 
 251 // return the month if the string is a month name or Inv_Month otherwise 
 252 static wxDateTime::Month 
GetMonthFromName(const wxString
& name
) 
 254     wxDateTime::Month mon
; 
 255     for ( mon 
= wxDateTime::Jan
; mon 
< wxDateTime::Inv_Month
; wxNextMonth(mon
) ) 
 257         // case-insensitive comparison with both abbreviated and not versions 
 258         if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
, TRUE
)) || 
 259              name
.CmpNoCase(wxDateTime::GetMonthName(mon
, FALSE
)) ) 
 268 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise 
 269 static wxDateTime::WeekDay 
GetWeekDayFromName(const wxString
& name
) 
 271     wxDateTime::WeekDay wd
; 
 272     for ( wd 
= wxDateTime::Sun
; wd 
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) ) 
 274         // case-insensitive comparison with both abbreviated and not versions 
 275         if ( name
.IsSameAs(wxDateTime::GetWeekDayName(wd
, TRUE
), FALSE
) || 
 276              name
.IsSameAs(wxDateTime::GetWeekDayName(wd
, FALSE
), FALSE
) ) 
 285 // ============================================================================ 
 286 // implementation of wxDateTime 
 287 // ============================================================================ 
 289 // ---------------------------------------------------------------------------- 
 291 // ---------------------------------------------------------------------------- 
 293 wxDateTime::Country 
wxDateTime::ms_country 
= wxDateTime::Country_Unknown
; 
 294 wxDateTime 
wxDateTime::ms_InvDateTime
; 
 296 // ---------------------------------------------------------------------------- 
 298 // ---------------------------------------------------------------------------- 
 302     year 
= (wxDateTime_t
)wxDateTime::Inv_Year
; 
 303     mon 
= wxDateTime::Inv_Month
; 
 305     hour 
= min 
= sec 
= msec 
= 0; 
 306     wday 
= wxDateTime::Inv_WeekDay
; 
 309 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
) 
 317     mon 
= (wxDateTime::Month
)tm
.tm_mon
; 
 318     year 
= 1900 + tm
.tm_year
; 
 323 bool wxDateTime::Tm::IsValid() const 
 325     // we allow for the leap seconds, although we don't use them (yet) 
 326     return (year 
!= wxDateTime::Inv_Year
) && (mon 
!= wxDateTime::Inv_Month
) && 
 327            (mday 
<= GetNumOfDaysInMonth(year
, mon
)) && 
 328            (hour 
< 24) && (min 
< 60) && (sec 
< 62) && (msec 
< 1000); 
 331 void wxDateTime::Tm::ComputeWeekDay() 
 333     // compute the week day from day/month/year: we use the dumbest algorithm 
 334     // possible: just compute our JDN and then use the (simple to derive) 
 335     // formula: weekday = (JDN + 1.5) % 7 
 336     wday 
= (wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7; 
 339 void wxDateTime::Tm::AddMonths(int monDiff
) 
 341     // normalize the months field 
 342     while ( monDiff 
< -mon 
) 
 346         monDiff 
+= MONTHS_IN_YEAR
; 
 349     while ( monDiff 
+ mon 
> MONTHS_IN_YEAR 
) 
 353         monDiff 
-= MONTHS_IN_YEAR
; 
 356     mon 
= (wxDateTime::Month
)(mon 
+ monDiff
); 
 358     wxASSERT_MSG( mon 
>= 0 && mon 
< MONTHS_IN_YEAR
, _T("logic error") ); 
 361 void wxDateTime::Tm::AddDays(int dayDiff
) 
 363     // normalize the days field 
 369         mday 
+= GetNumOfDaysInMonth(year
, mon
); 
 372     while ( mday 
> GetNumOfDaysInMonth(year
, mon
) ) 
 374         mday 
-= GetNumOfDaysInMonth(year
, mon
); 
 379     wxASSERT_MSG( mday 
> 0 && mday 
<= GetNumOfDaysInMonth(year
, mon
), 
 383 // ---------------------------------------------------------------------------- 
 385 // ---------------------------------------------------------------------------- 
 387 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
) 
 391         case wxDateTime::Local
: 
 392             // get the offset from C RTL: it returns the difference GMT-local 
 393             // while we want to have the offset _from_ GMT, hence the '-' 
 394             m_offset 
= -GetTimeZone(); 
 397         case wxDateTime::GMT_12
: 
 398         case wxDateTime::GMT_11
: 
 399         case wxDateTime::GMT_10
: 
 400         case wxDateTime::GMT_9
: 
 401         case wxDateTime::GMT_8
: 
 402         case wxDateTime::GMT_7
: 
 403         case wxDateTime::GMT_6
: 
 404         case wxDateTime::GMT_5
: 
 405         case wxDateTime::GMT_4
: 
 406         case wxDateTime::GMT_3
: 
 407         case wxDateTime::GMT_2
: 
 408         case wxDateTime::GMT_1
: 
 409             m_offset 
= -3600*(wxDateTime::GMT0 
- tz
); 
 412         case wxDateTime::GMT0
: 
 413         case wxDateTime::GMT1
: 
 414         case wxDateTime::GMT2
: 
 415         case wxDateTime::GMT3
: 
 416         case wxDateTime::GMT4
: 
 417         case wxDateTime::GMT5
: 
 418         case wxDateTime::GMT6
: 
 419         case wxDateTime::GMT7
: 
 420         case wxDateTime::GMT8
: 
 421         case wxDateTime::GMT9
: 
 422         case wxDateTime::GMT10
: 
 423         case wxDateTime::GMT11
: 
 424         case wxDateTime::GMT12
: 
 425             m_offset 
= 3600*(tz 
- wxDateTime::GMT0
); 
 428         case wxDateTime::A_CST
: 
 429             // Central Standard Time in use in Australia = UTC + 9.5 
 430             m_offset 
= 60l*(9*60 + 30); 
 434             wxFAIL_MSG( _T("unknown time zone") ); 
 438 // ---------------------------------------------------------------------------- 
 440 // ---------------------------------------------------------------------------- 
 443 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
) 
 445     if ( year 
== Inv_Year 
) 
 446         year 
= GetCurrentYear(); 
 448     if ( cal 
== Gregorian 
) 
 450         // in Gregorian calendar leap years are those divisible by 4 except 
 451         // those divisible by 100 unless they're also divisible by 400 
 452         // (in some countries, like Russia and Greece, additional corrections 
 453         // exist, but they won't manifest themselves until 2700) 
 454         return (year 
% 4 == 0) && ((year 
% 100 != 0) || (year 
% 400 == 0)); 
 456     else if ( cal 
== Julian 
) 
 458         // in Julian calendar the rule is simpler 
 459         return year 
% 4 == 0; 
 463         wxFAIL_MSG(_T("unknown calendar")); 
 470 int wxDateTime::GetCentury(int year
) 
 472     return year 
> 0 ? year 
/ 100 : year 
/ 100 - 1; 
 476 int wxDateTime::ConvertYearToBC(int year
) 
 479     return year 
> 0 ? year 
: year 
- 1; 
 483 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
) 
 488             return Now().GetYear(); 
 491             wxFAIL_MSG(_T("TODO")); 
 495             wxFAIL_MSG(_T("unsupported calendar")); 
 503 wxDateTime::Month 
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
) 
 508             return Now().GetMonth(); 
 512             wxFAIL_MSG(_T("TODO")); 
 516             wxFAIL_MSG(_T("unsupported calendar")); 
 524 wxDateTime::wxDateTime_t 
wxDateTime::GetNumberOfDays(int year
, Calendar cal
) 
 526     if ( year 
== Inv_Year 
) 
 528         // take the current year if none given 
 529         year 
= GetCurrentYear(); 
 536             return IsLeapYear(year
) ? 366 : 365; 
 540             wxFAIL_MSG(_T("unsupported calendar")); 
 548 wxDateTime::wxDateTime_t 
wxDateTime::GetNumberOfDays(wxDateTime::Month month
, 
 550                                                      wxDateTime::Calendar cal
) 
 552     wxCHECK_MSG( month 
< MONTHS_IN_YEAR
, 0, _T("invalid month") ); 
 554     if ( cal 
== Gregorian 
|| cal 
== Julian 
) 
 556         if ( year 
== Inv_Year 
) 
 558             // take the current year if none given 
 559             year 
= GetCurrentYear(); 
 562         return GetNumOfDaysInMonth(year
, month
); 
 566         wxFAIL_MSG(_T("unsupported calendar")); 
 573 wxString 
wxDateTime::GetMonthName(wxDateTime::Month month
, bool abbr
) 
 575     wxCHECK_MSG( month 
!= Inv_Month
, _T(""), _T("invalid month") ); 
 577     // notice that we must set all the fields to avoid confusing libc (GNU one 
 578     // gets confused to a crash if we don't do this) 
 587     tm
.tm_year 
= 76;  // any year will do 
 590     return CallStrftime(abbr 
? _T("%b") : _T("%B"), &tm
); 
 594 wxString 
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
, bool abbr
) 
 596     wxCHECK_MSG( wday 
!= Inv_WeekDay
, _T(""), _T("invalid weekday") ); 
 598     // take some arbitrary Sunday 
 599     tm tm 
= { 0, 0, 0, 28, Nov
, 99 }; 
 601     // and offset it by the number of days needed to get the correct wday 
 604     // call mktime() to normalize it... 
 607     // ... and call strftime() 
 608     return CallStrftime(abbr 
? _T("%a") : _T("%A"), &tm
); 
 611 // ---------------------------------------------------------------------------- 
 612 // Country stuff: date calculations depend on the country (DST, work days, 
 613 // ...), so we need to know which rules to follow. 
 614 // ---------------------------------------------------------------------------- 
 617 wxDateTime::Country 
wxDateTime::GetCountry() 
 619     if ( ms_country 
== Country_Unknown 
) 
 621         // try to guess from the time zone name 
 622         time_t t 
= time(NULL
); 
 623         struct tm 
*tm 
= localtime(&t
); 
 625         wxString tz 
= CallStrftime(_T("%Z"), tm
); 
 626         if ( tz 
== _T("WET") || tz 
== _T("WEST") ) 
 630         else if ( tz 
== _T("CET") || tz 
== _T("CEST") ) 
 632             ms_country 
= Country_EEC
; 
 634         else if ( tz 
== _T("MSK") || tz 
== _T("MSD") ) 
 638         else if ( tz 
== _T("AST") || tz 
== _T("ADT") || 
 639                   tz 
== _T("EST") || tz 
== _T("EDT") || 
 640                   tz 
== _T("CST") || tz 
== _T("CDT") || 
 641                   tz 
== _T("MST") || tz 
== _T("MDT") || 
 642                   tz 
== _T("PST") || tz 
== _T("PDT") ) 
 648             // well, choose a default one 
 657 void wxDateTime::SetCountry(wxDateTime::Country country
) 
 659     ms_country 
= country
; 
 663 bool wxDateTime::IsWestEuropeanCountry(Country country
) 
 665     if ( country 
== Country_Default 
) 
 667         country 
= GetCountry(); 
 670     return (Country_WesternEurope_Start 
<= country
) && 
 671            (country 
<= Country_WesternEurope_End
); 
 674 // ---------------------------------------------------------------------------- 
 675 // DST calculations: we use 3 different rules for the West European countries, 
 676 // USA and for the rest of the world. This is undoubtedly false for many 
 677 // countries, but I lack the necessary info (and the time to gather it), 
 678 // please add the other rules here! 
 679 // ---------------------------------------------------------------------------- 
 682 bool wxDateTime::IsDSTApplicable(int year
, Country country
) 
 684     if ( year 
== Inv_Year 
) 
 686         // take the current year if none given 
 687         year 
= GetCurrentYear(); 
 690     if ( country 
== Country_Default 
) 
 692         country 
= GetCountry(); 
 699             // DST was first observed in the US and UK during WWI, reused 
 700             // during WWII and used again since 1966 
 701             return year 
>= 1966 || 
 702                    (year 
>= 1942 && year 
<= 1945) || 
 703                    (year 
== 1918 || year 
== 1919); 
 706             // assume that it started after WWII 
 712 wxDateTime 
wxDateTime::GetBeginDST(int year
, Country country
) 
 714     if ( year 
== Inv_Year 
) 
 716         // take the current year if none given 
 717         year 
= GetCurrentYear(); 
 720     if ( country 
== Country_Default 
) 
 722         country 
= GetCountry(); 
 725     if ( !IsDSTApplicable(year
, country
) ) 
 727         return ms_InvDateTime
; 
 732     if ( IsWestEuropeanCountry(country
) || (country 
== Russia
) ) 
 734         // DST begins at 1 a.m. GMT on the last Sunday of March 
 735         if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) ) 
 738             wxFAIL_MSG( _T("no last Sunday in March?") ); 
 741         dt 
+= wxTimeSpan::Hours(1); 
 745     else switch ( country 
) 
 752                     // don't know for sure - assume it was in effect all year 
 757                     dt
.Set(1, Jan
, year
); 
 761                     // DST was installed Feb 2, 1942 by the Congress 
 762                     dt
.Set(2, Feb
, year
); 
 765                     // Oil embargo changed the DST period in the US 
 767                     dt
.Set(6, Jan
, 1974); 
 771                     dt
.Set(23, Feb
, 1975); 
 775                     // before 1986, DST begun on the last Sunday of April, but 
 776                     // in 1986 Reagan changed it to begin at 2 a.m. of the 
 777                     // first Sunday in April 
 780                         if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) ) 
 783                             wxFAIL_MSG( _T("no first Sunday in April?") ); 
 788                         if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) ) 
 791                             wxFAIL_MSG( _T("no first Sunday in April?") ); 
 795                     dt 
+= wxTimeSpan::Hours(2); 
 797                     // TODO what about timezone?? 
 803             // assume Mar 30 as the start of the DST for the rest of the world 
 804             // - totally bogus, of course 
 805             dt
.Set(30, Mar
, year
); 
 812 wxDateTime 
wxDateTime::GetEndDST(int year
, Country country
) 
 814     if ( year 
== Inv_Year 
) 
 816         // take the current year if none given 
 817         year 
= GetCurrentYear(); 
 820     if ( country 
== Country_Default 
) 
 822         country 
= GetCountry(); 
 825     if ( !IsDSTApplicable(year
, country
) ) 
 827         return ms_InvDateTime
; 
 832     if ( IsWestEuropeanCountry(country
) || (country 
== Russia
) ) 
 834         // DST ends at 1 a.m. GMT on the last Sunday of October  
 835         if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) ) 
 837             // weirder and weirder... 
 838             wxFAIL_MSG( _T("no last Sunday in October?") ); 
 841         dt 
+= wxTimeSpan::Hours(1); 
 845     else switch ( country 
) 
 852                     // don't know for sure - assume it was in effect all year 
 856                     dt
.Set(31, Dec
, year
); 
 860                     // the time was reset after the end of the WWII 
 861                     dt
.Set(30, Sep
, year
); 
 865                     // DST ends at 2 a.m. on the last Sunday of October  
 866                     if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) ) 
 868                         // weirder and weirder... 
 869                         wxFAIL_MSG( _T("no last Sunday in October?") ); 
 872                     dt 
+= wxTimeSpan::Hours(2); 
 874                     // TODO what about timezone?? 
 879             // assume October 26th as the end of the DST - totally bogus too 
 880             dt
.Set(26, Oct
, year
); 
 886 // ---------------------------------------------------------------------------- 
 887 // constructors and assignment operators 
 888 // ---------------------------------------------------------------------------- 
 890 // the values in the tm structure contain the local time 
 891 wxDateTime
& wxDateTime::Set(const struct tm
& tm
) 
 893     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
 896     time_t timet 
= mktime(&tm2
); 
 898     if ( timet 
== (time_t)-1 ) 
 900         // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is 
 901         // less than timezone - try to make it work for this case 
 902         if ( tm2
.tm_year 
== 70 && tm2
.tm_mon 
== 0 && tm2
.tm_mday 
== 1 ) 
 904             // add timezone to make sure that date is in range 
 905             tm2
.tm_sec 
-= GetTimeZone(); 
 907             timet 
= mktime(&tm2
); 
 908             if ( timet 
!= (time_t)-1 ) 
 910                 timet 
+= GetTimeZone(); 
 916         wxFAIL_MSG( _T("mktime() failed") ); 
 918         return ms_InvDateTime
; 
 926 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
, 
 929                             wxDateTime_t millisec
) 
 931     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
 933     // we allow seconds to be 61 to account for the leap seconds, even if we 
 934     // don't use them really 
 935     wxCHECK_MSG( hour 
< 24 && second 
< 62 && minute 
< 60 && millisec 
< 1000, 
 937                  _T("Invalid time in wxDateTime::Set()") ); 
 939     // get the current date from system 
 940     time_t timet 
= GetTimeNow(); 
 941     struct tm 
*tm 
= localtime(&timet
); 
 943     wxCHECK_MSG( tm
, ms_InvDateTime
, _T("localtime() failed") ); 
 952     // and finally adjust milliseconds 
 953     return SetMillisecond(millisec
); 
 956 wxDateTime
& wxDateTime::Set(wxDateTime_t day
, 
 962                             wxDateTime_t millisec
) 
 964     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
 966     wxCHECK_MSG( hour 
< 24 && second 
< 62 && minute 
< 60 && millisec 
< 1000, 
 968                  _T("Invalid time in wxDateTime::Set()") ); 
 970     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
 972     wxCHECK_MSG( (0 < day
) && (day 
<= GetNumberOfDays(month
, year
)), 
 974                  _T("Invalid date in wxDateTime::Set()") ); 
 976     // the range of time_t type (inclusive) 
 977     static const int yearMinInRange 
= 1970; 
 978     static const int yearMaxInRange 
= 2037; 
 980     // test only the year instead of testing for the exact end of the Unix 
 981     // time_t range - it doesn't bring anything to do more precise checks 
 982     if ( year 
>= yearMinInRange 
&& year 
<= yearMaxInRange 
) 
 984         // use the standard library version if the date is in range - this is 
 985         // probably more efficient than our code 
 987         tm
.tm_year 
= year 
- 1900; 
 993         tm
.tm_isdst 
= -1;       // mktime() will guess it 
 997         // and finally adjust milliseconds 
 998         return SetMillisecond(millisec
); 
1002         // do time calculations ourselves: we want to calculate the number of 
1003         // milliseconds between the given date and the epoch 
1005         // get the JDN for the midnight of this day 
1006         m_time 
= GetTruncatedJDN(day
, month
, year
); 
1007         m_time 
-= EPOCH_JDN
; 
1008         m_time 
*= SECONDS_PER_DAY 
* TIME_T_FACTOR
; 
1010         // JDN corresponds to GMT, we take localtime 
1011         Add(wxTimeSpan(hour
, minute
, second 
+ GetTimeZone(), millisec
)); 
1017 wxDateTime
& wxDateTime::Set(double jdn
) 
1019     // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn 
1021     jdn 
-= EPOCH_JDN 
+ 0.5; 
1023     jdn 
*= MILLISECONDS_PER_DAY
; 
1030 // ---------------------------------------------------------------------------- 
1031 // time_t <-> broken down time conversions 
1032 // ---------------------------------------------------------------------------- 
1034 wxDateTime::Tm 
wxDateTime::GetTm(const TimeZone
& tz
) const 
1036     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1038     time_t time 
= GetTicks(); 
1039     if ( time 
!= (time_t)-1 ) 
1041         // use C RTL functions 
1043         if ( tz
.GetOffset() == -GetTimeZone() ) 
1045             // we are working with local time 
1046             tm 
= localtime(&time
); 
1048             // should never happen 
1049             wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") ); 
1053             time 
+= tz
.GetOffset(); 
1058                 // should never happen 
1059                 wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") ); 
1063                 tm 
= (struct tm 
*)NULL
; 
1071         //else: use generic code below 
1074     // remember the time and do the calculations with the date only - this 
1075     // eliminates rounding errors of the floating point arithmetics 
1077     wxLongLong timeMidnight 
= m_time 
+ tz
.GetOffset() * 1000; 
1079     long timeOnly 
= (timeMidnight 
% MILLISECONDS_PER_DAY
).ToLong(); 
1081     // we want to always have positive time and timeMidnight to be really 
1082     // the midnight before it 
1085         timeOnly 
= MILLISECONDS_PER_DAY 
+ timeOnly
; 
1088     timeMidnight 
-= timeOnly
; 
1090     // calculate the Gregorian date from JDN for the midnight of our date: 
1091     // this will yield day, month (in 1..12 range) and year 
1093     // actually, this is the JDN for the noon of the previous day 
1094     long jdn 
= (timeMidnight 
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
; 
1096     // CREDIT: code below is by Scott E. Lee (but bugs are mine) 
1098     wxASSERT_MSG( jdn 
> -2, _T("JDN out of range") ); 
1100     // calculate the century 
1101     int temp 
= (jdn 
+ JDN_OFFSET
) * 4 - 1; 
1102     int century 
= temp 
/ DAYS_PER_400_YEARS
; 
1104     // then the year and day of year (1 <= dayOfYear <= 366) 
1105     temp 
= ((temp 
% DAYS_PER_400_YEARS
) / 4) * 4 + 3; 
1106     int year 
= (century 
* 100) + (temp 
/ DAYS_PER_4_YEARS
); 
1107     int dayOfYear 
= (temp 
% DAYS_PER_4_YEARS
) / 4 + 1; 
1109     // and finally the month and day of the month 
1110     temp 
= dayOfYear 
* 5 - 3; 
1111     int month 
= temp 
/ DAYS_PER_5_MONTHS
; 
1112     int day 
= (temp 
% DAYS_PER_5_MONTHS
) / 5 + 1; 
1114     // month is counted from March - convert to normal 
1125     // year is offset by 4800 
1128     // check that the algorithm gave us something reasonable 
1129     wxASSERT_MSG( (0 < month
) && (month 
<= 12), _T("invalid month") ); 
1130     wxASSERT_MSG( (1 <= day
) && (day 
< 32), _T("invalid day") ); 
1131     wxASSERT_MSG( (INT_MIN 
<= year
) && (year 
<= INT_MAX
), 
1132                   _T("year range overflow") ); 
1134     // construct Tm from these values 
1136     tm
.year 
= (int)year
; 
1137     tm
.mon 
= (Month
)(month 
- 1); // algorithm yields 1 for January, not 0 
1138     tm
.mday 
= (wxDateTime_t
)day
; 
1139     tm
.msec 
= timeOnly 
% 1000; 
1140     timeOnly 
-= tm
.msec
; 
1141     timeOnly 
/= 1000;               // now we have time in seconds 
1143     tm
.sec 
= timeOnly 
% 60; 
1145     timeOnly 
/= 60;                 // now we have time in minutes 
1147     tm
.min 
= timeOnly 
% 60; 
1150     tm
.hour 
= timeOnly 
/ 60; 
1155 wxDateTime
& wxDateTime::SetYear(int year
) 
1157     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1166 wxDateTime
& wxDateTime::SetMonth(Month month
) 
1168     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1177 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
) 
1179     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1188 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
) 
1190     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1199 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
) 
1201     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1210 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
) 
1212     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1221 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
) 
1223     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1225     // we don't need to use GetTm() for this one 
1226     m_time 
-= m_time 
% 1000l; 
1227     m_time 
+= millisecond
; 
1232 // ---------------------------------------------------------------------------- 
1233 // wxDateTime arithmetics 
1234 // ---------------------------------------------------------------------------- 
1236 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
) 
1240     tm
.year 
+= diff
.GetYears(); 
1241     tm
.AddMonths(diff
.GetMonths()); 
1242     tm
.AddDays(diff
.GetTotalDays()); 
1249 // ---------------------------------------------------------------------------- 
1250 // Weekday and monthday stuff 
1251 // ---------------------------------------------------------------------------- 
1253 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
, 
1256     // take the current month/year if none specified 
1257     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
1259     return Set(GetNumOfDaysInMonth(year
, month
), month
, year
); 
1262 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
) 
1264     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, ms_InvDateTime
, _T("invalid weekday") ); 
1266     WeekDay wdayThis 
= GetWeekDay(); 
1267     if ( weekday 
== wdayThis 
) 
1272     else if ( weekday 
< wdayThis 
) 
1274         return Substract(wxTimeSpan::Days(wdayThis 
- weekday
)); 
1276     else // weekday > wdayThis 
1278         return Add(wxTimeSpan::Days(weekday 
- wdayThis
)); 
1282 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
) 
1284     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, ms_InvDateTime
, _T("invalid weekday") ); 
1287     WeekDay wdayThis 
= GetWeekDay(); 
1288     if ( weekday 
== wdayThis 
) 
1293     else if ( weekday 
< wdayThis 
) 
1295         // need to advance a week 
1296         diff 
= 7 - (wdayThis 
- weekday
); 
1298     else // weekday > wdayThis 
1300         diff 
= weekday 
- wdayThis
; 
1303     return Add(wxTimeSpan::Days(diff
)); 
1306 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
) 
1308     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, ms_InvDateTime
, _T("invalid weekday") ); 
1311     WeekDay wdayThis 
= GetWeekDay(); 
1312     if ( weekday 
== wdayThis 
) 
1317     else if ( weekday 
> wdayThis 
) 
1319         // need to go to previous week 
1320         diff 
= 7 - (weekday 
- wdayThis
); 
1322     else // weekday < wdayThis 
1324         diff 
= wdayThis 
- weekday
; 
1327     return Substract(wxTimeSpan::Days(diff
)); 
1330 bool wxDateTime::SetToWeekDay(WeekDay weekday
, 
1335     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, FALSE
, _T("invalid weekday") ); 
1337     // we don't check explicitly that -5 <= n <= 5 because we will return FALSE 
1338     // anyhow in such case - but may be should still give an assert for it? 
1340     // take the current month/year if none specified 
1341     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
1345     // TODO this probably could be optimised somehow... 
1349         // get the first day of the month 
1350         dt
.Set(1, month
, year
); 
1353         WeekDay wdayFirst 
= dt
.GetWeekDay(); 
1355         // go to the first weekday of the month 
1356         int diff 
= weekday 
- wdayFirst
; 
1360         // add advance n-1 weeks more 
1363         dt 
+= wxDateSpan::Days(diff
); 
1365     else // count from the end of the month 
1367         // get the last day of the month 
1368         dt
.SetToLastMonthDay(month
, year
); 
1371         WeekDay wdayLast 
= dt
.GetWeekDay(); 
1373         // go to the last weekday of the month 
1374         int diff 
= wdayLast 
- weekday
; 
1378         // and rewind n-1 weeks from there 
1381         dt 
-= wxDateSpan::Days(diff
); 
1384     // check that it is still in the same month 
1385     if ( dt
.GetMonth() == month 
) 
1393         // no such day in this month 
1398 wxDateTime::wxDateTime_t 
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const 
1400     // this array contains the cumulated number of days in all previous months 
1401     // for normal and leap years 
1402     static const wxDateTime_t cumulatedDays
[2][MONTHS_IN_YEAR
] = 
1404         { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, 
1405         { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } 
1410     return cumulatedDays
[IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
; 
1413 wxDateTime::wxDateTime_t 
wxDateTime::GetWeekOfYear(const TimeZone
& tz
) const 
1416     // the first week of the year is the one which contains Jan, 4 (according 
1417     // to ISO standard rule), so the year day N0 = 4 + 7*W always lies in the 
1418     // week W+1. As any day N = 7*W + 4 + (N - 4)%7, it lies in the same week 
1419     // as N0 or in the next one. 
1421     // TODO this surely may be optimized - I got confused while writing it 
1423     wxDateTime_t nDayInYear 
= GetDayOfYear(tz
); 
1425     // the week day of the day lying in the first week 
1426     WeekDay wdayStart 
= wxDateTime(4, Jan
, GetYear()).GetWeekDay(); 
1428     wxDateTime_t week 
= (nDayInYear 
- 4) / 7 + 1; 
1430     // notice that Sunday shoould be counted as 7, not 0 here! 
1431     if ( ((nDayInYear 
- 4) % 7) + (!wdayStart 
? 7 : wdayStart
) > 7 ) 
1437 #else // this seems to be a bit simpler and I believe is also correct 
1438     return (WeekDay
)((GetDayOfYear() - (GetWeekDay() - 1 + 7) % 7 + 7) / 7); 
1442 wxDateTime::wxDateTime_t 
wxDateTime::GetWeekOfMonth(const TimeZone
& tz
) const 
1446     wxDateTime 
dt(*this); 
1451         dt 
-= wxTimeSpan::Week(); 
1453     while ( dt
.GetMonth(tz
) == GetMonth(tz
) ); 
1458 // ---------------------------------------------------------------------------- 
1459 // Julian day number conversion and related stuff 
1460 // ---------------------------------------------------------------------------- 
1462 double wxDateTime::GetJulianDayNumber() const 
1464     // JDN are always expressed for the GMT dates 
1465     Tm 
tm(ToTimezone(GMT0
).GetTm(GMT0
)); 
1467     double result 
= GetTruncatedJDN(tm
.mday
, tm
.mon
, tm
.year
); 
1469     // add the part GetTruncatedJDN() neglected 
1472     // and now add the time: 86400 sec = 1 JDN 
1473     return result 
+ ((double)(60*(60*tm
.hour 
+ tm
.min
) + tm
.sec
)) / 86400; 
1476 double wxDateTime::GetRataDie() const 
1478     // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5 
1479     return GetJulianDayNumber() - 1721119.5 - 306; 
1482 // ---------------------------------------------------------------------------- 
1483 // timezone and DST stuff 
1484 // ---------------------------------------------------------------------------- 
1486 int wxDateTime::IsDST(wxDateTime::Country country
) const 
1488     wxCHECK_MSG( country 
== Country_Default
, -1, 
1489                  _T("country support not implemented") ); 
1491     // use the C RTL for the dates in the standard range 
1492     time_t timet 
= GetTicks(); 
1493     if ( timet 
!= (time_t)-1 ) 
1495         tm 
*tm 
= localtime(&timet
); 
1497         wxCHECK_MSG( tm
, -1, _T("localtime() failed") ); 
1499         return tm
->tm_isdst
; 
1503         int year 
= GetYear(); 
1505         if ( !IsDSTApplicable(year
, country
) ) 
1507             // no DST time in this year in this country 
1511         return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
)); 
1515 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
) 
1517     int secDiff 
= GetTimeZone() + tz
.GetOffset(); 
1519     // we need to know whether DST is or not in effect for this date 
1522         // FIXME we assume that the DST is always shifted by 1 hour 
1526     return Substract(wxTimeSpan::Seconds(secDiff
)); 
1529 // ---------------------------------------------------------------------------- 
1530 // wxDateTime to/from text representations 
1531 // ---------------------------------------------------------------------------- 
1533 wxString 
wxDateTime::Format(const wxChar 
*format
, const TimeZone
& tz
) const 
1535     wxCHECK_MSG( format
, _T(""), _T("NULL format in wxDateTime::Format") ); 
1537     time_t time 
= GetTicks(); 
1538     if ( time 
!= (time_t)-1 ) 
1542         if ( tz
.GetOffset() == -GetTimeZone() ) 
1544             // we are working with local time 
1545             tm 
= localtime(&time
); 
1547             // should never happen 
1548             wxCHECK_MSG( tm
, wxEmptyString
, _T("localtime() failed") ); 
1552             time 
+= tz
.GetOffset(); 
1558                 // should never happen 
1559                 wxCHECK_MSG( tm
, wxEmptyString
, _T("gmtime() failed") ); 
1563                 tm 
= (struct tm 
*)NULL
; 
1569             return CallStrftime(format
, tm
); 
1571         //else: use generic code below 
1574     // we only parse ANSI C format specifications here, no POSIX 2 
1575     // complications, no GNU extensions 
1578     // used for calls to strftime() when we only deal with time 
1579     struct tm tmTimeOnly
; 
1580     tmTimeOnly
.tm_hour 
= tm
.hour
; 
1581     tmTimeOnly
.tm_min 
= tm
.min
; 
1582     tmTimeOnly
.tm_sec 
= tm
.sec
; 
1583     tmTimeOnly
.tm_wday 
= 0; 
1584     tmTimeOnly
.tm_yday 
= 0; 
1585     tmTimeOnly
.tm_mday 
= 1;         // any date will do 
1586     tmTimeOnly
.tm_mon 
= 0; 
1587     tmTimeOnly
.tm_year 
= 76; 
1588     tmTimeOnly
.tm_isdst 
= 0;        // no DST, we adjust for tz ourselves 
1590     wxString tmp
, res
, fmt
; 
1591     for ( const wxChar 
*p 
= format
; *p
; p
++ ) 
1593         if ( *p 
!= _T('%') ) 
1601         // set the default format 
1604             case _T('Y'):               // year has 4 digits 
1608             case _T('j'):               // day of year has 3 digits 
1613                 // it's either another valid format specifier in which case 
1614                 // the format is "%02d" (for all the rest) or we have the 
1615                 // field width preceding the format in which case it will 
1616                 // override the default format anyhow 
1621         // start of the format specification 
1624             case _T('a'):       // a weekday name 
1626                 // second parameter should be TRUE for abbreviated names 
1627                 res 
+= GetWeekDayName(tm
.GetWeekDay(), *p 
== _T('a')); 
1630             case _T('b'):       // a month name 
1632                 // second parameter should be TRUE for abbreviated names 
1633                 res 
+= GetMonthName(tm
.mon
, *p 
== _T('b')); 
1636             case _T('c'):       // locale default date and time  representation 
1637             case _T('x'):       // locale default date representation 
1639                 // the problem: there is no way to know what do these format 
1640                 // specifications correspond to for the current locale. 
1642                 // the solution: use a hack and still use strftime(): first 
1643                 // find the YEAR which is a year in the strftime() range (1970 
1644                 // - 2038) whose Jan 1 falls on the same week day as the Jan 1 
1645                 // of the real year. Then make a copy of the format and 
1646                 // replace all occurences of YEAR in it with some unique 
1647                 // string not appearing anywhere else in it, then use 
1648                 // strftime() to format the date in year YEAR and then replace 
1649                 // YEAR back by the real year and the unique replacement 
1650                 // string back with YEAR. Notice that "all occurences of YEAR" 
1651                 // means all occurences of 4 digit as well as 2 digit form! 
1653                 // the bugs: we assume that neither of %c nor %x contains any 
1654                 // fields which may change between the YEAR and real year. For 
1655                 // example, the week number (%U, %W) and the day number (%j) 
1656                 // will change if one of these years is leap and the other one 
1659                     // find the YEAR: normally, for any year X, Jan 1 or the 
1660                     // year X + 28 is the same weekday as Jan 1 of X (because 
1661                     // the weekday advances by 1 for each normal X and by 2 
1662                     // for each leap X, hence by 5 every 4 years or by 35 
1663                     // which is 0 mod 7 every 28 years) but this rule breaks 
1664                     // down if there are years between X and Y which are 
1665                     // divisible by 4 but not leap (i.e. divisible by 100 but 
1666                     // not 400), hence the correction. 
1668                     int yearReal 
= GetYear(tz
); 
1669                     int mod28 
= yearReal 
% 28; 
1671                     // be careful to not go too far - we risk to leave the 
1676                         year 
= 1988 + mod28
;      // 1988 == 0 (mod 28) 
1680                         year 
= 1970 + mod28 
- 10; // 1970 == 10 (mod 28) 
1683                     int nCentury 
= year 
/ 100, 
1684                         nCenturyReal 
= yearReal 
/ 100; 
1686                     // need to adjust for the years divisble by 400 which are 
1687                     // not leap but are counted like leap ones if we just take 
1688                     // the number of centuries in between for nLostWeekDays 
1689                     int nLostWeekDays 
= (nCentury 
- nCenturyReal
) - 
1690                                         (nCentury 
/ 4 - nCenturyReal 
/ 4); 
1692                     // we have to gain back the "lost" weekdays: note that the 
1693                     // effect of this loop is to not do anything to 
1694                     // nLostWeekDays (which we won't use any more), but to 
1695                     // (indirectly) set the year correctly 
1696                     while ( (nLostWeekDays 
% 7) != 0 ) 
1698                         nLostWeekDays 
+= year
++ % 4 ? 1 : 2; 
1701                     // at any rate, we couldn't go further than 1988 + 9 + 28! 
1702                     wxASSERT_MSG( year 
< 2030, 
1703                                   _T("logic error in wxDateTime::Format") ); 
1705                     wxString strYear
, strYear2
; 
1706                     strYear
.Printf(_T("%d"), year
); 
1707                     strYear2
.Printf(_T("%d"), year 
% 100); 
1709                     // find two strings not occuring in format (this is surely 
1710                     // not optimal way of doing it... improvements welcome!) 
1711                     wxString fmt 
= format
; 
1712                     wxString replacement 
= (wxChar
)-1; 
1713                     while ( fmt
.Find(replacement
) != wxNOT_FOUND 
) 
1715                         replacement 
<< (wxChar
)-1; 
1718                     wxString replacement2 
= (wxChar
)-2; 
1719                     while ( fmt
.Find(replacement
) != wxNOT_FOUND 
) 
1721                         replacement 
<< (wxChar
)-2; 
1724                     // replace all occurences of year with it 
1725                     bool wasReplaced 
= fmt
.Replace(strYear
, replacement
) > 0; 
1727                         wasReplaced 
= fmt
.Replace(strYear2
, replacement2
) > 0; 
1729                     // use strftime() to format the same date but in supported 
1732                     // NB: we assume that strftime() doesn't check for the 
1733                     //     date validity and will happily format the date 
1734                     //     corresponding to Feb 29 of a non leap year (which 
1735                     //     may happen if yearReal was leap and year is not) 
1736                     struct tm tmAdjusted
; 
1737                     tmAdjusted
.tm_hour 
= tm
.hour
; 
1738                     tmAdjusted
.tm_min 
= tm
.min
; 
1739                     tmAdjusted
.tm_sec 
= tm
.sec
; 
1740                     tmAdjusted
.tm_wday 
= tm
.GetWeekDay(); 
1741                     tmAdjusted
.tm_yday 
= GetDayOfYear(); 
1742                     tmAdjusted
.tm_mday 
= tm
.mday
; 
1743                     tmAdjusted
.tm_mon 
= tm
.mon
; 
1744                     tmAdjusted
.tm_year 
= year 
- 1900; 
1745                     tmAdjusted
.tm_isdst 
= 0; // no DST, already adjusted 
1746                     wxString str 
= CallStrftime(*p 
== _T('c') ? _T("%c") 
1750                     // now replace the occurence of 1999 with the real year 
1751                     wxString strYearReal
, strYearReal2
; 
1752                     strYearReal
.Printf(_T("%04d"), yearReal
); 
1753                     strYearReal2
.Printf(_T("%02d"), yearReal 
% 100); 
1754                     str
.Replace(strYear
, strYearReal
); 
1755                     str
.Replace(strYear2
, strYearReal2
); 
1757                     // and replace back all occurences of replacement string 
1760                         str
.Replace(replacement2
, strYear2
); 
1761                         str
.Replace(replacement
, strYear
); 
1768             case _T('d'):       // day of a month (01-31) 
1769                 res 
+= wxString::Format(fmt
, tm
.mday
); 
1772             case _T('H'):       // hour in 24h format (00-23) 
1773                 res 
+= wxString::Format(fmt
, tm
.hour
); 
1776             case _T('I'):       // hour in 12h format (01-12) 
1778                     // 24h -> 12h, 0h -> 12h too 
1779                     int hour12 
= tm
.hour 
> 12 ? tm
.hour 
- 12 
1780                                               : tm
.hour 
? tm
.hour 
: 12; 
1781                     res 
+= wxString::Format(fmt
, hour12
); 
1785             case _T('j'):       // day of the year 
1786                 res 
+= wxString::Format(fmt
, GetDayOfYear(tz
)); 
1789             case _T('m'):       // month as a number (01-12) 
1790                 res 
+= wxString::Format(fmt
, tm
.mon 
+ 1); 
1793             case _T('M'):       // minute as a decimal number (00-59) 
1794                 res 
+= wxString::Format(fmt
, tm
.min
); 
1797             case _T('p'):       // AM or PM string 
1798                 res 
+= CallStrftime(_T("%p"), &tmTimeOnly
); 
1801             case _T('S'):       // second as a decimal number (00-61) 
1802                 res 
+= wxString::Format(fmt
, tm
.sec
); 
1805             case _T('U'):       // week number in the year (Sunday 1st week day) 
1807                     int week 
= (GetDayOfYear(tz
) - tm
.GetWeekDay() + 7) / 7; 
1808                     res 
+= wxString::Format(fmt
, week
); 
1812             case _T('W'):       // week number in the year (Monday 1st week day) 
1813                 res 
+= wxString::Format(fmt
, GetWeekOfYear(tz
)); 
1816             case _T('w'):       // weekday as a number (0-6), Sunday = 0 
1817                 res 
+= wxString::Format(fmt
, tm
.GetWeekDay()); 
1820             // case _T('x'): -- handled with "%c" 
1822             case _T('X'):       // locale default time representation 
1823                 // just use strftime() to format the time for us 
1824                 res 
+= CallStrftime(_T("%X"), &tmTimeOnly
); 
1827             case _T('y'):       // year without century (00-99) 
1828                 res 
+= wxString::Format(fmt
, tm
.year 
% 100); 
1831             case _T('Y'):       // year with century 
1832                 res 
+= wxString::Format(fmt
, tm
.year
); 
1835             case _T('Z'):       // timezone name 
1836                 res 
+= CallStrftime(_T("%Z"), &tmTimeOnly
); 
1840                 // is it the format width? 
1842                 while ( *p 
== _T('-') || *p 
== _T('+') || 
1843                         *p 
== _T(' ') || wxIsdigit(*p
) ) 
1848                 if ( !fmt
.IsEmpty() ) 
1850                     // we've only got the flags and width so far in fmt 
1851                     fmt
.Prepend(_T('%')); 
1852                     fmt
.Append(_T('d')); 
1857                 // no, it wasn't the width 
1858                 wxFAIL_MSG(_T("unknown format specificator")); 
1860                 // fall through and just copy it nevertheless 
1862             case _T('%'):       // a percent sign 
1867                 wxFAIL_MSG(_T("missing format at the end of string")); 
1869                 // just put the '%' which was the last char in format 
1878 // this function parses a string in (strict) RFC 822 format: see the section 5 
1879 // of the RFC for the detailed description, but briefly it's something of the 
1880 // form "Sat, 18 Dec 1999 00:48:30 +0100" 
1882 // this function is "strict" by design - it must reject anything except true 
1883 // RFC822 time specs. 
1885 // TODO a great candidate for using reg exps 
1886 const wxChar 
*wxDateTime::ParseRfc822Date(const wxChar
* date
) 
1888     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
1890     const wxChar 
*p 
= date
; 
1891     const wxChar 
*comma 
= wxStrchr(p
, _T(',')); 
1894         // the part before comma is the weekday 
1896         // skip it for now - we don't use but might check that it really 
1897         // corresponds to the specfied date 
1900         if ( *p 
!= _T(' ') ) 
1902             wxLogDebug(_T("no space after weekday in RFC822 time spec")); 
1904             return (wxChar 
*)NULL
; 
1910     // the following 1 or 2 digits are the day number 
1911     if ( !wxIsdigit(*p
) ) 
1913         wxLogDebug(_T("day number expected in RFC822 time spec, none found")); 
1915         return (wxChar 
*)NULL
; 
1918     wxDateTime_t day 
= *p
++ - _T('0'); 
1919     if ( wxIsdigit(*p
) ) 
1922         day 
+= *p
++ - _T('0'); 
1925     if ( *p
++ != _T(' ') ) 
1927         return (wxChar 
*)NULL
; 
1930     // the following 3 letters specify the month 
1931     wxString 
monName(p
, 3); 
1933     if ( monName 
== _T("Jan") ) 
1935     else if ( monName 
== _T("Feb") ) 
1937     else if ( monName 
== _T("Mar") ) 
1939     else if ( monName 
== _T("Apr") ) 
1941     else if ( monName 
== _T("May") ) 
1943     else if ( monName 
== _T("Jun") ) 
1945     else if ( monName 
== _T("Jul") ) 
1947     else if ( monName 
== _T("Aug") ) 
1949     else if ( monName 
== _T("Sep") ) 
1951     else if ( monName 
== _T("Oct") ) 
1953     else if ( monName 
== _T("Nov") ) 
1955     else if ( monName 
== _T("Dec") ) 
1959         wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str()); 
1961         return (wxChar 
*)NULL
; 
1966     if ( *p
++ != _T(' ') ) 
1968         return (wxChar 
*)NULL
; 
1972     if ( !wxIsdigit(*p
) ) 
1975         return (wxChar 
*)NULL
; 
1978     int year 
= *p
++ - _T('0'); 
1980     if ( !wxIsdigit(*p
) ) 
1982         // should have at least 2 digits in the year 
1983         return (wxChar 
*)NULL
; 
1987     year 
+= *p
++ - _T('0'); 
1989     // is it a 2 digit year (as per original RFC 822) or a 4 digit one? 
1990     if ( wxIsdigit(*p
) ) 
1993         year 
+= *p
++ - _T('0'); 
1995         if ( !wxIsdigit(*p
) ) 
1997             // no 3 digit years please 
1998             return (wxChar 
*)NULL
; 
2002         year 
+= *p
++ - _T('0'); 
2005     if ( *p
++ != _T(' ') ) 
2007         return (wxChar 
*)NULL
; 
2010     // time is in the format hh:mm:ss and seconds are optional 
2011     if ( !wxIsdigit(*p
) ) 
2013         return (wxChar 
*)NULL
; 
2016     wxDateTime_t hour 
= *p
++ - _T('0'); 
2018     if ( !wxIsdigit(*p
) ) 
2020         return (wxChar 
*)NULL
; 
2024     hour 
+= *p
++ - _T('0'); 
2026     if ( *p
++ != _T(':') ) 
2028         return (wxChar 
*)NULL
; 
2031     if ( !wxIsdigit(*p
) ) 
2033         return (wxChar 
*)NULL
; 
2036     wxDateTime_t min 
= *p
++ - _T('0'); 
2038     if ( !wxIsdigit(*p
) ) 
2040         return (wxChar 
*)NULL
; 
2044     min 
+= *p
++ - _T('0'); 
2046     wxDateTime_t sec 
= 0; 
2047     if ( *p
++ == _T(':') ) 
2049         if ( !wxIsdigit(*p
) ) 
2051             return (wxChar 
*)NULL
; 
2054         sec 
= *p
++ - _T('0'); 
2056         if ( !wxIsdigit(*p
) ) 
2058             return (wxChar 
*)NULL
; 
2062         sec 
+= *p
++ - _T('0'); 
2065     if ( *p
++ != _T(' ') ) 
2067         return (wxChar 
*)NULL
; 
2070     // and now the interesting part: the timezone 
2072     if ( *p 
== _T('-') || *p 
== _T('+') ) 
2074         // the explicit offset given: it has the form of hhmm 
2075         bool plus 
= *p
++ == _T('+'); 
2077         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
2079             return (wxChar 
*)NULL
; 
2083         offset 
= 60*(10*(*p 
- _T('0')) + (*(p 
+ 1) - _T('0'))); 
2087         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
2089             return (wxChar 
*)NULL
; 
2093         offset 
+= 10*(*p 
- _T('0')) + (*(p 
+ 1) - _T('0')); 
2104         // the symbolic timezone given: may be either military timezone or one 
2105         // of standard abbreviations 
2108             // military: Z = UTC, J unused, A = -1, ..., Y = +12 
2109             static const int offsets
[26] = 
2111                 //A  B   C   D   E   F   G   H   I    J    K    L    M 
2112                 -1, -2, -3, -4, -5, -6, -7, -8, -9,   0, -10, -11, -12, 
2113                 //N  O   P   R   Q   S   T   U   V    W    Z    Y    Z 
2114                 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0 
2117             if ( *p 
< _T('A') || *p 
> _T('Z') || *p 
== _T('J') ) 
2119                 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
); 
2121                 return (wxChar 
*)NULL
; 
2124             offset 
= offsets
[*p
++ - _T('A')]; 
2130             if ( tz 
== _T("UT") || tz 
== _T("UTC") || tz 
== _T("GMT") ) 
2132             else if ( tz 
== _T("AST") ) 
2133                 offset 
= AST 
- GMT0
; 
2134             else if ( tz 
== _T("ADT") ) 
2135                 offset 
= ADT 
- GMT0
; 
2136             else if ( tz 
== _T("EST") ) 
2137                 offset 
= EST 
- GMT0
; 
2138             else if ( tz 
== _T("EDT") ) 
2139                 offset 
= EDT 
- GMT0
; 
2140             else if ( tz 
== _T("CST") ) 
2141                 offset 
= CST 
- GMT0
; 
2142             else if ( tz 
== _T("CDT") ) 
2143                 offset 
= CDT 
- GMT0
; 
2144             else if ( tz 
== _T("MST") ) 
2145                 offset 
= MST 
- GMT0
; 
2146             else if ( tz 
== _T("MDT") ) 
2147                 offset 
= MDT 
- GMT0
; 
2148             else if ( tz 
== _T("PST") ) 
2149                 offset 
= PST 
- GMT0
; 
2150             else if ( tz 
== _T("PDT") ) 
2151                 offset 
= PDT 
- GMT0
; 
2154                 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
); 
2156                 return (wxChar 
*)NULL
; 
2166     // the spec was correct 
2167     Set(day
, mon
, year
, hour
, min
, sec
); 
2168     MakeTimezone(60*offset
); 
2173 const wxChar 
*wxDateTime::ParseFormat(const wxChar 
*date
, const wxChar 
*format
) 
2175     wxCHECK_MSG( date 
&& format
, (wxChar 
*)NULL
, 
2176                  _T("NULL pointer in wxDateTime::Parse") ); 
2178     // there is a public domain version of getdate.y, but it only works for 
2180     wxFAIL_MSG(_T("TODO")); 
2182     return (wxChar 
*)NULL
; 
2185 const wxChar 
*wxDateTime::ParseDateTime(const wxChar 
*date
) 
2187     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
2189     // find a public domain version of strptime() somewhere? 
2190     wxFAIL_MSG(_T("TODO")); 
2192     return (wxChar 
*)NULL
; 
2195 const wxChar 
*wxDateTime::ParseDate(const wxChar 
*date
) 
2197     // this is a simplified version of ParseDateTime() which understands only 
2198     // "today" (for wxDate compatibility) and digits only otherwise (and not 
2199     // all esoteric constructions ParseDateTime() knows about) 
2201     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
2203     const wxChar 
*p 
= date
; 
2204     while ( wxIsspace(*p
) ) 
2207     wxString today 
= _T("today"); 
2208     size_t len 
= today
.length(); 
2209     if ( wxString(p
, len
).CmpNoCase(today
) == 0 ) 
2211         // nothing can follow this, so stop here 
2220     bool haveDay 
= FALSE
,       // the months day? 
2221          haveWDay 
= FALSE
,      // the day of week? 
2222          haveMon 
= FALSE
,       // the month? 
2223          haveYear 
= FALSE
;      // the year? 
2225     // and the value of the items we have (init them to get rid of warnings) 
2226     WeekDay wday 
= Inv_WeekDay
; 
2227     wxDateTime_t day 
= 0; 
2228     wxDateTime::Month mon 
= Inv_Month
; 
2231     // tokenize the string 
2232     wxStringTokenizer 
tok(p
, _T(",/-\t ")); 
2233     while ( tok
.HasMoreTokens() ) 
2235         wxString token 
= tok
.GetNextToken(); 
2239         if ( token
.ToULong(&val
) ) 
2241             // guess what this number is 
2245                  // only years are counted from 0 
2246                  isYear 
= (val 
== 0) || (val 
> 31); 
2249                 // may be the month or month day or the year, assume the 
2250                 // month day by default and fallback to month if the range 
2251                 // allow it or to the year if our assumption doesn't work 
2254                     // we already have the day, so may only be a month or year 
2264                 else // it may be day 
2271                         if ( val 
> GetNumOfDaysInMonth(haveYear 
? year
 
2275                             // oops, it can't be a day finally 
2291             // remember that we have this and stop the scan if it's the second 
2292             // time we find this, except for the day logic (see there) 
2302                 // no roll over - 99 means 99, not 1999 for us 
2314                 mon 
= (wxDateTime::Month
)val
; 
2318                 wxASSERT_MSG( isDay
, _T("logic error") ); 
2322                     // may be were mistaken when we found it for the first 
2323                     // time? may be it was a month or year instead? 
2325                     // this ability to "backtrack" allows us to correctly parse 
2326                     // both things like 01/13 and 13/01 - but, of course, we 
2327                     // still can't resolve the ambiguity in 01/02 (it will be 
2328                     // Feb 1 for us, not Jan 2 as americans might expect!) 
2329                     if ( (day 
<= 12) && !haveMon 
) 
2331                         // exchange day and month 
2332                         mon 
= (wxDateTime::Month
)day
; 
2336                     else if ( !haveYear 
) 
2338                         // exchange day and year 
2350         else // not a number 
2352             mon 
= GetMonthFromName(token
); 
2353             if ( mon 
!= Inv_Month 
) 
2365                 wday 
= GetWeekDayFromName(token
); 
2366                 if ( wday 
!= Inv_WeekDay 
) 
2379                     static const wxChar 
*ordinals
[] = 
2381                         wxTRANSLATE("first"), 
2382                         wxTRANSLATE("second"), 
2383                         wxTRANSLATE("third"), 
2384                         wxTRANSLATE("fourth"), 
2385                         wxTRANSLATE("fifth"), 
2386                         wxTRANSLATE("sixth"), 
2387                         wxTRANSLATE("seventh"), 
2388                         wxTRANSLATE("eighth"), 
2389                         wxTRANSLATE("ninth"), 
2390                         wxTRANSLATE("tenth"), 
2391                         wxTRANSLATE("eleventh"), 
2392                         wxTRANSLATE("twelfth"), 
2393                         wxTRANSLATE("thirteenth"), 
2394                         wxTRANSLATE("fourteenth"), 
2395                         wxTRANSLATE("fifteenth"), 
2396                         wxTRANSLATE("sixteenth"), 
2397                         wxTRANSLATE("seventeenth"), 
2398                         wxTRANSLATE("eighteenth"), 
2399                         wxTRANSLATE("nineteenth"), 
2400                         wxTRANSLATE("twentieth"), 
2401                         // that's enough - otherwise we'd have problems with 
2402                         // composite (or not) ordinals otherwise 
2406                     for ( n 
= 0; n 
< WXSIZEOF(ordinals
); n
++ ) 
2408                         if ( token
.CmpNoCase(ordinals
[n
]) == 0 ) 
2414                     if ( n 
== WXSIZEOF(ordinals
) ) 
2416                         // stop here - something unknown 
2423                         // don't try anything here (as in case of numeric day 
2424                         // above) - the symbolic day spec should always 
2425                         // precede the month/year 
2437     // either no more tokens or the scan was stopped by something we couldn't 
2438     // parse - in any case, see if we can construct a date from what we have 
2439     if ( !haveDay 
&& !haveWDay 
) 
2441         wxLogDebug(_T("no day, no weekday hence no date.")); 
2443         return (wxChar 
*)NULL
; 
2446     if ( haveWDay 
&& (haveMon 
|| haveYear 
|| haveDay
) && 
2447          !(haveMon 
&& haveMon 
&& haveYear
) ) 
2449         // without adjectives (which we don't support here) the week day only 
2450         // makes sense completely separately or with the full date 
2451         // specification (what would "Wed 1999" mean?) 
2452         return (wxChar 
*)NULL
; 
2457         mon 
= GetCurrentMonth(); 
2462         year 
= GetCurrentYear(); 
2467         Set(day
, mon
, year
); 
2471             // check that it is really the same 
2472             if ( GetWeekDay() != wday 
) 
2474                 // inconsistency detected 
2475                 return (wxChar 
*)NULL
; 
2483         SetToWeekDayInSameWeek(wday
); 
2486     // return the pointer to the next char 
2487     return p 
+ wxStrlen(p
) - wxStrlen(tok
.GetString()); 
2490 const wxChar 
*wxDateTime::ParseTime(const wxChar 
*time
) 
2492     wxCHECK_MSG( time
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
2494     // this function should be able to parse different time formats as well as 
2495     // timezones (take the code out from ParseRfc822Date()) and AM/PM. 
2496     wxFAIL_MSG(_T("TODO")); 
2498     return (wxChar 
*)NULL
; 
2501 // ============================================================================ 
2503 // ============================================================================ 
2505 // not all strftime(3) format specifiers make sense here because, for example, 
2506 // a time span doesn't have a year nor a timezone 
2508 // Here are the ones which are supported (all of them are supported by strftime 
2510 //  %H          hour in 24 hour format 
2511 //  %M          minute (00 - 59) 
2512 //  %S          second (00 - 59) 
2515 // Also, for MFC CTimeSpan compatibility, we support 
2516 //  %D          number of days 
2518 // And, to be better than MFC :-), we also have 
2519 //  %E          number of wEeks 
2520 //  %l          milliseconds (000 - 999) 
2521 wxString 
wxTimeSpan::Format(const wxChar 
*format
) const 
2523     wxCHECK_MSG( format
, _T(""), _T("NULL format in wxTimeSpan::Format") ); 
2526     str
.Alloc(strlen(format
)); 
2528     for ( const wxChar 
*pch 
= format
; pch
; pch
++ ) 
2540                     wxFAIL_MSG( _T("invalid format character") ); 
2544                     // will get to str << ch below 
2548                     tmp
.Printf(_T("%d"), GetDays()); 
2552                     tmp
.Printf(_T("%d"), GetWeeks()); 
2556                     tmp
.Printf(_T("%02d"), GetHours()); 
2560                     tmp
.Printf(_T("%03ld"), GetMilliseconds().ToLong()); 
2564                     tmp
.Printf(_T("%02d"), GetMinutes()); 
2568                     tmp
.Printf(_T("%02ld"), GetSeconds().ToLong()); 
2576                 // skip str += ch below