1/////////////////////////////////////////////////////////////////////////////// 2// Name: src/common/datetime.cpp 3// Purpose: implementation of time/date related classes 4// Author: Vadim Zeitlin 5// Modified by: 6// Created: 11.05.99 7// RCS-ID: $Id: datetime.cpp 65730 2010-10-02 16:50:34Z TIK $ 8// Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 9// parts of code taken from sndcal library by Scott E. Lee: 10// 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. 15// 16// Licence: wxWindows licence 17/////////////////////////////////////////////////////////////////////////////// 18 19/* 20 * Implementation notes: 21 * 22 * 1. the time is stored as a 64bit integer containing the signed number of 23 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always 24 * expressed in GMT. 25 * 26 * 2. the range is thus something about 580 million years, but due to current 27 * algorithms limitations, only dates from Nov 24, 4714BC are handled 28 * 29 * 3. standard ANSI C functions are used to do time calculations whenever 30 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038 31 * 32 * 4. otherwise, the calculations are done by converting the date to/from JDN 33 * first (the range limitation mentioned above comes from here: the 34 * algorithm used by Scott E. Lee's code only works for positive JDNs, more 35 * or less) 36 * 37 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to 38 * this moment in local time and may be converted to the object 39 * corresponding to the same date/time in another time zone by using 40 * ToTimezone() 41 * 42 * 6. the conversions to the current (or any other) timezone are done when the 43 * internal time representation is converted to the broken-down one in 44 * wxDateTime::Tm. 45 */ 46 47// ============================================================================ 48// declarations 49// ============================================================================ 50 51// ---------------------------------------------------------------------------- 52// headers 53// ---------------------------------------------------------------------------- 54 55// For compilers that support precompilation, includes "wx.h". 56#include "wx/wxprec.h" 57 58#ifdef __BORLANDC__ 59 #pragma hdrstop 60#endif 61 62#if !defined(wxUSE_DATETIME) || wxUSE_DATETIME 63 64#ifndef WX_PRECOMP 65 #ifdef __WXMSW__ 66 #include "wx/msw/wrapwin.h" 67 #endif 68 #include "wx/string.h" 69 #include "wx/log.h" 70 #include "wx/intl.h" 71 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis() 72 #include "wx/module.h" 73#endif // WX_PRECOMP 74 75#include "wx/thread.h" 76#include "wx/tokenzr.h" 77 78#include <ctype.h> 79 80#ifdef __WINDOWS__ 81 #include <winnls.h> 82 #ifndef __WXWINCE__ 83 #include <locale.h> 84 #endif 85#endif 86 87#include "wx/datetime.h" 88 89const long wxDateTime::TIME_T_FACTOR = 1000l; 90 91#if wxUSE_EXTENDED_RTTI 92 93template<> void wxStringReadValue(const wxString &s , wxDateTime &data ) 94{ 95 data.ParseFormat(s,wxT("%Y-%m-%d %H:%M:%S")) ; 96} 97 98template<> void wxStringWriteValue(wxString &s , const wxDateTime &data ) 99{ 100 s = data.Format(wxT("%Y-%m-%d %H:%M:%S")) ; 101} 102 103wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringConverter<wxDateTime>) 104 105#endif 106 107// 108// ---------------------------------------------------------------------------- 109// conditional compilation 110// ---------------------------------------------------------------------------- 111 112#if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \ 113 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0)) 114 // glibc 2.0.7 strptime() is broken - the following snippet causes it to 115 // crash (instead of just failing): 116 // 117 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128); 118 // strptime(buf, "%x", &tm); 119 // 120 // so don't use it 121 #undef HAVE_STRPTIME 122#endif // broken strptime() 123 124#if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS 125 // configure detects strptime as linkable because it's in the OS X 126 // System library but MSL headers don't declare it. 127 128// char *strptime(const char *, const char *, struct tm *); 129 // However, we DON'T want to just provide it here because we would 130 // crash and/or overwrite data when strptime from OS X tries 131 // to fill in MW's struct tm which is two fields shorter (no TZ stuff) 132 // So for now let's just say we don't have strptime 133 #undef HAVE_STRPTIME 134#endif 135 136#if defined(__MWERKS__) && wxUSE_UNICODE 137 #include <wtime.h> 138#endif 139 140// define a special symbol for VC8 instead of writing tests for 1400 repeatedly 141#ifdef __VISUALC__ 142 #if __VISUALC__ >= 1400 143 #define __VISUALC8__ 144 #endif 145#endif 146 147#if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM) 148 #if defined(__WXPALMOS__) 149 #define WX_GMTOFF_IN_TM 150 #elif defined(__WXMSW__) 151 static long wxGetTimeZone() 152 { 153 TIME_ZONE_INFORMATION info; 154 GetTimeZoneInformation(&info); 155 long timeZone = info.Bias * 60; // convert minutes to seconds 156 return timeZone; 157 } 158 #define WX_TIMEZONE wxGetTimeZone() 159 #elif defined(__VISAGECPP__) 160 #define WX_TIMEZONE _timezone 161 #elif defined(__MWERKS__) 162 long wxmw_timezone = 28800; 163 #define WX_TIMEZONE wxmw_timezone 164 #elif defined(__DARWIN__) 165 #define WX_GMTOFF_IN_TM 166 #else // unknown platform - try timezone 167 #define WX_TIMEZONE timezone 168 #endif 169#endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM 170 171// everyone has strftime except Win CE unless VC8 is used 172#if !defined(__WXWINCE__) || defined(__VISUALC8__) 173 #define HAVE_STRFTIME 174#endif 175 176// NB: VC8 safe time functions could/should be used for wxMSW as well probably 177#if defined(__WXWINCE__) && defined(__VISUALC8__) 178 179struct tm *wxLocaltime_r(const time_t *t, struct tm* tm) 180{ 181 __time64_t t64 = *t; 182 return _localtime64_s(tm, &t64) == 0 ? tm : NULL; 183} 184 185struct tm *wxGmtime_r(const time_t* t, struct tm* tm) 186{ 187 __time64_t t64 = *t; 188 return _gmtime64_s(tm, &t64) == 0 ? tm : NULL; 189} 190 191#else // !wxWinCE with VC8 192 193#if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__) 194static wxMutex timeLock; 195#endif 196 197#ifndef HAVE_LOCALTIME_R 198struct tm *wxLocaltime_r(const time_t* ticks, struct tm* temp) 199{ 200#if wxUSE_THREADS && !defined(__WINDOWS__) 201 // No need to waste time with a mutex on windows since it's using 202 // thread local storage for localtime anyway. 203 wxMutexLocker locker(timeLock); 204#endif 205 206 // Borland CRT crashes when passed 0 ticks for some reason, see SF bug 1704438 207#ifdef __BORLANDC__ 208 if ( !*ticks ) 209 return NULL; 210#endif 211 212 const tm * const t = localtime(ticks); 213 if ( !t ) 214 return NULL; 215 216 memcpy(temp, t, sizeof(struct tm)); 217 return temp; 218} 219#endif // !HAVE_LOCALTIME_R 220 221#ifndef HAVE_GMTIME_R 222struct tm *wxGmtime_r(const time_t* ticks, struct tm* temp) 223{ 224#if wxUSE_THREADS && !defined(__WINDOWS__) 225 // No need to waste time with a mutex on windows since it's 226 // using thread local storage for gmtime anyway. 227 wxMutexLocker locker(timeLock); 228#endif 229 230#ifdef __BORLANDC__ 231 if ( !*ticks ) 232 return NULL; 233#endif 234 235 const tm * const t = gmtime(ticks); 236 if ( !t ) 237 return NULL; 238 239 memcpy(temp, gmtime(ticks), sizeof(struct tm)); 240 return temp; 241} 242#endif // !HAVE_GMTIME_R 243 244#endif // wxWinCE with VC8/other platforms 245 246// ---------------------------------------------------------------------------- 247// macros 248// ---------------------------------------------------------------------------- 249 250// debugging helper: just a convenient replacement of wxCHECK() 251#define wxDATETIME_CHECK(expr, msg) \ 252 wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg) 253 254// ---------------------------------------------------------------------------- 255// private classes 256// ---------------------------------------------------------------------------- 257 258class wxDateTimeHolidaysModule : public wxModule 259{ 260public: 261 virtual bool OnInit() 262 { 263 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays); 264 265 return true; 266 } 267 268 virtual void OnExit() 269 { 270 wxDateTimeHolidayAuthority::ClearAllAuthorities(); 271 wxDateTimeHolidayAuthority::ms_authorities.clear(); 272 } 273 274private: 275 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule) 276}; 277 278IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule) 279 280// ---------------------------------------------------------------------------- 281// constants 282// ---------------------------------------------------------------------------- 283 284// some trivial ones 285static const int MONTHS_IN_YEAR = 12; 286 287static const int SEC_PER_MIN = 60; 288 289static const int MIN_PER_HOUR = 60; 290 291static const int HOURS_PER_DAY = 24; 292 293static const long SECONDS_PER_DAY = 86400l; 294 295static const int DAYS_PER_WEEK = 7; 296 297static const long MILLISECONDS_PER_DAY = 86400000l; 298 299// this is the integral part of JDN of the midnight of Jan 1, 1970 300// (i.e. JDN(Jan 1, 1970) = 2440587.5) 301static const long EPOCH_JDN = 2440587l; 302 303// used only in asserts 304#ifdef __WXDEBUG__ 305// the date of JDN -0.5 (as we don't work with fractional parts, this is the 306// reference date for us) is Nov 24, 4714BC 307static const int JDN_0_YEAR = -4713; 308static const int JDN_0_MONTH = wxDateTime::Nov; 309static const int JDN_0_DAY = 24; 310#endif // __WXDEBUG__ 311 312// the constants used for JDN calculations 313static const long JDN_OFFSET = 32046l; 314static const long DAYS_PER_5_MONTHS = 153l; 315static const long DAYS_PER_4_YEARS = 1461l; 316static const long DAYS_PER_400_YEARS = 146097l; 317 318// this array contains the cumulated number of days in all previous months for 319// normal and leap years 320static const wxDateTime::wxDateTime_t gs_cumulatedDays[2][MONTHS_IN_YEAR] = 321{ 322 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, 323 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } 324}; 325 326// ---------------------------------------------------------------------------- 327// global data 328// ---------------------------------------------------------------------------- 329 330const wxChar * wxDefaultDateTimeFormat = wxT("%c"); 331const wxChar * wxDefaultTimeSpanFormat = wxT("%H:%M:%S"); 332 333// in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to 334// indicate an invalid wxDateTime object 335const wxDateTime wxDefaultDateTime; 336 337wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown; 338 339// ---------------------------------------------------------------------------- 340// private functions 341// ---------------------------------------------------------------------------- 342 343// debugger helper: shows what the date really is 344#ifdef __WXDEBUG__ 345extern const wxChar *wxDumpDate(const wxDateTime* dt) 346{ 347 static wxChar buf[128]; 348 349 wxStrcpy(buf, dt->Format(_T("%Y-%m-%d (%a) %H:%M:%S"))); 350 351 return buf; 352} 353#endif // Debug 354 355// get the number of days in the given month of the given year 356static inline 357wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month) 358{ 359 // the number of days in month in Julian/Gregorian calendar: the first line 360 // is for normal years, the second one is for the leap ones 361 static wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] = 362 { 363 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 364 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 365 }; 366 367 return daysInMonth[wxDateTime::IsLeapYear(year)][month]; 368} 369 370// returns the time zone in the C sense, i.e. the difference UTC - local 371// (in seconds) 372static int GetTimeZone() 373{ 374#ifdef WX_GMTOFF_IN_TM 375 // set to true when the timezone is set 376 static bool s_timezoneSet = false; 377 static long gmtoffset = LONG_MAX; // invalid timezone 378 379 // ensure that the timezone variable is set by calling wxLocaltime_r 380 if ( !s_timezoneSet ) 381 { 382 // just call wxLocaltime_r() instead of figuring out whether this 383 // system supports tzset(), _tzset() or something else 384 time_t t = 0; 385 struct tm tm; 386 387 wxLocaltime_r(&t, &tm); 388 s_timezoneSet = true; 389 390 // note that GMT offset is the opposite of time zone and so to return 391 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM 392 // cases we have to negate it 393 gmtoffset = -tm.tm_gmtoff; 394 } 395 return (int)gmtoffset; 396#else // !WX_GMTOFF_IN_TM 397 return WX_TIMEZONE; 398#endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM 399} 400 401// return the integral part of the JDN for the midnight of the given date (to 402// get the real JDN you need to add 0.5, this is, in fact, JDN of the 403// noon of the previous day) 404static long GetTruncatedJDN(wxDateTime::wxDateTime_t day, 405 wxDateTime::Month mon, 406 int year) 407{ 408 // CREDIT: code below is by Scott E. Lee (but bugs are mine) 409 410 // check the date validity 411 wxASSERT_MSG( 412 (year > JDN_0_YEAR) || 413 ((year == JDN_0_YEAR) && (mon > JDN_0_MONTH)) || 414 ((year == JDN_0_YEAR) && (mon == JDN_0_MONTH) && (day >= JDN_0_DAY)), 415 _T("date out of range - can't convert to JDN") 416 ); 417 418 // make the year positive to avoid problems with negative numbers division 419 year += 4800; 420 421 // months are counted from March here 422 int month; 423 if ( mon >= wxDateTime::Mar ) 424 { 425 month = mon - 2; 426 } 427 else 428 { 429 month = mon + 10; 430 year--; 431 } 432 433 // now we can simply add all the contributions together 434 return ((year / 100) * DAYS_PER_400_YEARS) / 4 435 + ((year % 100) * DAYS_PER_4_YEARS) / 4 436 + (month * DAYS_PER_5_MONTHS + 2) / 5 437 + day 438 - JDN_OFFSET; 439} 440 441#ifdef HAVE_STRFTIME 442 443// this function is a wrapper around strftime(3) adding error checking 444static wxString CallStrftime(const wxChar *format, const tm* tm) 445{ 446 wxChar buf[4096]; 447 // Create temp wxString here to work around mingw/cygwin bug 1046059 448 // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435 449 wxString s; 450 451 if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) ) 452 { 453 // buffer is too small? 454 wxFAIL_MSG(_T("strftime() failed")); 455 } 456 457 s = buf; 458 return s; 459} 460 461#endif // HAVE_STRFTIME 462 463#ifdef HAVE_STRPTIME 464 465#if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL) 466 // configure detected that we had strptime() but not its declaration, 467 // provide it ourselves 468 extern "C" char *strptime(const char *, const char *, struct tm *); 469#endif 470 471// Unicode-friendly strptime() wrapper 472static const wxChar * 473CallStrptime(const wxChar *input, const char *fmt, tm *tm) 474{ 475 // the problem here is that strptime() returns pointer into the string we 476 // passed to it while we're really interested in the pointer into the 477 // original, Unicode, string so we try to transform the pointer back 478#if wxUSE_UNICODE 479 wxCharBuffer inputMB(wxConvertWX2MB(input)); 480#else // ASCII 481 const char * const inputMB = input; 482#endif // Unicode/Ascii 483 484 const char *result = strptime(inputMB, fmt, tm); 485 if ( !result ) 486 return NULL; 487 488#if wxUSE_UNICODE 489 // FIXME: this is wrong in presence of surrogates &c 490 return input + (result - inputMB.data()); 491#else // ASCII 492 return result; 493#endif // Unicode/Ascii 494} 495 496#endif // HAVE_STRPTIME 497 498// if year and/or month have invalid values, replace them with the current ones 499static void ReplaceDefaultYearMonthWithCurrent(int *year, 500 wxDateTime::Month *month) 501{ 502 struct tm *tmNow = NULL; 503 struct tm tmstruct; 504 505 if ( *year == wxDateTime::Inv_Year ) 506 { 507 tmNow = wxDateTime::GetTmNow(&tmstruct); 508 509 *year = 1900 + tmNow->tm_year; 510 } 511 512 if ( *month == wxDateTime::Inv_Month ) 513 { 514 if ( !tmNow ) 515 tmNow = wxDateTime::GetTmNow(&tmstruct); 516 517 *month = (wxDateTime::Month)tmNow->tm_mon; 518 } 519} 520 521// fll the struct tm with default values 522static void InitTm(struct tm& tm) 523{ 524 // struct tm may have etxra fields (undocumented and with unportable 525 // names) which, nevertheless, must be set to 0 526 memset(&tm, 0, sizeof(struct tm)); 527 528 tm.tm_mday = 1; // mday 0 is invalid 529 tm.tm_year = 76; // any valid year 530 tm.tm_isdst = -1; // auto determine 531} 532 533// parsing helpers 534// --------------- 535 536// return the month if the string is a month name or Inv_Month otherwise 537static wxDateTime::Month GetMonthFromName(const wxString& name, int flags) 538{ 539 wxDateTime::Month mon; 540 for ( mon = wxDateTime::Jan; mon < wxDateTime::Inv_Month; wxNextMonth(mon) ) 541 { 542 // case-insensitive comparison either one of or with both abbreviated 543 // and not versions 544 if ( flags & wxDateTime::Name_Full ) 545 { 546 if ( name.CmpNoCase(wxDateTime:: 547 GetMonthName(mon, wxDateTime::Name_Full)) == 0 ) 548 { 549 break; 550 } 551 } 552 553 if ( flags & wxDateTime::Name_Abbr ) 554 { 555 if ( name.CmpNoCase(wxDateTime:: 556 GetMonthName(mon, wxDateTime::Name_Abbr)) == 0 ) 557 { 558 break; 559 } 560 } 561 } 562 563 return mon; 564} 565 566// return the weekday if the string is a weekday name or Inv_WeekDay otherwise 567static wxDateTime::WeekDay GetWeekDayFromName(const wxString& name, int flags) 568{ 569 wxDateTime::WeekDay wd; 570 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) ) 571 { 572 // case-insensitive comparison either one of or with both abbreviated 573 // and not versions 574 if ( flags & wxDateTime::Name_Full ) 575 { 576 if ( name.CmpNoCase(wxDateTime:: 577 GetWeekDayName(wd, wxDateTime::Name_Full)) == 0 ) 578 { 579 break; 580 } 581 } 582 583 if ( flags & wxDateTime::Name_Abbr ) 584 { 585 if ( name.CmpNoCase(wxDateTime:: 586 GetWeekDayName(wd, wxDateTime::Name_Abbr)) == 0 ) 587 { 588 break; 589 } 590 } 591 } 592 593 return wd; 594} 595 596/* static */ 597struct tm *wxDateTime::GetTmNow(struct tm *tmstruct) 598{ 599 time_t t = GetTimeNow(); 600 return wxLocaltime_r(&t, tmstruct); 601} 602 603// scans all digits (but no more than len) and returns the resulting number 604static bool GetNumericToken(size_t len, const wxChar*& p, unsigned long *number) 605{ 606 size_t n = 1; 607 wxString s; 608 while ( wxIsdigit(*p) ) 609 { 610 s += *p++; 611 612 if ( len && ++n > len ) 613 break; 614 } 615 616 return !s.empty() && s.ToULong(number); 617} 618 619// scans all alphabetic characters and returns the resulting string 620static wxString GetAlphaToken(const wxChar*& p) 621{ 622 wxString s; 623 while ( wxIsalpha(*p) ) 624 { 625 s += *p++; 626 } 627 628 return s; 629} 630 631// ============================================================================ 632// implementation of wxDateTime 633// ============================================================================ 634 635// ---------------------------------------------------------------------------- 636// struct Tm 637// ---------------------------------------------------------------------------- 638 639wxDateTime::Tm::Tm() 640{ 641 year = (wxDateTime_t)wxDateTime::Inv_Year; 642 mon = wxDateTime::Inv_Month; 643 mday = 0; 644 hour = min = sec = msec = 0; 645 wday = wxDateTime::Inv_WeekDay; 646} 647 648wxDateTime::Tm::Tm(const struct tm& tm, const TimeZone& tz) 649 : m_tz(tz) 650{ 651 msec = 0; 652 sec = (wxDateTime::wxDateTime_t)tm.tm_sec; 653 min = (wxDateTime::wxDateTime_t)tm.tm_min; 654 hour = (wxDateTime::wxDateTime_t)tm.tm_hour; 655 mday = (wxDateTime::wxDateTime_t)tm.tm_mday; 656 mon = (wxDateTime::Month)tm.tm_mon; 657 year = 1900 + tm.tm_year; 658 wday = (wxDateTime::wxDateTime_t)tm.tm_wday; 659 yday = (wxDateTime::wxDateTime_t)tm.tm_yday; 660} 661 662bool wxDateTime::Tm::IsValid() const 663{ 664 // we allow for the leap seconds, although we don't use them (yet) 665 return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) && 666 (mday <= GetNumOfDaysInMonth(year, mon)) && 667 (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000); 668} 669 670void wxDateTime::Tm::ComputeWeekDay() 671{ 672 // compute the week day from day/month/year: we use the dumbest algorithm 673 // possible: just compute our JDN and then use the (simple to derive) 674 // formula: weekday = (JDN + 1.5) % 7 675 wday = (wxDateTime::wxDateTime_t)((GetTruncatedJDN(mday, mon, year) + 2) % 7); 676} 677 678void wxDateTime::Tm::AddMonths(int monDiff) 679{ 680 // normalize the months field 681 while ( monDiff < -mon ) 682 { 683 year--; 684 685 monDiff += MONTHS_IN_YEAR; 686 } 687 688 while ( monDiff + mon >= MONTHS_IN_YEAR ) 689 { 690 year++; 691 692 monDiff -= MONTHS_IN_YEAR; 693 } 694 695 mon = (wxDateTime::Month)(mon + monDiff); 696 697 wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") ); 698 699 // NB: we don't check here that the resulting date is valid, this function 700 // is private and the caller must check it if needed 701} 702 703void wxDateTime::Tm::AddDays(int dayDiff) 704{ 705 // normalize the days field 706 while ( dayDiff + mday < 1 ) 707 { 708 AddMonths(-1); 709 710 dayDiff += GetNumOfDaysInMonth(year, mon); 711 } 712 713 mday = (wxDateTime::wxDateTime_t)( mday + dayDiff ); 714 while ( mday > GetNumOfDaysInMonth(year, mon) ) 715 { 716 mday -= GetNumOfDaysInMonth(year, mon); 717 718 AddMonths(1); 719 } 720 721 wxASSERT_MSG( mday > 0 && mday <= GetNumOfDaysInMonth(year, mon), 722 _T("logic error") ); 723} 724 725// ---------------------------------------------------------------------------- 726// class TimeZone 727// ---------------------------------------------------------------------------- 728 729wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz) 730{ 731 switch ( tz ) 732 { 733 case wxDateTime::Local: 734 // get the offset from C RTL: it returns the difference GMT-local 735 // while we want to have the offset _from_ GMT, hence the '-' 736 m_offset = -GetTimeZone(); 737 break; 738 739 case wxDateTime::GMT_12: 740 case wxDateTime::GMT_11: 741 case wxDateTime::GMT_10: 742 case wxDateTime::GMT_9: 743 case wxDateTime::GMT_8: 744 case wxDateTime::GMT_7: 745 case wxDateTime::GMT_6: 746 case wxDateTime::GMT_5: 747 case wxDateTime::GMT_4: 748 case wxDateTime::GMT_3: 749 case wxDateTime::GMT_2: 750 case wxDateTime::GMT_1: 751 m_offset = -3600*(wxDateTime::GMT0 - tz); 752 break; 753 754 case wxDateTime::GMT0: 755 case wxDateTime::GMT1: 756 case wxDateTime::GMT2: 757 case wxDateTime::GMT3: 758 case wxDateTime::GMT4: 759 case wxDateTime::GMT5: 760 case wxDateTime::GMT6: 761 case wxDateTime::GMT7: 762 case wxDateTime::GMT8: 763 case wxDateTime::GMT9: 764 case wxDateTime::GMT10: 765 case wxDateTime::GMT11: 766 case wxDateTime::GMT12: 767 case wxDateTime::GMT13: 768 m_offset = 3600*(tz - wxDateTime::GMT0); 769 break; 770 771 case wxDateTime::A_CST: 772 // Central Standard Time in use in Australia = UTC + 9.5 773 m_offset = 60l*(9*MIN_PER_HOUR + MIN_PER_HOUR/2); 774 break; 775 776 default: 777 wxFAIL_MSG( _T("unknown time zone") ); 778 } 779} 780 781// ---------------------------------------------------------------------------- 782// static functions 783// ---------------------------------------------------------------------------- 784 785/* static */ 786bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal) 787{ 788 if ( year == Inv_Year ) 789 year = GetCurrentYear(); 790 791 if ( cal == Gregorian ) 792 { 793 // in Gregorian calendar leap years are those divisible by 4 except 794 // those divisible by 100 unless they're also divisible by 400 795 // (in some countries, like Russia and Greece, additional corrections 796 // exist, but they won't manifest themselves until 2700) 797 return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); 798 } 799 else if ( cal == Julian ) 800 { 801 // in Julian calendar the rule is simpler 802 return year % 4 == 0; 803 } 804 else 805 { 806 wxFAIL_MSG(_T("unknown calendar")); 807 808 return false; 809 } 810} 811 812/* static */ 813int wxDateTime::GetCentury(int year) 814{ 815 return year > 0 ? year / 100 : year / 100 - 1; 816} 817 818/* static */ 819int wxDateTime::ConvertYearToBC(int year) 820{ 821 // year 0 is BC 1 822 return year > 0 ? year : year - 1; 823} 824 825/* static */ 826int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal) 827{ 828 switch ( cal ) 829 { 830 case Gregorian: 831 return Now().GetYear(); 832 833 case Julian: 834 wxFAIL_MSG(_T("TODO")); 835 break; 836 837 default: 838 wxFAIL_MSG(_T("unsupported calendar")); 839 break; 840 } 841 842 return Inv_Year; 843} 844 845/* static */ 846wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal) 847{ 848 switch ( cal ) 849 { 850 case Gregorian: 851 return Now().GetMonth(); 852 853 case Julian: 854 wxFAIL_MSG(_T("TODO")); 855 break; 856 857 default: 858 wxFAIL_MSG(_T("unsupported calendar")); 859 break; 860 } 861 862 return Inv_Month; 863} 864 865/* static */ 866wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal) 867{ 868 if ( year == Inv_Year ) 869 { 870 // take the current year if none given 871 year = GetCurrentYear(); 872 } 873 874 switch ( cal ) 875 { 876 case Gregorian: 877 case Julian: 878 return IsLeapYear(year) ? 366 : 365; 879 880 default: 881 wxFAIL_MSG(_T("unsupported calendar")); 882 break; 883 } 884 885 return 0; 886} 887 888/* static */ 889wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month, 890 int year, 891 wxDateTime::Calendar cal) 892{ 893 wxCHECK_MSG( month < MONTHS_IN_YEAR, 0, _T("invalid month") ); 894 895 if ( cal == Gregorian || cal == Julian ) 896 { 897 if ( year == Inv_Year ) 898 { 899 // take the current year if none given 900 year = GetCurrentYear(); 901 } 902 903 return GetNumOfDaysInMonth(year, month); 904 } 905 else 906 { 907 wxFAIL_MSG(_T("unsupported calendar")); 908 909 return 0; 910 } 911} 912 913/* static */ 914wxString wxDateTime::GetMonthName(wxDateTime::Month month, 915 wxDateTime::NameFlags flags) 916{ 917 wxCHECK_MSG( month != Inv_Month, wxEmptyString, _T("invalid month") ); 918#ifdef HAVE_STRFTIME 919 // notice that we must set all the fields to avoid confusing libc (GNU one 920 // gets confused to a crash if we don't do this) 921 tm tm; 922 InitTm(tm); 923 tm.tm_mon = month; 924 925 return CallStrftime(flags == Name_Abbr ? _T("%b") : _T("%B"), &tm); 926#else // !HAVE_STRFTIME 927 wxString ret; 928 switch(month) 929 { 930 case Jan: 931 ret = (flags == Name_Abbr ? wxT("Jan"): wxT("January")); 932 break; 933 case Feb: 934 ret = (flags == Name_Abbr ? wxT("Feb"): wxT("Febuary")); 935 break; 936 case Mar: 937 ret = (flags == Name_Abbr ? wxT("Mar"): wxT("March")); 938 break; 939 case Apr: 940 ret = (flags == Name_Abbr ? wxT("Apr"): wxT("April")); 941 break; 942 case May: 943 ret = (flags == Name_Abbr ? wxT("May"): wxT("May")); 944 break; 945 case Jun: 946 ret = (flags == Name_Abbr ? wxT("Jun"): wxT("June")); 947 break; 948 case Jul: 949 ret = (flags == Name_Abbr ? wxT("Jul"): wxT("July")); 950 break; 951 case Aug: 952 ret = (flags == Name_Abbr ? wxT("Aug"): wxT("August")); 953 break; 954 case Sep: 955 ret = (flags == Name_Abbr ? wxT("Sep"): wxT("September")); 956 break; 957 case Oct: 958 ret = (flags == Name_Abbr ? wxT("Oct"): wxT("October")); 959 break; 960 case Nov: 961 ret = (flags == Name_Abbr ? wxT("Nov"): wxT("November")); 962 break; 963 case Dec: 964 ret = (flags == Name_Abbr ? wxT("Dec"): wxT("December")); 965 break; 966 } 967 return ret; 968#endif // HAVE_STRFTIME/!HAVE_STRFTIME 969} 970 971/* static */ 972wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday, 973 wxDateTime::NameFlags flags) 974{ 975 wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, _T("invalid weekday") ); 976#ifdef HAVE_STRFTIME 977 // take some arbitrary Sunday (but notice that the day should be such that 978 // after adding wday to it below we still have a valid date, e.g. don't 979 // take 28 here!) 980 tm tm; 981 InitTm(tm); 982 tm.tm_mday = 21; 983 tm.tm_mon = Nov; 984 tm.tm_year = 99; 985 986 // and offset it by the number of days needed to get the correct wday 987 tm.tm_mday += wday; 988 989 // call mktime() to normalize it... 990 (void)mktime(&tm); 991 992 // ... and call strftime() 993 return CallStrftime(flags == Name_Abbr ? _T("%a") : _T("%A"), &tm); 994#else // !HAVE_STRFTIME 995 wxString ret; 996 switch(wday) 997 { 998 case Sun: 999 ret = (flags == Name_Abbr ? wxT("Sun") : wxT("Sunday")); 1000 break; 1001 case Mon: 1002 ret = (flags == Name_Abbr ? wxT("Mon") : wxT("Monday")); 1003 break; 1004 case Tue: 1005 ret = (flags == Name_Abbr ? wxT("Tue") : wxT("Tuesday")); 1006 break; 1007 case Wed: 1008 ret = (flags == Name_Abbr ? wxT("Wed") : wxT("Wednesday")); 1009 break; 1010 case Thu: 1011 ret = (flags == Name_Abbr ? wxT("Thu") : wxT("Thursday")); 1012 break; 1013 case Fri: 1014 ret = (flags == Name_Abbr ? wxT("Fri") : wxT("Friday")); 1015 break; 1016 case Sat: 1017 ret = (flags == Name_Abbr ? wxT("Sat") : wxT("Saturday")); 1018 break; 1019 } 1020 return ret; 1021#endif // HAVE_STRFTIME/!HAVE_STRFTIME 1022} 1023 1024/* static */ 1025void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm) 1026{ 1027 tm tm; 1028 InitTm(tm); 1029 wxChar buffer[64]; 1030 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code 1031 // and causes an assertion failed if the buffer is to small (which is good) - OR - 1032 // if strftime does not return anything because the format string is invalid - OR - 1033 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good). 1034 // wxDateTime::ParseTime will try several different formats to parse the time. 1035 // As a result, GetAmPmStrings might get called, even if the current locale 1036 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would 1037 // assert, even though it is a perfectly legal use. 1038 if ( am ) 1039 { 1040 if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0) 1041 *am = wxString(buffer); 1042 else 1043 *am = wxString(); 1044 } 1045 if ( pm ) 1046 { 1047 tm.tm_hour = 13; 1048 if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0) 1049 *pm = wxString(buffer); 1050 else 1051 *pm = wxString(); 1052 } 1053} 1054 1055// ---------------------------------------------------------------------------- 1056// Country stuff: date calculations depend on the country (DST, work days, 1057// ...), so we need to know which rules to follow. 1058// ---------------------------------------------------------------------------- 1059 1060/* static */ 1061wxDateTime::Country wxDateTime::GetCountry() 1062{ 1063 // TODO use LOCALE_ICOUNTRY setting under Win32 1064#ifndef __WXWINCE__ 1065 if ( ms_country == Country_Unknown ) 1066 { 1067 // try to guess from the time zone name 1068 time_t t = time(NULL); 1069 struct tm tmstruct; 1070 struct tm *tm = wxLocaltime_r(&t, &tmstruct); 1071 1072 wxString tz = CallStrftime(_T("%Z"), tm); 1073 if ( tz == _T("WET") || tz == _T("WEST") ) 1074 { 1075 ms_country = UK; 1076 } 1077 else if ( tz == _T("CET") || tz == _T("CEST") ) 1078 { 1079 ms_country = Country_EEC; 1080 } 1081 else if ( tz == _T("MSK") || tz == _T("MSD") ) 1082 { 1083 ms_country = Russia; 1084 } 1085 else if ( tz == _T("AST") || tz == _T("ADT") || 1086 tz == _T("EST") || tz == _T("EDT") || 1087 tz == _T("CST") || tz == _T("CDT") || 1088 tz == _T("MST") || tz == _T("MDT") || 1089 tz == _T("PST") || tz == _T("PDT") ) 1090 { 1091 ms_country = USA; 1092 } 1093 else 1094 { 1095 // well, choose a default one 1096 ms_country = USA; 1097 } 1098 } 1099#else // __WXWINCE__ 1100 ms_country = USA; 1101#endif // !__WXWINCE__/__WXWINCE__ 1102 1103 return ms_country; 1104} 1105 1106/* static */ 1107void wxDateTime::SetCountry(wxDateTime::Country country) 1108{ 1109 ms_country = country; 1110} 1111 1112/* static */ 1113bool wxDateTime::IsWestEuropeanCountry(Country country) 1114{ 1115 if ( country == Country_Default ) 1116 { 1117 country = GetCountry(); 1118 } 1119 1120 return (Country_WesternEurope_Start <= country) && 1121 (country <= Country_WesternEurope_End); 1122} 1123 1124// ---------------------------------------------------------------------------- 1125// DST calculations: we use 3 different rules for the West European countries, 1126// USA and for the rest of the world. This is undoubtedly false for many 1127// countries, but I lack the necessary info (and the time to gather it), 1128// please add the other rules here! 1129// ---------------------------------------------------------------------------- 1130 1131/* static */ 1132bool wxDateTime::IsDSTApplicable(int year, Country country) 1133{ 1134 if ( year == Inv_Year ) 1135 { 1136 // take the current year if none given 1137 year = GetCurrentYear(); 1138 } 1139 1140 if ( country == Country_Default ) 1141 { 1142 country = GetCountry(); 1143 } 1144 1145 switch ( country ) 1146 { 1147 case USA: 1148 case UK: 1149 // DST was first observed in the US and UK during WWI, reused 1150 // during WWII and used again since 1966 1151 return year >= 1966 || 1152 (year >= 1942 && year <= 1945) || 1153 (year == 1918 || year == 1919); 1154 1155 default: 1156 // assume that it started after WWII 1157 return year > 1950; 1158 } 1159} 1160 1161/* static */ 1162wxDateTime wxDateTime::GetBeginDST(int year, Country country) 1163{ 1164 if ( year == Inv_Year ) 1165 { 1166 // take the current year if none given 1167 year = GetCurrentYear(); 1168 } 1169 1170 if ( country == Country_Default ) 1171 { 1172 country = GetCountry(); 1173 } 1174 1175 if ( !IsDSTApplicable(year, country) ) 1176 { 1177 return wxInvalidDateTime; 1178 } 1179 1180 wxDateTime dt; 1181 1182 if ( IsWestEuropeanCountry(country) || (country == Russia) ) 1183 { 1184 // DST begins at 1 a.m. GMT on the last Sunday of March 1185 if ( !dt.SetToLastWeekDay(Sun, Mar, year) ) 1186 { 1187 // weird... 1188 wxFAIL_MSG( _T("no last Sunday in March?") ); 1189 } 1190 1191 dt += wxTimeSpan::Hours(1); 1192 1193 // disable DST tests because it could result in an infinite recursion! 1194 dt.MakeGMT(true); 1195 } 1196 else switch ( country ) 1197 { 1198 case USA: 1199 switch ( year ) 1200 { 1201 case 1918: 1202 case 1919: 1203 // don't know for sure - assume it was in effect all year 1204 1205 case 1943: 1206 case 1944: 1207 case 1945: 1208 dt.Set(1, Jan, year); 1209 break; 1210 1211 case 1942: 1212 // DST was installed Feb 2, 1942 by the Congress 1213 dt.Set(2, Feb, year); 1214 break; 1215 1216 // Oil embargo changed the DST period in the US 1217 case 1974: 1218 dt.Set(6, Jan, 1974); 1219 break; 1220 1221 case 1975: 1222 dt.Set(23, Feb, 1975); 1223 break; 1224 1225 default: 1226 // before 1986, DST begun on the last Sunday of April, but 1227 // in 1986 Reagan changed it to begin at 2 a.m. of the 1228 // first Sunday in April 1229 if ( year < 1986 ) 1230 { 1231 if ( !dt.SetToLastWeekDay(Sun, Apr, year) ) 1232 { 1233 // weird... 1234 wxFAIL_MSG( _T("no first Sunday in April?") ); 1235 } 1236 } 1237 else if ( year > 2006 ) 1238 // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005). 1239 // Starting in 2007, daylight time begins in the United States on the 1240 // second Sunday in March and ends on the first Sunday in November 1241 { 1242 if ( !dt.SetToWeekDay(Sun, 2, Mar, year) ) 1243 { 1244 // weird... 1245 wxFAIL_MSG( _T("no second Sunday in March?") ); 1246 } 1247 } 1248 else 1249 { 1250 if ( !dt.SetToWeekDay(Sun, 1, Apr, year) ) 1251 { 1252 // weird... 1253 wxFAIL_MSG( _T("no first Sunday in April?") ); 1254 } 1255 } 1256 1257 dt += wxTimeSpan::Hours(2); 1258 1259 // TODO what about timezone?? 1260 } 1261 1262 break; 1263 1264 default: 1265 // assume Mar 30 as the start of the DST for the rest of the world 1266 // - totally bogus, of course 1267 dt.Set(30, Mar, year); 1268 } 1269 1270 return dt; 1271} 1272 1273/* static */ 1274wxDateTime wxDateTime::GetEndDST(int year, Country country) 1275{ 1276 if ( year == Inv_Year ) 1277 { 1278 // take the current year if none given 1279 year = GetCurrentYear(); 1280 } 1281 1282 if ( country == Country_Default ) 1283 { 1284 country = GetCountry(); 1285 } 1286 1287 if ( !IsDSTApplicable(year, country) ) 1288 { 1289 return wxInvalidDateTime; 1290 } 1291 1292 wxDateTime dt; 1293 1294 if ( IsWestEuropeanCountry(country) || (country == Russia) ) 1295 { 1296 // DST ends at 1 a.m. GMT on the last Sunday of October 1297 if ( !dt.SetToLastWeekDay(Sun, Oct, year) ) 1298 { 1299 // weirder and weirder... 1300 wxFAIL_MSG( _T("no last Sunday in October?") ); 1301 } 1302 1303 dt += wxTimeSpan::Hours(1); 1304 1305 // disable DST tests because it could result in an infinite recursion! 1306 dt.MakeGMT(true); 1307 } 1308 else switch ( country ) 1309 { 1310 case USA: 1311 switch ( year ) 1312 { 1313 case 1918: 1314 case 1919: 1315 // don't know for sure - assume it was in effect all year 1316 1317 case 1943: 1318 case 1944: 1319 dt.Set(31, Dec, year); 1320 break; 1321 1322 case 1945: 1323 // the time was reset after the end of the WWII 1324 dt.Set(30, Sep, year); 1325 break; 1326 1327 default: // default for switch (year) 1328 if ( year > 2006 ) 1329 // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005). 1330 // Starting in 2007, daylight time begins in the United States on the 1331 // second Sunday in March and ends on the first Sunday in November 1332 { 1333 if ( !dt.SetToWeekDay(Sun, 1, Nov, year) ) 1334 { 1335 // weird... 1336 wxFAIL_MSG( _T("no first Sunday in November?") ); 1337 } 1338 } 1339 else 1340 // pre-2007 1341 // DST ends at 2 a.m. on the last Sunday of October 1342 { 1343 if ( !dt.SetToLastWeekDay(Sun, Oct, year) ) 1344 { 1345 // weirder and weirder... 1346 wxFAIL_MSG( _T("no last Sunday in October?") ); 1347 } 1348 } 1349 1350 dt += wxTimeSpan::Hours(2); 1351 1352 // TODO: what about timezone?? 1353 } 1354 break; 1355 1356 default: // default for switch (country) 1357 // assume October 26th as the end of the DST - totally bogus too 1358 dt.Set(26, Oct, year); 1359 } 1360 1361 return dt; 1362} 1363 1364// ---------------------------------------------------------------------------- 1365// constructors and assignment operators 1366// ---------------------------------------------------------------------------- 1367 1368// return the current time with ms precision 1369/* static */ wxDateTime wxDateTime::UNow() 1370{ 1371 return wxDateTime(wxGetLocalTimeMillis()); 1372} 1373 1374// the values in the tm structure contain the local time 1375wxDateTime& wxDateTime::Set(const struct tm& tm) 1376{ 1377 struct tm tm2(tm); 1378 time_t timet = mktime(&tm2); 1379 1380 if ( timet == (time_t)-1 ) 1381 { 1382 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is 1383 // less than timezone - try to make it work for this case 1384 if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 ) 1385 { 1386 return Set((time_t)( 1387 GetTimeZone() + 1388 tm2.tm_hour * MIN_PER_HOUR * SEC_PER_MIN + 1389 tm2.tm_min * SEC_PER_MIN + 1390 tm2.tm_sec)); 1391 } 1392 1393 wxFAIL_MSG( _T("mktime() failed") ); 1394 1395 *this = wxInvalidDateTime; 1396 1397 return *this; 1398 } 1399 else 1400 { 1401 return Set(timet); 1402 } 1403} 1404 1405wxDateTime& wxDateTime::Set(wxDateTime_t hour, 1406 wxDateTime_t minute, 1407 wxDateTime_t second, 1408 wxDateTime_t millisec) 1409{ 1410 // we allow seconds to be 61 to account for the leap seconds, even if we 1411 // don't use them really 1412 wxDATETIME_CHECK( hour < 24 && 1413 second < 62 && 1414 minute < 60 && 1415 millisec < 1000, 1416 _T("Invalid time in wxDateTime::Set()") ); 1417 1418 // get the current date from system 1419 struct tm tmstruct; 1420 struct tm *tm = GetTmNow(&tmstruct); 1421 1422 wxDATETIME_CHECK( tm, _T("wxLocaltime_r() failed") ); 1423 1424 // make a copy so it isn't clobbered by the call to mktime() below 1425 struct tm tm1(*tm); 1426 1427 // adjust the time 1428 tm1.tm_hour = hour; 1429 tm1.tm_min = minute; 1430 tm1.tm_sec = second; 1431 1432 // and the DST in case it changes on this date 1433 struct tm tm2(tm1); 1434 mktime(&tm2); 1435 if ( tm2.tm_isdst != tm1.tm_isdst ) 1436 tm1.tm_isdst = tm2.tm_isdst; 1437 1438 (void)Set(tm1); 1439 1440 // and finally adjust milliseconds 1441 return SetMillisecond(millisec); 1442} 1443 1444wxDateTime& wxDateTime::Set(wxDateTime_t day, 1445 Month month, 1446 int year, 1447 wxDateTime_t hour, 1448 wxDateTime_t minute, 1449 wxDateTime_t second, 1450 wxDateTime_t millisec) 1451{ 1452 wxDATETIME_CHECK( hour < 24 && 1453 second < 62 && 1454 minute < 60 && 1455 millisec < 1000, 1456 _T("Invalid time in wxDateTime::Set()") ); 1457 1458 ReplaceDefaultYearMonthWithCurrent(&year, &month); 1459 1460 wxDATETIME_CHECK( (0 < day) && (day <= GetNumberOfDays(month, year)), 1461 _T("Invalid date in wxDateTime::Set()") ); 1462 1463 // the range of time_t type (inclusive) 1464 static const int yearMinInRange = 1970; 1465 static const int yearMaxInRange = 2037; 1466 1467 // test only the year instead of testing for the exact end of the Unix 1468 // time_t range - it doesn't bring anything to do more precise checks 1469 if ( year >= yearMinInRange && year <= yearMaxInRange ) 1470 { 1471 // use the standard library version if the date is in range - this is 1472 // probably more efficient than our code 1473 struct tm tm; 1474 tm.tm_year = year - 1900; 1475 tm.tm_mon = month; 1476 tm.tm_mday = day; 1477 tm.tm_hour = hour; 1478 tm.tm_min = minute; 1479 tm.tm_sec = second; 1480 tm.tm_isdst = -1; // mktime() will guess it 1481 1482 (void)Set(tm); 1483 1484 // and finally adjust milliseconds 1485 if (IsValid()) 1486 SetMillisecond(millisec); 1487 1488 return *this; 1489 } 1490 else 1491 { 1492 // do time calculations ourselves: we want to calculate the number of 1493 // milliseconds between the given date and the epoch 1494 1495 // get the JDN for the midnight of this day 1496 m_time = GetTruncatedJDN(day, month, year); 1497 m_time -= EPOCH_JDN; 1498 m_time *= SECONDS_PER_DAY * TIME_T_FACTOR; 1499 1500 // JDN corresponds to GMT, we take localtime 1501 Add(wxTimeSpan(hour, minute, second + GetTimeZone(), millisec)); 1502 } 1503 1504 return *this; 1505} 1506 1507wxDateTime& wxDateTime::Set(double jdn) 1508{ 1509 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn 1510 // EPOCH_JDN + 0.5 1511 jdn -= EPOCH_JDN + 0.5; 1512 1513 m_time.Assign(jdn*MILLISECONDS_PER_DAY); 1514 1515 // JDNs always are in UTC, so we don't need any adjustments for time zone 1516 1517 return *this; 1518} 1519 1520wxDateTime& wxDateTime::ResetTime() 1521{ 1522 Tm tm = GetTm(); 1523 1524 if ( tm.hour || tm.min || tm.sec || tm.msec ) 1525 { 1526 tm.msec = 1527 tm.sec = 1528 tm.min = 1529 tm.hour = 0; 1530 1531 Set(tm); 1532 } 1533 1534 return *this; 1535} 1536 1537wxDateTime wxDateTime::GetDateOnly() const 1538{ 1539 Tm tm = GetTm(); 1540 tm.msec = 1541 tm.sec = 1542 tm.min = 1543 tm.hour = 0; 1544 return wxDateTime(tm); 1545} 1546 1547// ---------------------------------------------------------------------------- 1548// DOS Date and Time Format functions 1549// ---------------------------------------------------------------------------- 1550// the dos date and time value is an unsigned 32 bit value in the format: 1551// YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss 1552// 1553// Y = year offset from 1980 (0-127) 1554// M = month (1-12) 1555// D = day of month (1-31) 1556// h = hour (0-23) 1557// m = minute (0-59) 1558// s = bisecond (0-29) each bisecond indicates two seconds 1559// ---------------------------------------------------------------------------- 1560 1561wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt) 1562{ 1563 struct tm tm; 1564 InitTm(tm); 1565 1566 long year = ddt & 0xFE000000; 1567 year >>= 25; 1568 year += 80; 1569 tm.tm_year = year; 1570 1571 long month = ddt & 0x1E00000; 1572 month >>= 21; 1573 month -= 1; 1574 tm.tm_mon = month; 1575 1576 long day = ddt & 0x1F0000; 1577 day >>= 16; 1578 tm.tm_mday = day; 1579 1580 long hour = ddt & 0xF800; 1581 hour >>= 11; 1582 tm.tm_hour = hour; 1583 1584 long minute = ddt & 0x7E0; 1585 minute >>= 5; 1586 tm.tm_min = minute; 1587 1588 long second = ddt & 0x1F; 1589 tm.tm_sec = second * 2; 1590 1591 return Set(mktime(&tm)); 1592} 1593 1594unsigned long wxDateTime::GetAsDOS() const 1595{ 1596 unsigned long ddt; 1597 time_t ticks = GetTicks(); 1598 struct tm tmstruct; 1599 struct tm *tm = wxLocaltime_r(&ticks, &tmstruct); 1600 wxCHECK_MSG( tm, ULONG_MAX, _T("time can't be represented in DOS format") ); 1601 1602 long year = tm->tm_year; 1603 year -= 80; 1604 year <<= 25; 1605 1606 long month = tm->tm_mon; 1607 month += 1; 1608 month <<= 21; 1609 1610 long day = tm->tm_mday; 1611 day <<= 16; 1612 1613 long hour = tm->tm_hour; 1614 hour <<= 11; 1615 1616 long minute = tm->tm_min; 1617 minute <<= 5; 1618 1619 long second = tm->tm_sec; 1620 second /= 2; 1621 1622 ddt = year | month | day | hour | minute | second; 1623 return ddt; 1624} 1625 1626// ---------------------------------------------------------------------------- 1627// time_t <-> broken down time conversions 1628// ---------------------------------------------------------------------------- 1629 1630wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const 1631{ 1632 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 1633 1634 time_t time = GetTicks(); 1635 if ( time != (time_t)-1 ) 1636 { 1637 // use C RTL functions 1638 struct tm tmstruct; 1639 tm *tm; 1640 if ( tz.GetOffset() == -GetTimeZone() ) 1641 { 1642 // we are working with local time 1643 tm = wxLocaltime_r(&time, &tmstruct); 1644 1645 // should never happen 1646 wxCHECK_MSG( tm, Tm(), _T("wxLocaltime_r() failed") ); 1647 } 1648 else 1649 { 1650 time += (time_t)tz.GetOffset(); 1651#if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning 1652 int time2 = (int) time; 1653 if ( time2 >= 0 ) 1654#else 1655 if ( time >= 0 ) 1656#endif 1657 { 1658 tm = wxGmtime_r(&time, &tmstruct); 1659 1660 // should never happen 1661 wxCHECK_MSG( tm, Tm(), _T("wxGmtime_r() failed") ); 1662 } 1663 else 1664 { 1665 tm = (struct tm *)NULL; 1666 } 1667 } 1668 1669 if ( tm ) 1670 { 1671 // adjust the milliseconds 1672 Tm tm2(*tm, tz); 1673 long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong(); 1674 tm2.msec = (wxDateTime_t)(timeOnly % 1000); 1675 return tm2; 1676 } 1677 //else: use generic code below 1678 } 1679 1680 // remember the time and do the calculations with the date only - this 1681 // eliminates rounding errors of the floating point arithmetics 1682 1683 wxLongLong timeMidnight = m_time + tz.GetOffset() * 1000; 1684 1685 long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong(); 1686 1687 // we want to always have positive time and timeMidnight to be really 1688 // the midnight before it 1689 if ( timeOnly < 0 ) 1690 { 1691 timeOnly = MILLISECONDS_PER_DAY + timeOnly; 1692 } 1693 1694 timeMidnight -= timeOnly; 1695 1696 // calculate the Gregorian date from JDN for the midnight of our date: 1697 // this will yield day, month (in 1..12 range) and year 1698 1699 // actually, this is the JDN for the noon of the previous day 1700 long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN; 1701 1702 // CREDIT: code below is by Scott E. Lee (but bugs are mine) 1703 1704 wxASSERT_MSG( jdn > -2, _T("JDN out of range") ); 1705 1706 // calculate the century 1707 long temp = (jdn + JDN_OFFSET) * 4 - 1; 1708 long century = temp / DAYS_PER_400_YEARS; 1709 1710 // then the year and day of year (1 <= dayOfYear <= 366) 1711 temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3; 1712 long year = (century * 100) + (temp / DAYS_PER_4_YEARS); 1713 long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1; 1714 1715 // and finally the month and day of the month 1716 temp = dayOfYear * 5 - 3; 1717 long month = temp / DAYS_PER_5_MONTHS; 1718 long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1; 1719 1720 // month is counted from March - convert to normal 1721 if ( month < 10 ) 1722 { 1723 month += 3; 1724 } 1725 else 1726 { 1727 year += 1; 1728 month -= 9; 1729 } 1730 1731 // year is offset by 4800 1732 year -= 4800; 1733 1734 // check that the algorithm gave us something reasonable 1735 wxASSERT_MSG( (0 < month) && (month <= 12), _T("invalid month") ); 1736 wxASSERT_MSG( (1 <= day) && (day < 32), _T("invalid day") ); 1737 1738 // construct Tm from these values 1739 Tm tm; 1740 tm.year = (int)year; 1741 tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0 1742 tm.mday = (wxDateTime_t)day; 1743 tm.msec = (wxDateTime_t)(timeOnly % 1000); 1744 timeOnly -= tm.msec; 1745 timeOnly /= 1000; // now we have time in seconds 1746 1747 tm.sec = (wxDateTime_t)(timeOnly % SEC_PER_MIN); 1748 timeOnly -= tm.sec; 1749 timeOnly /= SEC_PER_MIN; // now we have time in minutes 1750 1751 tm.min = (wxDateTime_t)(timeOnly % MIN_PER_HOUR); 1752 timeOnly -= tm.min; 1753 1754 tm.hour = (wxDateTime_t)(timeOnly / MIN_PER_HOUR); 1755 1756 return tm; 1757} 1758 1759wxDateTime& wxDateTime::SetYear(int year) 1760{ 1761 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 1762 1763 Tm tm(GetTm()); 1764 tm.year = year; 1765 Set(tm); 1766 1767 return *this; 1768} 1769 1770wxDateTime& wxDateTime::SetMonth(Month month) 1771{ 1772 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 1773 1774 Tm tm(GetTm()); 1775 tm.mon = month; 1776 Set(tm); 1777 1778 return *this; 1779} 1780 1781wxDateTime& wxDateTime::SetDay(wxDateTime_t mday) 1782{ 1783 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 1784 1785 Tm tm(GetTm()); 1786 tm.mday = mday; 1787 Set(tm); 1788 1789 return *this; 1790} 1791 1792wxDateTime& wxDateTime::SetHour(wxDateTime_t hour) 1793{ 1794 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 1795 1796 Tm tm(GetTm()); 1797 tm.hour = hour; 1798 Set(tm); 1799 1800 return *this; 1801} 1802 1803wxDateTime& wxDateTime::SetMinute(wxDateTime_t min) 1804{ 1805 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 1806 1807 Tm tm(GetTm()); 1808 tm.min = min; 1809 Set(tm); 1810 1811 return *this; 1812} 1813 1814wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec) 1815{ 1816 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 1817 1818 Tm tm(GetTm()); 1819 tm.sec = sec; 1820 Set(tm); 1821 1822 return *this; 1823} 1824 1825wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond) 1826{ 1827 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") ); 1828 1829 // we don't need to use GetTm() for this one 1830 m_time -= m_time % 1000l; 1831 m_time += millisecond; 1832 1833 return *this; 1834} 1835 1836// ---------------------------------------------------------------------------- 1837// wxDateTime arithmetics 1838// ---------------------------------------------------------------------------- 1839 1840wxDateTime& wxDateTime::Add(const wxDateSpan& diff) 1841{ 1842 Tm tm(GetTm()); 1843 1844 tm.year += diff.GetYears(); 1845 tm.AddMonths(diff.GetMonths()); 1846 1847 // check that the resulting date is valid 1848 if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) ) 1849 { 1850 // We suppose that when adding one month to Jan 31 we want to get Feb 1851 // 28 (or 29), i.e. adding a month to the last day of the month should 1852 // give the last day of the next month which is quite logical. 1853 // 1854 // Unfortunately, there is no logic way to understand what should 1855 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)? 1856 // We make it Feb 28 (last day too), but it is highly questionable. 1857 tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon); 1858 } 1859 1860 tm.AddDays(diff.GetTotalDays()); 1861 1862 Set(tm); 1863 1864 wxASSERT_MSG( IsSameTime(tm), 1865 _T("Add(wxDateSpan) shouldn't modify time") ); 1866 1867 return *this; 1868} 1869 1870// ---------------------------------------------------------------------------- 1871// Weekday and monthday stuff 1872// ---------------------------------------------------------------------------- 1873 1874// convert Sun, Mon, ..., Sat into 6, 0, ..., 5 1875static inline int ConvertWeekDayToMondayBase(int wd) 1876{ 1877 return wd == wxDateTime::Sun ? 6 : wd - 1; 1878} 1879 1880/* static */ 1881wxDateTime 1882wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd) 1883{ 1884 wxASSERT_MSG( numWeek > 0, 1885 _T("invalid week number: weeks are counted from 1") ); 1886 1887 // Jan 4 always lies in the 1st week of the year 1888 wxDateTime dt(4, Jan, year); 1889 dt.SetToWeekDayInSameWeek(wd); 1890 dt += wxDateSpan::Weeks(numWeek - 1); 1891 1892 return dt; 1893} 1894 1895#if WXWIN_COMPATIBILITY_2_6 1896// use a separate function to avoid warnings about using deprecated 1897// SetToTheWeek in GetWeek below 1898static wxDateTime 1899SetToTheWeek(int year, 1900 wxDateTime::wxDateTime_t numWeek, 1901 wxDateTime::WeekDay weekday, 1902 wxDateTime::WeekFlags flags) 1903{ 1904 // Jan 4 always lies in the 1st week of the year 1905 wxDateTime dt(4, wxDateTime::Jan, year); 1906 dt.SetToWeekDayInSameWeek(weekday, flags); 1907 dt += wxDateSpan::Weeks(numWeek - 1); 1908 1909 return dt; 1910} 1911 1912bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek, 1913 WeekDay weekday, 1914 WeekFlags flags) 1915{ 1916 int year = GetYear(); 1917 *this = ::SetToTheWeek(year, numWeek, weekday, flags); 1918 if ( GetYear() != year ) 1919 { 1920 // oops... numWeek was too big 1921 return false; 1922 } 1923 1924 return true; 1925} 1926 1927wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek, 1928 WeekDay weekday, 1929 WeekFlags flags) const 1930{ 1931 return ::SetToTheWeek(GetYear(), numWeek, weekday, flags); 1932} 1933#endif // WXWIN_COMPATIBILITY_2_6 1934 1935wxDateTime& wxDateTime::SetToLastMonthDay(Month month, 1936 int year) 1937{ 1938 // take the current month/year if none specified 1939 if ( year == Inv_Year ) 1940 year = GetYear(); 1941 if ( month == Inv_Month ) 1942 month = GetMonth(); 1943 1944 return Set(GetNumOfDaysInMonth(year, month), month, year); 1945} 1946 1947wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday, WeekFlags flags) 1948{ 1949 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") ); 1950 1951 int wdayDst = weekday, 1952 wdayThis = GetWeekDay(); 1953 if ( wdayDst == wdayThis ) 1954 { 1955 // nothing to do 1956 return *this; 1957 } 1958 1959 if ( flags == Default_First ) 1960 { 1961 flags = GetCountry() == USA ? Sunday_First : Monday_First; 1962 } 1963 1964 // the logic below based on comparing weekday and wdayThis works if Sun (0) 1965 // is the first day in the week, but breaks down for Monday_First case so 1966 // we adjust the week days in this case 1967 if ( flags == Monday_First ) 1968 { 1969 if ( wdayThis == Sun ) 1970 wdayThis += 7; 1971 if ( wdayDst == Sun ) 1972 wdayDst += 7; 1973 } 1974 //else: Sunday_First, nothing to do 1975 1976 // go forward or back in time to the day we want 1977 if ( wdayDst < wdayThis ) 1978 { 1979 return Subtract(wxDateSpan::Days(wdayThis - wdayDst)); 1980 } 1981 else // weekday > wdayThis 1982 { 1983 return Add(wxDateSpan::Days(wdayDst - wdayThis)); 1984 } 1985} 1986 1987wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday) 1988{ 1989 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") ); 1990 1991 int diff; 1992 WeekDay wdayThis = GetWeekDay(); 1993 if ( weekday == wdayThis ) 1994 { 1995 // nothing to do 1996 return *this; 1997 } 1998 else if ( weekday < wdayThis ) 1999 { 2000 // need to advance a week 2001 diff = 7 - (wdayThis - weekday); 2002 } 2003 else // weekday > wdayThis 2004 { 2005 diff = weekday - wdayThis; 2006 } 2007 2008 return Add(wxDateSpan::Days(diff)); 2009} 2010 2011wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday) 2012{ 2013 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") ); 2014 2015 int diff; 2016 WeekDay wdayThis = GetWeekDay(); 2017 if ( weekday == wdayThis ) 2018 { 2019 // nothing to do 2020 return *this; 2021 } 2022 else if ( weekday > wdayThis ) 2023 { 2024 // need to go to previous week 2025 diff = 7 - (weekday - wdayThis); 2026 } 2027 else // weekday < wdayThis 2028 { 2029 diff = wdayThis - weekday; 2030 } 2031 2032 return Subtract(wxDateSpan::Days(diff)); 2033} 2034 2035bool wxDateTime::SetToWeekDay(WeekDay weekday, 2036 int n, 2037 Month month, 2038 int year) 2039{ 2040 wxCHECK_MSG( weekday != Inv_WeekDay, false, _T("invalid weekday") ); 2041 2042 // we don't check explicitly that -5 <= n <= 5 because we will return false 2043 // anyhow in such case - but may be should still give an assert for it? 2044 2045 // take the current month/year if none specified 2046 ReplaceDefaultYearMonthWithCurrent(&year, &month); 2047 2048 wxDateTime dt; 2049 2050 // TODO this probably could be optimised somehow... 2051 2052 if ( n > 0 ) 2053 { 2054 // get the first day of the month 2055 dt.Set(1, month, year); 2056 2057 // get its wday 2058 WeekDay wdayFirst = dt.GetWeekDay(); 2059 2060 // go to the first weekday of the month 2061 int diff = weekday - wdayFirst; 2062 if ( diff < 0 ) 2063 diff += 7; 2064 2065 // add advance n-1 weeks more 2066 diff += 7*(n - 1); 2067 2068 dt += wxDateSpan::Days(diff); 2069 } 2070 else // count from the end of the month 2071 { 2072 // get the last day of the month 2073 dt.SetToLastMonthDay(month, year); 2074 2075 // get its wday 2076 WeekDay wdayLast = dt.GetWeekDay(); 2077 2078 // go to the last weekday of the month 2079 int diff = wdayLast - weekday; 2080 if ( diff < 0 ) 2081 diff += 7; 2082 2083 // and rewind n-1 weeks from there 2084 diff += 7*(-n - 1); 2085 2086 dt -= wxDateSpan::Days(diff); 2087 } 2088 2089 // check that it is still in the same month 2090 if ( dt.GetMonth() == month ) 2091 { 2092 *this = dt; 2093 2094 return true; 2095 } 2096 else 2097 { 2098 // no such day in this month 2099 return false; 2100 } 2101} 2102 2103static inline 2104wxDateTime::wxDateTime_t GetDayOfYearFromTm(const wxDateTime::Tm& tm) 2105{ 2106 return (wxDateTime::wxDateTime_t)(gs_cumulatedDays[wxDateTime::IsLeapYear(tm.year)][tm.mon] + tm.mday); 2107} 2108 2109wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const 2110{ 2111 return GetDayOfYearFromTm(GetTm(tz)); 2112} 2113 2114wxDateTime::wxDateTime_t 2115wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const 2116{ 2117 if ( flags == Default_First ) 2118 { 2119 flags = GetCountry() == USA ? Sunday_First : Monday_First; 2120 } 2121 2122 Tm tm(GetTm(tz)); 2123 wxDateTime_t nDayInYear = GetDayOfYearFromTm(tm); 2124 2125 int wdTarget = GetWeekDay(tz); 2126 int wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay(); 2127 int week; 2128 if ( flags == Sunday_First ) 2129 { 2130 // FIXME: First week is not calculated correctly. 2131 week = (nDayInYear - wdTarget + 7) / 7; 2132 if ( wdYearStart == Wed || wdYearStart == Thu ) 2133 week++; 2134 } 2135 else // week starts with monday 2136 { 2137 // adjust the weekdays to non-US style. 2138 wdYearStart = ConvertWeekDayToMondayBase(wdYearStart); 2139 wdTarget = ConvertWeekDayToMondayBase(wdTarget); 2140 2141 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html: 2142 // 2143 // Week 01 of a year is per definition the first week that has the 2144 // Thursday in this year, which is equivalent to the week that 2145 // contains the fourth day of January. In other words, the first 2146 // week of a new year is the week that has the majority of its 2147 // days in the new year. Week 01 might also contain days from the 2148 // previous year and the week before week 01 of a year is the last 2149 // week (52 or 53) of the previous year even if it contains days 2150 // from the new year. A week starts with Monday (day 1) and ends 2151 // with Sunday (day 7). 2152 // 2153 2154 // if Jan 1 is Thursday or less, it is in the first week of this year 2155 if ( wdYearStart < 4 ) 2156 { 2157 // count the number of entire weeks between Jan 1 and this date 2158 week = (nDayInYear + wdYearStart + 6 - wdTarget)/7; 2159 2160 // be careful to check for overflow in the next year 2161 if ( week == 53 && tm.mday - wdTarget > 28 ) 2162 week = 1; 2163 } 2164 else // Jan 1 is in the last week of the previous year 2165 { 2166 // check if we happen to be at the last week of previous year: 2167 if ( tm.mon == Jan && tm.mday < 8 - wdYearStart ) 2168 week = wxDateTime(31, Dec, GetYear()-1).GetWeekOfYear(); 2169 else 2170 week = (nDayInYear + wdYearStart - 1 - wdTarget)/7; 2171 } 2172 } 2173 2174 return (wxDateTime::wxDateTime_t)week; 2175} 2176 2177wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags, 2178 const TimeZone& tz) const 2179{ 2180 Tm tm = GetTm(tz); 2181 const wxDateTime dateFirst = wxDateTime(1, tm.mon, tm.year); 2182 const wxDateTime::WeekDay wdFirst = dateFirst.GetWeekDay(); 2183 2184 if ( flags == Default_First ) 2185 { 2186 flags = GetCountry() == USA ? Sunday_First : Monday_First; 2187 } 2188 2189 // compute offset of dateFirst from the beginning of the week 2190 int firstOffset; 2191 if ( flags == Sunday_First ) 2192 firstOffset = wdFirst - Sun; 2193 else 2194 firstOffset = wdFirst == Sun ? DAYS_PER_WEEK - 1 : wdFirst - Mon; 2195 2196 return (wxDateTime::wxDateTime_t)((tm.mday - 1 + firstOffset)/7 + 1); 2197} 2198 2199wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday) 2200{ 2201 int year = GetYear(); 2202 wxDATETIME_CHECK( (0 < yday) && (yday <= GetNumberOfDays(year)), 2203 _T("invalid year day") ); 2204 2205 bool isLeap = IsLeapYear(year); 2206 for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) ) 2207 { 2208 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we 2209 // don't need it neither - because of the CHECK above we know that 2210 // yday lies in December then 2211 if ( (mon == Dec) || (yday <= gs_cumulatedDays[isLeap][mon + 1]) ) 2212 { 2213 Set((wxDateTime::wxDateTime_t)(yday - gs_cumulatedDays[isLeap][mon]), mon, year); 2214 2215 break; 2216 } 2217 } 2218 2219 return *this; 2220} 2221 2222// ---------------------------------------------------------------------------- 2223// Julian day number conversion and related stuff 2224// ---------------------------------------------------------------------------- 2225 2226double wxDateTime::GetJulianDayNumber() const 2227{ 2228 return m_time.ToDouble() / MILLISECONDS_PER_DAY + EPOCH_JDN + 0.5; 2229} 2230 2231double wxDateTime::GetRataDie() const 2232{ 2233 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5 2234 return GetJulianDayNumber() - 1721119.5 - 306; 2235} 2236 2237// ---------------------------------------------------------------------------- 2238// timezone and DST stuff 2239// ---------------------------------------------------------------------------- 2240 2241int wxDateTime::IsDST(wxDateTime::Country country) const 2242{ 2243 wxCHECK_MSG( country == Country_Default, -1, 2244 _T("country support not implemented") ); 2245 2246 // use the C RTL for the dates in the standard range 2247 time_t timet = GetTicks(); 2248 if ( timet != (time_t)-1 ) 2249 { 2250 struct tm tmstruct; 2251 tm *tm = wxLocaltime_r(&timet, &tmstruct); 2252 2253 wxCHECK_MSG( tm, -1, _T("wxLocaltime_r() failed") ); 2254 2255 return tm->tm_isdst; 2256 } 2257 else 2258 { 2259 int year = GetYear(); 2260 2261 if ( !IsDSTApplicable(year, country) ) 2262 { 2263 // no DST time in this year in this country 2264 return -1; 2265 } 2266 2267 return IsBetween(GetBeginDST(year, country), GetEndDST(year, country)); 2268 } 2269} 2270 2271wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST) 2272{ 2273 long secDiff = GetTimeZone() + tz.GetOffset(); 2274 2275 // we need to know whether DST is or not in effect for this date unless 2276 // the test disabled by the caller 2277 if ( !noDST && (IsDST() == 1) ) 2278 { 2279 // FIXME we assume that the DST is always shifted by 1 hour 2280 secDiff -= 3600; 2281 } 2282 2283 return Add(wxTimeSpan::Seconds(secDiff)); 2284} 2285 2286wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST) 2287{ 2288 long secDiff = GetTimeZone() + tz.GetOffset(); 2289 2290 // we need to know whether DST is or not in effect for this date unless 2291 // the test disabled by the caller 2292 if ( !noDST && (IsDST() == 1) ) 2293 { 2294 // FIXME we assume that the DST is always shifted by 1 hour 2295 secDiff -= 3600; 2296 } 2297 2298 return Subtract(wxTimeSpan::Seconds(secDiff)); 2299} 2300 2301// ---------------------------------------------------------------------------- 2302// wxDateTime to/from text representations 2303// ---------------------------------------------------------------------------- 2304 2305wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const 2306{ 2307 wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxDateTime::Format") ); 2308 2309 time_t time = GetTicks(); 2310 2311 // we have to use our own implementation if the date is out of range of 2312 // strftime() or if we use non standard specificators 2313#ifdef HAVE_STRFTIME 2314 if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) ) 2315 { 2316 // use strftime() 2317 struct tm tmstruct; 2318 struct tm *tm; 2319 if ( tz.GetOffset() == -GetTimeZone() ) 2320 { 2321 // we are working with local time 2322 tm = wxLocaltime_r(&time, &tmstruct); 2323 2324 // should never happen 2325 wxCHECK_MSG( tm, wxEmptyString, _T("wxLocaltime_r() failed") ); 2326 } 2327 else 2328 { 2329 time += (int)tz.GetOffset(); 2330 2331#if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning 2332 int time2 = (int) time; 2333 if ( time2 >= 0 ) 2334#else 2335 if ( time >= 0 ) 2336#endif 2337 { 2338 tm = wxGmtime_r(&time, &tmstruct); 2339 2340 // should never happen 2341 wxCHECK_MSG( tm, wxEmptyString, _T("wxGmtime_r() failed") ); 2342 } 2343 else 2344 { 2345 tm = (struct tm *)NULL; 2346 } 2347 } 2348 2349 if ( tm ) 2350 { 2351 return CallStrftime(format, tm); 2352 } 2353 } 2354 //else: use generic code below 2355#endif // HAVE_STRFTIME 2356 2357 // we only parse ANSI C format specifications here, no POSIX 2 2358 // complications, no GNU extensions but we do add support for a "%l" format 2359 // specifier allowing to get the number of milliseconds 2360 Tm tm = GetTm(tz); 2361 2362 // used for calls to strftime() when we only deal with time 2363 struct tm tmTimeOnly; 2364 memset(&tmTimeOnly, 0, sizeof(tmTimeOnly)); 2365 tmTimeOnly.tm_hour = tm.hour; 2366 tmTimeOnly.tm_min = tm.min; 2367 tmTimeOnly.tm_sec = tm.sec; 2368 tmTimeOnly.tm_wday = 0; 2369 tmTimeOnly.tm_yday = 0; 2370 tmTimeOnly.tm_mday = 1; // any date will do, use 1976-01-01 2371 tmTimeOnly.tm_mon = 0; 2372 tmTimeOnly.tm_year = 76; 2373 tmTimeOnly.tm_isdst = 0; // no DST, we adjust for tz ourselves 2374 2375 wxString tmp, res, fmt; 2376 for ( const wxChar *p = format; *p; p++ ) 2377 { 2378 if ( *p != _T('%') ) 2379 { 2380 // copy as is 2381 res += *p; 2382 2383 continue; 2384 } 2385 2386 // set the default format 2387 switch ( *++p ) 2388 { 2389 case _T('Y'): // year has 4 digits 2390 fmt = _T("%04d"); 2391 break; 2392 2393 case _T('j'): // day of year has 3 digits 2394 case _T('l'): // milliseconds have 3 digits 2395 fmt = _T("%03d"); 2396 break; 2397 2398 case _T('w'): // week day as number has only one 2399 fmt = _T("%d"); 2400 break; 2401 2402 default: 2403 // it's either another valid format specifier in which case 2404 // the format is "%02d" (for all the rest) or we have the 2405 // field width preceding the format in which case it will 2406 // override the default format anyhow 2407 fmt = _T("%02d"); 2408 } 2409 2410 bool restart = true; 2411 while ( restart ) 2412 { 2413 restart = false; 2414 2415 // start of the format specification 2416 switch ( *p ) 2417 { 2418 case _T('a'): // a weekday name 2419 case _T('A'): 2420 // second parameter should be true for abbreviated names 2421 res += GetWeekDayName(tm.GetWeekDay(), 2422 *p == _T('a') ? Name_Abbr : Name_Full); 2423 break; 2424 2425 case _T('b'): // a month name 2426 case _T('B'): 2427 res += GetMonthName(tm.mon, 2428 *p == _T('b') ? Name_Abbr : Name_Full); 2429 break; 2430 2431 case _T('c'): // locale default date and time representation 2432 case _T('x'): // locale default date representation 2433#ifdef HAVE_STRFTIME 2434 // 2435 // the problem: there is no way to know what do these format 2436 // specifications correspond to for the current locale. 2437 // 2438 // the solution: use a hack and still use strftime(): first 2439 // find the YEAR which is a year in the strftime() range (1970 2440 // - 2038) whose Jan 1 falls on the same week day as the Jan 1 2441 // of the real year. Then make a copy of the format and 2442 // replace all occurrences of YEAR in it with some unique 2443 // string not appearing anywhere else in it, then use 2444 // strftime() to format the date in year YEAR and then replace 2445 // YEAR back by the real year and the unique replacement 2446 // string back with YEAR. Notice that "all occurrences of YEAR" 2447 // means all occurrences of 4 digit as well as 2 digit form! 2448 // 2449 // the bugs: we assume that neither of %c nor %x contains any 2450 // fields which may change between the YEAR and real year. For 2451 // example, the week number (%U, %W) and the day number (%j) 2452 // will change if one of these years is leap and the other one 2453 // is not! 2454 { 2455 // find the YEAR: normally, for any year X, Jan 1 or the 2456 // year X + 28 is the same weekday as Jan 1 of X (because 2457 // the weekday advances by 1 for each normal X and by 2 2458 // for each leap X, hence by 5 every 4 years or by 35 2459 // which is 0 mod 7 every 28 years) but this rule breaks 2460 // down if there are years between X and Y which are 2461 // divisible by 4 but not leap (i.e. divisible by 100 but 2462 // not 400), hence the correction. 2463 2464 int yearReal = GetYear(tz); 2465 int mod28 = yearReal % 28; 2466 2467 // be careful to not go too far - we risk to leave the 2468 // supported range 2469 int year; 2470 if ( mod28 < 10 ) 2471 { 2472 year = 1988 + mod28; // 1988 == 0 (mod 28) 2473 } 2474 else 2475 { 2476 year = 1970 + mod28 - 10; // 1970 == 10 (mod 28) 2477 } 2478 2479 int nCentury = year / 100, 2480 nCenturyReal = yearReal / 100; 2481 2482 // need to adjust for the years divisble by 400 which are 2483 // not leap but are counted like leap ones if we just take 2484 // the number of centuries in between for nLostWeekDays 2485 int nLostWeekDays = (nCentury - nCenturyReal) - 2486 (nCentury / 4 - nCenturyReal / 4); 2487 2488 // we have to gain back the "lost" weekdays: note that the 2489 // effect of this loop is to not do anything to 2490 // nLostWeekDays (which we won't use any more), but to 2491 // (indirectly) set the year correctly 2492 while ( (nLostWeekDays % 7) != 0 ) 2493 { 2494 nLostWeekDays += year++ % 4 ? 1 : 2; 2495 } 2496 2497 // Keep year below 2000 so the 2digit year number 2498 // can never match the month or day of the month 2499 if (year>=2000) year-=28; 2500 // at any rate, we couldn't go further than 1988 + 9 + 28! 2501 wxASSERT_MSG( year < 2030, 2502 _T("logic error in wxDateTime::Format") ); 2503 2504 wxString strYear, strYear2; 2505 strYear.Printf(_T("%d"), year); 2506 strYear2.Printf(_T("%d"), year % 100); 2507 2508 // find four strings not occurring in format (this is surely 2509 // not the optimal way of doing it... improvements welcome!) 2510 wxString fmt2 = format; 2511 wxString replacement,replacement2,replacement3,replacement4; 2512 for (int rnr=1; rnr<5 ; rnr++) 2513 { 2514 wxString r = (wxChar)-rnr; 2515 while ( fmt2.Find(r) != wxNOT_FOUND ) 2516 { 2517 r << (wxChar)-rnr; 2518 } 2519 2520 switch (rnr) 2521 { 2522 case 1: replacement=r; break; 2523 case 2: replacement2=r; break; 2524 case 3: replacement3=r; break; 2525 case 4: replacement4=r; break; 2526 } 2527 } 2528 // replace all occurrences of year with it 2529 bool wasReplaced = fmt2.Replace(strYear, replacement) > 0; 2530 // evaluation order ensures we always attempt the replacement. 2531 wasReplaced = (fmt2.Replace(strYear2, replacement2) > 0) || wasReplaced; 2532 2533 // use strftime() to format the same date but in supported 2534 // year 2535 // 2536 // NB: we assume that strftime() doesn't check for the 2537 // date validity and will happily format the date 2538 // corresponding to Feb 29 of a non leap year (which 2539 // may happen if yearReal was leap and year is not) 2540 struct tm tmAdjusted; 2541 InitTm(tmAdjusted); 2542 tmAdjusted.tm_hour = tm.hour; 2543 tmAdjusted.tm_min = tm.min; 2544 tmAdjusted.tm_sec = tm.sec; 2545 tmAdjusted.tm_wday = tm.GetWeekDay(); 2546 tmAdjusted.tm_yday = GetDayOfYear(); 2547 tmAdjusted.tm_mday = tm.mday; 2548 tmAdjusted.tm_mon = tm.mon; 2549 tmAdjusted.tm_year = year - 1900; 2550 tmAdjusted.tm_isdst = 0; // no DST, already adjusted 2551 wxString str = CallStrftime(*p == _T('c') ? _T("%c") 2552 : _T("%x"), 2553 &tmAdjusted); 2554 2555 // now replace the occurrence of 1999 with the real year 2556 // we do this in two stages to stop the 2 digit year 2557 // matching any substring of the 4 digit year. 2558 // Any day,month hours and minutes components should be safe due 2559 // to ensuring the range of the years. 2560 wxString strYearReal, strYearReal2; 2561 strYearReal.Printf(_T("%04d"), yearReal); 2562 strYearReal2.Printf(_T("%02d"), yearReal % 100); 2563 str.Replace(strYear, replacement3); 2564 str.Replace(strYear2,replacement4); 2565 str.Replace(replacement3, strYearReal); 2566 str.Replace(replacement4, strYearReal2); 2567 2568 // and replace back all occurrences of replacement string 2569 if ( wasReplaced ) 2570 { 2571 str.Replace(replacement2, strYear2); 2572 str.Replace(replacement, strYear); 2573 } 2574 2575 res += str; 2576 } 2577#else // !HAVE_STRFTIME 2578 // Use "%m/%d/%y %H:%M:%S" format instead 2579 res += wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"), 2580 tm.mon+1,tm.mday, tm.year, tm.hour, tm.min, tm.sec); 2581#endif // HAVE_STRFTIME/!HAVE_STRFTIME 2582 break; 2583 2584 case _T('d'): // day of a month (01-31) 2585 res += wxString::Format(fmt, tm.mday); 2586 break; 2587 2588 case _T('H'): // hour in 24h format (00-23) 2589 res += wxString::Format(fmt, tm.hour); 2590 break; 2591 2592 case _T('I'): // hour in 12h format (01-12) 2593 { 2594 // 24h -> 12h, 0h -> 12h too 2595 int hour12 = tm.hour > 12 ? tm.hour - 12 2596 : tm.hour ? tm.hour : 12; 2597 res += wxString::Format(fmt, hour12); 2598 } 2599 break; 2600 2601 case _T('j'): // day of the year 2602 res += wxString::Format(fmt, GetDayOfYear(tz)); 2603 break; 2604 2605 case _T('l'): // milliseconds (NOT STANDARD) 2606 res += wxString::Format(fmt, GetMillisecond(tz)); 2607 break; 2608 2609 case _T('m'): // month as a number (01-12) 2610 res += wxString::Format(fmt, tm.mon + 1); 2611 break; 2612 2613 case _T('M'): // minute as a decimal number (00-59) 2614 res += wxString::Format(fmt, tm.min); 2615 break; 2616 2617 case _T('p'): // AM or PM string 2618#ifdef HAVE_STRFTIME 2619 res += CallStrftime(_T("%p"), &tmTimeOnly); 2620#else // !HAVE_STRFTIME 2621 res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am"); 2622#endif // HAVE_STRFTIME/!HAVE_STRFTIME 2623 break; 2624 2625 case _T('S'): // second as a decimal number (00-61) 2626 res += wxString::Format(fmt, tm.sec); 2627 break; 2628 2629 case _T('U'): // week number in the year (Sunday 1st week day) 2630 res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz)); 2631 break; 2632 2633 case _T('W'): // week number in the year (Monday 1st week day) 2634 res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz)); 2635 break; 2636 2637 case _T('w'): // weekday as a number (0-6), Sunday = 0 2638 res += wxString::Format(fmt, tm.GetWeekDay()); 2639 break; 2640 2641 // case _T('x'): -- handled with "%c" 2642 2643 case _T('X'): // locale default time representation 2644 // just use strftime() to format the time for us 2645#ifdef HAVE_STRFTIME 2646 res += CallStrftime(_T("%X"), &tmTimeOnly); 2647#else // !HAVE_STRFTIME 2648 res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec); 2649#endif // HAVE_STRFTIME/!HAVE_STRFTIME 2650 break; 2651 2652 case _T('y'): // year without century (00-99) 2653 res += wxString::Format(fmt, tm.year % 100); 2654 break; 2655 2656 case _T('Y'): // year with century 2657 res += wxString::Format(fmt, tm.year); 2658 break; 2659 2660 case _T('Z'): // timezone name 2661#ifdef HAVE_STRFTIME 2662 res += CallStrftime(_T("%Z"), &tmTimeOnly); 2663#endif 2664 break; 2665 2666 default: 2667 // is it the format width? 2668 for( fmt.clear(); 2669 *p == _T('-') || *p == _T('+') || 2670 *p == _T(' ') || wxIsdigit(*p); 2671 ++p ) 2672 { 2673 fmt += *p; 2674 } 2675 2676 if ( !fmt.empty() ) 2677 { 2678 // we've only got the flags and width so far in fmt 2679 fmt.Prepend(_T('%')); 2680 fmt.Append(_T('d')); 2681 2682 restart = true; 2683 2684 break; 2685 } 2686 2687 // no, it wasn't the width 2688 wxFAIL_MSG(_T("unknown format specificator")); 2689 2690 // fall through and just copy it nevertheless 2691 2692 case _T('%'): // a percent sign 2693 res += *p; 2694 break; 2695 2696 case 0: // the end of string 2697 wxFAIL_MSG(_T("missing format at the end of string")); 2698 2699 // just put the '%' which was the last char in format 2700 res += _T('%'); 2701 break; 2702 } 2703 } 2704 } 2705 2706 return res; 2707} 2708 2709// this function parses a string in (strict) RFC 822 format: see the section 5 2710// of the RFC for the detailed description, but briefly it's something of the 2711// form "Sat, 18 Dec 1999 00:48:30 +0100" 2712// 2713// this function is "strict" by design - it must reject anything except true 2714// RFC822 time specs. 2715// 2716// TODO a great candidate for using reg exps 2717const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date) 2718{ 2719 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") ); 2720 2721 const wxChar *p = date; 2722 const wxChar *comma = wxStrchr(p, _T(',')); 2723 if ( comma ) 2724 { 2725 // the part before comma is the weekday 2726 2727 // skip it for now - we don't use but might check that it really 2728 // corresponds to the specfied date 2729 p = comma + 1; 2730 2731 if ( *p != _T(' ') ) 2732 { 2733 wxLogDebug(_T("no space after weekday in RFC822 time spec")); 2734 2735 return (wxChar *)NULL; 2736 } 2737 2738 p++; // skip space 2739 } 2740 2741 // the following 1 or 2 digits are the day number 2742 if ( !wxIsdigit(*p) ) 2743 { 2744 wxLogDebug(_T("day number expected in RFC822 time spec, none found")); 2745 2746 return (wxChar *)NULL; 2747 } 2748 2749 wxDateTime_t day = (wxDateTime_t)(*p++ - _T('0')); 2750 if ( wxIsdigit(*p) ) 2751 { 2752 day *= 10; 2753 day = (wxDateTime_t)(day + (*p++ - _T('0'))); 2754 } 2755 2756 if ( *p++ != _T(' ') ) 2757 { 2758 return (wxChar *)NULL; 2759 } 2760 2761 // the following 3 letters specify the month 2762 wxString monName(p, 3); 2763 Month mon; 2764 if ( monName == _T("Jan") ) 2765 mon = Jan; 2766 else if ( monName == _T("Feb") ) 2767 mon = Feb; 2768 else if ( monName == _T("Mar") ) 2769 mon = Mar; 2770 else if ( monName == _T("Apr") ) 2771 mon = Apr; 2772 else if ( monName == _T("May") ) 2773 mon = May; 2774 else if ( monName == _T("Jun") ) 2775 mon = Jun; 2776 else if ( monName == _T("Jul") ) 2777 mon = Jul; 2778 else if ( monName == _T("Aug") ) 2779 mon = Aug; 2780 else if ( monName == _T("Sep") ) 2781 mon = Sep; 2782 else if ( monName == _T("Oct") ) 2783 mon = Oct; 2784 else if ( monName == _T("Nov") ) 2785 mon = Nov; 2786 else if ( monName == _T("Dec") ) 2787 mon = Dec; 2788 else 2789 { 2790 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName.c_str()); 2791 2792 return (wxChar *)NULL; 2793 } 2794 2795 p += 3; 2796 2797 if ( *p++ != _T(' ') ) 2798 { 2799 return (wxChar *)NULL; 2800 } 2801 2802 // next is the year 2803 if ( !wxIsdigit(*p) ) 2804 { 2805 // no year? 2806 return (wxChar *)NULL; 2807 } 2808 2809 int year = *p++ - _T('0'); 2810 2811 if ( !wxIsdigit(*p) ) 2812 { 2813 // should have at least 2 digits in the year 2814 return (wxChar *)NULL; 2815 } 2816 2817 year *= 10; 2818 year += *p++ - _T('0'); 2819 2820 // is it a 2 digit year (as per original RFC 822) or a 4 digit one? 2821 if ( wxIsdigit(*p) ) 2822 { 2823 year *= 10; 2824 year += *p++ - _T('0'); 2825 2826 if ( !wxIsdigit(*p) ) 2827 { 2828 // no 3 digit years please 2829 return (wxChar *)NULL; 2830 } 2831 2832 year *= 10; 2833 year += *p++ - _T('0'); 2834 } 2835 2836 if ( *p++ != _T(' ') ) 2837 { 2838 return (wxChar *)NULL; 2839 } 2840 2841 // time is in the format hh:mm:ss and seconds are optional 2842 if ( !wxIsdigit(*p) ) 2843 { 2844 return (wxChar *)NULL; 2845 } 2846 2847 wxDateTime_t hour = (wxDateTime_t)(*p++ - _T('0')); 2848 2849 if ( !wxIsdigit(*p) ) 2850 { 2851 return (wxChar *)NULL; 2852 } 2853 2854 hour *= 10; 2855 hour = (wxDateTime_t)(hour + (*p++ - _T('0'))); 2856 2857 if ( *p++ != _T(':') ) 2858 { 2859 return (wxChar *)NULL; 2860 } 2861 2862 if ( !wxIsdigit(*p) ) 2863 { 2864 return (wxChar *)NULL; 2865 } 2866 2867 wxDateTime_t min = (wxDateTime_t)(*p++ - _T('0')); 2868 2869 if ( !wxIsdigit(*p) ) 2870 { 2871 return (wxChar *)NULL; 2872 } 2873 2874 min *= 10; 2875 min = (wxDateTime_t)(min + *p++ - _T('0')); 2876 2877 wxDateTime_t sec = 0; 2878 if ( *p == _T(':') ) 2879 { 2880 p++; 2881 if ( !wxIsdigit(*p) ) 2882 { 2883 return (wxChar *)NULL; 2884 } 2885 2886 sec = (wxDateTime_t)(*p++ - _T('0')); 2887 2888 if ( !wxIsdigit(*p) ) 2889 { 2890 return (wxChar *)NULL; 2891 } 2892 2893 sec *= 10; 2894 sec = (wxDateTime_t)(sec + *p++ - _T('0')); 2895 } 2896 2897 if ( *p++ != _T(' ') ) 2898 { 2899 return (wxChar *)NULL; 2900 } 2901 2902 // and now the interesting part: the timezone 2903 int offset wxDUMMY_INITIALIZE(0); 2904 if ( *p == _T('-') || *p == _T('+') ) 2905 { 2906 // the explicit offset given: it has the form of hhmm 2907 bool plus = *p++ == _T('+'); 2908 2909 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) ) 2910 { 2911 return (wxChar *)NULL; 2912 } 2913 2914 // hours 2915 offset = MIN_PER_HOUR*(10*(*p - _T('0')) + (*(p + 1) - _T('0'))); 2916 2917 p += 2; 2918 2919 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) ) 2920 { 2921 return (wxChar *)NULL; 2922 } 2923 2924 // minutes 2925 offset += 10*(*p - _T('0')) + (*(p + 1) - _T('0')); 2926 2927 if ( !plus ) 2928 { 2929 offset = -offset; 2930 } 2931 2932 p += 2; 2933 } 2934 else 2935 { 2936 // the symbolic timezone given: may be either military timezone or one 2937 // of standard abbreviations 2938 if ( !*(p + 1) ) 2939 { 2940 // military: Z = UTC, J unused, A = -1, ..., Y = +12 2941 static const int offsets[26] = 2942 { 2943 //A B C D E F G H I J K L M 2944 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12, 2945 //N O P R Q S T U V W Z Y Z 2946 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0 2947 }; 2948 2949 if ( *p < _T('A') || *p > _T('Z') || *p == _T('J') ) 2950 { 2951 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p); 2952 2953 return (wxChar *)NULL; 2954 } 2955 2956 offset = offsets[*p++ - _T('A')]; 2957 } 2958 else 2959 { 2960 // abbreviation 2961 wxString tz = p; 2962 if ( tz == _T("UT") || tz == _T("UTC") || tz == _T("GMT") ) 2963 offset = 0; 2964 else if ( tz == _T("AST") ) 2965 offset = AST - GMT0; 2966 else if ( tz == _T("ADT") ) 2967 offset = ADT - GMT0; 2968 else if ( tz == _T("EST") ) 2969 offset = EST - GMT0; 2970 else if ( tz == _T("EDT") ) 2971 offset = EDT - GMT0; 2972 else if ( tz == _T("CST") ) 2973 offset = CST - GMT0; 2974 else if ( tz == _T("CDT") ) 2975 offset = CDT - GMT0; 2976 else if ( tz == _T("MST") ) 2977 offset = MST - GMT0; 2978 else if ( tz == _T("MDT") ) 2979 offset = MDT - GMT0; 2980 else if ( tz == _T("PST") ) 2981 offset = PST - GMT0; 2982 else if ( tz == _T("PDT") ) 2983 offset = PDT - GMT0; 2984 else 2985 { 2986 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p); 2987 2988 return (wxChar *)NULL; 2989 } 2990 2991 p += tz.length(); 2992 } 2993 2994 // make it minutes 2995 offset *= MIN_PER_HOUR; 2996 } 2997 2998 // the spec was correct, construct the date from the values we found 2999 Set(day, mon, year, hour, min, sec); 3000 MakeFromTimezone(TimeZone::Make(offset*SEC_PER_MIN)); 3001 3002 return p; 3003} 3004 3005#ifdef __WINDOWS__ 3006 3007// returns the string containing strftime() format used for short dates in the 3008// current locale or an empty string 3009static wxString GetLocaleDateFormat() 3010{ 3011 wxString fmtWX; 3012 3013 // there is no setlocale() under Windows CE, so just always query the 3014 // system there 3015#ifndef __WXWINCE__ 3016 if ( strcmp(setlocale(LC_ALL, NULL), "C") != 0 ) 3017#endif 3018 { 3019 // The locale was programatically set to non-C. We assume that this was 3020 // done using wxLocale, in which case thread's current locale is also 3021 // set to correct LCID value and we can use GetLocaleInfo to determine 3022 // the correct formatting string: 3023#ifdef __WXWINCE__ 3024 LCID lcid = LOCALE_USER_DEFAULT; 3025#else 3026 LCID lcid = GetThreadLocale(); 3027#endif 3028 // according to MSDN 80 chars is max allowed for short date format 3029 wxChar fmt[81]; 3030 if ( ::GetLocaleInfo(lcid, LOCALE_SSHORTDATE, fmt, WXSIZEOF(fmt)) ) 3031 { 3032 wxChar chLast = _T('\0'); 3033 size_t lastCount = 0; 3034 for ( const wxChar *p = fmt; /* NUL handled inside */; p++ ) 3035 { 3036 if ( *p == chLast ) 3037 { 3038 lastCount++; 3039 continue; 3040 } 3041 3042 switch ( *p ) 3043 { 3044 // these characters come in groups, start counting them 3045 case _T('d'): 3046 case _T('M'): 3047 case _T('y'): 3048 case _T('g'): 3049 chLast = *p; 3050 lastCount = 1; 3051 break; 3052 3053 default: 3054 // first deal with any special characters we have had 3055 if ( lastCount ) 3056 { 3057 switch ( chLast ) 3058 { 3059 case _T('d'): 3060 switch ( lastCount ) 3061 { 3062 case 1: // d 3063 case 2: // dd 3064 // these two are the same as we 3065 // don't distinguish between 1 and 3066 // 2 digits for days 3067 fmtWX += _T("%d"); 3068 break; 3069 3070 case 3: // ddd 3071 fmtWX += _T("%a"); 3072 break; 3073 3074 case 4: // dddd 3075 fmtWX += _T("%A"); 3076 break; 3077 3078 default: 3079 wxFAIL_MSG( _T("too many 'd's") ); 3080 } 3081 break; 3082 3083 case _T('M'): 3084 switch ( lastCount ) 3085 { 3086 case 1: // M 3087 case 2: // MM 3088 // as for 'd' and 'dd' above 3089 fmtWX += _T("%m"); 3090 break; 3091 3092 case 3: 3093 fmtWX += _T("%b"); 3094 break; 3095 3096 case 4: 3097 fmtWX += _T("%B"); 3098 break; 3099 3100 default: 3101 wxFAIL_MSG( _T("too many 'M's") ); 3102 } 3103 break; 3104 3105 case _T('y'): 3106 switch ( lastCount ) 3107 { 3108 case 1: // y 3109 case 2: // yy 3110 fmtWX += _T("%y"); 3111 break; 3112 3113 case 4: // yyyy 3114 fmtWX += _T("%Y"); 3115 break; 3116 3117 default: 3118 wxFAIL_MSG( _T("wrong number of 'y's") ); 3119 } 3120 break; 3121 3122 case _T('g'): 3123 // strftime() doesn't have era string, 3124 // ignore this format 3125 wxASSERT_MSG( lastCount <= 2, 3126 _T("too many 'g's") ); 3127 break; 3128 3129 default: 3130 wxFAIL_MSG( _T("unreachable") ); 3131 } 3132 3133 chLast = _T('\0'); 3134 lastCount = 0; 3135 } 3136 3137 // not a special character so must be just a separator, 3138 // treat as is 3139 if ( *p != _T('\0') ) 3140 { 3141 if ( *p == _T('%') ) 3142 { 3143 // this one needs to be escaped 3144 fmtWX += _T('%'); 3145 } 3146 3147 fmtWX += *p; 3148 } 3149 } 3150 3151 if ( *p == _T('\0') ) 3152 break; 3153 } 3154 } 3155 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and 3156 // try our luck with the default formats 3157 } 3158 //else: default C locale, default formats should work 3159 3160 return fmtWX; 3161} 3162 3163#endif // __WINDOWS__ 3164 3165const wxChar *wxDateTime::ParseFormat(const wxChar *date, 3166 const wxChar *format, 3167 const wxDateTime& dateDef) 3168{ 3169 wxCHECK_MSG( date && format, (wxChar *)NULL, 3170 _T("NULL pointer in wxDateTime::ParseFormat()") ); 3171 3172 wxString str; 3173 unsigned long num; 3174 3175 // what fields have we found? 3176 bool haveWDay = false, 3177 haveYDay = false, 3178 haveDay = false, 3179 haveMon = false, 3180 haveYear = false, 3181 haveHour = false, 3182 haveMin = false, 3183 haveSec = false; 3184 3185 bool hourIsIn12hFormat = false, // or in 24h one? 3186 isPM = false; // AM by default 3187 3188 // and the value of the items we have (init them to get rid of warnings) 3189 wxDateTime_t sec = 0, 3190 min = 0, 3191 hour = 0; 3192 WeekDay wday = Inv_WeekDay; 3193 wxDateTime_t yday = 0, 3194 mday = 0; 3195 wxDateTime::Month mon = Inv_Month; 3196 int year = 0; 3197 3198 const wxChar *input = date; 3199 for ( const wxChar *fmt = format; *fmt; fmt++ ) 3200 { 3201 if ( *fmt != _T('%') ) 3202 { 3203 if ( wxIsspace(*fmt) ) 3204 { 3205 // a white space in the format string matches 0 or more white 3206 // spaces in the input 3207 while ( wxIsspace(*input) ) 3208 { 3209 input++; 3210 } 3211 } 3212 else // !space 3213 { 3214 // any other character (not whitespace, not '%') must be 3215 // matched by itself in the input 3216 if ( *input++ != *fmt ) 3217 { 3218 // no match 3219 return (wxChar *)NULL; 3220 } 3221 } 3222 3223 // done with this format char 3224 continue; 3225 } 3226 3227 // start of a format specification 3228 3229 // parse the optional width 3230 size_t width = 0; 3231 while ( wxIsdigit(*++fmt) ) 3232 { 3233 width *= 10; 3234 width += *fmt - _T('0'); 3235 } 3236 3237 // the default widths for the various fields 3238 if ( !width ) 3239 { 3240 switch ( *fmt ) 3241 { 3242 case _T('Y'): // year has 4 digits 3243 width = 4; 3244 break; 3245 3246 case _T('j'): // day of year has 3 digits 3247 case _T('l'): // milliseconds have 3 digits 3248 width = 3; 3249 break; 3250 3251 case _T('w'): // week day as number has only one 3252 width = 1; 3253 break; 3254 3255 default: 3256 // default for all other fields 3257 width = 2; 3258 } 3259 } 3260 3261 // then the format itself 3262 switch ( *fmt ) 3263 { 3264 case _T('a'): // a weekday name 3265 case _T('A'): 3266 { 3267 int flag = *fmt == _T('a') ? Name_Abbr : Name_Full; 3268 wday = GetWeekDayFromName(GetAlphaToken(input), flag); 3269 if ( wday == Inv_WeekDay ) 3270 { 3271 // no match 3272 return (wxChar *)NULL; 3273 } 3274 } 3275 haveWDay = true; 3276 break; 3277 3278 case _T('b'): // a month name 3279 case _T('B'): 3280 { 3281 int flag = *fmt == _T('b') ? Name_Abbr : Name_Full; 3282 mon = GetMonthFromName(GetAlphaToken(input), flag); 3283 if ( mon == Inv_Month ) 3284 { 3285 // no match 3286 return (wxChar *)NULL; 3287 } 3288 } 3289 haveMon = true; 3290 break; 3291 3292 case _T('c'): // locale default date and time representation 3293 { 3294 wxDateTime dt; 3295 3296 // this is the format which corresponds to ctime() output 3297 // and strptime("%c") should parse it, so try it first 3298 static const wxChar *fmtCtime = _T("%a %b %d %H:%M:%S %Y"); 3299 3300 const wxChar *result = dt.ParseFormat(input, fmtCtime); 3301 if ( !result ) 3302 { 3303 result = dt.ParseFormat(input, _T("%x %X")); 3304 } 3305 3306 if ( !result ) 3307 { 3308 result = dt.ParseFormat(input, _T("%X %x")); 3309 } 3310 3311 if ( !result ) 3312 { 3313 // we've tried everything and still no match 3314 return (wxChar *)NULL; 3315 } 3316 3317 Tm tm = dt.GetTm(); 3318 3319 haveDay = haveMon = haveYear = 3320 haveHour = haveMin = haveSec = true; 3321 3322 hour = tm.hour; 3323 min = tm.min; 3324 sec = tm.sec; 3325 3326 year = tm.year; 3327 mon = tm.mon; 3328 mday = tm.mday; 3329 3330 input = result; 3331 } 3332 break; 3333 3334 case _T('d'): // day of a month (01-31) 3335 if ( !GetNumericToken(width, input, &num) || 3336 (num > 31) || (num < 1) ) 3337 { 3338 // no match 3339 return (wxChar *)NULL; 3340 } 3341 3342 // we can't check whether the day range is correct yet, will 3343 // do it later - assume ok for now 3344 haveDay = true; 3345 mday = (wxDateTime_t)num; 3346 break; 3347 3348 case _T('H'): // hour in 24h format (00-23) 3349 if ( !GetNumericToken(width, input, &num) || (num > 23) ) 3350 { 3351 // no match 3352 return (wxChar *)NULL; 3353 } 3354 3355 haveHour = true; 3356 hour = (wxDateTime_t)num; 3357 break; 3358 3359 case _T('I'): // hour in 12h format (01-12) 3360 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) ) 3361 { 3362 // no match 3363 return (wxChar *)NULL; 3364 } 3365 3366 haveHour = true; 3367 hourIsIn12hFormat = true; 3368 hour = (wxDateTime_t)(num % 12); // 12 should be 0 3369 break; 3370 3371 case _T('j'): // day of the year 3372 if ( !GetNumericToken(width, input, &num) || !num || (num > 366) ) 3373 { 3374 // no match 3375 return (wxChar *)NULL; 3376 } 3377 3378 haveYDay = true; 3379 yday = (wxDateTime_t)num; 3380 break; 3381 3382 case _T('m'): // month as a number (01-12) 3383 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) ) 3384 { 3385 // no match 3386 return (wxChar *)NULL; 3387 } 3388 3389 haveMon = true; 3390 mon = (Month)(num - 1); 3391 break; 3392 3393 case _T('M'): // minute as a decimal number (00-59) 3394 if ( !GetNumericToken(width, input, &num) || (num > 59) ) 3395 { 3396 // no match 3397 return (wxChar *)NULL; 3398 } 3399 3400 haveMin = true; 3401 min = (wxDateTime_t)num; 3402 break; 3403 3404 case _T('p'): // AM or PM string 3405 { 3406 wxString am, pm, token = GetAlphaToken(input); 3407 3408 GetAmPmStrings(&am, &pm); 3409 if (am.empty() && pm.empty()) 3410 return (wxChar *)NULL; // no am/pm strings defined 3411 if ( token.CmpNoCase(pm) == 0 ) 3412 { 3413 isPM = true; 3414 } 3415 else if ( token.CmpNoCase(am) != 0 ) 3416 { 3417 // no match 3418 return (wxChar *)NULL; 3419 } 3420 } 3421 break; 3422 3423 case _T('r'): // time as %I:%M:%S %p 3424 { 3425 wxDateTime dt; 3426 input = dt.ParseFormat(input, _T("%I:%M:%S %p")); 3427 if ( !input ) 3428 { 3429 // no match 3430 return (wxChar *)NULL; 3431 } 3432 3433 haveHour = haveMin = haveSec = true; 3434 3435 Tm tm = dt.GetTm(); 3436 hour = tm.hour; 3437 min = tm.min; 3438 sec = tm.sec; 3439 } 3440 break; 3441 3442 case _T('R'): // time as %H:%M 3443 { 3444 wxDateTime dt; 3445 input = dt.ParseFormat(input, _T("%H:%M")); 3446 if ( !input ) 3447 { 3448 // no match 3449 return (wxChar *)NULL; 3450 } 3451 3452 haveHour = haveMin = true; 3453 3454 Tm tm = dt.GetTm(); 3455 hour = tm.hour; 3456 min = tm.min; 3457 } 3458 break; 3459 3460 case _T('S'): // second as a decimal number (00-61) 3461 if ( !GetNumericToken(width, input, &num) || (num > 61) ) 3462 { 3463 // no match 3464 return (wxChar *)NULL; 3465 } 3466 3467 haveSec = true; 3468 sec = (wxDateTime_t)num; 3469 break; 3470 3471 case _T('T'): // time as %H:%M:%S 3472 { 3473 wxDateTime dt; 3474 input = dt.ParseFormat(input, _T("%H:%M:%S")); 3475 if ( !input ) 3476 { 3477 // no match 3478 return (wxChar *)NULL; 3479 } 3480 3481 haveHour = haveMin = haveSec = true; 3482 3483 Tm tm = dt.GetTm(); 3484 hour = tm.hour; 3485 min = tm.min; 3486 sec = tm.sec; 3487 } 3488 break; 3489 3490 case _T('w'): // weekday as a number (0-6), Sunday = 0 3491 if ( !GetNumericToken(width, input, &num) || (wday > 6) ) 3492 { 3493 // no match 3494 return (wxChar *)NULL; 3495 } 3496 3497 haveWDay = true; 3498 wday = (WeekDay)num; 3499 break; 3500 3501 case _T('x'): // locale default date representation 3502#ifdef HAVE_STRPTIME 3503 // try using strptime() -- it may fail even if the input is 3504 // correct but the date is out of range, so we will fall back 3505 // to our generic code anyhow 3506 { 3507 struct tm tm; 3508 3509 const wxChar *result = CallStrptime(input, "%x", &tm); 3510 if ( result ) 3511 { 3512 input = result; 3513 3514 haveDay = haveMon = haveYear = true; 3515 3516 year = 1900 + tm.tm_year; 3517 mon = (Month)tm.tm_mon; 3518 mday = tm.tm_mday; 3519 3520 break; 3521 } 3522 } 3523#endif // HAVE_STRPTIME 3524 3525 { 3526 wxDateTime dt; 3527 wxString fmtDate, 3528 fmtDateAlt; 3529 3530#ifdef __WINDOWS__ 3531 // The above doesn't work for all locales, try to query 3532 // Windows for the right way of formatting the date: 3533 fmtDate = GetLocaleDateFormat(); 3534 if ( fmtDate.empty() ) 3535#endif 3536 { 3537 if ( IsWestEuropeanCountry(GetCountry()) || 3538 GetCountry() == Russia ) 3539 { 3540 fmtDate = _T("%d/%m/%y"); 3541 fmtDateAlt = _T("%m/%d/%y"); 3542 } 3543 else // assume USA 3544 { 3545 fmtDate = _T("%m/%d/%y"); 3546 fmtDateAlt = _T("%d/%m/%y"); 3547 } 3548 } 3549 3550 const wxChar *result = dt.ParseFormat(input, fmtDate); 3551 3552 if ( !result && !fmtDateAlt.empty() ) 3553 { 3554 // ok, be nice and try another one 3555 result = dt.ParseFormat(input, fmtDateAlt); 3556 } 3557 3558 if ( !result ) 3559 { 3560 // bad luck 3561 return (wxChar *)NULL; 3562 } 3563 3564 Tm tm = dt.GetTm(); 3565 3566 haveDay = haveMon = haveYear = true; 3567 3568 year = tm.year; 3569 mon = tm.mon; 3570 mday = tm.mday; 3571 3572 input = result; 3573 } 3574 3575 break; 3576 3577 case _T('X'): // locale default time representation 3578#ifdef HAVE_STRPTIME 3579 { 3580 // use strptime() to do it for us (FIXME !Unicode friendly) 3581 struct tm tm; 3582 input = CallStrptime(input, "%X", &tm); 3583 if ( !input ) 3584 { 3585 return (wxChar *)NULL; 3586 } 3587 3588 haveHour = haveMin = haveSec = true; 3589 3590 hour = tm.tm_hour; 3591 min = tm.tm_min; 3592 sec = tm.tm_sec; 3593 } 3594#else // !HAVE_STRPTIME 3595 // TODO under Win32 we can query the LOCALE_ITIME system 3596 // setting which says whether the default time format is 3597 // 24 or 12 hour 3598 { 3599 // try to parse what follows as "%H:%M:%S" and, if this 3600 // fails, as "%I:%M:%S %p" - this should catch the most 3601 // common cases 3602 wxDateTime dt; 3603 3604 const wxChar *result = dt.ParseFormat(input, _T("%T")); 3605 if ( !result ) 3606 { 3607 result = dt.ParseFormat(input, _T("%r")); 3608 } 3609 3610 if ( !result ) 3611 { 3612 // no match 3613 return (wxChar *)NULL; 3614 } 3615 3616 haveHour = haveMin = haveSec = true; 3617 3618 Tm tm = dt.GetTm(); 3619 hour = tm.hour; 3620 min = tm.min; 3621 sec = tm.sec; 3622 3623 input = result; 3624 } 3625#endif // HAVE_STRPTIME/!HAVE_STRPTIME 3626 break; 3627 3628 case _T('y'): // year without century (00-99) 3629 if ( !GetNumericToken(width, input, &num) || (num > 99) ) 3630 { 3631 // no match 3632 return (wxChar *)NULL; 3633 } 3634 3635 haveYear = true; 3636 3637 // TODO should have an option for roll over date instead of 3638 // hard coding it here 3639 year = (num > 30 ? 1900 : 2000) + (wxDateTime_t)num; 3640 break; 3641 3642 case _T('Y'): // year with century 3643 if ( !GetNumericToken(width, input, &num) ) 3644 { 3645 // no match 3646 return (wxChar *)NULL; 3647 } 3648 3649 haveYear = true; 3650 year = (wxDateTime_t)num; 3651 break; 3652 3653 case _T('Z'): // timezone name 3654 wxFAIL_MSG(_T("TODO")); 3655 break; 3656 3657 case _T('%'): // a percent sign 3658 if ( *input++ != _T('%') ) 3659 { 3660 // no match 3661 return (wxChar *)NULL; 3662 } 3663 break; 3664 3665 case 0: // the end of string 3666 wxFAIL_MSG(_T("unexpected format end")); 3667 3668 // fall through 3669 3670 default: // not a known format spec 3671 return (wxChar *)NULL; 3672 } 3673 } 3674 3675 // format matched, try to construct a date from what we have now 3676 Tm tmDef; 3677 if ( dateDef.IsValid() ) 3678 { 3679 // take this date as default 3680 tmDef = dateDef.GetTm(); 3681 } 3682 else if ( IsValid() ) 3683 { 3684 // if this date is valid, don't change it 3685 tmDef = GetTm(); 3686 } 3687 else 3688 { 3689 // no default and this date is invalid - fall back to Today() 3690 tmDef = Today().GetTm(); 3691 } 3692 3693 Tm tm = tmDef; 3694 3695 // set the date 3696 if ( haveYear ) 3697 { 3698 tm.year = year; 3699 } 3700 3701 // TODO we don't check here that the values are consistent, if both year 3702 // day and month/day were found, we just ignore the year day and we 3703 // also always ignore the week day 3704 if ( haveMon && haveDay ) 3705 { 3706 if ( mday > GetNumOfDaysInMonth(tm.year, mon) ) 3707 { 3708 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat")); 3709 3710 return (wxChar *)NULL; 3711 } 3712 3713 tm.mon = mon; 3714 tm.mday = mday; 3715 } 3716 else if ( haveYDay ) 3717 { 3718 if ( yday > GetNumberOfDays(tm.year) ) 3719 { 3720 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat")); 3721 3722 return (wxChar *)NULL; 3723 } 3724 3725 Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm(); 3726 3727 tm.mon = tm2.mon; 3728 tm.mday = tm2.mday; 3729 } 3730 3731 // deal with AM/PM 3732 if ( haveHour && hourIsIn12hFormat && isPM ) 3733 { 3734 // translate to 24hour format 3735 hour += 12; 3736 } 3737 //else: either already in 24h format or no translation needed 3738 3739 // set the time 3740 if ( haveHour ) 3741 { 3742 tm.hour = hour; 3743 } 3744 3745 if ( haveMin ) 3746 { 3747 tm.min = min; 3748 } 3749 3750 if ( haveSec ) 3751 { 3752 tm.sec = sec; 3753 } 3754 3755 Set(tm); 3756 3757 // finally check that the week day is consistent -- if we had it 3758 if ( haveWDay && GetWeekDay() != wday ) 3759 { 3760 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()")); 3761 3762 return NULL; 3763 } 3764 3765 return input; 3766} 3767 3768const wxChar *wxDateTime::ParseDateTime(const wxChar *date) 3769{ 3770 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") ); 3771 3772 // Set to current day and hour, so strings like '14:00' becomes today at 3773 // 14, not some other random date 3774 wxDateTime dtDate = wxDateTime::Today(); 3775 wxDateTime dtTime = wxDateTime::Today(); 3776 3777 const wxChar* pchTime; 3778 3779 // Try to parse the beginning of the string as a date 3780 const wxChar* pchDate = dtDate.ParseDate(date); 3781 3782 // We got a date in the beginning, see if there is a time specified after the date 3783 if ( pchDate ) 3784 { 3785 // Skip spaces, as the ParseTime() function fails on spaces 3786 while ( wxIsspace(*pchDate) ) 3787 pchDate++; 3788 3789 pchTime = dtTime.ParseTime(pchDate); 3790 } 3791 else // no date in the beginning 3792 { 3793 // check and see if we have a time followed by a date 3794 pchTime = dtTime.ParseTime(date); 3795 if ( pchTime ) 3796 { 3797 while ( wxIsspace(*pchTime) ) 3798 pchTime++; 3799 3800 pchDate = dtDate.ParseDate(pchTime); 3801 } 3802 } 3803 3804 // If we have a date specified, set our own data to the same date 3805 if ( !pchDate || !pchTime ) 3806 return NULL; 3807 3808 Set(dtDate.GetDay(), dtDate.GetMonth(), dtDate.GetYear(), 3809 dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(), 3810 dtTime.GetMillisecond()); 3811 3812 // Return endpoint of scan 3813 return pchDate > pchTime ? pchDate : pchTime; 3814} 3815 3816const wxChar *wxDateTime::ParseDate(const wxChar *date) 3817{ 3818 // this is a simplified version of ParseDateTime() which understands only 3819 // "today" (for wxDate compatibility) and digits only otherwise (and not 3820 // all esoteric constructions ParseDateTime() knows about) 3821 3822 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") ); 3823 3824 const wxChar *p = date; 3825 while ( wxIsspace(*p) ) 3826 p++; 3827 3828 // some special cases 3829 static struct 3830 { 3831 const wxChar *str; 3832 int dayDiffFromToday; 3833 } literalDates[] = 3834 { 3835 { wxTRANSLATE("today"), 0 }, 3836 { wxTRANSLATE("yesterday"), -1 }, 3837 { wxTRANSLATE("tomorrow"), 1 }, 3838 }; 3839 3840 for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ ) 3841 { 3842 const wxString dateStr = wxGetTranslation(literalDates[n].str); 3843 size_t len = dateStr.length(); 3844 if ( wxStrlen(p) >= len ) 3845 { 3846 wxString str(p, len); 3847 if ( str.CmpNoCase(dateStr) == 0 ) 3848 { 3849 // nothing can follow this, so stop here 3850 p += len; 3851 3852 int dayDiffFromToday = literalDates[n].dayDiffFromToday; 3853 *this = Today(); 3854 if ( dayDiffFromToday ) 3855 { 3856 *this += wxDateSpan::Days(dayDiffFromToday); 3857 } 3858 3859 return p; 3860 } 3861 } 3862 } 3863 3864 // We try to guess what we have here: for each new (numeric) token, we 3865 // determine if it can be a month, day or a year. Of course, there is an 3866 // ambiguity as some numbers may be days as well as months, so we also 3867 // have the ability to back track. 3868 3869 // what do we have? 3870 bool haveDay = false, // the months day? 3871 haveWDay = false, // the day of week? 3872 haveMon = false, // the month? 3873 haveYear = false; // the year? 3874 3875 // and the value of the items we have (init them to get rid of warnings) 3876 WeekDay wday = Inv_WeekDay; 3877 wxDateTime_t day = 0; 3878 wxDateTime::Month mon = Inv_Month; 3879 int year = 0; 3880 3881 // tokenize the string 3882 size_t nPosCur = 0; 3883 static const wxChar *dateDelimiters = _T(".,/-\t\r\n "); 3884 wxStringTokenizer tok(p, dateDelimiters); 3885 while ( tok.HasMoreTokens() ) 3886 { 3887 wxString token = tok.GetNextToken(); 3888 if ( !token ) 3889 continue; 3890 3891 // is it a number? 3892 unsigned long val; 3893 if ( token.ToULong(&val) ) 3894 { 3895 // guess what this number is 3896 3897 bool isDay = false, 3898 isMonth = false, 3899 isYear = false; 3900 3901 if ( !haveMon && val > 0 && val <= 12 ) 3902 { 3903 // assume it is month 3904 isMonth = true; 3905 } 3906 else // not the month 3907 { 3908 if ( haveDay ) 3909 { 3910 // this can only be the year 3911 isYear = true; 3912 } 3913 else // may be either day or year 3914 { 3915 // use a leap year if we don't have the year yet to allow 3916 // dates like 2/29/1976 which would be rejected otherwise 3917 wxDateTime_t max_days = (wxDateTime_t)( 3918 haveMon 3919 ? GetNumOfDaysInMonth(haveYear ? year : 1976, mon) 3920 : 31 3921 ); 3922 3923 // can it be day? 3924 if ( (val == 0) || (val > (unsigned long)max_days) ) 3925 { 3926 // no 3927 isYear = true; 3928 } 3929 else // yes, suppose it's the day 3930 { 3931 isDay = true; 3932 } 3933 } 3934 } 3935 3936 if ( isYear ) 3937 { 3938 if ( haveYear ) 3939 break; 3940 3941 haveYear = true; 3942 3943 year = (wxDateTime_t)val; 3944 } 3945 else if ( isDay ) 3946 { 3947 if ( haveDay ) 3948 break; 3949 3950 haveDay = true; 3951 3952 day = (wxDateTime_t)val; 3953 } 3954 else if ( isMonth ) 3955 { 3956 haveMon = true; 3957 3958 mon = (Month)(val - 1); 3959 } 3960 } 3961 else // not a number 3962 { 3963 // be careful not to overwrite the current mon value 3964 Month mon2 = GetMonthFromName(token, Name_Full | Name_Abbr); 3965 if ( mon2 != Inv_Month ) 3966 { 3967 // it's a month 3968 if ( haveMon ) 3969 { 3970 // but we already have a month - maybe we guessed wrong? 3971 if ( !haveDay ) 3972 { 3973 // no need to check in month range as always < 12, but 3974 // the days are counted from 1 unlike the months 3975 day = (wxDateTime_t)(mon + 1); 3976 haveDay = true; 3977 } 3978 else 3979 { 3980 // could possible be the year (doesn't the year come 3981 // before the month in the japanese format?) (FIXME) 3982 break; 3983 } 3984 } 3985 3986 mon = mon2; 3987 3988 haveMon = true; 3989 } 3990 else // not a valid month name 3991 { 3992 WeekDay wday2 = GetWeekDayFromName(token, Name_Full | Name_Abbr); 3993 if ( wday2 != Inv_WeekDay ) 3994 { 3995 // a week day 3996 if ( haveWDay ) 3997 { 3998 break; 3999 } 4000 4001 wday = wday2; 4002 4003 haveWDay = true; 4004 } 4005 else // not a valid weekday name 4006 { 4007 // try the ordinals 4008 static const wxChar *ordinals[] = 4009 { 4010 wxTRANSLATE("first"), 4011 wxTRANSLATE("second"), 4012 wxTRANSLATE("third"), 4013 wxTRANSLATE("fourth"), 4014 wxTRANSLATE("fifth"), 4015 wxTRANSLATE("sixth"), 4016 wxTRANSLATE("seventh"), 4017 wxTRANSLATE("eighth"), 4018 wxTRANSLATE("ninth"), 4019 wxTRANSLATE("tenth"), 4020 wxTRANSLATE("eleventh"), 4021 wxTRANSLATE("twelfth"), 4022 wxTRANSLATE("thirteenth"), 4023 wxTRANSLATE("fourteenth"), 4024 wxTRANSLATE("fifteenth"), 4025 wxTRANSLATE("sixteenth"), 4026 wxTRANSLATE("seventeenth"), 4027 wxTRANSLATE("eighteenth"), 4028 wxTRANSLATE("nineteenth"), 4029 wxTRANSLATE("twentieth"), 4030 // that's enough - otherwise we'd have problems with 4031 // composite (or not) ordinals 4032 }; 4033 4034 size_t n; 4035 for ( n = 0; n < WXSIZEOF(ordinals); n++ ) 4036 { 4037 if ( token.CmpNoCase(ordinals[n]) == 0 ) 4038 { 4039 break; 4040 } 4041 } 4042 4043 if ( n == WXSIZEOF(ordinals) ) 4044 { 4045 // stop here - something unknown 4046 break; 4047 } 4048 4049 // it's a day 4050 if ( haveDay ) 4051 { 4052 // don't try anything here (as in case of numeric day 4053 // above) - the symbolic day spec should always 4054 // precede the month/year 4055 break; 4056 } 4057 4058 haveDay = true; 4059 4060 day = (wxDateTime_t)(n + 1); 4061 } 4062 } 4063 } 4064 4065 nPosCur = tok.GetPosition(); 4066 } 4067 4068 // either no more tokens or the scan was stopped by something we couldn't 4069 // parse - in any case, see if we can construct a date from what we have 4070 if ( !haveDay && !haveWDay ) 4071 { 4072 wxLogDebug(_T("ParseDate: no day, no weekday hence no date.")); 4073 4074 return NULL; 4075 } 4076 4077 if ( haveWDay && (haveMon || haveYear || haveDay) && 4078 !(haveDay && haveMon && haveYear) ) 4079 { 4080 // without adjectives (which we don't support here) the week day only 4081 // makes sense completely separately or with the full date 4082 // specification (what would "Wed 1999" mean?) 4083 return NULL; 4084 } 4085 4086 if ( !haveWDay && haveYear && !(haveDay && haveMon) ) 4087 { 4088 // may be we have month and day instead of day and year? 4089 if ( haveDay && !haveMon ) 4090 { 4091 if ( day <= 12 ) 4092 { 4093 // exchange day and month 4094 mon = (wxDateTime::Month)(day - 1); 4095 4096 // we're in the current year then 4097 if ( (year > 0) && (year <= (int)GetNumOfDaysInMonth(Inv_Year, mon)) ) 4098 { 4099 day = (wxDateTime_t)year; 4100 4101 haveMon = true; 4102 haveYear = false; 4103 } 4104 //else: no, can't exchange, leave haveMon == false 4105 } 4106 } 4107 4108 if ( !haveMon ) 4109 { 4110 // if we give the year, month and day must be given too 4111 wxLogDebug(_T("ParseDate: day and month should be specified if year is.")); 4112 4113 return NULL; 4114 } 4115 } 4116 4117 if ( !haveMon ) 4118 { 4119 mon = GetCurrentMonth(); 4120 } 4121 4122 if ( !haveYear ) 4123 { 4124 year = GetCurrentYear(); 4125 } 4126 4127 if ( haveDay ) 4128 { 4129 // normally we check the day above but the check is optimistic in case 4130 // we find the day before its month/year so we have to redo it now 4131 if ( day > GetNumOfDaysInMonth(year, mon) ) 4132 return NULL; 4133 4134 Set(day, mon, year); 4135 4136 if ( haveWDay ) 4137 { 4138 // check that it is really the same 4139 if ( GetWeekDay() != wday ) 4140 { 4141 // inconsistency detected 4142 wxLogDebug(_T("ParseDate: inconsistent day/weekday.")); 4143 4144 return (wxChar *)NULL; 4145 } 4146 } 4147 } 4148 else // haveWDay 4149 { 4150 *this = Today(); 4151 4152 SetToWeekDayInSameWeek(wday); 4153 } 4154 4155 // return the pointer to the first unparsed char 4156 p += nPosCur; 4157 if ( nPosCur && wxStrchr(dateDelimiters, *(p - 1)) ) 4158 { 4159 // if we couldn't parse the token after the delimiter, put back the 4160 // delimiter as well 4161 p--; 4162 } 4163 4164 return p; 4165} 4166 4167const wxChar *wxDateTime::ParseTime(const wxChar *time) 4168{ 4169 wxCHECK_MSG( time, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") ); 4170 4171 // first try some extra things 4172 static const struct 4173 { 4174 const wxChar *name; 4175 wxDateTime_t hour; 4176 } stdTimes[] = 4177 { 4178 { wxTRANSLATE("noon"), 12 }, 4179 { wxTRANSLATE("midnight"), 00 }, 4180 // anything else? 4181 }; 4182 4183 for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ ) 4184 { 4185 wxString timeString = wxGetTranslation(stdTimes[n].name); 4186 size_t len = timeString.length(); 4187 if ( timeString.CmpNoCase(wxString(time, len)) == 0 ) 4188 { 4189 // casts required by DigitalMars 4190 Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0)); 4191 4192 return time + len; 4193 } 4194 } 4195 4196 // try all time formats we may think about in the order from longest to 4197 // shortest 4198 4199 // 12hour with AM/PM? 4200 const wxChar *result = ParseFormat(time, _T("%I:%M:%S %p")); 4201 4202 if ( !result ) 4203 { 4204 // normally, it's the same, but why not try it? 4205 result = ParseFormat(time, _T("%H:%M:%S")); 4206 } 4207 4208 if ( !result ) 4209 { 4210 // 12hour with AM/PM but without seconds? 4211 result = ParseFormat(time, _T("%I:%M %p")); 4212 } 4213 4214 if ( !result ) 4215 { 4216 // without seconds? 4217 result = ParseFormat(time, _T("%H:%M")); 4218 } 4219 4220 if ( !result ) 4221 { 4222 // just the hour and AM/PM? 4223 result = ParseFormat(time, _T("%I %p")); 4224 } 4225 4226 if ( !result ) 4227 { 4228 // just the hour? 4229 result = ParseFormat(time, _T("%H")); 4230 } 4231 4232 if ( !result ) 4233 { 4234 // parse the standard format: normally it is one of the formats above 4235 // but it may be set to something completely different by the user 4236 result = ParseFormat(time, _T("%X")); 4237 } 4238 4239 // TODO: parse timezones 4240 4241 return result; 4242} 4243 4244// ---------------------------------------------------------------------------- 4245// Workdays and holidays support 4246// ---------------------------------------------------------------------------- 4247 4248bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const 4249{ 4250 return !wxDateTimeHolidayAuthority::IsHoliday(*this); 4251} 4252 4253// ============================================================================ 4254// wxDateSpan 4255// ============================================================================ 4256 4257wxDateSpan WXDLLIMPEXP_BASE operator*(int n, const wxDateSpan& ds) 4258{ 4259 wxDateSpan ds1(ds); 4260 return ds1.Multiply(n); 4261} 4262 4263// ============================================================================ 4264// wxTimeSpan 4265// ============================================================================ 4266 4267wxTimeSpan WXDLLIMPEXP_BASE operator*(int n, const wxTimeSpan& ts) 4268{ 4269 return wxTimeSpan(ts).Multiply(n); 4270} 4271 4272// this enum is only used in wxTimeSpan::Format() below but we can't declare 4273// it locally to the method as it provokes an internal compiler error in egcs 4274// 2.91.60 when building with -O2 4275enum TimeSpanPart 4276{ 4277 Part_Week, 4278 Part_Day, 4279 Part_Hour, 4280 Part_Min, 4281 Part_Sec, 4282 Part_MSec 4283}; 4284 4285// not all strftime(3) format specifiers make sense here because, for example, 4286// a time span doesn't have a year nor a timezone 4287// 4288// Here are the ones which are supported (all of them are supported by strftime 4289// as well): 4290// %H hour in 24 hour format 4291// %M minute (00 - 59) 4292// %S second (00 - 59) 4293// %% percent sign 4294// 4295// Also, for MFC CTimeSpan compatibility, we support 4296// %D number of days 4297// 4298// And, to be better than MFC :-), we also have 4299// %E number of wEeks 4300// %l milliseconds (000 - 999) 4301wxString wxTimeSpan::Format(const wxChar *format) const 4302{ 4303 // we deal with only positive time spans here and just add the sign in 4304 // front for the negative ones 4305 if ( IsNegative() ) 4306 { 4307 wxString str(Negate().Format(format)); 4308 return _T("-") + str; 4309 } 4310 4311 wxCHECK_MSG( format, wxEmptyString, 4312 _T("NULL format in wxTimeSpan::Format") ); 4313 4314 wxString str; 4315 str.Alloc(wxStrlen(format)); 4316 4317 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */) 4318 // 4319 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the 4320 // question is what should ts.Format("%S") do? The code here returns "3273" 4321 // in this case (i.e. the total number of seconds, not just seconds % 60) 4322 // because, for me, this call means "give me entire time interval in 4323 // seconds" and not "give me the seconds part of the time interval" 4324 // 4325 // If we agree that it should behave like this, it is clear that the 4326 // interpretation of each format specifier depends on the presence of the 4327 // other format specs in the string: if there was "%H" before "%M", we 4328 // should use GetMinutes() % 60, otherwise just GetMinutes() &c 4329 4330 // we remember the most important unit found so far 4331 TimeSpanPart partBiggest = Part_MSec; 4332 4333 for ( const wxChar *pch = format; *pch; pch++ ) 4334 { 4335 wxChar ch = *pch; 4336 4337 if ( ch == _T('%') ) 4338 { 4339 // the start of the format specification of the printf() below 4340 wxString fmtPrefix(_T('%')); 4341 4342 // the number 4343 long n; 4344 4345 // the number of digits for the format string, 0 if unused 4346 unsigned digits = 0; 4347 4348 ch = *++pch; // get the format spec char 4349 switch ( ch ) 4350 { 4351 default: 4352 wxFAIL_MSG( _T("invalid format character") ); 4353 // fall through 4354 4355 case _T('%'): 4356 str += ch; 4357 4358 // skip the part below switch 4359 continue; 4360 4361 case _T('D'): 4362 n = GetDays(); 4363 if ( partBiggest < Part_Day ) 4364 { 4365 n %= DAYS_PER_WEEK; 4366 } 4367 else 4368 { 4369 partBiggest = Part_Day; 4370 } 4371 break; 4372 4373 case _T('E'): 4374 partBiggest = Part_Week; 4375 n = GetWeeks(); 4376 break; 4377 4378 case _T('H'): 4379 n = GetHours(); 4380 if ( partBiggest < Part_Hour ) 4381 { 4382 n %= HOURS_PER_DAY; 4383 } 4384 else 4385 { 4386 partBiggest = Part_Hour; 4387 } 4388 4389 digits = 2; 4390 break; 4391 4392 case _T('l'): 4393 n = GetMilliseconds().ToLong(); 4394 if ( partBiggest < Part_MSec ) 4395 { 4396 n %= 1000; 4397 } 4398 //else: no need to reset partBiggest to Part_MSec, it is 4399 // the least significant one anyhow 4400 4401 digits = 3; 4402 break; 4403 4404 case _T('M'): 4405 n = GetMinutes(); 4406 if ( partBiggest < Part_Min ) 4407 { 4408 n %= MIN_PER_HOUR; 4409 } 4410 else 4411 { 4412 partBiggest = Part_Min; 4413 } 4414 4415 digits = 2; 4416 break; 4417 4418 case _T('S'): 4419 n = GetSeconds().ToLong(); 4420 if ( partBiggest < Part_Sec ) 4421 { 4422 n %= SEC_PER_MIN; 4423 } 4424 else 4425 { 4426 partBiggest = Part_Sec; 4427 } 4428 4429 digits = 2; 4430 break; 4431 } 4432 4433 if ( digits ) 4434 { 4435 fmtPrefix << _T("0") << digits; 4436 } 4437 4438 str += wxString::Format(fmtPrefix + _T("ld"), n); 4439 } 4440 else 4441 { 4442 // normal character, just copy 4443 str += ch; 4444 } 4445 } 4446 4447 return str; 4448} 4449 4450// ============================================================================ 4451// wxDateTimeHolidayAuthority and related classes 4452// ============================================================================ 4453 4454#include "wx/arrimpl.cpp" 4455 4456WX_DEFINE_OBJARRAY(wxDateTimeArray) 4457 4458static int wxCMPFUNC_CONV 4459wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second) 4460{ 4461 wxDateTime dt1 = **first, 4462 dt2 = **second; 4463 4464 return dt1 == dt2 ? 0 : dt1 < dt2 ? -1 : +1; 4465} 4466 4467// ---------------------------------------------------------------------------- 4468// wxDateTimeHolidayAuthority 4469// ---------------------------------------------------------------------------- 4470 4471wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities; 4472 4473/* static */ 4474bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt) 4475{ 4476 size_t count = ms_authorities.size(); 4477 for ( size_t n = 0; n < count; n++ ) 4478 { 4479 if ( ms_authorities[n]->DoIsHoliday(dt) ) 4480 { 4481 return true; 4482 } 4483 } 4484 4485 return false; 4486} 4487 4488/* static */ 4489size_t 4490wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart, 4491 const wxDateTime& dtEnd, 4492 wxDateTimeArray& holidays) 4493{ 4494 wxDateTimeArray hol; 4495 4496 holidays.Clear(); 4497 4498 const size_t countAuth = ms_authorities.size(); 4499 for ( size_t nAuth = 0; nAuth < countAuth; nAuth++ ) 4500 { 4501 ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol); 4502 4503 WX_APPEND_ARRAY(holidays, hol); 4504 } 4505 4506 holidays.Sort(wxDateTimeCompareFunc); 4507 4508 return holidays.size(); 4509} 4510 4511/* static */ 4512void wxDateTimeHolidayAuthority::ClearAllAuthorities() 4513{ 4514 WX_CLEAR_ARRAY(ms_authorities); 4515} 4516 4517/* static */ 4518void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth) 4519{ 4520 ms_authorities.push_back(auth); 4521} 4522 4523wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority() 4524{ 4525 // required here for Darwin 4526} 4527 4528// ---------------------------------------------------------------------------- 4529// wxDateTimeWorkDays 4530// ---------------------------------------------------------------------------- 4531 4532bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const 4533{ 4534 wxDateTime::WeekDay wd = dt.GetWeekDay(); 4535 4536 return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat); 4537} 4538 4539size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart, 4540 const wxDateTime& dtEnd, 4541 wxDateTimeArray& holidays) const 4542{ 4543 if ( dtStart > dtEnd ) 4544 { 4545 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") ); 4546 4547 return 0u; 4548 } 4549 4550 holidays.Empty(); 4551 4552 // instead of checking all days, start with the first Sat after dtStart and 4553 // end with the last Sun before dtEnd 4554 wxDateTime dtSatFirst = dtStart.GetNextWeekDay(wxDateTime::Sat), 4555 dtSatLast = dtEnd.GetPrevWeekDay(wxDateTime::Sat), 4556 dtSunFirst = dtStart.GetNextWeekDay(wxDateTime::Sun), 4557 dtSunLast = dtEnd.GetPrevWeekDay(wxDateTime::Sun), 4558 dt; 4559 4560 for ( dt = dtSatFirst; dt <= dtSatLast; dt += wxDateSpan::Week() ) 4561 { 4562 holidays.Add(dt); 4563 } 4564 4565 for ( dt = dtSunFirst; dt <= dtSunLast; dt += wxDateSpan::Week() ) 4566 { 4567 holidays.Add(dt); 4568 } 4569 4570 return holidays.GetCount(); 4571} 4572 4573// ============================================================================ 4574// other helper functions 4575// ============================================================================ 4576 4577// ---------------------------------------------------------------------------- 4578// iteration helpers: can be used to write a for loop over enum variable like 4579// this: 4580// for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) ) 4581// ---------------------------------------------------------------------------- 4582 4583WXDLLIMPEXP_BASE void wxNextMonth(wxDateTime::Month& m) 4584{ 4585 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") ); 4586 4587 // no wrapping or the for loop above would never end! 4588 m = (wxDateTime::Month)(m + 1); 4589} 4590 4591WXDLLIMPEXP_BASE void wxPrevMonth(wxDateTime::Month& m) 4592{ 4593 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") ); 4594 4595 m = m == wxDateTime::Jan ? wxDateTime::Inv_Month 4596 : (wxDateTime::Month)(m - 1); 4597} 4598 4599WXDLLIMPEXP_BASE void wxNextWDay(wxDateTime::WeekDay& wd) 4600{ 4601 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") ); 4602 4603 // no wrapping or the for loop above would never end! 4604 wd = (wxDateTime::WeekDay)(wd + 1); 4605} 4606 4607WXDLLIMPEXP_BASE void wxPrevWDay(wxDateTime::WeekDay& wd) 4608{ 4609 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") ); 4610 4611 wd = wd == wxDateTime::Sun ? wxDateTime::Inv_WeekDay 4612 : (wxDateTime::WeekDay)(wd - 1); 4613} 4614 4615#endif // wxUSE_DATETIME 4616