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 // before including datetime.h 
  77 #include "wx/datetime.h" 
  79 // ---------------------------------------------------------------------------- 
  80 // conditional compilation 
  81 // ---------------------------------------------------------------------------- 
  83 #if defined(HAVE_STRPTIME) && defined(__LINUX__) 
  84     // glibc 2.0.7 strptime() is broken - the following snippet causes it to 
  85     // crash (instead of just failing): 
  87     //      strncpy(buf, "Tue Dec 21 20:25:40 1999", 128); 
  88     //      strptime(buf, "%x", &tm); 
  92 #endif // broken strptime() 
  95     #if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__) 
  96         #define WX_TIMEZONE _timezone 
  97     #else // unknown platform - try timezone 
  98         #define WX_TIMEZONE timezone 
 100 #endif // !WX_TIMEZONE 
 102 // ---------------------------------------------------------------------------- 
 104 // ---------------------------------------------------------------------------- 
 107 static const int MONTHS_IN_YEAR 
= 12; 
 109 static const int SECONDS_IN_MINUTE 
= 60; 
 111 static const long SECONDS_PER_DAY 
= 86400l; 
 113 static const long MILLISECONDS_PER_DAY 
= 86400000l; 
 115 // this is the integral part of JDN of the midnight of Jan 1, 1970 
 116 // (i.e. JDN(Jan 1, 1970) = 2440587.5) 
 117 static const long EPOCH_JDN 
= 2440587l; 
 119 // the date of JDN -0.5 (as we don't work with fractional parts, this is the 
 120 // reference date for us) is Nov 24, 4714BC 
 121 static const int JDN_0_YEAR 
= -4713; 
 122 static const int JDN_0_MONTH 
= wxDateTime::Nov
; 
 123 static const int JDN_0_DAY 
= 24; 
 125 // the constants used for JDN calculations 
 126 static const long JDN_OFFSET         
= 32046l; 
 127 static const long DAYS_PER_5_MONTHS  
= 153l; 
 128 static const long DAYS_PER_4_YEARS   
= 1461l; 
 129 static const long DAYS_PER_400_YEARS 
= 146097l; 
 131 // this array contains the cumulated number of days in all previous months for 
 132 // normal and leap years 
 133 static const wxDateTime::wxDateTime_t gs_cumulatedDays
[2][MONTHS_IN_YEAR
] = 
 135     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, 
 136     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } 
 139 // ---------------------------------------------------------------------------- 
 141 // ---------------------------------------------------------------------------- 
 143 // a critical section is needed to protect GetTimeZone() static 
 144 // variable in MT case 
 146     wxCriticalSection gs_critsectTimezone
; 
 147 #endif // wxUSE_THREADS 
 149 // ---------------------------------------------------------------------------- 
 151 // ---------------------------------------------------------------------------- 
 153 // get the number of days in the given month of the given year 
 155 wxDateTime::wxDateTime_t 
GetNumOfDaysInMonth(int year
, wxDateTime::Month month
) 
 157     // the number of days in month in Julian/Gregorian calendar: the first line 
 158     // is for normal years, the second one is for the leap ones 
 159     static wxDateTime::wxDateTime_t daysInMonth
[2][MONTHS_IN_YEAR
] = 
 161         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 
 162         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 
 165     return daysInMonth
[wxDateTime::IsLeapYear(year
)][month
]; 
 168 // ensure that the timezone variable is set by calling localtime 
 169 static int GetTimeZone() 
 171     // set to TRUE when the timezone is set 
 172     static bool s_timezoneSet 
= FALSE
; 
 174     wxCRIT_SECT_LOCKER(lock
, gs_critsectTimezone
); 
 176     if ( !s_timezoneSet 
) 
 178         // just call localtime() instead of figuring out whether this system 
 179         // supports tzset(), _tzset() or something else 
 183         s_timezoneSet 
= TRUE
; 
 186     return (int)WX_TIMEZONE
; 
 189 // return the integral part of the JDN for the midnight of the given date (to 
 190 // get the real JDN you need to add 0.5, this is, in fact, JDN of the 
 191 // noon of the previous day) 
 192 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day
, 
 193                             wxDateTime::Month mon
, 
 196     // CREDIT: code below is by Scott E. Lee (but bugs are mine) 
 198     // check the date validity 
 200       (year 
> JDN_0_YEAR
) || 
 201       ((year 
== JDN_0_YEAR
) && (mon 
> JDN_0_MONTH
)) || 
 202       ((year 
== JDN_0_YEAR
) && (mon 
== JDN_0_MONTH
) && (day 
>= JDN_0_DAY
)), 
 203       _T("date out of range - can't convert to JDN") 
 206     // make the year positive to avoid problems with negative numbers division 
 209     // months are counted from March here 
 211     if ( mon 
>= wxDateTime::Mar 
) 
 221     // now we can simply add all the contributions together 
 222     return ((year 
/ 100) * DAYS_PER_400_YEARS
) / 4 
 223             + ((year 
% 100) * DAYS_PER_4_YEARS
) / 4 
 224             + (month 
* DAYS_PER_5_MONTHS 
+ 2) / 5 
 229 // this function is a wrapper around strftime(3) 
 230 static wxString 
CallStrftime(const wxChar 
*format
, const tm
* tm
) 
 233     if ( !wxStrftime(buf
, WXSIZEOF(buf
), format
, tm
) ) 
 235         // buffer is too small? 
 236         wxFAIL_MSG(_T("strftime() failed")); 
 239     return wxString(buf
); 
 242 // if year and/or month have invalid values, replace them with the current ones 
 243 static void ReplaceDefaultYearMonthWithCurrent(int *year
, 
 244                                                wxDateTime::Month 
*month
) 
 246     struct tm 
*tmNow 
= NULL
; 
 248     if ( *year 
== wxDateTime::Inv_Year 
) 
 250         tmNow 
= wxDateTime::GetTmNow(); 
 252         *year 
= 1900 + tmNow
->tm_year
; 
 255     if ( *month 
== wxDateTime::Inv_Month 
) 
 258             tmNow 
= wxDateTime::GetTmNow(); 
 260         *month 
= (wxDateTime::Month
)tmNow
->tm_mon
; 
 264 // fll the struct tm with default values 
 265 static void InitTm(struct tm
& tm
) 
 267     // struct tm may have etxra fields (undocumented and with unportable 
 268     // names) which, nevertheless, must be set to 0 
 269     memset(&tm
, 0, sizeof(struct tm
)); 
 271     tm
.tm_mday 
= 1;   // mday 0 is invalid 
 272     tm
.tm_year 
= 76;  // any valid year 
 273     tm
.tm_isdst 
= -1; // auto determine 
 279 // return the month if the string is a month name or Inv_Month otherwise 
 280 static wxDateTime::Month 
GetMonthFromName(const wxString
& name
, int flags
) 
 282     wxDateTime::Month mon
; 
 283     for ( mon 
= wxDateTime::Jan
; mon 
< wxDateTime::Inv_Month
; wxNextMonth(mon
) ) 
 285         // case-insensitive comparison either one of or with both abbreviated 
 287         if ( flags 
& wxDateTime::Name_Full 
) 
 289             if ( name
.CmpNoCase(wxDateTime:: 
 290                         GetMonthName(mon
, wxDateTime::Name_Full
)) == 0 ) 
 296         if ( flags 
& wxDateTime::Name_Abbr 
) 
 298             if ( name
.CmpNoCase(wxDateTime:: 
 299                         GetMonthName(mon
, wxDateTime::Name_Abbr
)) == 0 ) 
 309 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise 
 310 static wxDateTime::WeekDay 
GetWeekDayFromName(const wxString
& name
, int flags
) 
 312     wxDateTime::WeekDay wd
; 
 313     for ( wd 
= wxDateTime::Sun
; wd 
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) ) 
 315         // case-insensitive comparison either one of or with both abbreviated 
 317         if ( flags 
& wxDateTime::Name_Full 
) 
 319             if ( name
.CmpNoCase(wxDateTime:: 
 320                         GetWeekDayName(wd
, wxDateTime::Name_Full
)) == 0 ) 
 326         if ( flags 
& wxDateTime::Name_Abbr 
) 
 328             if ( name
.CmpNoCase(wxDateTime:: 
 329                         GetWeekDayName(wd
, wxDateTime::Name_Abbr
)) == 0 ) 
 339 // scans all digits and returns the resulting number 
 340 static bool GetNumericToken(const wxChar
*& p
, unsigned long *number
) 
 343     while ( wxIsdigit(*p
) ) 
 348     return !!s 
&& s
.ToULong(number
); 
 351 // scans all alphabetic characters and returns the resulting string 
 352 static wxString 
GetAlphaToken(const wxChar
*& p
) 
 355     while ( wxIsalpha(*p
) ) 
 363 // ============================================================================ 
 364 // implementation of wxDateTime 
 365 // ============================================================================ 
 367 // ---------------------------------------------------------------------------- 
 369 // ---------------------------------------------------------------------------- 
 371 wxDateTime::Country 
wxDateTime::ms_country 
= wxDateTime::Country_Unknown
; 
 372 wxDateTime 
wxDateTime::ms_InvDateTime
; 
 374 // ---------------------------------------------------------------------------- 
 376 // ---------------------------------------------------------------------------- 
 380     year 
= (wxDateTime_t
)wxDateTime::Inv_Year
; 
 381     mon 
= wxDateTime::Inv_Month
; 
 383     hour 
= min 
= sec 
= msec 
= 0; 
 384     wday 
= wxDateTime::Inv_WeekDay
; 
 387 wxDateTime::Tm::Tm(const struct tm
& tm
, const TimeZone
& tz
) 
 395     mon 
= (wxDateTime::Month
)tm
.tm_mon
; 
 396     year 
= 1900 + tm
.tm_year
; 
 401 bool wxDateTime::Tm::IsValid() const 
 403     // we allow for the leap seconds, although we don't use them (yet) 
 404     return (year 
!= wxDateTime::Inv_Year
) && (mon 
!= wxDateTime::Inv_Month
) && 
 405            (mday 
<= GetNumOfDaysInMonth(year
, mon
)) && 
 406            (hour 
< 24) && (min 
< 60) && (sec 
< 62) && (msec 
< 1000); 
 409 void wxDateTime::Tm::ComputeWeekDay() 
 411     // compute the week day from day/month/year: we use the dumbest algorithm 
 412     // possible: just compute our JDN and then use the (simple to derive) 
 413     // formula: weekday = (JDN + 1.5) % 7 
 414     wday 
= (wxDateTime::WeekDay
)(GetTruncatedJDN(mday
, mon
, year
) + 2) % 7; 
 417 void wxDateTime::Tm::AddMonths(int monDiff
) 
 419     // normalize the months field 
 420     while ( monDiff 
< -mon 
) 
 424         monDiff 
+= MONTHS_IN_YEAR
; 
 427     while ( monDiff 
+ mon 
> MONTHS_IN_YEAR 
) 
 431         monDiff 
-= MONTHS_IN_YEAR
; 
 434     mon 
= (wxDateTime::Month
)(mon 
+ monDiff
); 
 436     wxASSERT_MSG( mon 
>= 0 && mon 
< MONTHS_IN_YEAR
, _T("logic error") ); 
 439 void wxDateTime::Tm::AddDays(int dayDiff
) 
 441     // normalize the days field 
 447         mday 
+= GetNumOfDaysInMonth(year
, mon
); 
 450     while ( mday 
> GetNumOfDaysInMonth(year
, mon
) ) 
 452         mday 
-= GetNumOfDaysInMonth(year
, mon
); 
 457     wxASSERT_MSG( mday 
> 0 && mday 
<= GetNumOfDaysInMonth(year
, mon
), 
 461 // ---------------------------------------------------------------------------- 
 463 // ---------------------------------------------------------------------------- 
 465 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz
) 
 469         case wxDateTime::Local
: 
 470             // get the offset from C RTL: it returns the difference GMT-local 
 471             // while we want to have the offset _from_ GMT, hence the '-' 
 472             m_offset 
= -GetTimeZone(); 
 475         case wxDateTime::GMT_12
: 
 476         case wxDateTime::GMT_11
: 
 477         case wxDateTime::GMT_10
: 
 478         case wxDateTime::GMT_9
: 
 479         case wxDateTime::GMT_8
: 
 480         case wxDateTime::GMT_7
: 
 481         case wxDateTime::GMT_6
: 
 482         case wxDateTime::GMT_5
: 
 483         case wxDateTime::GMT_4
: 
 484         case wxDateTime::GMT_3
: 
 485         case wxDateTime::GMT_2
: 
 486         case wxDateTime::GMT_1
: 
 487             m_offset 
= -3600*(wxDateTime::GMT0 
- tz
); 
 490         case wxDateTime::GMT0
: 
 491         case wxDateTime::GMT1
: 
 492         case wxDateTime::GMT2
: 
 493         case wxDateTime::GMT3
: 
 494         case wxDateTime::GMT4
: 
 495         case wxDateTime::GMT5
: 
 496         case wxDateTime::GMT6
: 
 497         case wxDateTime::GMT7
: 
 498         case wxDateTime::GMT8
: 
 499         case wxDateTime::GMT9
: 
 500         case wxDateTime::GMT10
: 
 501         case wxDateTime::GMT11
: 
 502         case wxDateTime::GMT12
: 
 503             m_offset 
= 3600*(tz 
- wxDateTime::GMT0
); 
 506         case wxDateTime::A_CST
: 
 507             // Central Standard Time in use in Australia = UTC + 9.5 
 508             m_offset 
= 60l*(9*60 + 30); 
 512             wxFAIL_MSG( _T("unknown time zone") ); 
 516 // ---------------------------------------------------------------------------- 
 518 // ---------------------------------------------------------------------------- 
 521 bool wxDateTime::IsLeapYear(int year
, wxDateTime::Calendar cal
) 
 523     if ( year 
== Inv_Year 
) 
 524         year 
= GetCurrentYear(); 
 526     if ( cal 
== Gregorian 
) 
 528         // in Gregorian calendar leap years are those divisible by 4 except 
 529         // those divisible by 100 unless they're also divisible by 400 
 530         // (in some countries, like Russia and Greece, additional corrections 
 531         // exist, but they won't manifest themselves until 2700) 
 532         return (year 
% 4 == 0) && ((year 
% 100 != 0) || (year 
% 400 == 0)); 
 534     else if ( cal 
== Julian 
) 
 536         // in Julian calendar the rule is simpler 
 537         return year 
% 4 == 0; 
 541         wxFAIL_MSG(_T("unknown calendar")); 
 548 int wxDateTime::GetCentury(int year
) 
 550     return year 
> 0 ? year 
/ 100 : year 
/ 100 - 1; 
 554 int wxDateTime::ConvertYearToBC(int year
) 
 557     return year 
> 0 ? year 
: year 
- 1; 
 561 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal
) 
 566             return Now().GetYear(); 
 569             wxFAIL_MSG(_T("TODO")); 
 573             wxFAIL_MSG(_T("unsupported calendar")); 
 581 wxDateTime::Month 
wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal
) 
 586             return Now().GetMonth(); 
 589             wxFAIL_MSG(_T("TODO")); 
 593             wxFAIL_MSG(_T("unsupported calendar")); 
 601 wxDateTime::wxDateTime_t 
wxDateTime::GetNumberOfDays(int year
, Calendar cal
) 
 603     if ( year 
== Inv_Year 
) 
 605         // take the current year if none given 
 606         year 
= GetCurrentYear(); 
 613             return IsLeapYear(year
) ? 366 : 365; 
 616             wxFAIL_MSG(_T("unsupported calendar")); 
 624 wxDateTime::wxDateTime_t 
wxDateTime::GetNumberOfDays(wxDateTime::Month month
, 
 626                                                      wxDateTime::Calendar cal
) 
 628     wxCHECK_MSG( month 
< MONTHS_IN_YEAR
, 0, _T("invalid month") ); 
 630     if ( cal 
== Gregorian 
|| cal 
== Julian 
) 
 632         if ( year 
== Inv_Year 
) 
 634             // take the current year if none given 
 635             year 
= GetCurrentYear(); 
 638         return GetNumOfDaysInMonth(year
, month
); 
 642         wxFAIL_MSG(_T("unsupported calendar")); 
 649 wxString 
wxDateTime::GetMonthName(wxDateTime::Month month
, 
 650                                   wxDateTime::NameFlags flags
) 
 652     wxCHECK_MSG( month 
!= Inv_Month
, _T(""), _T("invalid month") ); 
 654     // notice that we must set all the fields to avoid confusing libc (GNU one 
 655     // gets confused to a crash if we don't do this) 
 660     return CallStrftime(flags 
== Name_Abbr 
? _T("%b") : _T("%B"), &tm
); 
 664 wxString 
wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday
, 
 665                                     wxDateTime::NameFlags flags
) 
 667     wxCHECK_MSG( wday 
!= Inv_WeekDay
, _T(""), _T("invalid weekday") ); 
 669     // take some arbitrary Sunday 
 676     // and offset it by the number of days needed to get the correct wday 
 679     // call mktime() to normalize it... 
 682     // ... and call strftime() 
 683     return CallStrftime(flags 
== Name_Abbr 
? _T("%a") : _T("%A"), &tm
); 
 687 void wxDateTime::GetAmPmStrings(wxString 
*am
, wxString 
*pm
) 
 693         *am 
= CallStrftime(_T("%p"), &tm
); 
 698         *pm 
= CallStrftime(_T("%p"), &tm
); 
 702 // ---------------------------------------------------------------------------- 
 703 // Country stuff: date calculations depend on the country (DST, work days, 
 704 // ...), so we need to know which rules to follow. 
 705 // ---------------------------------------------------------------------------- 
 708 wxDateTime::Country 
wxDateTime::GetCountry() 
 710     // TODO use LOCALE_ICOUNTRY setting under Win32 
 712     if ( ms_country 
== Country_Unknown 
) 
 714         // try to guess from the time zone name 
 715         time_t t 
= time(NULL
); 
 716         struct tm 
*tm 
= localtime(&t
); 
 718         wxString tz 
= CallStrftime(_T("%Z"), tm
); 
 719         if ( tz 
== _T("WET") || tz 
== _T("WEST") ) 
 723         else if ( tz 
== _T("CET") || tz 
== _T("CEST") ) 
 725             ms_country 
= Country_EEC
; 
 727         else if ( tz 
== _T("MSK") || tz 
== _T("MSD") ) 
 731         else if ( tz 
== _T("AST") || tz 
== _T("ADT") || 
 732                   tz 
== _T("EST") || tz 
== _T("EDT") || 
 733                   tz 
== _T("CST") || tz 
== _T("CDT") || 
 734                   tz 
== _T("MST") || tz 
== _T("MDT") || 
 735                   tz 
== _T("PST") || tz 
== _T("PDT") ) 
 741             // well, choose a default one 
 750 void wxDateTime::SetCountry(wxDateTime::Country country
) 
 752     ms_country 
= country
; 
 756 bool wxDateTime::IsWestEuropeanCountry(Country country
) 
 758     if ( country 
== Country_Default 
) 
 760         country 
= GetCountry(); 
 763     return (Country_WesternEurope_Start 
<= country
) && 
 764            (country 
<= Country_WesternEurope_End
); 
 767 // ---------------------------------------------------------------------------- 
 768 // DST calculations: we use 3 different rules for the West European countries, 
 769 // USA and for the rest of the world. This is undoubtedly false for many 
 770 // countries, but I lack the necessary info (and the time to gather it), 
 771 // please add the other rules here! 
 772 // ---------------------------------------------------------------------------- 
 775 bool wxDateTime::IsDSTApplicable(int year
, Country country
) 
 777     if ( year 
== Inv_Year 
) 
 779         // take the current year if none given 
 780         year 
= GetCurrentYear(); 
 783     if ( country 
== Country_Default 
) 
 785         country 
= GetCountry(); 
 792             // DST was first observed in the US and UK during WWI, reused 
 793             // during WWII and used again since 1966 
 794             return year 
>= 1966 || 
 795                    (year 
>= 1942 && year 
<= 1945) || 
 796                    (year 
== 1918 || year 
== 1919); 
 799             // assume that it started after WWII 
 805 wxDateTime 
wxDateTime::GetBeginDST(int year
, Country country
) 
 807     if ( year 
== Inv_Year 
) 
 809         // take the current year if none given 
 810         year 
= GetCurrentYear(); 
 813     if ( country 
== Country_Default 
) 
 815         country 
= GetCountry(); 
 818     if ( !IsDSTApplicable(year
, country
) ) 
 820         return ms_InvDateTime
; 
 825     if ( IsWestEuropeanCountry(country
) || (country 
== Russia
) ) 
 827         // DST begins at 1 a.m. GMT on the last Sunday of March 
 828         if ( !dt
.SetToLastWeekDay(Sun
, Mar
, year
) ) 
 831             wxFAIL_MSG( _T("no last Sunday in March?") ); 
 834         dt 
+= wxTimeSpan::Hours(1); 
 836         // disable DST tests because it could result in an infinite recursion! 
 839     else switch ( country 
) 
 846                     // don't know for sure - assume it was in effect all year 
 851                     dt
.Set(1, Jan
, year
); 
 855                     // DST was installed Feb 2, 1942 by the Congress 
 856                     dt
.Set(2, Feb
, year
); 
 859                     // Oil embargo changed the DST period in the US 
 861                     dt
.Set(6, Jan
, 1974); 
 865                     dt
.Set(23, Feb
, 1975); 
 869                     // before 1986, DST begun on the last Sunday of April, but 
 870                     // in 1986 Reagan changed it to begin at 2 a.m. of the 
 871                     // first Sunday in April 
 874                         if ( !dt
.SetToLastWeekDay(Sun
, Apr
, year
) ) 
 877                             wxFAIL_MSG( _T("no first Sunday in April?") ); 
 882                         if ( !dt
.SetToWeekDay(Sun
, 1, Apr
, year
) ) 
 885                             wxFAIL_MSG( _T("no first Sunday in April?") ); 
 889                     dt 
+= wxTimeSpan::Hours(2); 
 891                     // TODO what about timezone?? 
 897             // assume Mar 30 as the start of the DST for the rest of the world 
 898             // - totally bogus, of course 
 899             dt
.Set(30, Mar
, year
); 
 906 wxDateTime 
wxDateTime::GetEndDST(int year
, Country country
) 
 908     if ( year 
== Inv_Year 
) 
 910         // take the current year if none given 
 911         year 
= GetCurrentYear(); 
 914     if ( country 
== Country_Default 
) 
 916         country 
= GetCountry(); 
 919     if ( !IsDSTApplicable(year
, country
) ) 
 921         return ms_InvDateTime
; 
 926     if ( IsWestEuropeanCountry(country
) || (country 
== Russia
) ) 
 928         // DST ends at 1 a.m. GMT on the last Sunday of October 
 929         if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) ) 
 931             // weirder and weirder... 
 932             wxFAIL_MSG( _T("no last Sunday in October?") ); 
 935         dt 
+= wxTimeSpan::Hours(1); 
 937         // disable DST tests because it could result in an infinite recursion! 
 940     else switch ( country 
) 
 947                     // don't know for sure - assume it was in effect all year 
 951                     dt
.Set(31, Dec
, year
); 
 955                     // the time was reset after the end of the WWII 
 956                     dt
.Set(30, Sep
, year
); 
 960                     // DST ends at 2 a.m. on the last Sunday of October 
 961                     if ( !dt
.SetToLastWeekDay(Sun
, Oct
, year
) ) 
 963                         // weirder and weirder... 
 964                         wxFAIL_MSG( _T("no last Sunday in October?") ); 
 967                     dt 
+= wxTimeSpan::Hours(2); 
 969                     // TODO what about timezone?? 
 974             // assume October 26th as the end of the DST - totally bogus too 
 975             dt
.Set(26, Oct
, year
); 
 981 // ---------------------------------------------------------------------------- 
 982 // constructors and assignment operators 
 983 // ---------------------------------------------------------------------------- 
 985 // the values in the tm structure contain the local time 
 986 wxDateTime
& wxDateTime::Set(const struct tm
& tm
) 
 988     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
 991     time_t timet 
= mktime(&tm2
); 
 993     if ( timet 
== (time_t)-1 ) 
 995         // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is 
 996         // less than timezone - try to make it work for this case 
 997         if ( tm2
.tm_year 
== 70 && tm2
.tm_mon 
== 0 && tm2
.tm_mday 
== 1 ) 
 999             // add timezone to make sure that date is in range 
1000             tm2
.tm_sec 
-= GetTimeZone(); 
1002             timet 
= mktime(&tm2
); 
1003             if ( timet 
!= (time_t)-1 ) 
1005                 timet 
+= GetTimeZone(); 
1011         wxFAIL_MSG( _T("mktime() failed") ); 
1013         return ms_InvDateTime
; 
1021 wxDateTime
& wxDateTime::Set(wxDateTime_t hour
, 
1022                             wxDateTime_t minute
, 
1023                             wxDateTime_t second
, 
1024                             wxDateTime_t millisec
) 
1026     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1028     // we allow seconds to be 61 to account for the leap seconds, even if we 
1029     // don't use them really 
1030     wxCHECK_MSG( hour 
< 24 && second 
< 62 && minute 
< 60 && millisec 
< 1000, 
1032                  _T("Invalid time in wxDateTime::Set()") ); 
1034     // get the current date from system 
1035     time_t timet 
= GetTimeNow(); 
1036     struct tm 
*tm 
= localtime(&timet
); 
1038     wxCHECK_MSG( tm
, ms_InvDateTime
, _T("localtime() failed") ); 
1042     tm
->tm_min 
= minute
; 
1043     tm
->tm_sec 
= second
; 
1047     // and finally adjust milliseconds 
1048     return SetMillisecond(millisec
); 
1051 wxDateTime
& wxDateTime::Set(wxDateTime_t day
, 
1055                             wxDateTime_t minute
, 
1056                             wxDateTime_t second
, 
1057                             wxDateTime_t millisec
) 
1059     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1061     wxCHECK_MSG( hour 
< 24 && second 
< 62 && minute 
< 60 && millisec 
< 1000, 
1063                  _T("Invalid time in wxDateTime::Set()") ); 
1065     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
1067     wxCHECK_MSG( (0 < day
) && (day 
<= GetNumberOfDays(month
, year
)), 
1069                  _T("Invalid date in wxDateTime::Set()") ); 
1071     // the range of time_t type (inclusive) 
1072     static const int yearMinInRange 
= 1970; 
1073     static const int yearMaxInRange 
= 2037; 
1075     // test only the year instead of testing for the exact end of the Unix 
1076     // time_t range - it doesn't bring anything to do more precise checks 
1077     if ( year 
>= yearMinInRange 
&& year 
<= yearMaxInRange 
) 
1079         // use the standard library version if the date is in range - this is 
1080         // probably more efficient than our code 
1082         tm
.tm_year 
= year 
- 1900; 
1088         tm
.tm_isdst 
= -1;       // mktime() will guess it 
1092         // and finally adjust milliseconds 
1093         return SetMillisecond(millisec
); 
1097         // do time calculations ourselves: we want to calculate the number of 
1098         // milliseconds between the given date and the epoch 
1100         // get the JDN for the midnight of this day 
1101         m_time 
= GetTruncatedJDN(day
, month
, year
); 
1102         m_time 
-= EPOCH_JDN
; 
1103         m_time 
*= SECONDS_PER_DAY 
* TIME_T_FACTOR
; 
1105         // JDN corresponds to GMT, we take localtime 
1106         Add(wxTimeSpan(hour
, minute
, second 
+ GetTimeZone(), millisec
)); 
1112 wxDateTime
& wxDateTime::Set(double jdn
) 
1114     // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn 
1116     jdn 
-= EPOCH_JDN 
+ 0.5; 
1118     jdn 
*= MILLISECONDS_PER_DAY
; 
1125 // ---------------------------------------------------------------------------- 
1126 // time_t <-> broken down time conversions 
1127 // ---------------------------------------------------------------------------- 
1129 wxDateTime::Tm 
wxDateTime::GetTm(const TimeZone
& tz
) const 
1134     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1136     time_t time 
= GetTicks(); 
1137     if ( time 
!= (time_t)-1 ) 
1139         // use C RTL functions 
1141         if ( tz
.GetOffset() == -GetTimeZone() ) 
1143             // we are working with local time 
1144             tm 
= localtime(&time
); 
1146             // should never happen 
1147             wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") ); 
1151             time 
+= tz
.GetOffset(); 
1152 #ifdef __VMS__ /* time is unsigned so VMS gives a warning on the original */ 
1161                 // should never happen 
1162                 wxCHECK_MSG( tm
, Tm(), _T("gmtime() failed") ); 
1166                 tm 
= (struct tm 
*)NULL
; 
1174         //else: use generic code below 
1177     // remember the time and do the calculations with the date only - this 
1178     // eliminates rounding errors of the floating point arithmetics 
1180     wxLongLong timeMidnight 
= m_time 
+ tz
.GetOffset() * 1000; 
1182     long timeOnly 
= (timeMidnight 
% MILLISECONDS_PER_DAY
).ToLong(); 
1184     // we want to always have positive time and timeMidnight to be really 
1185     // the midnight before it 
1188         timeOnly 
= MILLISECONDS_PER_DAY 
+ timeOnly
; 
1191     timeMidnight 
-= timeOnly
; 
1193     // calculate the Gregorian date from JDN for the midnight of our date: 
1194     // this will yield day, month (in 1..12 range) and year 
1196     // actually, this is the JDN for the noon of the previous day 
1197     long jdn 
= (timeMidnight 
/ MILLISECONDS_PER_DAY
).ToLong() + EPOCH_JDN
; 
1199     // CREDIT: code below is by Scott E. Lee (but bugs are mine) 
1201     wxASSERT_MSG( jdn 
> -2, _T("JDN out of range") ); 
1203     // calculate the century 
1204     int temp 
= (jdn 
+ JDN_OFFSET
) * 4 - 1; 
1205     int century 
= temp 
/ DAYS_PER_400_YEARS
; 
1207     // then the year and day of year (1 <= dayOfYear <= 366) 
1208     temp 
= ((temp 
% DAYS_PER_400_YEARS
) / 4) * 4 + 3; 
1209     int year 
= (century 
* 100) + (temp 
/ DAYS_PER_4_YEARS
); 
1210     int dayOfYear 
= (temp 
% DAYS_PER_4_YEARS
) / 4 + 1; 
1212     // and finally the month and day of the month 
1213     temp 
= dayOfYear 
* 5 - 3; 
1214     int month 
= temp 
/ DAYS_PER_5_MONTHS
; 
1215     int day 
= (temp 
% DAYS_PER_5_MONTHS
) / 5 + 1; 
1217     // month is counted from March - convert to normal 
1228     // year is offset by 4800 
1231     // check that the algorithm gave us something reasonable 
1232     wxASSERT_MSG( (0 < month
) && (month 
<= 12), _T("invalid month") ); 
1233     wxASSERT_MSG( (1 <= day
) && (day 
< 32), _T("invalid day") ); 
1234     wxASSERT_MSG( (INT_MIN 
<= year
) && (year 
<= INT_MAX
), 
1235                   _T("year range overflow") ); 
1237     // construct Tm from these values 
1239     tm
.year 
= (int)year
; 
1240     tm
.mon 
= (Month
)(month 
- 1); // algorithm yields 1 for January, not 0 
1241     tm
.mday 
= (wxDateTime_t
)day
; 
1242     tm
.msec 
= timeOnly 
% 1000; 
1243     timeOnly 
-= tm
.msec
; 
1244     timeOnly 
/= 1000;               // now we have time in seconds 
1246     tm
.sec 
= timeOnly 
% 60; 
1248     timeOnly 
/= 60;                 // now we have time in minutes 
1250     tm
.min 
= timeOnly 
% 60; 
1253     tm
.hour 
= timeOnly 
/ 60; 
1258 wxDateTime
& wxDateTime::SetYear(int year
) 
1260     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1269 wxDateTime
& wxDateTime::SetMonth(Month month
) 
1271     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1280 wxDateTime
& wxDateTime::SetDay(wxDateTime_t mday
) 
1282     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1291 wxDateTime
& wxDateTime::SetHour(wxDateTime_t hour
) 
1293     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1302 wxDateTime
& wxDateTime::SetMinute(wxDateTime_t min
) 
1304     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1313 wxDateTime
& wxDateTime::SetSecond(wxDateTime_t sec
) 
1315     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1324 wxDateTime
& wxDateTime::SetMillisecond(wxDateTime_t millisecond
) 
1326     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 
1328     // we don't need to use GetTm() for this one 
1329     m_time 
-= m_time 
% 1000l; 
1330     m_time 
+= millisecond
; 
1335 // ---------------------------------------------------------------------------- 
1336 // wxDateTime arithmetics 
1337 // ---------------------------------------------------------------------------- 
1339 wxDateTime
& wxDateTime::Add(const wxDateSpan
& diff
) 
1343     tm
.year 
+= diff
.GetYears(); 
1344     tm
.AddMonths(diff
.GetMonths()); 
1345     tm
.AddDays(diff
.GetTotalDays()); 
1352 // ---------------------------------------------------------------------------- 
1353 // Weekday and monthday stuff 
1354 // ---------------------------------------------------------------------------- 
1356 wxDateTime
& wxDateTime::SetToLastMonthDay(Month month
, 
1359     // take the current month/year if none specified 
1360     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
1362     return Set(GetNumOfDaysInMonth(year
, month
), month
, year
); 
1365 wxDateTime
& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday
) 
1367     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, ms_InvDateTime
, _T("invalid weekday") ); 
1369     WeekDay wdayThis 
= GetWeekDay(); 
1370     if ( weekday 
== wdayThis 
) 
1375     else if ( weekday 
< wdayThis 
) 
1377         return Substract(wxTimeSpan::Days(wdayThis 
- weekday
)); 
1379     else // weekday > wdayThis 
1381         return Add(wxTimeSpan::Days(weekday 
- wdayThis
)); 
1385 wxDateTime
& wxDateTime::SetToNextWeekDay(WeekDay weekday
) 
1387     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, ms_InvDateTime
, _T("invalid weekday") ); 
1390     WeekDay wdayThis 
= GetWeekDay(); 
1391     if ( weekday 
== wdayThis 
) 
1396     else if ( weekday 
< wdayThis 
) 
1398         // need to advance a week 
1399         diff 
= 7 - (wdayThis 
- weekday
); 
1401     else // weekday > wdayThis 
1403         diff 
= weekday 
- wdayThis
; 
1406     return Add(wxTimeSpan::Days(diff
)); 
1409 wxDateTime
& wxDateTime::SetToPrevWeekDay(WeekDay weekday
) 
1411     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, ms_InvDateTime
, _T("invalid weekday") ); 
1414     WeekDay wdayThis 
= GetWeekDay(); 
1415     if ( weekday 
== wdayThis 
) 
1420     else if ( weekday 
> wdayThis 
) 
1422         // need to go to previous week 
1423         diff 
= 7 - (weekday 
- wdayThis
); 
1425     else // weekday < wdayThis 
1427         diff 
= wdayThis 
- weekday
; 
1430     return Substract(wxTimeSpan::Days(diff
)); 
1433 bool wxDateTime::SetToWeekDay(WeekDay weekday
, 
1438     wxCHECK_MSG( weekday 
!= Inv_WeekDay
, FALSE
, _T("invalid weekday") ); 
1440     // we don't check explicitly that -5 <= n <= 5 because we will return FALSE 
1441     // anyhow in such case - but may be should still give an assert for it? 
1443     // take the current month/year if none specified 
1444     ReplaceDefaultYearMonthWithCurrent(&year
, &month
); 
1448     // TODO this probably could be optimised somehow... 
1452         // get the first day of the month 
1453         dt
.Set(1, month
, year
); 
1456         WeekDay wdayFirst 
= dt
.GetWeekDay(); 
1458         // go to the first weekday of the month 
1459         int diff 
= weekday 
- wdayFirst
; 
1463         // add advance n-1 weeks more 
1466         dt 
+= wxDateSpan::Days(diff
); 
1468     else // count from the end of the month 
1470         // get the last day of the month 
1471         dt
.SetToLastMonthDay(month
, year
); 
1474         WeekDay wdayLast 
= dt
.GetWeekDay(); 
1476         // go to the last weekday of the month 
1477         int diff 
= wdayLast 
- weekday
; 
1481         // and rewind n-1 weeks from there 
1484         dt 
-= wxDateSpan::Days(diff
); 
1487     // check that it is still in the same month 
1488     if ( dt
.GetMonth() == month 
) 
1496         // no such day in this month 
1501 wxDateTime::wxDateTime_t 
wxDateTime::GetDayOfYear(const TimeZone
& tz
) const 
1505     return gs_cumulatedDays
[IsLeapYear(tm
.year
)][tm
.mon
] + tm
.mday
; 
1508 wxDateTime::wxDateTime_t 
wxDateTime::GetWeekOfYear(const TimeZone
& tz
) const 
1511     // the first week of the year is the one which contains Jan, 4 (according 
1512     // to ISO standard rule), so the year day N0 = 4 + 7*W always lies in the 
1513     // week W+1. As any day N = 7*W + 4 + (N - 4)%7, it lies in the same week 
1514     // as N0 or in the next one. 
1516     // TODO this surely may be optimized - I got confused while writing it 
1518     wxDateTime_t nDayInYear 
= GetDayOfYear(tz
); 
1520     // the week day of the day lying in the first week 
1521     WeekDay wdayStart 
= wxDateTime(4, Jan
, GetYear()).GetWeekDay(); 
1523     wxDateTime_t week 
= (nDayInYear 
- 4) / 7 + 1; 
1525     // notice that Sunday shoould be counted as 7, not 0 here! 
1526     if ( ((nDayInYear 
- 4) % 7) + (!wdayStart 
? 7 : wdayStart
) > 7 ) 
1533     // an attempt at doing it simpler - but this doesn't quite work... 
1534     return (WeekDay
)((GetDayOfYear(tz
) - (GetWeekDay(tz
) - 1 + 7) % 7 + 7) / 7); 
1538 wxDateTime::wxDateTime_t 
wxDateTime::GetWeekOfMonth(const TimeZone
& tz
) const 
1542     wxDateTime 
dt(*this); 
1547         dt 
-= wxTimeSpan::Week(); 
1549     while ( dt
.GetMonth(tz
) == GetMonth(tz
) ); 
1554 wxDateTime
& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday
) 
1556     int year 
= GetYear(); 
1557     wxCHECK_MSG( (0 < yday
) && (yday 
<= GetNumberOfDays(year
)), 
1558                  ms_InvDateTime
, _T("invalid year day") ); 
1560     bool isLeap 
= IsLeapYear(year
); 
1561     for ( Month mon 
= Jan
; mon 
< Inv_Month
; wxNextMonth(mon
) ) 
1563         // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we 
1564         // don't need it neither - because of the CHECK above we know that 
1565         // yday lies in December then 
1566         if ( (mon 
== Dec
) || (yday 
< gs_cumulatedDays
[isLeap
][mon 
+ 1]) ) 
1568             Set(yday 
- gs_cumulatedDays
[isLeap
][mon
], mon
, year
); 
1577 // ---------------------------------------------------------------------------- 
1578 // Julian day number conversion and related stuff 
1579 // ---------------------------------------------------------------------------- 
1581 double wxDateTime::GetJulianDayNumber() const 
1583     // JDN are always expressed for the GMT dates 
1584     Tm 
tm(ToTimezone(GMT0
).GetTm(GMT0
)); 
1586     double result 
= GetTruncatedJDN(tm
.mday
, tm
.mon
, tm
.year
); 
1588     // add the part GetTruncatedJDN() neglected 
1591     // and now add the time: 86400 sec = 1 JDN 
1592     return result 
+ ((double)(60*(60*tm
.hour 
+ tm
.min
) + tm
.sec
)) / 86400; 
1595 double wxDateTime::GetRataDie() const 
1597     // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5 
1598     return GetJulianDayNumber() - 1721119.5 - 306; 
1601 // ---------------------------------------------------------------------------- 
1602 // timezone and DST stuff 
1603 // ---------------------------------------------------------------------------- 
1605 int wxDateTime::IsDST(wxDateTime::Country country
) const 
1607     wxCHECK_MSG( country 
== Country_Default
, -1, 
1608                  _T("country support not implemented") ); 
1610     // use the C RTL for the dates in the standard range 
1611     time_t timet 
= GetTicks(); 
1612     if ( timet 
!= (time_t)-1 ) 
1614         tm 
*tm 
= localtime(&timet
); 
1616         wxCHECK_MSG( tm
, -1, _T("localtime() failed") ); 
1618         return tm
->tm_isdst
; 
1622         int year 
= GetYear(); 
1624         if ( !IsDSTApplicable(year
, country
) ) 
1626             // no DST time in this year in this country 
1630         return IsBetween(GetBeginDST(year
, country
), GetEndDST(year
, country
)); 
1634 wxDateTime
& wxDateTime::MakeTimezone(const TimeZone
& tz
, bool noDST
) 
1636     int secDiff 
= GetTimeZone() + tz
.GetOffset(); 
1638     // we need to know whether DST is or not in effect for this date unless 
1639     // the test disabled by the caller 
1640     if ( !noDST 
&& (IsDST() == 1) ) 
1642         // FIXME we assume that the DST is always shifted by 1 hour 
1646     return Substract(wxTimeSpan::Seconds(secDiff
)); 
1649 // ---------------------------------------------------------------------------- 
1650 // wxDateTime to/from text representations 
1651 // ---------------------------------------------------------------------------- 
1653 wxString 
wxDateTime::Format(const wxChar 
*format
, const TimeZone
& tz
) const 
1658     wxCHECK_MSG( format
, _T(""), _T("NULL format in wxDateTime::Format") ); 
1660     time_t time 
= GetTicks(); 
1661     if ( time 
!= (time_t)-1 ) 
1665         if ( tz
.GetOffset() == -GetTimeZone() ) 
1667             // we are working with local time 
1668             tm 
= localtime(&time
); 
1670             // should never happen 
1671             wxCHECK_MSG( tm
, wxEmptyString
, _T("localtime() failed") ); 
1675             time 
+= tz
.GetOffset(); 
1677 #ifdef __VMS__ /* time is unsigned so VMS gives a warning on the original */ 
1686                 // should never happen 
1687                 wxCHECK_MSG( tm
, wxEmptyString
, _T("gmtime() failed") ); 
1691                 tm 
= (struct tm 
*)NULL
; 
1697             return CallStrftime(format
, tm
); 
1699         //else: use generic code below 
1702     // we only parse ANSI C format specifications here, no POSIX 2 
1703     // complications, no GNU extensions 
1706     // used for calls to strftime() when we only deal with time 
1707     struct tm tmTimeOnly
; 
1708     tmTimeOnly
.tm_hour 
= tm
.hour
; 
1709     tmTimeOnly
.tm_min 
= tm
.min
; 
1710     tmTimeOnly
.tm_sec 
= tm
.sec
; 
1711     tmTimeOnly
.tm_wday 
= 0; 
1712     tmTimeOnly
.tm_yday 
= 0; 
1713     tmTimeOnly
.tm_mday 
= 1;         // any date will do 
1714     tmTimeOnly
.tm_mon 
= 0; 
1715     tmTimeOnly
.tm_year 
= 76; 
1716     tmTimeOnly
.tm_isdst 
= 0;        // no DST, we adjust for tz ourselves 
1718     wxString tmp
, res
, fmt
; 
1719     for ( const wxChar 
*p 
= format
; *p
; p
++ ) 
1721         if ( *p 
!= _T('%') ) 
1729         // set the default format 
1732             case _T('Y'):               // year has 4 digits 
1736             case _T('j'):               // day of year has 3 digits 
1741                 // it's either another valid format specifier in which case 
1742                 // the format is "%02d" (for all the rest) or we have the 
1743                 // field width preceding the format in which case it will 
1744                 // override the default format anyhow 
1749         // start of the format specification 
1752             case _T('a'):       // a weekday name 
1754                 // second parameter should be TRUE for abbreviated names 
1755                 res 
+= GetWeekDayName(tm
.GetWeekDay(), 
1756                                       *p 
== _T('a') ? Name_Abbr 
: Name_Full
); 
1759             case _T('b'):       // a month name 
1761                 res 
+= GetMonthName(tm
.mon
, 
1762                                     *p 
== _T('b') ? Name_Abbr 
: Name_Full
); 
1765             case _T('c'):       // locale default date and time  representation 
1766             case _T('x'):       // locale default date representation 
1768                 // the problem: there is no way to know what do these format 
1769                 // specifications correspond to for the current locale. 
1771                 // the solution: use a hack and still use strftime(): first 
1772                 // find the YEAR which is a year in the strftime() range (1970 
1773                 // - 2038) whose Jan 1 falls on the same week day as the Jan 1 
1774                 // of the real year. Then make a copy of the format and 
1775                 // replace all occurences of YEAR in it with some unique 
1776                 // string not appearing anywhere else in it, then use 
1777                 // strftime() to format the date in year YEAR and then replace 
1778                 // YEAR back by the real year and the unique replacement 
1779                 // string back with YEAR. Notice that "all occurences of YEAR" 
1780                 // means all occurences of 4 digit as well as 2 digit form! 
1782                 // the bugs: we assume that neither of %c nor %x contains any 
1783                 // fields which may change between the YEAR and real year. For 
1784                 // example, the week number (%U, %W) and the day number (%j) 
1785                 // will change if one of these years is leap and the other one 
1788                     // find the YEAR: normally, for any year X, Jan 1 or the 
1789                     // year X + 28 is the same weekday as Jan 1 of X (because 
1790                     // the weekday advances by 1 for each normal X and by 2 
1791                     // for each leap X, hence by 5 every 4 years or by 35 
1792                     // which is 0 mod 7 every 28 years) but this rule breaks 
1793                     // down if there are years between X and Y which are 
1794                     // divisible by 4 but not leap (i.e. divisible by 100 but 
1795                     // not 400), hence the correction. 
1797                     int yearReal 
= GetYear(tz
); 
1798                     int mod28 
= yearReal 
% 28; 
1800                     // be careful to not go too far - we risk to leave the 
1805                         year 
= 1988 + mod28
;      // 1988 == 0 (mod 28) 
1809                         year 
= 1970 + mod28 
- 10; // 1970 == 10 (mod 28) 
1812                     int nCentury 
= year 
/ 100, 
1813                         nCenturyReal 
= yearReal 
/ 100; 
1815                     // need to adjust for the years divisble by 400 which are 
1816                     // not leap but are counted like leap ones if we just take 
1817                     // the number of centuries in between for nLostWeekDays 
1818                     int nLostWeekDays 
= (nCentury 
- nCenturyReal
) - 
1819                                         (nCentury 
/ 4 - nCenturyReal 
/ 4); 
1821                     // we have to gain back the "lost" weekdays: note that the 
1822                     // effect of this loop is to not do anything to 
1823                     // nLostWeekDays (which we won't use any more), but to 
1824                     // (indirectly) set the year correctly 
1825                     while ( (nLostWeekDays 
% 7) != 0 ) 
1827                         nLostWeekDays 
+= year
++ % 4 ? 1 : 2; 
1830                     // at any rate, we couldn't go further than 1988 + 9 + 28! 
1831                     wxASSERT_MSG( year 
< 2030, 
1832                                   _T("logic error in wxDateTime::Format") ); 
1834                     wxString strYear
, strYear2
; 
1835                     strYear
.Printf(_T("%d"), year
); 
1836                     strYear2
.Printf(_T("%d"), year 
% 100); 
1838                     // find two strings not occuring in format (this is surely 
1839                     // not optimal way of doing it... improvements welcome!) 
1840                     wxString fmt 
= format
; 
1841                     wxString replacement 
= (wxChar
)-1; 
1842                     while ( fmt
.Find(replacement
) != wxNOT_FOUND 
) 
1844                         replacement 
<< (wxChar
)-1; 
1847                     wxString replacement2 
= (wxChar
)-2; 
1848                     while ( fmt
.Find(replacement
) != wxNOT_FOUND 
) 
1850                         replacement 
<< (wxChar
)-2; 
1853                     // replace all occurences of year with it 
1854                     bool wasReplaced 
= fmt
.Replace(strYear
, replacement
) > 0; 
1856                         wasReplaced 
= fmt
.Replace(strYear2
, replacement2
) > 0; 
1858                     // use strftime() to format the same date but in supported 
1861                     // NB: we assume that strftime() doesn't check for the 
1862                     //     date validity and will happily format the date 
1863                     //     corresponding to Feb 29 of a non leap year (which 
1864                     //     may happen if yearReal was leap and year is not) 
1865                     struct tm tmAdjusted
; 
1867                     tmAdjusted
.tm_hour 
= tm
.hour
; 
1868                     tmAdjusted
.tm_min 
= tm
.min
; 
1869                     tmAdjusted
.tm_sec 
= tm
.sec
; 
1870                     tmAdjusted
.tm_wday 
= tm
.GetWeekDay(); 
1871                     tmAdjusted
.tm_yday 
= GetDayOfYear(); 
1872                     tmAdjusted
.tm_mday 
= tm
.mday
; 
1873                     tmAdjusted
.tm_mon 
= tm
.mon
; 
1874                     tmAdjusted
.tm_year 
= year 
- 1900; 
1875                     tmAdjusted
.tm_isdst 
= 0; // no DST, already adjusted 
1876                     wxString str 
= CallStrftime(*p 
== _T('c') ? _T("%c") 
1880                     // now replace the occurence of 1999 with the real year 
1881                     wxString strYearReal
, strYearReal2
; 
1882                     strYearReal
.Printf(_T("%04d"), yearReal
); 
1883                     strYearReal2
.Printf(_T("%02d"), yearReal 
% 100); 
1884                     str
.Replace(strYear
, strYearReal
); 
1885                     str
.Replace(strYear2
, strYearReal2
); 
1887                     // and replace back all occurences of replacement string 
1890                         str
.Replace(replacement2
, strYear2
); 
1891                         str
.Replace(replacement
, strYear
); 
1898             case _T('d'):       // day of a month (01-31) 
1899                 res 
+= wxString::Format(fmt
, tm
.mday
); 
1902             case _T('H'):       // hour in 24h format (00-23) 
1903                 res 
+= wxString::Format(fmt
, tm
.hour
); 
1906             case _T('I'):       // hour in 12h format (01-12) 
1908                     // 24h -> 12h, 0h -> 12h too 
1909                     int hour12 
= tm
.hour 
> 12 ? tm
.hour 
- 12 
1910                                               : tm
.hour 
? tm
.hour 
: 12; 
1911                     res 
+= wxString::Format(fmt
, hour12
); 
1915             case _T('j'):       // day of the year 
1916                 res 
+= wxString::Format(fmt
, GetDayOfYear(tz
)); 
1919             case _T('m'):       // month as a number (01-12) 
1920                 res 
+= wxString::Format(fmt
, tm
.mon 
+ 1); 
1923             case _T('M'):       // minute as a decimal number (00-59) 
1924                 res 
+= wxString::Format(fmt
, tm
.min
); 
1927             case _T('p'):       // AM or PM string 
1928                 res 
+= CallStrftime(_T("%p"), &tmTimeOnly
); 
1931             case _T('S'):       // second as a decimal number (00-61) 
1932                 res 
+= wxString::Format(fmt
, tm
.sec
); 
1935             case _T('U'):       // week number in the year (Sunday 1st week day) 
1937                     int week 
= (GetDayOfYear(tz
) - tm
.GetWeekDay() + 7) / 7; 
1938                     res 
+= wxString::Format(fmt
, week
); 
1942             case _T('W'):       // week number in the year (Monday 1st week day) 
1943                 res 
+= wxString::Format(fmt
, GetWeekOfYear(tz
)); 
1946             case _T('w'):       // weekday as a number (0-6), Sunday = 0 
1947                 res 
+= wxString::Format(fmt
, tm
.GetWeekDay()); 
1950             // case _T('x'): -- handled with "%c" 
1952             case _T('X'):       // locale default time representation 
1953                 // just use strftime() to format the time for us 
1954                 res 
+= CallStrftime(_T("%X"), &tmTimeOnly
); 
1957             case _T('y'):       // year without century (00-99) 
1958                 res 
+= wxString::Format(fmt
, tm
.year 
% 100); 
1961             case _T('Y'):       // year with century 
1962                 res 
+= wxString::Format(fmt
, tm
.year
); 
1965             case _T('Z'):       // timezone name 
1966                 res 
+= CallStrftime(_T("%Z"), &tmTimeOnly
); 
1970                 // is it the format width? 
1972                 while ( *p 
== _T('-') || *p 
== _T('+') || 
1973                         *p 
== _T(' ') || wxIsdigit(*p
) ) 
1978                 if ( !fmt
.IsEmpty() ) 
1980                     // we've only got the flags and width so far in fmt 
1981                     fmt
.Prepend(_T('%')); 
1982                     fmt
.Append(_T('d')); 
1987                 // no, it wasn't the width 
1988                 wxFAIL_MSG(_T("unknown format specificator")); 
1990                 // fall through and just copy it nevertheless 
1992             case _T('%'):       // a percent sign 
1996             case 0:             // the end of string 
1997                 wxFAIL_MSG(_T("missing format at the end of string")); 
1999                 // just put the '%' which was the last char in format 
2008 // this function parses a string in (strict) RFC 822 format: see the section 5 
2009 // of the RFC for the detailed description, but briefly it's something of the 
2010 // form "Sat, 18 Dec 1999 00:48:30 +0100" 
2012 // this function is "strict" by design - it must reject anything except true 
2013 // RFC822 time specs. 
2015 // TODO a great candidate for using reg exps 
2016 const wxChar 
*wxDateTime::ParseRfc822Date(const wxChar
* date
) 
2018     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
2020     const wxChar 
*p 
= date
; 
2021     const wxChar 
*comma 
= wxStrchr(p
, _T(',')); 
2024         // the part before comma is the weekday 
2026         // skip it for now - we don't use but might check that it really 
2027         // corresponds to the specfied date 
2030         if ( *p 
!= _T(' ') ) 
2032             wxLogDebug(_T("no space after weekday in RFC822 time spec")); 
2034             return (wxChar 
*)NULL
; 
2040     // the following 1 or 2 digits are the day number 
2041     if ( !wxIsdigit(*p
) ) 
2043         wxLogDebug(_T("day number expected in RFC822 time spec, none found")); 
2045         return (wxChar 
*)NULL
; 
2048     wxDateTime_t day 
= *p
++ - _T('0'); 
2049     if ( wxIsdigit(*p
) ) 
2052         day 
+= *p
++ - _T('0'); 
2055     if ( *p
++ != _T(' ') ) 
2057         return (wxChar 
*)NULL
; 
2060     // the following 3 letters specify the month 
2061     wxString 
monName(p
, 3); 
2063     if ( monName 
== _T("Jan") ) 
2065     else if ( monName 
== _T("Feb") ) 
2067     else if ( monName 
== _T("Mar") ) 
2069     else if ( monName 
== _T("Apr") ) 
2071     else if ( monName 
== _T("May") ) 
2073     else if ( monName 
== _T("Jun") ) 
2075     else if ( monName 
== _T("Jul") ) 
2077     else if ( monName 
== _T("Aug") ) 
2079     else if ( monName 
== _T("Sep") ) 
2081     else if ( monName 
== _T("Oct") ) 
2083     else if ( monName 
== _T("Nov") ) 
2085     else if ( monName 
== _T("Dec") ) 
2089         wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName
.c_str()); 
2091         return (wxChar 
*)NULL
; 
2096     if ( *p
++ != _T(' ') ) 
2098         return (wxChar 
*)NULL
; 
2102     if ( !wxIsdigit(*p
) ) 
2105         return (wxChar 
*)NULL
; 
2108     int year 
= *p
++ - _T('0'); 
2110     if ( !wxIsdigit(*p
) ) 
2112         // should have at least 2 digits in the year 
2113         return (wxChar 
*)NULL
; 
2117     year 
+= *p
++ - _T('0'); 
2119     // is it a 2 digit year (as per original RFC 822) or a 4 digit one? 
2120     if ( wxIsdigit(*p
) ) 
2123         year 
+= *p
++ - _T('0'); 
2125         if ( !wxIsdigit(*p
) ) 
2127             // no 3 digit years please 
2128             return (wxChar 
*)NULL
; 
2132         year 
+= *p
++ - _T('0'); 
2135     if ( *p
++ != _T(' ') ) 
2137         return (wxChar 
*)NULL
; 
2140     // time is in the format hh:mm:ss and seconds are optional 
2141     if ( !wxIsdigit(*p
) ) 
2143         return (wxChar 
*)NULL
; 
2146     wxDateTime_t hour 
= *p
++ - _T('0'); 
2148     if ( !wxIsdigit(*p
) ) 
2150         return (wxChar 
*)NULL
; 
2154     hour 
+= *p
++ - _T('0'); 
2156     if ( *p
++ != _T(':') ) 
2158         return (wxChar 
*)NULL
; 
2161     if ( !wxIsdigit(*p
) ) 
2163         return (wxChar 
*)NULL
; 
2166     wxDateTime_t min 
= *p
++ - _T('0'); 
2168     if ( !wxIsdigit(*p
) ) 
2170         return (wxChar 
*)NULL
; 
2174     min 
+= *p
++ - _T('0'); 
2176     wxDateTime_t sec 
= 0; 
2177     if ( *p
++ == _T(':') ) 
2179         if ( !wxIsdigit(*p
) ) 
2181             return (wxChar 
*)NULL
; 
2184         sec 
= *p
++ - _T('0'); 
2186         if ( !wxIsdigit(*p
) ) 
2188             return (wxChar 
*)NULL
; 
2192         sec 
+= *p
++ - _T('0'); 
2195     if ( *p
++ != _T(' ') ) 
2197         return (wxChar 
*)NULL
; 
2200     // and now the interesting part: the timezone 
2202     if ( *p 
== _T('-') || *p 
== _T('+') ) 
2204         // the explicit offset given: it has the form of hhmm 
2205         bool plus 
= *p
++ == _T('+'); 
2207         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
2209             return (wxChar 
*)NULL
; 
2213         offset 
= 60*(10*(*p 
- _T('0')) + (*(p 
+ 1) - _T('0'))); 
2217         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
2219             return (wxChar 
*)NULL
; 
2223         offset 
+= 10*(*p 
- _T('0')) + (*(p 
+ 1) - _T('0')); 
2234         // the symbolic timezone given: may be either military timezone or one 
2235         // of standard abbreviations 
2238             // military: Z = UTC, J unused, A = -1, ..., Y = +12 
2239             static const int offsets
[26] = 
2241                 //A  B   C   D   E   F   G   H   I    J    K    L    M 
2242                 -1, -2, -3, -4, -5, -6, -7, -8, -9,   0, -10, -11, -12, 
2243                 //N  O   P   R   Q   S   T   U   V    W    Z    Y    Z 
2244                 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0 
2247             if ( *p 
< _T('A') || *p 
> _T('Z') || *p 
== _T('J') ) 
2249                 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p
); 
2251                 return (wxChar 
*)NULL
; 
2254             offset 
= offsets
[*p
++ - _T('A')]; 
2260             if ( tz 
== _T("UT") || tz 
== _T("UTC") || tz 
== _T("GMT") ) 
2262             else if ( tz 
== _T("AST") ) 
2263                 offset 
= AST 
- GMT0
; 
2264             else if ( tz 
== _T("ADT") ) 
2265                 offset 
= ADT 
- GMT0
; 
2266             else if ( tz 
== _T("EST") ) 
2267                 offset 
= EST 
- GMT0
; 
2268             else if ( tz 
== _T("EDT") ) 
2269                 offset 
= EDT 
- GMT0
; 
2270             else if ( tz 
== _T("CST") ) 
2271                 offset 
= CST 
- GMT0
; 
2272             else if ( tz 
== _T("CDT") ) 
2273                 offset 
= CDT 
- GMT0
; 
2274             else if ( tz 
== _T("MST") ) 
2275                 offset 
= MST 
- GMT0
; 
2276             else if ( tz 
== _T("MDT") ) 
2277                 offset 
= MDT 
- GMT0
; 
2278             else if ( tz 
== _T("PST") ) 
2279                 offset 
= PST 
- GMT0
; 
2280             else if ( tz 
== _T("PDT") ) 
2281                 offset 
= PDT 
- GMT0
; 
2284                 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p
); 
2286                 return (wxChar 
*)NULL
; 
2296     // the spec was correct 
2297     Set(day
, mon
, year
, hour
, min
, sec
); 
2298     MakeTimezone(60*(wxDateTime_t
)offset
); 
2303 const wxChar 
*wxDateTime::ParseFormat(const wxChar 
*date
, 
2304                                       const wxChar 
*format
, 
2305                                       const wxDateTime
& dateDef
) 
2307     wxCHECK_MSG( date 
&& format
, (wxChar 
*)NULL
, 
2308                  _T("NULL pointer in wxDateTime::ParseFormat()") ); 
2313     // what fields have we found? 
2314     bool haveWDay 
= FALSE
, 
2323     bool hourIsIn12hFormat 
= FALSE
, // or in 24h one? 
2324          isPM 
= FALSE
;              // AM by default 
2326     // and the value of the items we have (init them to get rid of warnings) 
2327     wxDateTime_t sec 
= 0, 
2330     WeekDay wday 
= Inv_WeekDay
; 
2331     wxDateTime_t yday 
= 0, 
2333     wxDateTime::Month mon 
= Inv_Month
; 
2336     const wxChar 
*input 
= date
; 
2337     for ( const wxChar 
*fmt 
= format
; *fmt
; fmt
++ ) 
2339         if ( *fmt 
!= _T('%') ) 
2341             if ( wxIsspace(*fmt
) ) 
2343                 // a white space in the format string matches 0 or more white 
2344                 // spaces in the input 
2345                 while ( wxIsspace(*input
) ) 
2352                 // any other character (not whitespace, not '%') must be 
2353                 // matched by itself in the input 
2354                 if ( *input
++ != *fmt 
) 
2357                     return (wxChar 
*)NULL
; 
2361             // done with this format char 
2365         // start of a format specification 
2368             case _T('a'):       // a weekday name 
2371                     int flag 
= *fmt 
== _T('a') ? Name_Abbr 
: Name_Full
; 
2372                     wday 
= GetWeekDayFromName(GetAlphaToken(input
), flag
); 
2373                     if ( wday 
== Inv_WeekDay 
) 
2376                         return (wxChar 
*)NULL
; 
2382             case _T('b'):       // a month name 
2385                     int flag 
= *fmt 
== _T('b') ? Name_Abbr 
: Name_Full
; 
2386                     mon 
= GetMonthFromName(GetAlphaToken(input
), flag
); 
2387                     if ( mon 
== Inv_Month 
) 
2390                         return (wxChar 
*)NULL
; 
2396             case _T('c'):       // locale default date and time  representation 
2400                     // this is the format which corresponds to ctime() output 
2401                     // and strptime("%c") should parse it, so try it first 
2402                     static const wxChar 
*fmtCtime 
= _T("%a %b %d %H:%M:%S %Y"); 
2404                     const wxChar 
*result 
= dt
.ParseFormat(input
, fmtCtime
); 
2407                         result 
= dt
.ParseFormat(input
, _T("%x %X")); 
2412                         result 
= dt
.ParseFormat(input
, _T("%X %x")); 
2417                         // we've tried everything and still no match 
2418                         return (wxChar 
*)NULL
; 
2423                     haveDay 
= haveMon 
= haveYear 
= 
2424                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2438             case _T('d'):       // day of a month (01-31) 
2439                 if ( !GetNumericToken(input
, &num
) || (num 
> 31) ) 
2442                     return (wxChar 
*)NULL
; 
2445                 // we can't check whether the day range is correct yet, will 
2446                 // do it later - assume ok for now 
2448                 mday 
= (wxDateTime_t
)num
; 
2451             case _T('H'):       // hour in 24h format (00-23) 
2452                 if ( !GetNumericToken(input
, &num
) || (num 
> 23) ) 
2455                     return (wxChar 
*)NULL
; 
2459                 hour 
= (wxDateTime_t
)num
; 
2462             case _T('I'):       // hour in 12h format (01-12) 
2463                 if ( !GetNumericToken(input
, &num
) || !num 
|| (num 
> 12) ) 
2466                     return (wxChar 
*)NULL
; 
2470                 hourIsIn12hFormat 
= TRUE
; 
2471                 hour 
= num 
% 12;        // 12 should be 0 
2474             case _T('j'):       // day of the year 
2475                 if ( !GetNumericToken(input
, &num
) || !num 
|| (num 
> 366) ) 
2478                     return (wxChar 
*)NULL
; 
2482                 yday 
= (wxDateTime_t
)num
; 
2485             case _T('m'):       // month as a number (01-12) 
2486                 if ( !GetNumericToken(input
, &num
) || !num 
|| (num 
> 12) ) 
2489                     return (wxChar 
*)NULL
; 
2493                 mon 
= (Month
)(num 
- 1); 
2496             case _T('M'):       // minute as a decimal number (00-59) 
2497                 if ( !GetNumericToken(input
, &num
) || (num 
> 59) ) 
2500                     return (wxChar 
*)NULL
; 
2504                 min 
= (wxDateTime_t
)num
; 
2507             case _T('p'):       // AM or PM string 
2509                     wxString am
, pm
, token 
= GetAlphaToken(input
); 
2511                     GetAmPmStrings(&am
, &pm
); 
2512                     if ( token
.CmpNoCase(pm
) == 0 ) 
2516                     else if ( token
.CmpNoCase(am
) != 0 ) 
2519                         return (wxChar 
*)NULL
; 
2524             case _T('r'):       // time as %I:%M:%S %p 
2527                     input 
= dt
.ParseFormat(input
, _T("%I:%M:%S %p")); 
2531                         return (wxChar 
*)NULL
; 
2534                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2543             case _T('R'):       // time as %H:%M 
2546                     input 
= dt
.ParseFormat(input
, _T("%H:%M")); 
2550                         return (wxChar 
*)NULL
; 
2553                     haveHour 
= haveMin 
= TRUE
; 
2560             case _T('S'):       // second as a decimal number (00-61) 
2561                 if ( !GetNumericToken(input
, &num
) || (num 
> 61) ) 
2564                     return (wxChar 
*)NULL
; 
2568                 sec 
= (wxDateTime_t
)num
; 
2571             case _T('T'):       // time as %H:%M:%S 
2574                     input 
= dt
.ParseFormat(input
, _T("%H:%M:%S")); 
2578                         return (wxChar 
*)NULL
; 
2581                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2590             case _T('w'):       // weekday as a number (0-6), Sunday = 0 
2591                 if ( !GetNumericToken(input
, &num
) || (wday 
> 6) ) 
2594                     return (wxChar 
*)NULL
; 
2598                 wday 
= (WeekDay
)num
; 
2601             case _T('x'):       // locale default date representation 
2602 #ifdef HAVE_STRPTIME 
2603                 // try using strptime() - it may fail even if the input is 
2604                 // correct but the date is out of range, so we will fall back 
2605                 // to our generic code anyhow (FIXME !Unicode friendly) 
2608                     const wxChar 
*result 
= strptime(input
, "%x", &tm
); 
2613                         haveDay 
= haveMon 
= haveYear 
= TRUE
; 
2615                         year 
= 1900 + tm
.tm_year
; 
2616                         mon 
= (Month
)tm
.tm_mon
; 
2622 #endif // HAVE_STRPTIME 
2624                 // TODO query the LOCALE_IDATE setting under Win32 
2628                     wxString fmtDate
, fmtDateAlt
; 
2629                     if ( IsWestEuropeanCountry(GetCountry()) || 
2630                          GetCountry() == Russia 
) 
2632                         fmtDate 
= _T("%d/%m/%y"); 
2633                         fmtDateAlt 
= _T("%m/%d/%y"); 
2637                         fmtDate 
= _T("%m/%d/%y"); 
2638                         fmtDateAlt 
= _T("%d/%m/%y"); 
2641                     const wxChar 
*result 
= dt
.ParseFormat(input
, fmtDate
); 
2645                         // ok, be nice and try another one 
2646                         result 
= dt
.ParseFormat(input
, fmtDateAlt
); 
2652                         return (wxChar 
*)NULL
; 
2657                     haveDay 
= haveMon 
= haveYear 
= TRUE
; 
2668             case _T('X'):       // locale default time representation 
2669 #ifdef HAVE_STRPTIME 
2671                     // use strptime() to do it for us (FIXME !Unicode friendly) 
2673                     input 
= strptime(input
, "%X", &tm
); 
2676                         return (wxChar 
*)NULL
; 
2679                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2685 #else // !HAVE_STRPTIME 
2686                 // TODO under Win32 we can query the LOCALE_ITIME system 
2687                 //      setting which says whether the default time format is 
2690                     // try to parse what follows as "%H:%M:%S" and, if this 
2691                     // fails, as "%I:%M:%S %p" - this should catch the most 
2695                     const wxChar 
*result 
= dt
.ParseFormat(input
, _T("%T")); 
2698                         result 
= dt
.ParseFormat(input
, _T("%r")); 
2704                         return (wxChar 
*)NULL
; 
2707                     haveHour 
= haveMin 
= haveSec 
= TRUE
; 
2716 #endif // HAVE_STRPTIME/!HAVE_STRPTIME 
2719             case _T('y'):       // year without century (00-99) 
2720                 if ( !GetNumericToken(input
, &num
) || (num 
> 99) ) 
2723                     return (wxChar 
*)NULL
; 
2730             case _T('Y'):       // year with century 
2731                 if ( !GetNumericToken(input
, &num
) ) 
2734                     return (wxChar 
*)NULL
; 
2738                 year 
= (wxDateTime_t
)num
; 
2741             case _T('Z'):       // timezone name 
2742                 wxFAIL_MSG(_T("TODO")); 
2745             case _T('%'):       // a percent sign 
2746                 if ( *input
++ != _T('%') ) 
2749                     return (wxChar 
*)NULL
; 
2753             case 0:             // the end of string 
2754                 wxFAIL_MSG(_T("unexpected format end")); 
2758             default:            // not a known format spec 
2759                 return (wxChar 
*)NULL
; 
2763     // format matched, try to construct a date from what we have now 
2765     if ( dateDef
.IsValid() ) 
2767         // take this date as default 
2768         tmDef 
= dateDef
.GetTm(); 
2770     else if ( m_time 
!= 0 ) 
2772         // if this date is valid, don't change it 
2777         // no default and this date is invalid - fall back to Today() 
2778         tmDef 
= Today().GetTm(); 
2789     // TODO we don't check here that the values are consistent, if both year 
2790     //      day and month/day were found, we just ignore the year day and we 
2791     //      also always ignore the week day 
2792     if ( haveMon 
&& haveDay 
) 
2794         if ( mday 
> GetNumOfDaysInMonth(tm
.year
, mon
) ) 
2796             wxLogDebug(_T("bad month day in wxDateTime::ParseFormat")); 
2798             return (wxChar 
*)NULL
; 
2804     else if ( haveYDay 
) 
2806         if ( yday 
> GetNumberOfDays(tm
.year
) ) 
2808             wxLogDebug(_T("bad year day in wxDateTime::ParseFormat")); 
2810             return (wxChar 
*)NULL
; 
2813         Tm tm2 
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm(); 
2820     if ( haveHour 
&& hourIsIn12hFormat 
&& isPM 
) 
2822         // translate to 24hour format 
2825     //else: either already in 24h format or no translation needed 
2848 const wxChar 
*wxDateTime::ParseDateTime(const wxChar 
*date
) 
2850     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
2852     // there is a public domain version of getdate.y, but it only works for 
2854     wxFAIL_MSG(_T("TODO")); 
2856     return (wxChar 
*)NULL
; 
2859 const wxChar 
*wxDateTime::ParseDate(const wxChar 
*date
) 
2861     // this is a simplified version of ParseDateTime() which understands only 
2862     // "today" (for wxDate compatibility) and digits only otherwise (and not 
2863     // all esoteric constructions ParseDateTime() knows about) 
2865     wxCHECK_MSG( date
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
2867     const wxChar 
*p 
= date
; 
2868     while ( wxIsspace(*p
) ) 
2871     wxString today 
= _T("today"); 
2872     size_t len 
= today
.length(); 
2873     if ( wxString(p
, len
).CmpNoCase(today
) == 0 ) 
2875         // nothing can follow this, so stop here 
2884     bool haveDay 
= FALSE
,       // the months day? 
2885          haveWDay 
= FALSE
,      // the day of week? 
2886          haveMon 
= FALSE
,       // the month? 
2887          haveYear 
= FALSE
;      // the year? 
2889     // and the value of the items we have (init them to get rid of warnings) 
2890     WeekDay wday 
= Inv_WeekDay
; 
2891     wxDateTime_t day 
= 0; 
2892     wxDateTime::Month mon 
= Inv_Month
; 
2895     // tokenize the string 
2896     wxStringTokenizer 
tok(p
, _T(",/-\t ")); 
2897     while ( tok
.HasMoreTokens() ) 
2899         wxString token 
= tok
.GetNextToken(); 
2903         if ( token
.ToULong(&val
) ) 
2905             // guess what this number is 
2909                  // only years are counted from 0 
2910                  isYear 
= (val 
== 0) || (val 
> 31); 
2913                 // may be the month or month day or the year, assume the 
2914                 // month day by default and fallback to month if the range 
2915                 // allow it or to the year if our assumption doesn't work 
2918                     // we already have the day, so may only be a month or year 
2928                 else // it may be day 
2935                         if ( val 
> GetNumOfDaysInMonth(haveYear 
? year
 
2939                             // oops, it can't be a day finally 
2955             // remember that we have this and stop the scan if it's the second 
2956             // time we find this, except for the day logic (see there) 
2966                 // no roll over - 99 means 99, not 1999 for us 
2978                 mon 
= (wxDateTime::Month
)val
; 
2982                 wxASSERT_MSG( isDay
, _T("logic error") ); 
2986                     // may be were mistaken when we found it for the first 
2987                     // time? may be it was a month or year instead? 
2989                     // this ability to "backtrack" allows us to correctly parse 
2990                     // both things like 01/13 and 13/01 - but, of course, we 
2991                     // still can't resolve the ambiguity in 01/02 (it will be 
2992                     // Feb 1 for us, not Jan 2 as americans might expect!) 
2993                     if ( (day 
<= 12) && !haveMon 
) 
2995                         // exchange day and month 
2996                         mon 
= (wxDateTime::Month
)day
; 
3000                     else if ( !haveYear 
) 
3002                         // exchange day and year 
3014         else // not a number 
3016             mon 
= GetMonthFromName(token
, Name_Full 
| Name_Abbr
); 
3017             if ( mon 
!= Inv_Month 
) 
3029                 wday 
= GetWeekDayFromName(token
, Name_Full 
| Name_Abbr
); 
3030                 if ( wday 
!= Inv_WeekDay 
) 
3043                     static const wxChar 
*ordinals
[] = 
3045                         wxTRANSLATE("first"), 
3046                         wxTRANSLATE("second"), 
3047                         wxTRANSLATE("third"), 
3048                         wxTRANSLATE("fourth"), 
3049                         wxTRANSLATE("fifth"), 
3050                         wxTRANSLATE("sixth"), 
3051                         wxTRANSLATE("seventh"), 
3052                         wxTRANSLATE("eighth"), 
3053                         wxTRANSLATE("ninth"), 
3054                         wxTRANSLATE("tenth"), 
3055                         wxTRANSLATE("eleventh"), 
3056                         wxTRANSLATE("twelfth"), 
3057                         wxTRANSLATE("thirteenth"), 
3058                         wxTRANSLATE("fourteenth"), 
3059                         wxTRANSLATE("fifteenth"), 
3060                         wxTRANSLATE("sixteenth"), 
3061                         wxTRANSLATE("seventeenth"), 
3062                         wxTRANSLATE("eighteenth"), 
3063                         wxTRANSLATE("nineteenth"), 
3064                         wxTRANSLATE("twentieth"), 
3065                         // that's enough - otherwise we'd have problems with 
3066                         // composite (or not) ordinals otherwise 
3070                     for ( n 
= 0; n 
< WXSIZEOF(ordinals
); n
++ ) 
3072                         if ( token
.CmpNoCase(ordinals
[n
]) == 0 ) 
3078                     if ( n 
== WXSIZEOF(ordinals
) ) 
3080                         // stop here - something unknown 
3087                         // don't try anything here (as in case of numeric day 
3088                         // above) - the symbolic day spec should always 
3089                         // precede the month/year 
3101     // either no more tokens or the scan was stopped by something we couldn't 
3102     // parse - in any case, see if we can construct a date from what we have 
3103     if ( !haveDay 
&& !haveWDay 
) 
3105         wxLogDebug(_T("no day, no weekday hence no date.")); 
3107         return (wxChar 
*)NULL
; 
3110     if ( haveWDay 
&& (haveMon 
|| haveYear 
|| haveDay
) && 
3111          !(haveMon 
&& haveMon 
&& haveYear
) ) 
3113         // without adjectives (which we don't support here) the week day only 
3114         // makes sense completely separately or with the full date 
3115         // specification (what would "Wed 1999" mean?) 
3116         return (wxChar 
*)NULL
; 
3121         mon 
= GetCurrentMonth(); 
3126         year 
= GetCurrentYear(); 
3131         Set(day
, mon
, year
); 
3135             // check that it is really the same 
3136             if ( GetWeekDay() != wday 
) 
3138                 // inconsistency detected 
3139                 return (wxChar 
*)NULL
; 
3147         SetToWeekDayInSameWeek(wday
); 
3150     // return the pointer to the next char 
3151     return p 
+ wxStrlen(p
) - wxStrlen(tok
.GetString()); 
3154 const wxChar 
*wxDateTime::ParseTime(const wxChar 
*time
) 
3156     wxCHECK_MSG( time
, (wxChar 
*)NULL
, _T("NULL pointer in wxDateTime::Parse") ); 
3158     // first try some extra things 
3165         { wxTRANSLATE("noon"),      12 }, 
3166         { wxTRANSLATE("midnight"),  00 }, 
3170     for ( size_t n 
= 0; n 
< WXSIZEOF(stdTimes
); n
++ ) 
3172         wxString timeString 
= wxGetTranslation(stdTimes
[n
].name
); 
3173         size_t len 
= timeString
.length(); 
3174         if ( timeString
.CmpNoCase(wxString(time
, len
)) == 0 ) 
3176             Set(stdTimes
[n
].hour
, 0, 0); 
3182     // try all time formats we may think about starting with the standard one 
3183     const wxChar 
*result 
= ParseFormat(time
, _T("%X")); 
3186         // normally, it's the same, but why not try it? 
3187         result 
= ParseFormat(time
, _T("%H:%M:%S")); 
3192         // 12hour with AM/PM? 
3193         result 
= ParseFormat(time
, _T("%I:%M:%S %p")); 
3199         result 
= ParseFormat(time
, _T("%H:%M")); 
3204         // 12hour with AM/PM but without seconds? 
3205         result 
= ParseFormat(time
, _T("%I:%M %p")); 
3211         result 
= ParseFormat(time
, _T("%H")); 
3216         // just the hour and AM/PM? 
3217         result 
= ParseFormat(time
, _T("%I %p")); 
3220     // TODO: parse timezones 
3225 // ============================================================================ 
3227 // ============================================================================ 
3229 // not all strftime(3) format specifiers make sense here because, for example, 
3230 // a time span doesn't have a year nor a timezone 
3232 // Here are the ones which are supported (all of them are supported by strftime 
3234 //  %H          hour in 24 hour format 
3235 //  %M          minute (00 - 59) 
3236 //  %S          second (00 - 59) 
3239 // Also, for MFC CTimeSpan compatibility, we support 
3240 //  %D          number of days 
3242 // And, to be better than MFC :-), we also have 
3243 //  %E          number of wEeks 
3244 //  %l          milliseconds (000 - 999) 
3245 wxString 
wxTimeSpan::Format(const wxChar 
*format
) const 
3247     wxCHECK_MSG( format
, _T(""), _T("NULL format in wxTimeSpan::Format") ); 
3250     str
.Alloc(strlen(format
)); 
3252     for ( const wxChar 
*pch 
= format
; pch
; pch
++ ) 
3264                     wxFAIL_MSG( _T("invalid format character") ); 
3268                     // will get to str << ch below 
3272                     tmp
.Printf(_T("%d"), GetDays()); 
3276                     tmp
.Printf(_T("%d"), GetWeeks()); 
3280                     tmp
.Printf(_T("%02d"), GetHours()); 
3284                     tmp
.Printf(_T("%03ld"), GetMilliseconds().ToLong()); 
3288                     tmp
.Printf(_T("%02d"), GetMinutes()); 
3292                     tmp
.Printf(_T("%02ld"), GetSeconds().ToLong()); 
3300                 // skip str += ch below