1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/common/datetimefmt.cpp 
   3 // Purpose:     wxDateTime formatting & parsing code 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 //              parts of code taken from sndcal library by Scott E. Lee: 
  11 //               Copyright 1993-1995, Scott E. Lee, all rights reserved. 
  12 //               Permission granted to use, copy, modify, distribute and sell 
  13 //               so long as the above copyright and this permission statement 
  14 //               are retained in all copies. 
  16 // Licence:     wxWindows licence 
  17 /////////////////////////////////////////////////////////////////////////////// 
  19 // ============================================================================ 
  21 // ============================================================================ 
  23 // ---------------------------------------------------------------------------- 
  25 // ---------------------------------------------------------------------------- 
  27 // For compilers that support precompilation, includes "wx.h". 
  28 #include "wx/wxprec.h" 
  34 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME 
  38         #include "wx/msw/wrapwin.h" 
  40     #include "wx/string.h" 
  43     #include "wx/stopwatch.h"           // for wxGetLocalTimeMillis() 
  44     #include "wx/module.h" 
  48 #include "wx/thread.h" 
  49 #include "wx/tokenzr.h" 
  60 #include "wx/datetime.h" 
  62 // ============================================================================ 
  63 // implementation of wxDateTime 
  64 // ============================================================================ 
  66 // ---------------------------------------------------------------------------- 
  67 // helpers shared between datetime.cpp and datetimefmt.cpp 
  68 // ---------------------------------------------------------------------------- 
  70 extern void InitTm(struct tm
& tm
); 
  72 extern int GetTimeZone(); 
  74 extern wxString 
CallStrftime(const wxString
& format
, const tm
* tm
); 
  76 // ---------------------------------------------------------------------------- 
  77 // constants (see also datetime.cpp) 
  78 // ---------------------------------------------------------------------------- 
  80 static const int DAYS_PER_WEEK 
= 7; 
  82 static const int HOURS_PER_DAY 
= 24; 
  84 static const int SEC_PER_MIN 
= 60; 
  86 static const int MIN_PER_HOUR 
= 60; 
  88 // ---------------------------------------------------------------------------- 
  90 // ---------------------------------------------------------------------------- 
  97 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL) 
  98     // configure detected that we had strptime() but not its declaration, 
  99     // provide it ourselves 
 100     extern "C" char *strptime(const char *, const char *, struct tm 
*); 
 103 // strptime() wrapper: call strptime() for the string starting at the given 
 104 // iterator and fill output tm struct with the results and modify input to 
 105 // point to the end of the string consumed by strptime() if successful, 
 106 // otherwise return false and don't modify anything 
 108 CallStrptime(const wxString
& str
, 
 109              wxString::const_iterator
& p
, 
 113     // convert from iterator to char pointer: this is simple as wxCStrData 
 114     // already supports this 
 115     const char * const start 
= str
.c_str() + (p 
- str
.begin()); 
 117     const char * const end 
= strptime(start
, fmt
, tm
); 
 121     // convert back from char pointer to iterator: unfortunately we have no way 
 122     // to do it efficiently currently so create a temporary string just to 
 123     // compute the number of characters between start and end 
 124     p 
+= wxString(start
, end 
- start
).length(); 
 129 #endif // HAVE_STRPTIME 
 133     DateLang_English 
= 1, 
 137 // return the month if the string is a month name or Inv_Month otherwise 
 139 // flags can contain wxDateTime::Name_Abbr/Name_Full or both of them and lang 
 140 // can be either DateLang_Local (default) to interpret string as a localized 
 141 // month name or DateLang_English to parse it as a standard English name or 
 142 // their combination to interpret it in any way 
 144 GetMonthFromName(const wxString
& name
, int flags
, int lang
) 
 146     wxDateTime::Month mon
; 
 147     for ( mon 
= wxDateTime::Jan
; mon 
< wxDateTime::Inv_Month
; wxNextMonth(mon
) ) 
 149         // case-insensitive comparison either one of or with both abbreviated 
 151         if ( flags 
& wxDateTime::Name_Full 
) 
 153             if ( lang 
& DateLang_English 
) 
 155                 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
, 
 156                         wxDateTime::Name_Full
)) == 0 ) 
 160             if ( lang 
& DateLang_Local 
) 
 162                 if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
, 
 163                         wxDateTime::Name_Full
)) == 0 ) 
 168         if ( flags 
& wxDateTime::Name_Abbr 
) 
 170             if ( lang 
& DateLang_English 
) 
 172                 if ( name
.CmpNoCase(wxDateTime::GetEnglishMonthName(mon
, 
 173                         wxDateTime::Name_Abbr
)) == 0 ) 
 177             if ( lang 
& DateLang_Local 
) 
 179                 if ( name
.CmpNoCase(wxDateTime::GetMonthName(mon
, 
 180                         wxDateTime::Name_Abbr
)) == 0 ) 
 189 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise 
 191 // flags and lang parameters have the same meaning as for GetMonthFromName() 
 194 GetWeekDayFromName(const wxString
& name
, int flags
, int lang
) 
 196     wxDateTime::WeekDay wd
; 
 197     for ( wd 
= wxDateTime::Sun
; wd 
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) ) 
 199         if ( flags 
& wxDateTime::Name_Full 
) 
 201             if ( lang 
& DateLang_English 
) 
 203                 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
, 
 204                         wxDateTime::Name_Full
)) == 0 ) 
 208             if ( lang 
& DateLang_Local 
) 
 210                 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
, 
 211                         wxDateTime::Name_Full
)) == 0 ) 
 216         if ( flags 
& wxDateTime::Name_Abbr 
) 
 218             if ( lang 
& DateLang_English 
) 
 220                 if ( name
.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd
, 
 221                         wxDateTime::Name_Abbr
)) == 0 ) 
 225             if ( lang 
& DateLang_Local 
) 
 227                 if ( name
.CmpNoCase(wxDateTime::GetWeekDayName(wd
, 
 228                         wxDateTime::Name_Abbr
)) == 0 ) 
 237 // scans all digits (but no more than len) and returns the resulting number 
 238 bool GetNumericToken(size_t len
, 
 239                      wxString::const_iterator
& p
, 
 240                      unsigned long *number
) 
 244     while ( wxIsdigit(*p
) ) 
 248         if ( len 
&& ++n 
> len 
) 
 252     return !s
.empty() && s
.ToULong(number
); 
 255 // scans all alphabetic characters and returns the resulting string 
 256 wxString 
GetAlphaToken(wxString::const_iterator
& p
) 
 259     while ( wxIsalpha(*p
) ) 
 267 // parses string starting at given iterator using the specified format and, 
 268 // optionally, a fall back format (and optionally another one... but it stops 
 271 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise 
 272 // advance p to the end of the match and returns wxDateTime containing the 
 273 // results of the parsing 
 275 ParseFormatAt(wxString::const_iterator
& p
, 
 276               const wxString::const_iterator
& end
, 
 278               // FIXME-VC6: using wxString() instead of wxEmptyString in the 
 279               //            line below results in error C2062: type 'class 
 280               //            wxString (__cdecl *)(void)' unexpected 
 281               const wxString
& fmtAlt 
= wxEmptyString
, 
 282               const wxString
& fmtAlt2 
= wxString()) 
 284     const wxString 
str(p
, end
); 
 285     wxString::const_iterator endParse
; 
 287     if ( dt
.ParseFormat(str
, fmt
, &endParse
) || 
 288             (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, &endParse
)) || 
 289                 (!fmtAlt2
.empty() && dt
.ParseFormat(str
, fmtAlt2
, &endParse
)) ) 
 291         p 
+= endParse 
- str
.begin(); 
 293     //else: all formats failed 
 298 } // anonymous namespace 
 300 // ---------------------------------------------------------------------------- 
 301 // wxDateTime to/from text representations 
 302 // ---------------------------------------------------------------------------- 
 304 wxString 
wxDateTime::Format(const wxString
& format
, const TimeZone
& tz
) const 
 306     wxCHECK_MSG( !format
.empty(), wxEmptyString
, 
 307                  _T("NULL format in wxDateTime::Format") ); 
 309     // we have to use our own implementation if the date is out of range of 
 310     // strftime() or if we use non standard specificators 
 312     time_t time 
= GetTicks(); 
 314     if ( (time 
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) ) 
 319         if ( tz
.GetOffset() == -GetTimeZone() ) 
 321             // we are working with local time 
 322             tm 
= wxLocaltime_r(&time
, &tmstruct
); 
 324             // should never happen 
 325             wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") ); 
 329             time 
+= (int)tz
.GetOffset(); 
 331 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning 
 332             int time2 
= (int) time
; 
 338                 tm 
= wxGmtime_r(&time
, &tmstruct
); 
 340                 // should never happen 
 341                 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") ); 
 345                 tm 
= (struct tm 
*)NULL
; 
 351             return CallStrftime(format
, tm
); 
 354     //else: use generic code below 
 355 #endif // HAVE_STRFTIME 
 357     // we only parse ANSI C format specifications here, no POSIX 2 
 358     // complications, no GNU extensions but we do add support for a "%l" format 
 359     // specifier allowing to get the number of milliseconds 
 362     // used for calls to strftime() when we only deal with time 
 363     struct tm tmTimeOnly
; 
 364     tmTimeOnly
.tm_hour 
= tm
.hour
; 
 365     tmTimeOnly
.tm_min 
= tm
.min
; 
 366     tmTimeOnly
.tm_sec 
= tm
.sec
; 
 367     tmTimeOnly
.tm_wday 
= 0; 
 368     tmTimeOnly
.tm_yday 
= 0; 
 369     tmTimeOnly
.tm_mday 
= 1;         // any date will do 
 370     tmTimeOnly
.tm_mon 
= 0; 
 371     tmTimeOnly
.tm_year 
= 76; 
 372     tmTimeOnly
.tm_isdst 
= 0;        // no DST, we adjust for tz ourselves 
 374     wxString tmp
, res
, fmt
; 
 375     for ( wxString::const_iterator p 
= format
.begin(); p 
!= format
.end(); ++p 
) 
 385         // set the default format 
 386         switch ( (*++p
).GetValue() ) 
 388             case _T('Y'):               // year has 4 digits 
 392             case _T('j'):               // day of year has 3 digits 
 393             case _T('l'):               // milliseconds have 3 digits 
 397             case _T('w'):               // week day as number has only one 
 402                 // it's either another valid format specifier in which case 
 403                 // the format is "%02d" (for all the rest) or we have the 
 404                 // field width preceding the format in which case it will 
 405                 // override the default format anyhow 
 414             // start of the format specification 
 415             switch ( (*p
).GetValue() ) 
 417                 case _T('a'):       // a weekday name 
 419                     // second parameter should be true for abbreviated names 
 420                     res 
+= GetWeekDayName(tm
.GetWeekDay(), 
 421                                           *p 
== _T('a') ? Name_Abbr 
: Name_Full
); 
 424                 case _T('b'):       // a month name 
 426                     res 
+= GetMonthName(tm
.mon
, 
 427                                         *p 
== _T('b') ? Name_Abbr 
: Name_Full
); 
 430                 case _T('c'):       // locale default date and time  representation 
 431                 case _T('x'):       // locale default date representation 
 434                     // the problem: there is no way to know what do these format 
 435                     // specifications correspond to for the current locale. 
 437                     // the solution: use a hack and still use strftime(): first 
 438                     // find the YEAR which is a year in the strftime() range (1970 
 439                     // - 2038) whose Jan 1 falls on the same week day as the Jan 1 
 440                     // of the real year. Then make a copy of the format and 
 441                     // replace all occurrences of YEAR in it with some unique 
 442                     // string not appearing anywhere else in it, then use 
 443                     // strftime() to format the date in year YEAR and then replace 
 444                     // YEAR back by the real year and the unique replacement 
 445                     // string back with YEAR. Notice that "all occurrences of YEAR" 
 446                     // means all occurrences of 4 digit as well as 2 digit form! 
 448                     // the bugs: we assume that neither of %c nor %x contains any 
 449                     // fields which may change between the YEAR and real year. For 
 450                     // example, the week number (%U, %W) and the day number (%j) 
 451                     // will change if one of these years is leap and the other one 
 454                         // find the YEAR: normally, for any year X, Jan 1 of the 
 455                         // year X + 28 is the same weekday as Jan 1 of X (because 
 456                         // the weekday advances by 1 for each normal X and by 2 
 457                         // for each leap X, hence by 5 every 4 years or by 35 
 458                         // which is 0 mod 7 every 28 years) but this rule breaks 
 459                         // down if there are years between X and Y which are 
 460                         // divisible by 4 but not leap (i.e. divisible by 100 but 
 461                         // not 400), hence the correction. 
 463                         int yearReal 
= GetYear(tz
); 
 464                         int mod28 
= yearReal 
% 28; 
 466                         // be careful to not go too far - we risk to leave the 
 471                             year 
= 1988 + mod28
;      // 1988 == 0 (mod 28) 
 475                             year 
= 1970 + mod28 
- 10; // 1970 == 10 (mod 28) 
 478                         int nCentury 
= year 
/ 100, 
 479                             nCenturyReal 
= yearReal 
/ 100; 
 481                         // need to adjust for the years divisble by 400 which are 
 482                         // not leap but are counted like leap ones if we just take 
 483                         // the number of centuries in between for nLostWeekDays 
 484                         int nLostWeekDays 
= (nCentury 
- nCenturyReal
) - 
 485                                             (nCentury 
/ 4 - nCenturyReal 
/ 4); 
 487                         // we have to gain back the "lost" weekdays: note that the 
 488                         // effect of this loop is to not do anything to 
 489                         // nLostWeekDays (which we won't use any more), but to 
 490                         // (indirectly) set the year correctly 
 491                         while ( (nLostWeekDays 
% 7) != 0 ) 
 493                             nLostWeekDays 
+= year
++ % 4 ? 1 : 2; 
 496                         // finally move the year below 2000 so that the 2-digit 
 497                         // year number can never match the month or day of the 
 498                         // month when we do the replacements below 
 502                         wxASSERT_MSG( year 
>= 1970 && year 
< 2000, 
 503                                       _T("logic error in wxDateTime::Format") ); 
 506                         // use strftime() to format the same date but in supported 
 509                         // NB: we assume that strftime() doesn't check for the 
 510                         //     date validity and will happily format the date 
 511                         //     corresponding to Feb 29 of a non leap year (which 
 512                         //     may happen if yearReal was leap and year is not) 
 513                         struct tm tmAdjusted
; 
 515                         tmAdjusted
.tm_hour 
= tm
.hour
; 
 516                         tmAdjusted
.tm_min 
= tm
.min
; 
 517                         tmAdjusted
.tm_sec 
= tm
.sec
; 
 518                         tmAdjusted
.tm_wday 
= tm
.GetWeekDay(); 
 519                         tmAdjusted
.tm_yday 
= GetDayOfYear(); 
 520                         tmAdjusted
.tm_mday 
= tm
.mday
; 
 521                         tmAdjusted
.tm_mon 
= tm
.mon
; 
 522                         tmAdjusted
.tm_year 
= year 
- 1900; 
 523                         tmAdjusted
.tm_isdst 
= 0; // no DST, already adjusted 
 524                         wxString str 
= CallStrftime(*p 
== _T('c') ? _T("%c") 
 528                         // now replace the replacement year with the real year: 
 529                         // notice that we have to replace the 4 digit year with 
 530                         // a unique string not appearing in strftime() output 
 531                         // first to prevent the 2 digit year from matching any 
 532                         // substring of the 4 digit year (but any day, month, 
 533                         // hours or minutes components should be safe because 
 534                         // they are never in 70-99 range) 
 535                         wxString 
replacement("|"); 
 536                         while ( str
.find(replacement
) != wxString::npos 
) 
 539                         str
.Replace(wxString::Format("%d", year
), 
 541                         str
.Replace(wxString::Format("%d", year 
% 100), 
 542                                     wxString::Format("%d", yearReal 
% 100)); 
 543                         str
.Replace(replacement
, 
 544                                     wxString::Format("%d", yearReal
)); 
 548 #else // !HAVE_STRFTIME 
 549                     // Use "%m/%d/%y %H:%M:%S" format instead 
 550                     res 
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"), 
 551                             tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
); 
 552 #endif // HAVE_STRFTIME/!HAVE_STRFTIME 
 555                 case _T('d'):       // day of a month (01-31) 
 556                     res 
+= wxString::Format(fmt
, tm
.mday
); 
 559                 case _T('H'):       // hour in 24h format (00-23) 
 560                     res 
+= wxString::Format(fmt
, tm
.hour
); 
 563                 case _T('I'):       // hour in 12h format (01-12) 
 565                         // 24h -> 12h, 0h -> 12h too 
 566                         int hour12 
= tm
.hour 
> 12 ? tm
.hour 
- 12 
 567                                                   : tm
.hour 
? tm
.hour 
: 12; 
 568                         res 
+= wxString::Format(fmt
, hour12
); 
 572                 case _T('j'):       // day of the year 
 573                     res 
+= wxString::Format(fmt
, GetDayOfYear(tz
)); 
 576                 case _T('l'):       // milliseconds (NOT STANDARD) 
 577                     res 
+= wxString::Format(fmt
, GetMillisecond(tz
)); 
 580                 case _T('m'):       // month as a number (01-12) 
 581                     res 
+= wxString::Format(fmt
, tm
.mon 
+ 1); 
 584                 case _T('M'):       // minute as a decimal number (00-59) 
 585                     res 
+= wxString::Format(fmt
, tm
.min
); 
 588                 case _T('p'):       // AM or PM string 
 590                     res 
+= CallStrftime(_T("%p"), &tmTimeOnly
); 
 591 #else // !HAVE_STRFTIME 
 592                     res 
+= (tmTimeOnly
.tm_hour 
> 12) ? wxT("pm") : wxT("am"); 
 593 #endif // HAVE_STRFTIME/!HAVE_STRFTIME 
 596                 case _T('S'):       // second as a decimal number (00-61) 
 597                     res 
+= wxString::Format(fmt
, tm
.sec
); 
 600                 case _T('U'):       // week number in the year (Sunday 1st week day) 
 601                     res 
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
)); 
 604                 case _T('W'):       // week number in the year (Monday 1st week day) 
 605                     res 
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
)); 
 608                 case _T('w'):       // weekday as a number (0-6), Sunday = 0 
 609                     res 
+= wxString::Format(fmt
, tm
.GetWeekDay()); 
 612                 // case _T('x'): -- handled with "%c" 
 614                 case _T('X'):       // locale default time representation 
 615                     // just use strftime() to format the time for us 
 617                     res 
+= CallStrftime(_T("%X"), &tmTimeOnly
); 
 618 #else // !HAVE_STRFTIME 
 619                     res 
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
); 
 620 #endif // HAVE_STRFTIME/!HAVE_STRFTIME 
 623                 case _T('y'):       // year without century (00-99) 
 624                     res 
+= wxString::Format(fmt
, tm
.year 
% 100); 
 627                 case _T('Y'):       // year with century 
 628                     res 
+= wxString::Format(fmt
, tm
.year
); 
 631                 case _T('Z'):       // timezone name 
 633                     res 
+= CallStrftime(_T("%Z"), &tmTimeOnly
); 
 638                     // is it the format width? 
 640                     while ( *p 
== _T('-') || *p 
== _T('+') || 
 641                             *p 
== _T(' ') || wxIsdigit(*p
) ) 
 648                         // we've only got the flags and width so far in fmt 
 649                         fmt
.Prepend(_T('%')); 
 657                     // no, it wasn't the width 
 658                     wxFAIL_MSG(_T("unknown format specificator")); 
 660                     // fall through and just copy it nevertheless 
 662                 case _T('%'):       // a percent sign 
 666                 case 0:             // the end of string 
 667                     wxFAIL_MSG(_T("missing format at the end of string")); 
 669                     // just put the '%' which was the last char in format 
 679 // this function parses a string in (strict) RFC 822 format: see the section 5 
 680 // of the RFC for the detailed description, but briefly it's something of the 
 681 // form "Sat, 18 Dec 1999 00:48:30 +0100" 
 683 // this function is "strict" by design - it must reject anything except true 
 684 // RFC822 time specs. 
 686 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator 
*end
) 
 688     wxString::const_iterator p 
= date
.begin(); 
 691     static const int WDAY_LEN 
= 3; 
 692     const wxString::const_iterator endWday 
= p 
+ WDAY_LEN
; 
 693     const wxString 
wday(p
, endWday
); 
 694     if ( GetWeekDayFromName(wday
, Name_Abbr
, DateLang_English
) == Inv_WeekDay 
) 
 696     //else: ignore week day for now, we could also check that it really 
 697     //      corresponds to the specified date 
 701     // 2. separating comma 
 702     if ( *p
++ != ',' || *p
++ != ' ' ) 
 706     if ( !wxIsdigit(*p
) ) 
 709     wxDateTime_t day 
= (wxDateTime_t
)(*p
++ - '0'); 
 713         day 
= (wxDateTime_t
)(day 
+ (*p
++ - '0')); 
 720     static const int MONTH_LEN 
= 3; 
 721     const wxString::const_iterator endMonth 
= p 
+ MONTH_LEN
; 
 722     const wxString 
monName(p
, endMonth
); 
 723     Month mon 
= GetMonthFromName(monName
, Name_Abbr
, DateLang_English
); 
 724     if ( mon 
== Inv_Month 
) 
 733     if ( !wxIsdigit(*p
) ) 
 736     int year 
= *p
++ - '0'; 
 737     if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year 
 743     // is it a 2 digit year (as per original RFC 822) or a 4 digit one? 
 749         if ( !wxIsdigit(*p
) ) 
 751             // no 3 digit years please 
 762     // 6. time in hh:mm:ss format with seconds being optional 
 763     if ( !wxIsdigit(*p
) ) 
 766     wxDateTime_t hour 
= (wxDateTime_t
)(*p
++ - '0'); 
 768     if ( !wxIsdigit(*p
) ) 
 772     hour 
= (wxDateTime_t
)(hour 
+ (*p
++ - '0')); 
 777     if ( !wxIsdigit(*p
) ) 
 780     wxDateTime_t min 
= (wxDateTime_t
)(*p
++ - '0'); 
 782     if ( !wxIsdigit(*p
) ) 
 786     min 
+= (wxDateTime_t
)(*p
++ - '0'); 
 788     wxDateTime_t sec 
= 0; 
 792         if ( !wxIsdigit(*p
) ) 
 795         sec 
= (wxDateTime_t
)(*p
++ - '0'); 
 797         if ( !wxIsdigit(*p
) ) 
 801         sec 
+= (wxDateTime_t
)(*p
++ - '0'); 
 807     // 7. now the interesting part: the timezone 
 808     int offset 
wxDUMMY_INITIALIZE(0); 
 809     if ( *p 
== '-' || *p 
== '+' ) 
 811         // the explicit offset given: it has the form of hhmm 
 812         bool plus 
= *p
++ == '+'; 
 814         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
 819         offset 
= MIN_PER_HOUR
*(10*(*p 
- '0') + (*(p 
+ 1) - '0')); 
 823         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
 827         offset 
+= 10*(*p 
- '0') + (*(p 
+ 1) - '0'); 
 836         // the symbolic timezone given: may be either military timezone or one 
 837         // of standard abbreviations 
 840             // military: Z = UTC, J unused, A = -1, ..., Y = +12 
 841             static const int offsets
[26] = 
 843                 //A  B   C   D   E   F   G   H   I    J    K    L    M 
 844                 -1, -2, -3, -4, -5, -6, -7, -8, -9,   0, -10, -11, -12, 
 845                 //N  O   P   R   Q   S   T   U   V    W    Z    Y    Z 
 846                 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0 
 849             if ( *p 
< _T('A') || *p 
> _T('Z') || *p 
== _T('J') ) 
 852             offset 
= offsets
[*p
++ - 'A']; 
 857             const wxString 
tz(p
, date
.end()); 
 858             if ( tz 
== _T("UT") || tz 
== _T("UTC") || tz 
== _T("GMT") ) 
 860             else if ( tz 
== _T("AST") ) 
 862             else if ( tz 
== _T("ADT") ) 
 864             else if ( tz 
== _T("EST") ) 
 866             else if ( tz 
== _T("EDT") ) 
 868             else if ( tz 
== _T("CST") ) 
 870             else if ( tz 
== _T("CDT") ) 
 872             else if ( tz 
== _T("MST") ) 
 874             else if ( tz 
== _T("MDT") ) 
 876             else if ( tz 
== _T("PST") ) 
 878             else if ( tz 
== _T("PDT") ) 
 887         offset 
*= MIN_PER_HOUR
; 
 891     // the spec was correct, construct the date from the values we found 
 892     Set(day
, mon
, year
, hour
, min
, sec
); 
 893     MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
)); 
 903 // returns the string containing strftime() format used for short dates in the 
 904 // current locale or an empty string 
 905 static wxString 
GetLocaleDateFormat() 
 909     // there is no setlocale() under Windows CE, so just always query the 
 912     if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 ) 
 915         // The locale was programatically set to non-C. We assume that this was 
 916         // done using wxLocale, in which case thread's current locale is also 
 917         // set to correct LCID value and we can use GetLocaleInfo to determine 
 918         // the correct formatting string: 
 920         LCID lcid 
= LOCALE_USER_DEFAULT
; 
 922         LCID lcid 
= GetThreadLocale(); 
 924         // according to MSDN 80 chars is max allowed for short date format 
 926         if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) ) 
 928             wxChar chLast 
= _T('\0'); 
 929             size_t lastCount 
= 0; 
 930             for ( const wxChar 
*p 
= fmt
; /* NUL handled inside */; p
++ ) 
 940                     // these characters come in groups, start counting them 
 950                         // first deal with any special characters we have had 
 960                                             // these two are the same as we 
 961                                             // don't distinguish between 1 and 
 975                                             wxFAIL_MSG( _T("too many 'd's") ); 
 984                                             // as for 'd' and 'dd' above 
 997                                             wxFAIL_MSG( _T("too many 'M's") ); 
1002                                     switch ( lastCount 
) 
1014                                             wxFAIL_MSG( _T("wrong number of 'y's") ); 
1019                                     // strftime() doesn't have era string, 
1020                                     // ignore this format 
1021                                     wxASSERT_MSG( lastCount 
<= 2, 
1022                                                   _T("too many 'g's") ); 
1026                                     wxFAIL_MSG( _T("unreachable") ); 
1033                         // not a special character so must be just a separator, 
1035                         if ( *p 
!= _T('\0') ) 
1037                             if ( *p 
== _T('%') ) 
1039                                 // this one needs to be escaped 
1047                 if ( *p 
== _T('\0') ) 
1051         //else: GetLocaleInfo() failed, leave fmtDate value unchanged and 
1052         //      try our luck with the default formats 
1054     //else: default C locale, default formats should work 
1059 #endif // __WINDOWS__ 
1062 wxDateTime::ParseFormat(const wxString
& date
, 
1063                         const wxString
& format
, 
1064                         const wxDateTime
& dateDef
, 
1065                         wxString::const_iterator 
*end
) 
1067     wxCHECK_MSG( !format
.empty(), false, "format can't be empty" ); 
1068     wxCHECK_MSG( end
, false, "end iterator pointer must be specified" ); 
1073     // what fields have we found? 
1074     bool haveWDay 
= false, 
1084     bool hourIsIn12hFormat 
= false, // or in 24h one? 
1085          isPM 
= false;              // AM by default 
1087     // and the value of the items we have (init them to get rid of warnings) 
1088     wxDateTime_t msec 
= 0, 
1092     WeekDay wday 
= Inv_WeekDay
; 
1093     wxDateTime_t yday 
= 0, 
1095     wxDateTime::Month mon 
= Inv_Month
; 
1098     wxString::const_iterator input 
= date
.begin(); 
1099     for ( wxString::const_iterator fmt 
= format
.begin(); fmt 
!= format
.end(); ++fmt 
) 
1101         if ( *fmt 
!= _T('%') ) 
1103             if ( wxIsspace(*fmt
) ) 
1105                 // a white space in the format string matches 0 or more white 
1106                 // spaces in the input 
1107                 while ( wxIsspace(*input
) ) 
1114                 // any other character (not whitespace, not '%') must be 
1115                 // matched by itself in the input 
1116                 if ( *input
++ != *fmt 
) 
1123             // done with this format char 
1127         // start of a format specification 
1129         // parse the optional width 
1131         while ( wxIsdigit(*++fmt
) ) 
1134             width 
+= *fmt 
- '0'; 
1137         // the default widths for the various fields 
1140             switch ( (*fmt
).GetValue() ) 
1142                 case _T('Y'):               // year has 4 digits 
1146                 case _T('j'):               // day of year has 3 digits 
1147                 case _T('l'):               // milliseconds have 3 digits 
1151                 case _T('w'):               // week day as number has only one 
1156                     // default for all other fields 
1161         // then the format itself 
1162         switch ( (*fmt
).GetValue() ) 
1164             case _T('a'):       // a weekday name 
1167                     wday 
= GetWeekDayFromName
 
1169                             GetAlphaToken(input
), 
1170                             *fmt 
== 'a' ? Name_Abbr 
: Name_Full
, 
1173                     if ( wday 
== Inv_WeekDay 
) 
1182             case _T('b'):       // a month name 
1185                     mon 
= GetMonthFromName
 
1187                             GetAlphaToken(input
), 
1188                             *fmt 
== 'b' ? Name_Abbr 
: Name_Full
, 
1191                     if ( mon 
== Inv_Month 
) 
1200             case _T('c'):       // locale default date and time  representation 
1202 #ifdef HAVE_STRPTIME 
1205                     // try using strptime() -- it may fail even if the input is 
1206                     // correct but the date is out of range, so we will fall back 
1207                     // to our generic code anyhow 
1208                     if ( CallStrptime(date
, input
, "%c", &tm
) ) 
1214                         year 
= 1900 + tm
.tm_year
; 
1215                         mon 
= (Month
)tm
.tm_mon
; 
1218                     else // strptime() failed; try generic heuristic code 
1219 #endif // HAVE_STRPTIME 
1222                         // try the format which corresponds to ctime() output 
1223                         // first, then the generic date/time formats 
1224                         const wxDateTime dt 
= ParseFormatAt
 
1228                                                 wxS("%a %b %d %H:%M:%S %Y"), 
1232                         if ( !dt
.IsValid() ) 
1246                     haveDay 
= haveMon 
= haveYear 
= 
1247                     haveHour 
= haveMin 
= haveSec 
= true; 
1251             case _T('d'):       // day of a month (01-31) 
1252                 if ( !GetNumericToken(width
, input
, &num
) || 
1253                         (num 
> 31) || (num 
< 1) ) 
1259                 // we can't check whether the day range is correct yet, will 
1260                 // do it later - assume ok for now 
1262                 mday 
= (wxDateTime_t
)num
; 
1265             case _T('H'):       // hour in 24h format (00-23) 
1266                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 23) ) 
1273                 hour 
= (wxDateTime_t
)num
; 
1276             case _T('I'):       // hour in 12h format (01-12) 
1277                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 12) ) 
1284                 hourIsIn12hFormat 
= true; 
1285                 hour 
= (wxDateTime_t
)(num 
% 12);        // 12 should be 0 
1288             case _T('j'):       // day of the year 
1289                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 366) ) 
1296                 yday 
= (wxDateTime_t
)num
; 
1299             case _T('l'):       // milliseconds (0-999) 
1300                 if ( !GetNumericToken(width
, input
, &num
) ) 
1304                 msec 
= (wxDateTime_t
)num
; 
1307             case _T('m'):       // month as a number (01-12) 
1308                 if ( !GetNumericToken(width
, input
, &num
) || !num 
|| (num 
> 12) ) 
1315                 mon 
= (Month
)(num 
- 1); 
1318             case _T('M'):       // minute as a decimal number (00-59) 
1319                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 59) ) 
1326                 min 
= (wxDateTime_t
)num
; 
1329             case _T('p'):       // AM or PM string 
1331                     wxString am
, pm
, token 
= GetAlphaToken(input
); 
1333                     // some locales have empty AM/PM tokens and thus when formatting 
1334                     // dates with the %p specifier nothing is generated; when trying to 
1335                     // parse them back, we get an empty token here... but that's not 
1340                     GetAmPmStrings(&am
, &pm
); 
1341                     if (am
.empty() && pm
.empty()) 
1342                         return false;  // no am/pm strings defined 
1343                     if ( token
.CmpNoCase(pm
) == 0 ) 
1347                     else if ( token
.CmpNoCase(am
) != 0 ) 
1355             case _T('r'):       // time as %I:%M:%S %p 
1358                     if ( !dt
.ParseFormat(wxString(input
, date
.end()), 
1359                                          wxS("%I:%M:%S %p"), &input
) ) 
1362                     haveHour 
= haveMin 
= haveSec 
= true; 
1371             case _T('R'):       // time as %H:%M 
1374                         dt 
= ParseFormatAt(input
, date
.end(), wxS("%H:%M")); 
1375                     if ( !dt
.IsValid() ) 
1387             case _T('S'):       // second as a decimal number (00-61) 
1388                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 61) ) 
1395                 sec 
= (wxDateTime_t
)num
; 
1398             case _T('T'):       // time as %H:%M:%S 
1401                         dt 
= ParseFormatAt(input
, date
.end(), wxS("%H:%M:%S")); 
1402                     if ( !dt
.IsValid() ) 
1416             case _T('w'):       // weekday as a number (0-6), Sunday = 0 
1417                 if ( !GetNumericToken(width
, input
, &num
) || (wday 
> 6) ) 
1424                 wday 
= (WeekDay
)num
; 
1427             case _T('x'):       // locale default date representation 
1428 #ifdef HAVE_STRPTIME 
1429                 // try using strptime() -- it may fail even if the input is 
1430                 // correct but the date is out of range, so we will fall back 
1431                 // to our generic code anyhow 
1435                     if ( CallStrptime(date
, input
, "%x", &tm
) ) 
1437                         haveDay 
= haveMon 
= haveYear 
= true; 
1439                         year 
= 1900 + tm
.tm_year
; 
1440                         mon 
= (Month
)tm
.tm_mon
; 
1446 #endif // HAVE_STRPTIME 
1453                     // The above doesn't work for all locales, try to query 
1454                     // Windows for the right way of formatting the date: 
1455                     fmtDate 
= GetLocaleDateFormat(); 
1456                     if ( fmtDate
.empty() ) 
1457 #endif // __WINDOWS__ 
1459                         if ( IsWestEuropeanCountry(GetCountry()) || 
1460                              GetCountry() == Russia 
) 
1462                             fmtDate 
= _T("%d/%m/%y"); 
1463                             fmtDateAlt 
= _T("%m/%d/%y"); 
1467                             fmtDate 
= _T("%m/%d/%y"); 
1468                             fmtDateAlt 
= _T("%d/%m/%y"); 
1473                         dt 
= ParseFormatAt(input
, date
.end(), 
1474                                            fmtDate
, fmtDateAlt
); 
1475                     if ( !dt
.IsValid() ) 
1491             case _T('X'):       // locale default time representation 
1492 #ifdef HAVE_STRPTIME 
1494                     // use strptime() to do it for us (FIXME !Unicode friendly) 
1496                     if ( !CallStrptime(date
, input
, "%X", &tm
) ) 
1499                     haveHour 
= haveMin 
= haveSec 
= true; 
1505 #else // !HAVE_STRPTIME 
1506                 // TODO under Win32 we can query the LOCALE_ITIME system 
1507                 //      setting which says whether the default time format is 
1510                     // try to parse what follows as "%H:%M:%S" and, if this 
1511                     // fails, as "%I:%M:%S %p" - this should catch the most 
1514                         dt 
= ParseFormatAt(input
, date
.end(), "%T", "%r"); 
1515                     if ( !dt
.IsValid() ) 
1527 #endif // HAVE_STRPTIME/!HAVE_STRPTIME 
1530             case _T('y'):       // year without century (00-99) 
1531                 if ( !GetNumericToken(width
, input
, &num
) || (num 
> 99) ) 
1539                 // TODO should have an option for roll over date instead of 
1540                 //      hard coding it here 
1541                 year 
= (num 
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
; 
1544             case _T('Y'):       // year with century 
1545                 if ( !GetNumericToken(width
, input
, &num
) ) 
1552                 year 
= (wxDateTime_t
)num
; 
1555             case _T('Z'):       // timezone name 
1556                 wxFAIL_MSG(_T("TODO")); 
1559             case _T('%'):       // a percent sign 
1560                 if ( *input
++ != _T('%') ) 
1567             case 0:             // the end of string 
1568                 wxFAIL_MSG(_T("unexpected format end")); 
1572             default:            // not a known format spec 
1577     // format matched, try to construct a date from what we have now 
1579     if ( dateDef
.IsValid() ) 
1581         // take this date as default 
1582         tmDef 
= dateDef
.GetTm(); 
1584     else if ( IsValid() ) 
1586         // if this date is valid, don't change it 
1591         // no default and this date is invalid - fall back to Today() 
1592         tmDef 
= Today().GetTm(); 
1608     // TODO we don't check here that the values are consistent, if both year 
1609     //      day and month/day were found, we just ignore the year day and we 
1610     //      also always ignore the week day 
1613         if ( mday 
> GetNumberOfDays(tm
.mon
, tm
.year
) ) 
1618     else if ( haveYDay 
) 
1620         if ( yday 
> GetNumberOfDays(tm
.year
) ) 
1623         Tm tm2 
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm(); 
1630     if ( haveHour 
&& hourIsIn12hFormat 
&& isPM 
) 
1632         // translate to 24hour format 
1635     //else: either already in 24h format or no translation needed 
1658     // finally check that the week day is consistent -- if we had it 
1659     if ( haveWDay 
&& GetWeekDay() != wday 
) 
1668 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator 
*end
) 
1670     wxCHECK_MSG( end
, false, "end iterator pointer must be specified" ); 
1672     // Set to current day and hour, so strings like '14:00' becomes today at 
1673     // 14, not some other random date 
1674     wxDateTime dtDate 
= wxDateTime::Today(); 
1675     wxDateTime dtTime 
= wxDateTime::Today(); 
1677     wxString::const_iterator
 
1682     // If we got a date in the beginning, see if there is a time specified 
1684     if ( dtDate
.ParseDate(date
, &endDate
) ) 
1686         // Skip spaces, as the ParseTime() function fails on spaces 
1687         while ( endDate 
!= date
.end() && wxIsspace(*endDate
) ) 
1690         const wxString 
timestr(endDate
, date
.end()); 
1691         if ( !dtTime
.ParseTime(timestr
, &endTime
) ) 
1694         endBoth 
= endDate 
+ (endTime 
- timestr
.begin()); 
1696     else // no date in the beginning 
1698         // check if we have a time followed by a date 
1699         if ( !dtTime
.ParseTime(date
, &endTime
) ) 
1702         while ( endTime 
!= date
.end() && wxIsspace(*endTime
) ) 
1705         const wxString 
datestr(endTime
, date
.end()); 
1706         if ( !dtDate
.ParseDate(datestr
, &endDate
) ) 
1709         endBoth 
= endTime 
+ (endDate 
- datestr
.begin()); 
1712     Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(), 
1713         dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(), 
1714         dtTime
.GetMillisecond()); 
1722 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator 
*end
) 
1724     wxCHECK_MSG( end
, false, "end iterator pointer must be specified" ); 
1726     // this is a simplified version of ParseDateTime() which understands only 
1727     // "today" (for wxDate compatibility) and digits only otherwise (and not 
1728     // all esoteric constructions ParseDateTime() knows about) 
1730     const wxString::const_iterator pBegin 
= date
.begin(); 
1732     wxString::const_iterator p 
= pBegin
; 
1733     while ( wxIsspace(*p
) ) 
1736     // some special cases 
1740         int dayDiffFromToday
; 
1743         { wxTRANSLATE("today"),             0 }, 
1744         { wxTRANSLATE("yesterday"),        -1 }, 
1745         { wxTRANSLATE("tomorrow"),          1 }, 
1748     for ( size_t n 
= 0; n 
< WXSIZEOF(literalDates
); n
++ ) 
1750         const wxString dateStr 
= wxGetTranslation(literalDates
[n
].str
); 
1751         size_t len 
= dateStr
.length(); 
1753         const wxString::const_iterator pEnd 
= p 
+ len
; 
1754         if ( wxString(p
, pEnd
).CmpNoCase(dateStr
) == 0 ) 
1756             // nothing can follow this, so stop here 
1760             int dayDiffFromToday 
= literalDates
[n
].dayDiffFromToday
; 
1762             if ( dayDiffFromToday 
) 
1764                 *this += wxDateSpan::Days(dayDiffFromToday
); 
1773     // We try to guess what we have here: for each new (numeric) token, we 
1774     // determine if it can be a month, day or a year. Of course, there is an 
1775     // ambiguity as some numbers may be days as well as months, so we also 
1776     // have the ability to back track. 
1779     bool haveDay 
= false,       // the months day? 
1780          haveWDay 
= false,      // the day of week? 
1781          haveMon 
= false,       // the month? 
1782          haveYear 
= false;      // the year? 
1784     // and the value of the items we have (init them to get rid of warnings) 
1785     WeekDay wday 
= Inv_WeekDay
; 
1786     wxDateTime_t day 
= 0; 
1787     wxDateTime::Month mon 
= Inv_Month
; 
1790     // tokenize the string 
1792     static const wxStringCharType 
*dateDelimiters 
= wxS(".,/-\t\r\n "); 
1793     wxStringTokenizer 
tok(wxString(p
, date
.end()), dateDelimiters
); 
1794     while ( tok
.HasMoreTokens() ) 
1796         wxString token 
= tok
.GetNextToken(); 
1802         if ( token
.ToULong(&val
) ) 
1804             // guess what this number is 
1810             if ( !haveMon 
&& val 
> 0 && val 
<= 12 ) 
1812                 // assume it is month 
1815             else // not the month 
1819                     // this can only be the year 
1822                 else // may be either day or year 
1824                     // use a leap year if we don't have the year yet to allow 
1825                     // dates like 2/29/1976 which would be rejected otherwise 
1826                     wxDateTime_t max_days 
= (wxDateTime_t
)( 
1828                         ? GetNumberOfDays(mon
, haveYear 
? year 
: 1976) 
1833                     if ( (val 
== 0) || (val 
> (unsigned long)max_days
) ) 
1838                     else // yes, suppose it's the day 
1852                 year 
= (wxDateTime_t
)val
; 
1861                 day 
= (wxDateTime_t
)val
; 
1867                 mon 
= (Month
)(val 
- 1); 
1870         else // not a number 
1872             // be careful not to overwrite the current mon value 
1873             Month mon2 
= GetMonthFromName
 
1876                             Name_Full 
| Name_Abbr
, 
1877                             DateLang_Local 
| DateLang_English
 
1879             if ( mon2 
!= Inv_Month 
) 
1884                     // but we already have a month - maybe we guessed wrong? 
1887                         // no need to check in month range as always < 12, but 
1888                         // the days are counted from 1 unlike the months 
1889                         day 
= (wxDateTime_t
)(mon 
+ 1); 
1894                         // could possible be the year (doesn't the year come 
1895                         // before the month in the japanese format?) (FIXME) 
1904             else // not a valid month name 
1906                 WeekDay wday2 
= GetWeekDayFromName
 
1909                                     Name_Full 
| Name_Abbr
, 
1910                                     DateLang_Local 
| DateLang_English
 
1912                 if ( wday2 
!= Inv_WeekDay 
) 
1924                 else // not a valid weekday name 
1927                     static const char *ordinals
[] = 
1929                         wxTRANSLATE("first"), 
1930                         wxTRANSLATE("second"), 
1931                         wxTRANSLATE("third"), 
1932                         wxTRANSLATE("fourth"), 
1933                         wxTRANSLATE("fifth"), 
1934                         wxTRANSLATE("sixth"), 
1935                         wxTRANSLATE("seventh"), 
1936                         wxTRANSLATE("eighth"), 
1937                         wxTRANSLATE("ninth"), 
1938                         wxTRANSLATE("tenth"), 
1939                         wxTRANSLATE("eleventh"), 
1940                         wxTRANSLATE("twelfth"), 
1941                         wxTRANSLATE("thirteenth"), 
1942                         wxTRANSLATE("fourteenth"), 
1943                         wxTRANSLATE("fifteenth"), 
1944                         wxTRANSLATE("sixteenth"), 
1945                         wxTRANSLATE("seventeenth"), 
1946                         wxTRANSLATE("eighteenth"), 
1947                         wxTRANSLATE("nineteenth"), 
1948                         wxTRANSLATE("twentieth"), 
1949                         // that's enough - otherwise we'd have problems with 
1950                         // composite (or not) ordinals 
1954                     for ( n 
= 0; n 
< WXSIZEOF(ordinals
); n
++ ) 
1956                         if ( token
.CmpNoCase(ordinals
[n
]) == 0 ) 
1962                     if ( n 
== WXSIZEOF(ordinals
) ) 
1964                         // stop here - something unknown 
1971                         // don't try anything here (as in case of numeric day 
1972                         // above) - the symbolic day spec should always 
1973                         // precede the month/year 
1979                     day 
= (wxDateTime_t
)(n 
+ 1); 
1984         nPosCur 
= tok
.GetPosition(); 
1987     // either no more tokens or the scan was stopped by something we couldn't 
1988     // parse - in any case, see if we can construct a date from what we have 
1989     if ( !haveDay 
&& !haveWDay 
) 
1992     if ( haveWDay 
&& (haveMon 
|| haveYear 
|| haveDay
) && 
1993          !(haveDay 
&& haveMon 
&& haveYear
) ) 
1995         // without adjectives (which we don't support here) the week day only 
1996         // makes sense completely separately or with the full date 
1997         // specification (what would "Wed 1999" mean?) 
2001     if ( !haveWDay 
&& haveYear 
&& !(haveDay 
&& haveMon
) ) 
2003         // may be we have month and day instead of day and year? 
2004         if ( haveDay 
&& !haveMon 
) 
2008                 // exchange day and month 
2009                 mon 
= (wxDateTime::Month
)(day 
- 1); 
2011                 // we're in the current year then 
2012                 if ( (year 
> 0) && (year 
<= (int)GetNumberOfDays(mon
, Inv_Year
)) ) 
2014                     day 
= (wxDateTime_t
)year
; 
2019                 //else: no, can't exchange, leave haveMon == false 
2029         mon 
= GetCurrentMonth(); 
2034         year 
= GetCurrentYear(); 
2039         // normally we check the day above but the check is optimistic in case 
2040         // we find the day before its month/year so we have to redo it now 
2041         if ( day 
> GetNumberOfDays(mon
, year
) ) 
2044         Set(day
, mon
, year
); 
2048             // check that it is really the same 
2049             if ( GetWeekDay() != wday 
) 
2057         SetToWeekDayInSameWeek(wday
); 
2060     // return the pointer to the first unparsed char 
2062     if ( nPosCur 
&& wxStrchr(dateDelimiters
, *(p 
- 1)) ) 
2064         // if we couldn't parse the token after the delimiter, put back the 
2065         // delimiter as well 
2075 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator 
*end
) 
2077     wxCHECK_MSG( end
, false, "end iterator pointer must be specified" ); 
2079     // first try some extra things 
2086         { wxTRANSLATE("noon"),      12 }, 
2087         { wxTRANSLATE("midnight"),  00 }, 
2091     for ( size_t n 
= 0; n 
< WXSIZEOF(stdTimes
); n
++ ) 
2093         const wxString timeString 
= wxGetTranslation(stdTimes
[n
].name
); 
2094         const wxString::const_iterator p 
= time
.begin() + timeString
.length(); 
2095         if ( timeString
.CmpNoCase(wxString(time
.begin(), p
)) == 0 ) 
2097             // casts required by DigitalMars 
2098             Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0)); 
2107     // try all time formats we may think about in the order from longest to 
2109     static const char *timeFormats
[] = 
2111         "%I:%M:%S %p",  // 12hour with AM/PM 
2112         "%H:%M:%S",     // could be the same or 24 hour one so try it too 
2113         "%I:%M %p",     // 12hour with AM/PM but without seconds 
2114         "%H:%M:%S",     // and a possibly 24 hour version without seconds 
2115         "%X",           // possibly something from above or maybe something 
2116                         // completely different -- try it last 
2118         // TODO: parse timezones 
2121     for ( size_t nFmt 
= 0; nFmt 
< WXSIZEOF(timeFormats
); nFmt
++ ) 
2123         if ( ParseFormat(time
, timeFormats
[nFmt
], end
) ) 
2130 // ---------------------------------------------------------------------------- 
2131 // Workdays and holidays support 
2132 // ---------------------------------------------------------------------------- 
2134 bool wxDateTime::IsWorkDay(Country 
WXUNUSED(country
)) const 
2136     return !wxDateTimeHolidayAuthority::IsHoliday(*this); 
2139 // ============================================================================ 
2141 // ============================================================================ 
2143 wxDateSpan WXDLLIMPEXP_BASE 
operator*(int n
, const wxDateSpan
& ds
) 
2146     return ds1
.Multiply(n
); 
2149 // ============================================================================ 
2151 // ============================================================================ 
2153 wxTimeSpan WXDLLIMPEXP_BASE 
operator*(int n
, const wxTimeSpan
& ts
) 
2155     return wxTimeSpan(ts
).Multiply(n
); 
2158 // this enum is only used in wxTimeSpan::Format() below but we can't declare 
2159 // it locally to the method as it provokes an internal compiler error in egcs 
2160 // 2.91.60 when building with -O2 
2171 // not all strftime(3) format specifiers make sense here because, for example, 
2172 // a time span doesn't have a year nor a timezone 
2174 // Here are the ones which are supported (all of them are supported by strftime 
2176 //  %H          hour in 24 hour format 
2177 //  %M          minute (00 - 59) 
2178 //  %S          second (00 - 59) 
2181 // Also, for MFC CTimeSpan compatibility, we support 
2182 //  %D          number of days 
2184 // And, to be better than MFC :-), we also have 
2185 //  %E          number of wEeks 
2186 //  %l          milliseconds (000 - 999) 
2187 wxString 
wxTimeSpan::Format(const wxString
& format
) const 
2189     // we deal with only positive time spans here and just add the sign in 
2190     // front for the negative ones 
2193         wxString 
str(Negate().Format(format
)); 
2197     wxCHECK_MSG( !format
.empty(), wxEmptyString
, 
2198                  _T("NULL format in wxTimeSpan::Format") ); 
2201     str
.Alloc(format
.length()); 
2203     // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */) 
2205     // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the 
2206     // question is what should ts.Format("%S") do? The code here returns "3273" 
2207     // in this case (i.e. the total number of seconds, not just seconds % 60) 
2208     // because, for me, this call means "give me entire time interval in 
2209     // seconds" and not "give me the seconds part of the time interval" 
2211     // If we agree that it should behave like this, it is clear that the 
2212     // interpretation of each format specifier depends on the presence of the 
2213     // other format specs in the string: if there was "%H" before "%M", we 
2214     // should use GetMinutes() % 60, otherwise just GetMinutes() &c 
2216     // we remember the most important unit found so far 
2217     TimeSpanPart partBiggest 
= Part_MSec
; 
2219     for ( wxString::const_iterator pch 
= format
.begin(); pch 
!= format
.end(); ++pch 
) 
2223         if ( ch 
== _T('%') ) 
2225             // the start of the format specification of the printf() below 
2226             wxString 
fmtPrefix(_T('%')); 
2231             // the number of digits for the format string, 0 if unused 
2232             unsigned digits 
= 0; 
2234             ch 
= *++pch
;    // get the format spec char 
2238                     wxFAIL_MSG( _T("invalid format character") ); 
2244                     // skip the part below switch 
2249                     if ( partBiggest 
< Part_Day 
) 
2255                         partBiggest 
= Part_Day
; 
2260                     partBiggest 
= Part_Week
; 
2266                     if ( partBiggest 
< Part_Hour 
) 
2272                         partBiggest 
= Part_Hour
; 
2279                     n 
= GetMilliseconds().ToLong(); 
2280                     if ( partBiggest 
< Part_MSec 
) 
2284                     //else: no need to reset partBiggest to Part_MSec, it is 
2285                     //      the least significant one anyhow 
2292                     if ( partBiggest 
< Part_Min 
) 
2298                         partBiggest 
= Part_Min
; 
2305                     n 
= GetSeconds().ToLong(); 
2306                     if ( partBiggest 
< Part_Sec 
) 
2312                         partBiggest 
= Part_Sec
; 
2321                 fmtPrefix 
<< _T("0") << digits
; 
2324             str 
+= wxString::Format(fmtPrefix 
+ _T("ld"), n
); 
2328             // normal character, just copy 
2336 #endif // wxUSE_DATETIME