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