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                      const wxString::const_iterator
& end
, 
 241                      unsigned long *number
) 
 245     while ( p 
!= end 
&& wxIsdigit(*p
) ) 
 249         if ( len 
&& ++n 
> len 
) 
 253     return !s
.empty() && s
.ToULong(number
); 
 256 // scans all alphabetic characters and returns the resulting string 
 258 GetAlphaToken(wxString::const_iterator
& p
, 
 259               const wxString::const_iterator
& end
) 
 262     while ( p 
!= end 
&& wxIsalpha(*p
) ) 
 270 // parses string starting at given iterator using the specified format and, 
 271 // optionally, a fall back format (and optionally another one... but it stops 
 274 // if unsuccessful, returns invalid wxDateTime without changing p; otherwise 
 275 // advance p to the end of the match and returns wxDateTime containing the 
 276 // results of the parsing 
 278 ParseFormatAt(wxString::const_iterator
& p
, 
 279               const wxString::const_iterator
& end
, 
 281               // FIXME-VC6: using wxString() instead of wxEmptyString in the 
 282               //            line below results in error C2062: type 'class 
 283               //            wxString (__cdecl *)(void)' unexpected 
 284               const wxString
& fmtAlt 
= wxEmptyString
, 
 285               const wxString
& fmtAlt2 
= wxString()) 
 287     const wxString 
str(p
, end
); 
 288     wxString::const_iterator endParse
; 
 290     if ( dt
.ParseFormat(str
, fmt
, &endParse
) || 
 291             (!fmtAlt
.empty() && dt
.ParseFormat(str
, fmtAlt
, &endParse
)) || 
 292                 (!fmtAlt2
.empty() && dt
.ParseFormat(str
, fmtAlt2
, &endParse
)) ) 
 294         p 
+= endParse 
- str
.begin(); 
 296     //else: all formats failed 
 301 } // anonymous namespace 
 303 // ---------------------------------------------------------------------------- 
 304 // wxDateTime to/from text representations 
 305 // ---------------------------------------------------------------------------- 
 307 wxString 
wxDateTime::Format(const wxString
& format
, const TimeZone
& tz
) const 
 309     wxCHECK_MSG( !format
.empty(), wxEmptyString
, 
 310                  _T("NULL format in wxDateTime::Format") ); 
 312     // we have to use our own implementation if the date is out of range of 
 313     // strftime() or if we use non standard specificators 
 315     time_t time 
= GetTicks(); 
 317     if ( (time 
!= (time_t)-1) && !wxStrstr(format
, _T("%l")) ) 
 322         if ( tz
.GetOffset() == -GetTimeZone() ) 
 324             // we are working with local time 
 325             tm 
= wxLocaltime_r(&time
, &tmstruct
); 
 327             // should never happen 
 328             wxCHECK_MSG( tm
, wxEmptyString
, _T("wxLocaltime_r() failed") ); 
 332             time 
+= (int)tz
.GetOffset(); 
 334 #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning 
 335             int time2 
= (int) time
; 
 341                 tm 
= wxGmtime_r(&time
, &tmstruct
); 
 343                 // should never happen 
 344                 wxCHECK_MSG( tm
, wxEmptyString
, _T("wxGmtime_r() failed") ); 
 348                 tm 
= (struct tm 
*)NULL
; 
 354             return CallStrftime(format
, tm
); 
 357     //else: use generic code below 
 358 #endif // HAVE_STRFTIME 
 360     // we only parse ANSI C format specifications here, no POSIX 2 
 361     // complications, no GNU extensions but we do add support for a "%l" format 
 362     // specifier allowing to get the number of milliseconds 
 365     // used for calls to strftime() when we only deal with time 
 366     struct tm tmTimeOnly
; 
 367     tmTimeOnly
.tm_hour 
= tm
.hour
; 
 368     tmTimeOnly
.tm_min 
= tm
.min
; 
 369     tmTimeOnly
.tm_sec 
= tm
.sec
; 
 370     tmTimeOnly
.tm_wday 
= 0; 
 371     tmTimeOnly
.tm_yday 
= 0; 
 372     tmTimeOnly
.tm_mday 
= 1;         // any date will do 
 373     tmTimeOnly
.tm_mon 
= 0; 
 374     tmTimeOnly
.tm_year 
= 76; 
 375     tmTimeOnly
.tm_isdst 
= 0;        // no DST, we adjust for tz ourselves 
 377     wxString tmp
, res
, fmt
; 
 378     for ( wxString::const_iterator p 
= format
.begin(); p 
!= format
.end(); ++p 
) 
 388         // set the default format 
 389         switch ( (*++p
).GetValue() ) 
 391             case _T('Y'):               // year has 4 digits 
 395             case _T('j'):               // day of year has 3 digits 
 396             case _T('l'):               // milliseconds have 3 digits 
 400             case _T('w'):               // week day as number has only one 
 405                 // it's either another valid format specifier in which case 
 406                 // the format is "%02d" (for all the rest) or we have the 
 407                 // field width preceding the format in which case it will 
 408                 // override the default format anyhow 
 417             // start of the format specification 
 418             switch ( (*p
).GetValue() ) 
 420                 case _T('a'):       // a weekday name 
 422                     // second parameter should be true for abbreviated names 
 423                     res 
+= GetWeekDayName(tm
.GetWeekDay(), 
 424                                           *p 
== _T('a') ? Name_Abbr 
: Name_Full
); 
 427                 case _T('b'):       // a month name 
 429                     res 
+= GetMonthName(tm
.mon
, 
 430                                         *p 
== _T('b') ? Name_Abbr 
: Name_Full
); 
 433                 case _T('c'):       // locale default date and time  representation 
 434                 case _T('x'):       // locale default date representation 
 437                     // the problem: there is no way to know what do these format 
 438                     // specifications correspond to for the current locale. 
 440                     // the solution: use a hack and still use strftime(): first 
 441                     // find the YEAR which is a year in the strftime() range (1970 
 442                     // - 2038) whose Jan 1 falls on the same week day as the Jan 1 
 443                     // of the real year. Then make a copy of the format and 
 444                     // replace all occurrences of YEAR in it with some unique 
 445                     // string not appearing anywhere else in it, then use 
 446                     // strftime() to format the date in year YEAR and then replace 
 447                     // YEAR back by the real year and the unique replacement 
 448                     // string back with YEAR. Notice that "all occurrences of YEAR" 
 449                     // means all occurrences of 4 digit as well as 2 digit form! 
 451                     // the bugs: we assume that neither of %c nor %x contains any 
 452                     // fields which may change between the YEAR and real year. For 
 453                     // example, the week number (%U, %W) and the day number (%j) 
 454                     // will change if one of these years is leap and the other one 
 457                         // find the YEAR: normally, for any year X, Jan 1 of the 
 458                         // year X + 28 is the same weekday as Jan 1 of X (because 
 459                         // the weekday advances by 1 for each normal X and by 2 
 460                         // for each leap X, hence by 5 every 4 years or by 35 
 461                         // which is 0 mod 7 every 28 years) but this rule breaks 
 462                         // down if there are years between X and Y which are 
 463                         // divisible by 4 but not leap (i.e. divisible by 100 but 
 464                         // not 400), hence the correction. 
 466                         int yearReal 
= GetYear(tz
); 
 467                         int mod28 
= yearReal 
% 28; 
 469                         // be careful to not go too far - we risk to leave the 
 474                             year 
= 1988 + mod28
;      // 1988 == 0 (mod 28) 
 478                             year 
= 1970 + mod28 
- 10; // 1970 == 10 (mod 28) 
 481                         int nCentury 
= year 
/ 100, 
 482                             nCenturyReal 
= yearReal 
/ 100; 
 484                         // need to adjust for the years divisble by 400 which are 
 485                         // not leap but are counted like leap ones if we just take 
 486                         // the number of centuries in between for nLostWeekDays 
 487                         int nLostWeekDays 
= (nCentury 
- nCenturyReal
) - 
 488                                             (nCentury 
/ 4 - nCenturyReal 
/ 4); 
 490                         // we have to gain back the "lost" weekdays: note that the 
 491                         // effect of this loop is to not do anything to 
 492                         // nLostWeekDays (which we won't use any more), but to 
 493                         // (indirectly) set the year correctly 
 494                         while ( (nLostWeekDays 
% 7) != 0 ) 
 496                             nLostWeekDays 
+= year
++ % 4 ? 1 : 2; 
 499                         // finally move the year below 2000 so that the 2-digit 
 500                         // year number can never match the month or day of the 
 501                         // month when we do the replacements below 
 505                         wxASSERT_MSG( year 
>= 1970 && year 
< 2000, 
 506                                       _T("logic error in wxDateTime::Format") ); 
 509                         // use strftime() to format the same date but in supported 
 512                         // NB: we assume that strftime() doesn't check for the 
 513                         //     date validity and will happily format the date 
 514                         //     corresponding to Feb 29 of a non leap year (which 
 515                         //     may happen if yearReal was leap and year is not) 
 516                         struct tm tmAdjusted
; 
 518                         tmAdjusted
.tm_hour 
= tm
.hour
; 
 519                         tmAdjusted
.tm_min 
= tm
.min
; 
 520                         tmAdjusted
.tm_sec 
= tm
.sec
; 
 521                         tmAdjusted
.tm_wday 
= tm
.GetWeekDay(); 
 522                         tmAdjusted
.tm_yday 
= GetDayOfYear(); 
 523                         tmAdjusted
.tm_mday 
= tm
.mday
; 
 524                         tmAdjusted
.tm_mon 
= tm
.mon
; 
 525                         tmAdjusted
.tm_year 
= year 
- 1900; 
 526                         tmAdjusted
.tm_isdst 
= 0; // no DST, already adjusted 
 527                         wxString str 
= CallStrftime(*p 
== _T('c') ? _T("%c") 
 531                         // now replace the replacement year with the real year: 
 532                         // notice that we have to replace the 4 digit year with 
 533                         // a unique string not appearing in strftime() output 
 534                         // first to prevent the 2 digit year from matching any 
 535                         // substring of the 4 digit year (but any day, month, 
 536                         // hours or minutes components should be safe because 
 537                         // they are never in 70-99 range) 
 538                         wxString 
replacement("|"); 
 539                         while ( str
.find(replacement
) != wxString::npos 
) 
 542                         str
.Replace(wxString::Format("%d", year
), 
 544                         str
.Replace(wxString::Format("%d", year 
% 100), 
 545                                     wxString::Format("%d", yearReal 
% 100)); 
 546                         str
.Replace(replacement
, 
 547                                     wxString::Format("%d", yearReal
)); 
 551 #else // !HAVE_STRFTIME 
 552                     // Use "%m/%d/%y %H:%M:%S" format instead 
 553                     res 
+= wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"), 
 554                             tm
.mon
+1,tm
.mday
, tm
.year
, tm
.hour
, tm
.min
, tm
.sec
); 
 555 #endif // HAVE_STRFTIME/!HAVE_STRFTIME 
 558                 case _T('d'):       // day of a month (01-31) 
 559                     res 
+= wxString::Format(fmt
, tm
.mday
); 
 562                 case _T('H'):       // hour in 24h format (00-23) 
 563                     res 
+= wxString::Format(fmt
, tm
.hour
); 
 566                 case _T('I'):       // hour in 12h format (01-12) 
 568                         // 24h -> 12h, 0h -> 12h too 
 569                         int hour12 
= tm
.hour 
> 12 ? tm
.hour 
- 12 
 570                                                   : tm
.hour 
? tm
.hour 
: 12; 
 571                         res 
+= wxString::Format(fmt
, hour12
); 
 575                 case _T('j'):       // day of the year 
 576                     res 
+= wxString::Format(fmt
, GetDayOfYear(tz
)); 
 579                 case _T('l'):       // milliseconds (NOT STANDARD) 
 580                     res 
+= wxString::Format(fmt
, GetMillisecond(tz
)); 
 583                 case _T('m'):       // month as a number (01-12) 
 584                     res 
+= wxString::Format(fmt
, tm
.mon 
+ 1); 
 587                 case _T('M'):       // minute as a decimal number (00-59) 
 588                     res 
+= wxString::Format(fmt
, tm
.min
); 
 591                 case _T('p'):       // AM or PM string 
 593                     res 
+= CallStrftime(_T("%p"), &tmTimeOnly
); 
 594 #else // !HAVE_STRFTIME 
 595                     res 
+= (tmTimeOnly
.tm_hour 
> 12) ? wxT("pm") : wxT("am"); 
 596 #endif // HAVE_STRFTIME/!HAVE_STRFTIME 
 599                 case _T('S'):       // second as a decimal number (00-61) 
 600                     res 
+= wxString::Format(fmt
, tm
.sec
); 
 603                 case _T('U'):       // week number in the year (Sunday 1st week day) 
 604                     res 
+= wxString::Format(fmt
, GetWeekOfYear(Sunday_First
, tz
)); 
 607                 case _T('W'):       // week number in the year (Monday 1st week day) 
 608                     res 
+= wxString::Format(fmt
, GetWeekOfYear(Monday_First
, tz
)); 
 611                 case _T('w'):       // weekday as a number (0-6), Sunday = 0 
 612                     res 
+= wxString::Format(fmt
, tm
.GetWeekDay()); 
 615                 // case _T('x'): -- handled with "%c" 
 617                 case _T('X'):       // locale default time representation 
 618                     // just use strftime() to format the time for us 
 620                     res 
+= CallStrftime(_T("%X"), &tmTimeOnly
); 
 621 #else // !HAVE_STRFTIME 
 622                     res 
+= wxString::Format(wxT("%02d:%02d:%02d"),tm
.hour
, tm
.min
, tm
.sec
); 
 623 #endif // HAVE_STRFTIME/!HAVE_STRFTIME 
 626                 case _T('y'):       // year without century (00-99) 
 627                     res 
+= wxString::Format(fmt
, tm
.year 
% 100); 
 630                 case _T('Y'):       // year with century 
 631                     res 
+= wxString::Format(fmt
, tm
.year
); 
 634                 case _T('Z'):       // timezone name 
 636                     res 
+= CallStrftime(_T("%Z"), &tmTimeOnly
); 
 641                     // is it the format width? 
 643                     while ( *p 
== _T('-') || *p 
== _T('+') || 
 644                             *p 
== _T(' ') || wxIsdigit(*p
) ) 
 651                         // we've only got the flags and width so far in fmt 
 652                         fmt
.Prepend(_T('%')); 
 660                     // no, it wasn't the width 
 661                     wxFAIL_MSG(_T("unknown format specificator")); 
 663                     // fall through and just copy it nevertheless 
 665                 case _T('%'):       // a percent sign 
 669                 case 0:             // the end of string 
 670                     wxFAIL_MSG(_T("missing format at the end of string")); 
 672                     // just put the '%' which was the last char in format 
 682 // this function parses a string in (strict) RFC 822 format: see the section 5 
 683 // of the RFC for the detailed description, but briefly it's something of the 
 684 // form "Sat, 18 Dec 1999 00:48:30 +0100" 
 686 // this function is "strict" by design - it must reject anything except true 
 687 // RFC822 time specs. 
 689 wxDateTime::ParseRfc822Date(const wxString
& date
, wxString::const_iterator 
*end
) 
 691     wxString::const_iterator p 
= date
.begin(); 
 694     static const int WDAY_LEN 
= 3; 
 695     const wxString::const_iterator endWday 
= p 
+ WDAY_LEN
; 
 696     const wxString 
wday(p
, endWday
); 
 697     if ( GetWeekDayFromName(wday
, Name_Abbr
, DateLang_English
) == Inv_WeekDay 
) 
 699     //else: ignore week day for now, we could also check that it really 
 700     //      corresponds to the specified date 
 704     // 2. separating comma 
 705     if ( *p
++ != ',' || *p
++ != ' ' ) 
 709     if ( !wxIsdigit(*p
) ) 
 712     wxDateTime_t day 
= (wxDateTime_t
)(*p
++ - '0'); 
 716         day 
= (wxDateTime_t
)(day 
+ (*p
++ - '0')); 
 723     static const int MONTH_LEN 
= 3; 
 724     const wxString::const_iterator endMonth 
= p 
+ MONTH_LEN
; 
 725     const wxString 
monName(p
, endMonth
); 
 726     Month mon 
= GetMonthFromName(monName
, Name_Abbr
, DateLang_English
); 
 727     if ( mon 
== Inv_Month 
) 
 736     if ( !wxIsdigit(*p
) ) 
 739     int year 
= *p
++ - '0'; 
 740     if ( !wxIsdigit(*p
) ) // should have at least 2 digits in the year 
 746     // is it a 2 digit year (as per original RFC 822) or a 4 digit one? 
 752         if ( !wxIsdigit(*p
) ) 
 754             // no 3 digit years please 
 765     // 6. time in hh:mm:ss format with seconds being optional 
 766     if ( !wxIsdigit(*p
) ) 
 769     wxDateTime_t hour 
= (wxDateTime_t
)(*p
++ - '0'); 
 771     if ( !wxIsdigit(*p
) ) 
 775     hour 
= (wxDateTime_t
)(hour 
+ (*p
++ - '0')); 
 780     if ( !wxIsdigit(*p
) ) 
 783     wxDateTime_t min 
= (wxDateTime_t
)(*p
++ - '0'); 
 785     if ( !wxIsdigit(*p
) ) 
 789     min 
+= (wxDateTime_t
)(*p
++ - '0'); 
 791     wxDateTime_t sec 
= 0; 
 795         if ( !wxIsdigit(*p
) ) 
 798         sec 
= (wxDateTime_t
)(*p
++ - '0'); 
 800         if ( !wxIsdigit(*p
) ) 
 804         sec 
+= (wxDateTime_t
)(*p
++ - '0'); 
 810     // 7. now the interesting part: the timezone 
 811     int offset 
wxDUMMY_INITIALIZE(0); 
 812     if ( *p 
== '-' || *p 
== '+' ) 
 814         // the explicit offset given: it has the form of hhmm 
 815         bool plus 
= *p
++ == '+'; 
 817         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
 822         offset 
= MIN_PER_HOUR
*(10*(*p 
- '0') + (*(p 
+ 1) - '0')); 
 826         if ( !wxIsdigit(*p
) || !wxIsdigit(*(p 
+ 1)) ) 
 830         offset 
+= 10*(*p 
- '0') + (*(p 
+ 1) - '0'); 
 839         // the symbolic timezone given: may be either military timezone or one 
 840         // of standard abbreviations 
 843             // military: Z = UTC, J unused, A = -1, ..., Y = +12 
 844             static const int offsets
[26] = 
 846                 //A  B   C   D   E   F   G   H   I    J    K    L    M 
 847                 -1, -2, -3, -4, -5, -6, -7, -8, -9,   0, -10, -11, -12, 
 848                 //N  O   P   R   Q   S   T   U   V    W    Z    Y    Z 
 849                 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0 
 852             if ( *p 
< _T('A') || *p 
> _T('Z') || *p 
== _T('J') ) 
 855             offset 
= offsets
[*p
++ - 'A']; 
 860             const wxString 
tz(p
, date
.end()); 
 861             if ( tz 
== _T("UT") || tz 
== _T("UTC") || tz 
== _T("GMT") ) 
 863             else if ( tz 
== _T("AST") ) 
 865             else if ( tz 
== _T("ADT") ) 
 867             else if ( tz 
== _T("EST") ) 
 869             else if ( tz 
== _T("EDT") ) 
 871             else if ( tz 
== _T("CST") ) 
 873             else if ( tz 
== _T("CDT") ) 
 875             else if ( tz 
== _T("MST") ) 
 877             else if ( tz 
== _T("MDT") ) 
 879             else if ( tz 
== _T("PST") ) 
 881             else if ( tz 
== _T("PDT") ) 
 890         offset 
*= MIN_PER_HOUR
; 
 894     // the spec was correct, construct the date from the values we found 
 895     Set(day
, mon
, year
, hour
, min
, sec
); 
 896     MakeFromTimezone(TimeZone::Make(offset
*SEC_PER_MIN
)); 
 906 // returns the string containing strftime() format used for short dates in the 
 907 // current locale or an empty string 
 908 static wxString 
GetLocaleDateFormat() 
 912     // there is no setlocale() under Windows CE, so just always query the 
 915     if ( strcmp(setlocale(LC_ALL
, NULL
), "C") != 0 ) 
 918         // The locale was programatically set to non-C. We assume that this was 
 919         // done using wxLocale, in which case thread's current locale is also 
 920         // set to correct LCID value and we can use GetLocaleInfo to determine 
 921         // the correct formatting string: 
 923         LCID lcid 
= LOCALE_USER_DEFAULT
; 
 925         LCID lcid 
= GetThreadLocale(); 
 927         // according to MSDN 80 chars is max allowed for short date format 
 929         if ( ::GetLocaleInfo(lcid
, LOCALE_SSHORTDATE
, fmt
, WXSIZEOF(fmt
)) ) 
 931             wxChar chLast 
= _T('\0'); 
 932             size_t lastCount 
= 0; 
 933             for ( const wxChar 
*p 
= fmt
; /* NUL handled inside */; p
++ ) 
 943                     // these characters come in groups, start counting them 
 953                         // first deal with any special characters we have had 
 963                                             // these two are the same as we 
 964                                             // don't distinguish between 1 and 
 978                                             wxFAIL_MSG( _T("too many 'd's") ); 
 987                                             // as for 'd' and 'dd' above 
1000                                             wxFAIL_MSG( _T("too many 'M's") ); 
1005                                     switch ( lastCount 
) 
1017                                             wxFAIL_MSG( _T("wrong number of 'y's") ); 
1022                                     // strftime() doesn't have era string, 
1023                                     // ignore this format 
1024                                     wxASSERT_MSG( lastCount 
<= 2, 
1025                                                   _T("too many 'g's") ); 
1029                                     wxFAIL_MSG( _T("unreachable") ); 
1036                         // not a special character so must be just a separator, 
1038                         if ( *p 
!= _T('\0') ) 
1040                             if ( *p 
== _T('%') ) 
1042                                 // this one needs to be escaped 
1050                 if ( *p 
== _T('\0') ) 
1054         //else: GetLocaleInfo() failed, leave fmtDate value unchanged and 
1055         //      try our luck with the default formats 
1057     //else: default C locale, default formats should work 
1062 #endif // __WINDOWS__ 
1066 #include "wx/osx/private.h" 
1068 // under OSX locale formats are defined using 
1069 // http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns 
1071 // so we need a translation function, bluntly copied from the windows 
1072 // version above and enhanced with the additional elements needed 
1074 static wxString 
TranslateFromUnicodeFormat( const wxString
& fmt
) 
1079     wxChar chLast 
= _T('\0'); 
1080     size_t lastCount 
= 0; 
1081     for ( wxString::const_iterator p 
= fmt
.begin(); /* NUL handled inside */; p
++ ) 
1089         switch ( (char) *p 
) 
1091             // these characters come in groups, start counting them 
1105                 // first deal with any special characters we have had 
1111                             switch ( lastCount 
) 
1115                                     // these two are the same as we 
1116                                     // don't distinguish between 1 and 
1117                                     // 2 digits for days 
1130                                     wxFAIL_MSG( _T("too many 'd's") ); 
1135                             switch ( lastCount 
) 
1139                                     // as for 'd' and 'dd' above 
1152                                     wxFAIL_MSG( _T("too many 'M's") ); 
1157                             switch ( lastCount 
) 
1169                                     wxFAIL_MSG( _T("wrong number of 'y's") ); 
1174                             switch ( lastCount 
) 
1182                                     wxFAIL_MSG( _T("wrong number of 'H's") ); 
1187                             switch ( lastCount 
) 
1195                                     wxFAIL_MSG( _T("wrong number of 'h's") ); 
1200                             switch ( lastCount 
) 
1208                                     wxFAIL_MSG( _T("wrong number of 'm's") ); 
1213                             switch ( lastCount 
) 
1221                                     wxFAIL_MSG( _T("wrong number of 's's") ); 
1226                             // strftime() doesn't have era string, 
1227                             // ignore this format 
1228                             wxASSERT_MSG( lastCount 
<= 2, 
1229                                           _T("too many 'g's") ); 
1233                             wxFAIL_MSG( _T("unreachable") ); 
1240                 // not a special character so must be just a separator, 
1242                 if ( *p 
!= _T('\0') ) 
1244                     if ( *p 
== _T('%') ) 
1246                         // this one needs to be escaped 
1254         if ( *p 
== _T('\0') ) 
1261 static wxString 
GetLocaleDateFormat() 
1263     wxCFRef
<CFLocaleRef
> currentLocale( CFLocaleCopyCurrent() ); 
1265     wxCFRef
<CFDateFormatterRef
> dateFormatter( CFDateFormatterCreate
 
1266         (NULL
, currentLocale
, kCFDateFormatterShortStyle
, kCFDateFormatterNoStyle
)); 
1267     wxCFStringRef cfs 
= wxCFRetain( CFDateFormatterGetFormat(dateFormatter 
)); 
1268     return TranslateFromUnicodeFormat(cfs
.AsString()); 
1271 static wxString 
GetLocaleFullDateFormat() 
1273     wxCFRef
<CFLocaleRef
> currentLocale( CFLocaleCopyCurrent() ); 
1275     wxCFRef
<CFDateFormatterRef
> dateFormatter( CFDateFormatterCreate
 
1276         (NULL
, currentLocale
, kCFDateFormatterLongStyle
, kCFDateFormatterMediumStyle
)); 
1277     wxCFStringRef cfs 
= wxCFRetain( CFDateFormatterGetFormat(dateFormatter 
)); 
1278     return TranslateFromUnicodeFormat(cfs
.AsString()); 
1286 wxDateTime::ParseFormat(const wxString
& date
, 
1287                         const wxString
& format
, 
1288                         const wxDateTime
& dateDef
, 
1289                         wxString::const_iterator 
*endParse
) 
1291     wxCHECK_MSG( !format
.empty(), false, "format can't be empty" ); 
1292     wxCHECK_MSG( endParse
, false, "end iterator pointer must be specified" ); 
1297     // what fields have we found? 
1298     bool haveWDay 
= false, 
1308     bool hourIsIn12hFormat 
= false, // or in 24h one? 
1309          isPM 
= false;              // AM by default 
1311     // and the value of the items we have (init them to get rid of warnings) 
1312     wxDateTime_t msec 
= 0, 
1316     WeekDay wday 
= Inv_WeekDay
; 
1317     wxDateTime_t yday 
= 0, 
1319     wxDateTime::Month mon 
= Inv_Month
; 
1322     wxString::const_iterator input 
= date
.begin(); 
1323     const wxString::const_iterator end 
= date
.end(); 
1324     for ( wxString::const_iterator fmt 
= format
.begin(); fmt 
!= format
.end(); ++fmt 
) 
1326         if ( *fmt 
!= _T('%') ) 
1328             if ( wxIsspace(*fmt
) ) 
1330                 // a white space in the format string matches 0 or more white 
1331                 // spaces in the input 
1332                 while ( wxIsspace(*input
) ) 
1339                 // any other character (not whitespace, not '%') must be 
1340                 // matched by itself in the input 
1341                 if ( *input
++ != *fmt 
) 
1348             // done with this format char 
1352         // start of a format specification 
1354         // parse the optional width 
1356         while ( wxIsdigit(*++fmt
) ) 
1359             width 
+= *fmt 
- '0'; 
1362         // the default widths for the various fields 
1365             switch ( (*fmt
).GetValue() ) 
1367                 case _T('Y'):               // year has 4 digits 
1371                 case _T('j'):               // day of year has 3 digits 
1372                 case _T('l'):               // milliseconds have 3 digits 
1376                 case _T('w'):               // week day as number has only one 
1381                     // default for all other fields 
1386         // then the format itself 
1387         switch ( (*fmt
).GetValue() ) 
1389             case _T('a'):       // a weekday name 
1392                     wday 
= GetWeekDayFromName
 
1394                             GetAlphaToken(input
, end
), 
1395                             *fmt 
== 'a' ? Name_Abbr 
: Name_Full
, 
1398                     if ( wday 
== Inv_WeekDay 
) 
1407             case _T('b'):       // a month name 
1410                     mon 
= GetMonthFromName
 
1412                             GetAlphaToken(input
, end
), 
1413                             *fmt 
== 'b' ? Name_Abbr 
: Name_Full
, 
1416                     if ( mon 
== Inv_Month 
) 
1425             case _T('c'):       // locale default date and time  representation 
1427 #ifdef HAVE_STRPTIME 
1430                     // try using strptime() -- it may fail even if the input is 
1431                     // correct but the date is out of range, so we will fall back 
1432                     // to our generic code anyhow 
1433                     if ( CallStrptime(date
, input
, "%c", &tm
) ) 
1439                         year 
= 1900 + tm
.tm_year
; 
1440                         mon 
= (Month
)tm
.tm_mon
; 
1443                     else // strptime() failed; try generic heuristic code 
1444 #endif // HAVE_STRPTIME 
1448                         bool hasValidDate 
= false; 
1449                         wxString fmtDate 
= GetLocaleFullDateFormat(); 
1450                         if ( !fmtDate
.empty() ) 
1452                             const wxDateTime dt 
= ParseFormatAt
 
1461                                 hasValidDate 
= true; 
1465                         if ( !hasValidDate 
) 
1468                             // try the format which corresponds to ctime() output 
1469                             // first, then the generic date/time formats 
1470                             const wxDateTime dt 
= ParseFormatAt
 
1474                                                     wxS("%a %b %d %H:%M:%S %Y"), 
1478                             if ( !dt
.IsValid() ) 
1493                     haveDay 
= haveMon 
= haveYear 
= 
1494                     haveHour 
= haveMin 
= haveSec 
= true; 
1498             case _T('d'):       // day of a month (01-31) 
1499                 if ( !GetNumericToken(width
, input
, end
, &num
) || 
1500                         (num 
> 31) || (num 
< 1) ) 
1506                 // we can't check whether the day range is correct yet, will 
1507                 // do it later - assume ok for now 
1509                 mday 
= (wxDateTime_t
)num
; 
1512             case _T('H'):       // hour in 24h format (00-23) 
1513                 if ( !GetNumericToken(width
, input
, end
, &num
) || (num 
> 23) ) 
1520                 hour 
= (wxDateTime_t
)num
; 
1523             case _T('I'):       // hour in 12h format (01-12) 
1524                 if ( !GetNumericToken(width
, input
, end
, &num
) || 
1525                         !num 
|| (num 
> 12) ) 
1532                 hourIsIn12hFormat 
= true; 
1533                 hour 
= (wxDateTime_t
)(num 
% 12);        // 12 should be 0 
1536             case _T('j'):       // day of the year 
1537                 if ( !GetNumericToken(width
, input
, end
, &num
) || 
1538                         !num 
|| (num 
> 366) ) 
1545                 yday 
= (wxDateTime_t
)num
; 
1548             case _T('l'):       // milliseconds (0-999) 
1549                 if ( !GetNumericToken(width
, input
, end
, &num
) ) 
1553                 msec 
= (wxDateTime_t
)num
; 
1556             case _T('m'):       // month as a number (01-12) 
1557                 if ( !GetNumericToken(width
, input
, end
, &num
) || 
1558                         !num 
|| (num 
> 12) ) 
1565                 mon 
= (Month
)(num 
- 1); 
1568             case _T('M'):       // minute as a decimal number (00-59) 
1569                 if ( !GetNumericToken(width
, input
, end
, &num
) || 
1577                 min 
= (wxDateTime_t
)num
; 
1580             case _T('p'):       // AM or PM string 
1582                     wxString am
, pm
, token 
= GetAlphaToken(input
, end
); 
1584                     // some locales have empty AM/PM tokens and thus when formatting 
1585                     // dates with the %p specifier nothing is generated; when trying to 
1586                     // parse them back, we get an empty token here... but that's not 
1591                     GetAmPmStrings(&am
, &pm
); 
1592                     if (am
.empty() && pm
.empty()) 
1593                         return false;  // no am/pm strings defined 
1594                     if ( token
.CmpNoCase(pm
) == 0 ) 
1598                     else if ( token
.CmpNoCase(am
) != 0 ) 
1606             case _T('r'):       // time as %I:%M:%S %p 
1609                     if ( !dt
.ParseFormat(wxString(input
, end
), 
1610                                          wxS("%I:%M:%S %p"), &input
) ) 
1613                     haveHour 
= haveMin 
= haveSec 
= true; 
1622             case _T('R'):       // time as %H:%M 
1625                         dt 
= ParseFormatAt(input
, end
, wxS("%H:%M")); 
1626                     if ( !dt
.IsValid() ) 
1638             case _T('S'):       // second as a decimal number (00-61) 
1639                 if ( !GetNumericToken(width
, input
, end
, &num
) || 
1647                 sec 
= (wxDateTime_t
)num
; 
1650             case _T('T'):       // time as %H:%M:%S 
1653                         dt 
= ParseFormatAt(input
, end
, wxS("%H:%M:%S")); 
1654                     if ( !dt
.IsValid() ) 
1668             case _T('w'):       // weekday as a number (0-6), Sunday = 0 
1669                 if ( !GetNumericToken(width
, input
, end
, &num
) || 
1677                 wday 
= (WeekDay
)num
; 
1680             case _T('x'):       // locale default date representation 
1681 #ifdef HAVE_STRPTIME 
1682                 // try using strptime() -- it may fail even if the input is 
1683                 // correct but the date is out of range, so we will fall back 
1684                 // to our generic code anyhow 
1688                     if ( CallStrptime(date
, input
, "%x", &tm
) ) 
1690                         haveDay 
= haveMon 
= haveYear 
= true; 
1692                         year 
= 1900 + tm
.tm_year
; 
1693                         mon 
= (Month
)tm
.tm_mon
; 
1699 #endif // HAVE_STRPTIME 
1705 #if defined( __WINDOWS__ ) || defined( __WXOSX__ ) 
1706                     // The above doesn't work for all locales, try to query 
1707                     // the OS for the right way of formatting the date: 
1708                     fmtDate 
= GetLocaleDateFormat(); 
1709                     if ( fmtDate
.empty() ) 
1710 #endif // __WINDOWS__ 
1712                         if ( IsWestEuropeanCountry(GetCountry()) || 
1713                              GetCountry() == Russia 
) 
1715                             fmtDate 
= _T("%d/%m/%y"); 
1716                             fmtDateAlt 
= _T("%m/%d/%y"); 
1720                             fmtDate 
= _T("%m/%d/%y"); 
1721                             fmtDateAlt 
= _T("%d/%m/%y"); 
1726                         dt 
= ParseFormatAt(input
, end
, 
1727                                            fmtDate
, fmtDateAlt
); 
1730                     if ( !dt
.IsValid() ) 
1732                         wxString fmtDateLong 
= fmtDate
; 
1733                         wxString fmtDateLongAlt 
= fmtDateAlt
; 
1736                         if ( !fmtDateLong
.empty() ) 
1738                             fmtDateLong
.Replace("%y","%Y"); 
1739                             fmtDateLongAlt
.Replace("%y","%Y"); 
1740                             const wxDateTime dtLong 
= ParseFormatAt(input
, end
, 
1741                                            fmtDateLong
, fmtDateLongAlt
); 
1742                             if ( !dtLong
.IsValid() ) 
1745                             tm 
= dtLong
.GetTm(); 
1764             case _T('X'):       // locale default time representation 
1765 #ifdef HAVE_STRPTIME 
1767                     // use strptime() to do it for us (FIXME !Unicode friendly) 
1769                     if ( !CallStrptime(date
, input
, "%X", &tm
) ) 
1772                     haveHour 
= haveMin 
= haveSec 
= true; 
1778 #else // !HAVE_STRPTIME 
1779                 // TODO under Win32 we can query the LOCALE_ITIME system 
1780                 //      setting which says whether the default time format is 
1783                     // try to parse what follows as "%H:%M:%S" and, if this 
1784                     // fails, as "%I:%M:%S %p" - this should catch the most 
1787                         dt 
= ParseFormatAt(input
, end
, "%T", "%r"); 
1788                     if ( !dt
.IsValid() ) 
1800 #endif // HAVE_STRPTIME/!HAVE_STRPTIME 
1803             case _T('y'):       // year without century (00-99) 
1804                 if ( !GetNumericToken(width
, input
, end
, &num
) || 
1813                 // TODO should have an option for roll over date instead of 
1814                 //      hard coding it here 
1815                 year 
= (num 
> 30 ? 1900 : 2000) + (wxDateTime_t
)num
; 
1818             case _T('Y'):       // year with century 
1819                 if ( !GetNumericToken(width
, input
, end
, &num
) ) 
1826                 year 
= (wxDateTime_t
)num
; 
1829             case _T('Z'):       // timezone name 
1830                 wxFAIL_MSG(_T("TODO")); 
1833             case _T('%'):       // a percent sign 
1834                 if ( *input
++ != _T('%') ) 
1841             case 0:             // the end of string 
1842                 wxFAIL_MSG(_T("unexpected format end")); 
1846             default:            // not a known format spec 
1851     // format matched, try to construct a date from what we have now 
1853     if ( dateDef
.IsValid() ) 
1855         // take this date as default 
1856         tmDef 
= dateDef
.GetTm(); 
1858     else if ( IsValid() ) 
1860         // if this date is valid, don't change it 
1865         // no default and this date is invalid - fall back to Today() 
1866         tmDef 
= Today().GetTm(); 
1882     // TODO we don't check here that the values are consistent, if both year 
1883     //      day and month/day were found, we just ignore the year day and we 
1884     //      also always ignore the week day 
1887         if ( mday 
> GetNumberOfDays(tm
.mon
, tm
.year
) ) 
1892     else if ( haveYDay 
) 
1894         if ( yday 
> GetNumberOfDays(tm
.year
) ) 
1897         Tm tm2 
= wxDateTime(1, Jan
, tm
.year
).SetToYearDay(yday
).GetTm(); 
1904     if ( haveHour 
&& hourIsIn12hFormat 
&& isPM 
) 
1906         // translate to 24hour format 
1909     //else: either already in 24h format or no translation needed 
1932     // finally check that the week day is consistent -- if we had it 
1933     if ( haveWDay 
&& GetWeekDay() != wday 
) 
1942 wxDateTime::ParseDateTime(const wxString
& date
, wxString::const_iterator 
*end
) 
1944     wxCHECK_MSG( end
, false, "end iterator pointer must be specified" ); 
1946     // Set to current day and hour, so strings like '14:00' becomes today at 
1947     // 14, not some other random date 
1948     wxDateTime dtDate 
= wxDateTime::Today(); 
1949     wxDateTime dtTime 
= wxDateTime::Today(); 
1951     wxString::const_iterator
 
1956     // If we got a date in the beginning, see if there is a time specified 
1958     if ( dtDate
.ParseDate(date
, &endDate
) ) 
1960         // Skip spaces, as the ParseTime() function fails on spaces 
1961         while ( endDate 
!= date
.end() && wxIsspace(*endDate
) ) 
1964         const wxString 
timestr(endDate
, date
.end()); 
1965         if ( !dtTime
.ParseTime(timestr
, &endTime
) ) 
1968         endBoth 
= endDate 
+ (endTime 
- timestr
.begin()); 
1970     else // no date in the beginning 
1972         // check if we have a time followed by a date 
1973         if ( !dtTime
.ParseTime(date
, &endTime
) ) 
1976         while ( endTime 
!= date
.end() && wxIsspace(*endTime
) ) 
1979         const wxString 
datestr(endTime
, date
.end()); 
1980         if ( !dtDate
.ParseDate(datestr
, &endDate
) ) 
1983         endBoth 
= endTime 
+ (endDate 
- datestr
.begin()); 
1986     Set(dtDate
.GetDay(), dtDate
.GetMonth(), dtDate
.GetYear(), 
1987         dtTime
.GetHour(), dtTime
.GetMinute(), dtTime
.GetSecond(), 
1988         dtTime
.GetMillisecond()); 
1996 wxDateTime::ParseDate(const wxString
& date
, wxString::const_iterator 
*end
) 
1998     wxCHECK_MSG( end
, false, "end iterator pointer must be specified" ); 
2000     // this is a simplified version of ParseDateTime() which understands only 
2001     // "today" (for wxDate compatibility) and digits only otherwise (and not 
2002     // all esoteric constructions ParseDateTime() knows about) 
2004     const wxString::const_iterator pBegin 
= date
.begin(); 
2006     wxString::const_iterator p 
= pBegin
; 
2007     while ( wxIsspace(*p
) ) 
2010     // some special cases 
2014         int dayDiffFromToday
; 
2017         { wxTRANSLATE("today"),             0 }, 
2018         { wxTRANSLATE("yesterday"),        -1 }, 
2019         { wxTRANSLATE("tomorrow"),          1 }, 
2022     const size_t lenRest 
= date
.end() - p
; 
2023     for ( size_t n 
= 0; n 
< WXSIZEOF(literalDates
); n
++ ) 
2025         const wxString dateStr 
= wxGetTranslation(literalDates
[n
].str
); 
2026         size_t len 
= dateStr
.length(); 
2028         if ( len 
> lenRest 
) 
2031         const wxString::const_iterator pEnd 
= p 
+ len
; 
2032         if ( wxString(p
, pEnd
).CmpNoCase(dateStr
) == 0 ) 
2034             // nothing can follow this, so stop here 
2038             int dayDiffFromToday 
= literalDates
[n
].dayDiffFromToday
; 
2040             if ( dayDiffFromToday 
) 
2042                 *this += wxDateSpan::Days(dayDiffFromToday
); 
2051     // We try to guess what we have here: for each new (numeric) token, we 
2052     // determine if it can be a month, day or a year. Of course, there is an 
2053     // ambiguity as some numbers may be days as well as months, so we also 
2054     // have the ability to back track. 
2057     bool haveDay 
= false,       // the months day? 
2058          haveWDay 
= false,      // the day of week? 
2059          haveMon 
= false,       // the month? 
2060          haveYear 
= false;      // the year? 
2062     // and the value of the items we have (init them to get rid of warnings) 
2063     WeekDay wday 
= Inv_WeekDay
; 
2064     wxDateTime_t day 
= 0; 
2065     wxDateTime::Month mon 
= Inv_Month
; 
2068     // tokenize the string 
2070     static const wxStringCharType 
*dateDelimiters 
= wxS(".,/-\t\r\n "); 
2071     wxStringTokenizer 
tok(wxString(p
, date
.end()), dateDelimiters
); 
2072     while ( tok
.HasMoreTokens() ) 
2074         wxString token 
= tok
.GetNextToken(); 
2080         if ( token
.ToULong(&val
) ) 
2082             // guess what this number is 
2088             if ( !haveMon 
&& val 
> 0 && val 
<= 12 ) 
2090                 // assume it is month 
2093             else // not the month 
2097                     // this can only be the year 
2100                 else // may be either day or year 
2102                     // use a leap year if we don't have the year yet to allow 
2103                     // dates like 2/29/1976 which would be rejected otherwise 
2104                     wxDateTime_t max_days 
= (wxDateTime_t
)( 
2106                         ? GetNumberOfDays(mon
, haveYear 
? year 
: 1976) 
2111                     if ( (val 
== 0) || (val 
> (unsigned long)max_days
) ) 
2116                     else // yes, suppose it's the day 
2130                 year 
= (wxDateTime_t
)val
; 
2139                 day 
= (wxDateTime_t
)val
; 
2145                 mon 
= (Month
)(val 
- 1); 
2148         else // not a number 
2150             // be careful not to overwrite the current mon value 
2151             Month mon2 
= GetMonthFromName
 
2154                             Name_Full 
| Name_Abbr
, 
2155                             DateLang_Local 
| DateLang_English
 
2157             if ( mon2 
!= Inv_Month 
) 
2162                     // but we already have a month - maybe we guessed wrong? 
2165                         // no need to check in month range as always < 12, but 
2166                         // the days are counted from 1 unlike the months 
2167                         day 
= (wxDateTime_t
)(mon 
+ 1); 
2172                         // could possible be the year (doesn't the year come 
2173                         // before the month in the japanese format?) (FIXME) 
2182             else // not a valid month name 
2184                 WeekDay wday2 
= GetWeekDayFromName
 
2187                                     Name_Full 
| Name_Abbr
, 
2188                                     DateLang_Local 
| DateLang_English
 
2190                 if ( wday2 
!= Inv_WeekDay 
) 
2202                 else // not a valid weekday name 
2205                     static const char *ordinals
[] = 
2207                         wxTRANSLATE("first"), 
2208                         wxTRANSLATE("second"), 
2209                         wxTRANSLATE("third"), 
2210                         wxTRANSLATE("fourth"), 
2211                         wxTRANSLATE("fifth"), 
2212                         wxTRANSLATE("sixth"), 
2213                         wxTRANSLATE("seventh"), 
2214                         wxTRANSLATE("eighth"), 
2215                         wxTRANSLATE("ninth"), 
2216                         wxTRANSLATE("tenth"), 
2217                         wxTRANSLATE("eleventh"), 
2218                         wxTRANSLATE("twelfth"), 
2219                         wxTRANSLATE("thirteenth"), 
2220                         wxTRANSLATE("fourteenth"), 
2221                         wxTRANSLATE("fifteenth"), 
2222                         wxTRANSLATE("sixteenth"), 
2223                         wxTRANSLATE("seventeenth"), 
2224                         wxTRANSLATE("eighteenth"), 
2225                         wxTRANSLATE("nineteenth"), 
2226                         wxTRANSLATE("twentieth"), 
2227                         // that's enough - otherwise we'd have problems with 
2228                         // composite (or not) ordinals 
2232                     for ( n 
= 0; n 
< WXSIZEOF(ordinals
); n
++ ) 
2234                         if ( token
.CmpNoCase(ordinals
[n
]) == 0 ) 
2240                     if ( n 
== WXSIZEOF(ordinals
) ) 
2242                         // stop here - something unknown 
2249                         // don't try anything here (as in case of numeric day 
2250                         // above) - the symbolic day spec should always 
2251                         // precede the month/year 
2257                     day 
= (wxDateTime_t
)(n 
+ 1); 
2262         nPosCur 
= tok
.GetPosition(); 
2265     // either no more tokens or the scan was stopped by something we couldn't 
2266     // parse - in any case, see if we can construct a date from what we have 
2267     if ( !haveDay 
&& !haveWDay 
) 
2270     if ( haveWDay 
&& (haveMon 
|| haveYear 
|| haveDay
) && 
2271          !(haveDay 
&& haveMon 
&& haveYear
) ) 
2273         // without adjectives (which we don't support here) the week day only 
2274         // makes sense completely separately or with the full date 
2275         // specification (what would "Wed 1999" mean?) 
2279     if ( !haveWDay 
&& haveYear 
&& !(haveDay 
&& haveMon
) ) 
2281         // may be we have month and day instead of day and year? 
2282         if ( haveDay 
&& !haveMon 
) 
2286                 // exchange day and month 
2287                 mon 
= (wxDateTime::Month
)(day 
- 1); 
2289                 // we're in the current year then 
2290                 if ( (year 
> 0) && (year 
<= (int)GetNumberOfDays(mon
, Inv_Year
)) ) 
2292                     day 
= (wxDateTime_t
)year
; 
2297                 //else: no, can't exchange, leave haveMon == false 
2307         mon 
= GetCurrentMonth(); 
2312         year 
= GetCurrentYear(); 
2317         // normally we check the day above but the check is optimistic in case 
2318         // we find the day before its month/year so we have to redo it now 
2319         if ( day 
> GetNumberOfDays(mon
, year
) ) 
2322         Set(day
, mon
, year
); 
2326             // check that it is really the same 
2327             if ( GetWeekDay() != wday 
) 
2335         SetToWeekDayInSameWeek(wday
); 
2338     // return the pointer to the first unparsed char 
2340     if ( nPosCur 
&& wxStrchr(dateDelimiters
, *(p 
- 1)) ) 
2342         // if we couldn't parse the token after the delimiter, put back the 
2343         // delimiter as well 
2353 wxDateTime::ParseTime(const wxString
& time
, wxString::const_iterator 
*end
) 
2355     wxCHECK_MSG( end
, false, "end iterator pointer must be specified" ); 
2357     // first try some extra things 
2364         { wxTRANSLATE("noon"),      12 }, 
2365         { wxTRANSLATE("midnight"),  00 }, 
2369     for ( size_t n 
= 0; n 
< WXSIZEOF(stdTimes
); n
++ ) 
2371         const wxString timeString 
= wxGetTranslation(stdTimes
[n
].name
); 
2372         const wxString::const_iterator p 
= time
.begin() + timeString
.length(); 
2373         if ( timeString
.CmpNoCase(wxString(time
.begin(), p
)) == 0 ) 
2375             // casts required by DigitalMars 
2376             Set(stdTimes
[n
].hour
, wxDateTime_t(0), wxDateTime_t(0)); 
2385     // try all time formats we may think about in the order from longest to 
2387     static const char *timeFormats
[] = 
2389         "%I:%M:%S %p",  // 12hour with AM/PM 
2390         "%H:%M:%S",     // could be the same or 24 hour one so try it too 
2391         "%I:%M %p",     // 12hour with AM/PM but without seconds 
2392         "%H:%M:%S",     // and a possibly 24 hour version without seconds 
2393         "%X",           // possibly something from above or maybe something 
2394                         // completely different -- try it last 
2396         // TODO: parse timezones 
2399     for ( size_t nFmt 
= 0; nFmt 
< WXSIZEOF(timeFormats
); nFmt
++ ) 
2401         if ( ParseFormat(time
, timeFormats
[nFmt
], end
) ) 
2408 // ---------------------------------------------------------------------------- 
2409 // Workdays and holidays support 
2410 // ---------------------------------------------------------------------------- 
2412 bool wxDateTime::IsWorkDay(Country 
WXUNUSED(country
)) const 
2414     return !wxDateTimeHolidayAuthority::IsHoliday(*this); 
2417 // ============================================================================ 
2419 // ============================================================================ 
2421 wxDateSpan WXDLLIMPEXP_BASE 
operator*(int n
, const wxDateSpan
& ds
) 
2424     return ds1
.Multiply(n
); 
2427 // ============================================================================ 
2429 // ============================================================================ 
2431 wxTimeSpan WXDLLIMPEXP_BASE 
operator*(int n
, const wxTimeSpan
& ts
) 
2433     return wxTimeSpan(ts
).Multiply(n
); 
2436 // this enum is only used in wxTimeSpan::Format() below but we can't declare 
2437 // it locally to the method as it provokes an internal compiler error in egcs 
2438 // 2.91.60 when building with -O2 
2449 // not all strftime(3) format specifiers make sense here because, for example, 
2450 // a time span doesn't have a year nor a timezone 
2452 // Here are the ones which are supported (all of them are supported by strftime 
2454 //  %H          hour in 24 hour format 
2455 //  %M          minute (00 - 59) 
2456 //  %S          second (00 - 59) 
2459 // Also, for MFC CTimeSpan compatibility, we support 
2460 //  %D          number of days 
2462 // And, to be better than MFC :-), we also have 
2463 //  %E          number of wEeks 
2464 //  %l          milliseconds (000 - 999) 
2465 wxString 
wxTimeSpan::Format(const wxString
& format
) const 
2467     // we deal with only positive time spans here and just add the sign in 
2468     // front for the negative ones 
2471         wxString 
str(Negate().Format(format
)); 
2475     wxCHECK_MSG( !format
.empty(), wxEmptyString
, 
2476                  _T("NULL format in wxTimeSpan::Format") ); 
2479     str
.Alloc(format
.length()); 
2481     // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */) 
2483     // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the 
2484     // question is what should ts.Format("%S") do? The code here returns "3273" 
2485     // in this case (i.e. the total number of seconds, not just seconds % 60) 
2486     // because, for me, this call means "give me entire time interval in 
2487     // seconds" and not "give me the seconds part of the time interval" 
2489     // If we agree that it should behave like this, it is clear that the 
2490     // interpretation of each format specifier depends on the presence of the 
2491     // other format specs in the string: if there was "%H" before "%M", we 
2492     // should use GetMinutes() % 60, otherwise just GetMinutes() &c 
2494     // we remember the most important unit found so far 
2495     TimeSpanPart partBiggest 
= Part_MSec
; 
2497     for ( wxString::const_iterator pch 
= format
.begin(); pch 
!= format
.end(); ++pch 
) 
2501         if ( ch 
== _T('%') ) 
2503             // the start of the format specification of the printf() below 
2504             wxString 
fmtPrefix(_T('%')); 
2509             // the number of digits for the format string, 0 if unused 
2510             unsigned digits 
= 0; 
2512             ch 
= *++pch
;    // get the format spec char 
2516                     wxFAIL_MSG( _T("invalid format character") ); 
2522                     // skip the part below switch 
2527                     if ( partBiggest 
< Part_Day 
) 
2533                         partBiggest 
= Part_Day
; 
2538                     partBiggest 
= Part_Week
; 
2544                     if ( partBiggest 
< Part_Hour 
) 
2550                         partBiggest 
= Part_Hour
; 
2557                     n 
= GetMilliseconds().ToLong(); 
2558                     if ( partBiggest 
< Part_MSec 
) 
2562                     //else: no need to reset partBiggest to Part_MSec, it is 
2563                     //      the least significant one anyhow 
2570                     if ( partBiggest 
< Part_Min 
) 
2576                         partBiggest 
= Part_Min
; 
2583                     n 
= GetSeconds().ToLong(); 
2584                     if ( partBiggest 
< Part_Sec 
) 
2590                         partBiggest 
= Part_Sec
; 
2599                 fmtPrefix 
<< _T("0") << digits
; 
2602             str 
+= wxString::Format(fmtPrefix 
+ _T("ld"), n
); 
2606             // normal character, just copy 
2614 #endif // wxUSE_DATETIME