]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/datetime.cpp
Borland #include hack
[wxWidgets.git] / src / common / datetime.cpp
index 8685319432773f5b489ef0ba33379f7020c282d2..7598153308a080559c6d6a4973690949c2582892 100644 (file)
@@ -67,6 +67,7 @@
     #include "wx/string.h"
     #include "wx/intl.h"
     #include "wx/log.h"
+    #include "wx/module.h"
 #endif // WX_PRECOMP
 
 #include "wx/thread.h"
     #endif
 #endif // !WX_TIMEZONE
 
+// ----------------------------------------------------------------------------
+// private classes
+// ----------------------------------------------------------------------------
+
+class wxDateTimeHolidaysModule : public wxModule
+{
+public:
+    virtual bool OnInit()
+    {
+        wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays);
+
+        return TRUE;
+    }
+
+    virtual void OnExit()
+    {
+        wxDateTimeHolidayAuthority::ClearAllAuthorities();
+    }
+
+private:
+    DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule)
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule)
+
 // ----------------------------------------------------------------------------
 // constants
 // ----------------------------------------------------------------------------
@@ -137,19 +163,41 @@ static const wxDateTime::wxDateTime_t gs_cumulatedDays[2][MONTHS_IN_YEAR] =
 };
 
 // ----------------------------------------------------------------------------
-// globals
+// global data
+// ----------------------------------------------------------------------------
+
+static wxDateTime gs_dtDefault;
+
+wxDateTime& wxDefaultDateTime = gs_dtDefault;
+
+wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
+
+// ----------------------------------------------------------------------------
+// private globals
 // ----------------------------------------------------------------------------
 
 // a critical section is needed to protect GetTimeZone() static
 // variable in MT case
 #if wxUSE_THREADS
-    wxCriticalSection gs_critsectTimezone;
+    static wxCriticalSection gs_critsectTimezone;
 #endif // wxUSE_THREADS
 
 // ----------------------------------------------------------------------------
 // private functions
 // ----------------------------------------------------------------------------
 
+// debugger helper: shows what the date really is
+#ifdef __WXDEBUG__
+extern const wxChar *wxDumpDate(const wxDateTime* dt)
+{
+    static wxChar buf[20];
+
+    wxStrcpy(buf, dt->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
+
+    return buf;
+}
+#endif // Debug
+
 // get the number of days in the given month of the given year
 static inline
 wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
@@ -364,13 +412,6 @@ static wxString GetAlphaToken(const wxChar*& p)
 // implementation of wxDateTime
 // ============================================================================
 
-// ----------------------------------------------------------------------------
-// static data
-// ----------------------------------------------------------------------------
-
-wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
-wxDateTime wxDateTime::ms_InvDateTime;
-
 // ----------------------------------------------------------------------------
 // struct Tm
 // ----------------------------------------------------------------------------
@@ -424,7 +465,7 @@ void wxDateTime::Tm::AddMonths(int monDiff)
         monDiff += MONTHS_IN_YEAR;
     }
 
-    while ( monDiff + mon > MONTHS_IN_YEAR )
+    while ( monDiff + mon >= MONTHS_IN_YEAR )
     {
         year++;
 
@@ -434,19 +475,22 @@ void wxDateTime::Tm::AddMonths(int monDiff)
     mon = (wxDateTime::Month)(mon + monDiff);
 
     wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") );
+
+    // NB: we don't check here that the resulting date is valid, this function
+    //     is private and the caller must check it if needed
 }
 
 void wxDateTime::Tm::AddDays(int dayDiff)
 {
     // normalize the days field
-    mday += dayDiff;
-    while ( mday < 1 )
+    while ( dayDiff + mday < 1 )
     {
         AddMonths(-1);
 
-        mday += GetNumOfDaysInMonth(year, mon);
+        dayDiff += GetNumOfDaysInMonth(year, mon);
     }
 
+    mday += dayDiff;
     while ( mday > GetNumOfDaysInMonth(year, mon) )
     {
         mday -= GetNumOfDaysInMonth(year, mon);
@@ -584,7 +628,6 @@ wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
     {
         case Gregorian:
             return Now().GetMonth();
-            break;
 
         case Julian:
             wxFAIL_MSG(_T("TODO"));
@@ -612,7 +655,6 @@ wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal)
         case Gregorian:
         case Julian:
             return IsLeapYear(year) ? 366 : 365;
-            break;
 
         default:
             wxFAIL_MSG(_T("unsupported calendar"));
@@ -819,7 +861,7 @@ wxDateTime wxDateTime::GetBeginDST(int year, Country country)
 
     if ( !IsDSTApplicable(year, country) )
     {
-        return ms_InvDateTime;
+        return wxInvalidDateTime;
     }
 
     wxDateTime dt;
@@ -835,7 +877,8 @@ wxDateTime wxDateTime::GetBeginDST(int year, Country country)
 
         dt += wxTimeSpan::Hours(1);
 
-        dt.MakeGMT();
+        // disable DST tests because it could result in an infinite recursion!
+        dt.MakeGMT(TRUE);
     }
     else switch ( country )
     {
@@ -919,7 +962,7 @@ wxDateTime wxDateTime::GetEndDST(int year, Country country)
 
     if ( !IsDSTApplicable(year, country) )
     {
-        return ms_InvDateTime;
+        return wxInvalidDateTime;
     }
 
     wxDateTime dt;
@@ -935,7 +978,8 @@ wxDateTime wxDateTime::GetEndDST(int year, Country country)
 
         dt += wxTimeSpan::Hours(1);
 
-        dt.MakeGMT();
+        // disable DST tests because it could result in an infinite recursion!
+        dt.MakeGMT(TRUE);
     }
     else switch ( country )
     {
@@ -1010,7 +1054,7 @@ wxDateTime& wxDateTime::Set(const struct tm& tm)
 
         wxFAIL_MSG( _T("mktime() failed") );
 
-        return ms_InvDateTime;
+        return wxInvalidDateTime;
     }
     else
     {
@@ -1028,14 +1072,13 @@ wxDateTime& wxDateTime::Set(wxDateTime_t hour,
     // we allow seconds to be 61 to account for the leap seconds, even if we
     // don't use them really
     wxCHECK_MSG( hour < 24 && second < 62 && minute < 60 && millisec < 1000,
-                 ms_InvDateTime,
+                 wxInvalidDateTime,
                  _T("Invalid time in wxDateTime::Set()") );
 
     // get the current date from system
-    time_t timet = GetTimeNow();
-    struct tm *tm = localtime(&timet);
+    struct tm *tm = GetTmNow();
 
-    wxCHECK_MSG( tm, ms_InvDateTime, _T("localtime() failed") );
+    wxCHECK_MSG( tm, wxInvalidDateTime, _T("localtime() failed") );
 
     // adjust the time
     tm->tm_hour = hour;
@@ -1059,13 +1102,13 @@ wxDateTime& wxDateTime::Set(wxDateTime_t day,
     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
 
     wxCHECK_MSG( hour < 24 && second < 62 && minute < 60 && millisec < 1000,
-                 ms_InvDateTime,
+                 wxInvalidDateTime,
                  _T("Invalid time in wxDateTime::Set()") );
 
     ReplaceDefaultYearMonthWithCurrent(&year, &month);
 
     wxCHECK_MSG( (0 < day) && (day <= GetNumberOfDays(month, year)),
-                 ms_InvDateTime,
+                 wxInvalidDateTime,
                  _T("Invalid date in wxDateTime::Set()") );
 
     // the range of time_t type (inclusive)
@@ -1117,7 +1160,24 @@ wxDateTime& wxDateTime::Set(double jdn)
 
     jdn *= MILLISECONDS_PER_DAY;
 
-    m_time = jdn;
+    m_time.Assign(jdn);
+
+    return *this;
+}
+
+wxDateTime& wxDateTime::ResetTime()
+{
+    Tm tm = GetTm();
+
+    if ( tm.hour || tm.min || tm.sec || tm.msec )
+    {
+        tm.msec =
+        tm.sec =
+        tm.min =
+        tm.hour = 0;
+
+        Set(tm);
+    }
 
     return *this;
 }
@@ -1128,9 +1188,6 @@ wxDateTime& wxDateTime::Set(double jdn)
 
 wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
 {
-#ifdef __VMS__
-   int time2;
-#endif
     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
 
     time_t time = GetTicks();
@@ -1148,12 +1205,12 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
         }
         else
         {
-            time += tz.GetOffset();
-#ifdef __VMS__ /* time is unsigned so VMS gives a warning on the original */
-          time2 = (int) time;
-          if ( time2 >= 0 )
+            time += (time_t)tz.GetOffset();
+#ifdef __VMS__ // time is unsigned so avoid warning
+            int time2 = (int) time;
+            if ( time2 >= 0 )
 #else
-          if ( time >= 0 )
+            if ( time >= 0 )
 #endif
             {
                 tm = gmtime(&time);
@@ -1342,10 +1399,27 @@ wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
 
     tm.year += diff.GetYears();
     tm.AddMonths(diff.GetMonths());
+
+    // check that the resulting date is valid
+    if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
+    {
+        // We suppose that when adding one month to Jan 31 we want to get Feb
+        // 28 (or 29), i.e. adding a month to the last day of the month should
+        // give the last day of the next month which is quite logical.
+        //
+        // Unfortunately, there is no logic way to understand what should
+        // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
+        // We make it Feb 28 (last day too), but it is highly questionable.
+        tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon);
+    }
+
     tm.AddDays(diff.GetTotalDays());
 
     Set(tm);
 
+    wxASSERT_MSG( IsSameTime(tm),
+                  _T("Add(wxDateSpan) shouldn't modify time") );
+
     return *this;
 }
 
@@ -1353,18 +1427,38 @@ wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
 // Weekday and monthday stuff
 // ----------------------------------------------------------------------------
 
+bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek, WeekDay weekday)
+{
+    int year = GetYear();
+
+    // Jan 4 always lies in the 1st week of the year
+    Set(4, Jan, year);
+    SetToWeekDayInSameWeek(weekday) += wxDateSpan::Weeks(numWeek);
+
+    if ( GetYear() != year )
+    {
+        // oops... numWeek was too big
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
 wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
                                           int year)
 {
     // take the current month/year if none specified
-    ReplaceDefaultYearMonthWithCurrent(&year, &month);
+    if ( year == Inv_Year )
+        year = GetYear();
+    if ( month == Inv_Month )
+        month = GetMonth();
 
     return Set(GetNumOfDaysInMonth(year, month), month, year);
 }
 
 wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday)
 {
-    wxCHECK_MSG( weekday != Inv_WeekDay, ms_InvDateTime, _T("invalid weekday") );
+    wxCHECK_MSG( weekday != Inv_WeekDay, wxInvalidDateTime, _T("invalid weekday") );
 
     WeekDay wdayThis = GetWeekDay();
     if ( weekday == wdayThis )
@@ -1374,17 +1468,17 @@ wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday)
     }
     else if ( weekday < wdayThis )
     {
-        return Substract(wxTimeSpan::Days(wdayThis - weekday));
+        return Substract(wxDateSpan::Days(wdayThis - weekday));
     }
     else // weekday > wdayThis
     {
-        return Add(wxTimeSpan::Days(weekday - wdayThis));
+        return Add(wxDateSpan::Days(weekday - wdayThis));
     }
 }
 
 wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
 {
-    wxCHECK_MSG( weekday != Inv_WeekDay, ms_InvDateTime, _T("invalid weekday") );
+    wxCHECK_MSG( weekday != Inv_WeekDay, wxInvalidDateTime, _T("invalid weekday") );
 
     int diff;
     WeekDay wdayThis = GetWeekDay();
@@ -1403,12 +1497,12 @@ wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
         diff = weekday - wdayThis;
     }
 
-    return Add(wxTimeSpan::Days(diff));
+    return Add(wxDateSpan::Days(diff));
 }
 
 wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
 {
-    wxCHECK_MSG( weekday != Inv_WeekDay, ms_InvDateTime, _T("invalid weekday") );
+    wxCHECK_MSG( weekday != Inv_WeekDay, wxInvalidDateTime, _T("invalid weekday") );
 
     int diff;
     WeekDay wdayThis = GetWeekDay();
@@ -1427,7 +1521,7 @@ wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
         diff = wdayThis - weekday;
     }
 
-    return Substract(wxTimeSpan::Days(diff));
+    return Substract(wxDateSpan::Days(diff));
 }
 
 bool wxDateTime::SetToWeekDay(WeekDay weekday,
@@ -1505,47 +1599,50 @@ wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
     return gs_cumulatedDays[IsLeapYear(tm.year)][tm.mon] + tm.mday;
 }
 
-wxDateTime::wxDateTime_t wxDateTime::GetWeekOfYear(const TimeZone& tz) const
+wxDateTime::wxDateTime_t wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags,
+                                                   const TimeZone& tz) const
 {
-#if 0
-    // the first week of the year is the one which contains Jan, 4 (according
-    // to ISO standard rule), so the year day N0 = 4 + 7*W always lies in the
-    // week W+1. As any day N = 7*W + 4 + (N - 4)%7, it lies in the same week
-    // as N0 or in the next one.
-
-    // TODO this surely may be optimized - I got confused while writing it
+    if ( flags == Default_First )
+    {
+        flags = GetCountry() == USA ? Sunday_First : Monday_First;
+    }
 
     wxDateTime_t nDayInYear = GetDayOfYear(tz);
+    wxDateTime_t week;
 
-    // the week day of the day lying in the first week
-    WeekDay wdayStart = wxDateTime(4, Jan, GetYear()).GetWeekDay();
-
-    wxDateTime_t week = (nDayInYear - 4) / 7 + 1;
+    WeekDay wd = GetWeekDay(tz);
+    if ( flags == Sunday_First )
+    {
+        week = (nDayInYear - wd + 7) / 7;
+    }
+    else
+    {
+        // have to shift the week days values
+        week = (nDayInYear - (wd - 1 + 7) % 7 + 7) / 7;
+    }
 
-    // notice that Sunday shoould be counted as 7, not 0 here!
-    if ( ((nDayInYear - 4) % 7) + (!wdayStart ? 7 : wdayStart) > 7 )
+    // FIXME some more elegant way??
+    WeekDay wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
+    if ( wdYearStart == Wed || wdYearStart == Thu )
     {
         week++;
     }
 
     return week;
-#else // this seems to be a bit simpler and I believe is also correct
-    return (WeekDay)((GetDayOfYear() - (GetWeekDay() - 1 + 7) % 7 + 7) / 7);
-#endif // 0/1
 }
 
-wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(const TimeZone& tz) const
+wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
+                                                    const TimeZone& tz) const
 {
-    size_t nWeek = 0;
-
-    wxDateTime dt(*this);
-    do
+    Tm tm = GetTm(tz);
+    wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year);
+    size_t nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
+    if ( nWeek < 0 )
     {
-        nWeek++;
-
-        dt -= wxTimeSpan::Week();
+        // this may happen for January when Jan, 1 is the last week of the
+        // previous year
+        nWeek += IsLeapYear(tm.year - 1) ? 53 : 52;
     }
-    while ( dt.GetMonth(tz) == GetMonth(tz) );
 
     return nWeek;
 }
@@ -1554,7 +1651,7 @@ wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
 {
     int year = GetYear();
     wxCHECK_MSG( (0 < yday) && (yday <= GetNumberOfDays(year)),
-                 ms_InvDateTime, _T("invalid year day") );
+                 wxInvalidDateTime, _T("invalid year day") );
 
     bool isLeap = IsLeapYear(year);
     for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) )
@@ -1630,12 +1727,13 @@ int wxDateTime::IsDST(wxDateTime::Country country) const
     }
 }
 
-wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz)
+wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
 {
     int secDiff = GetTimeZone() + tz.GetOffset();
 
-    // we need to know whether DST is or not in effect for this date
-    if ( IsDST() == 1 )
+    // we need to know whether DST is or not in effect for this date unless
+    // the test disabled by the caller
+    if ( !noDST && (IsDST() == 1) )
     {
         // FIXME we assume that the DST is always shifted by 1 hour
         secDiff -= 3600;
@@ -1650,9 +1748,6 @@ wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz)
 
 wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
 {
-#ifdef __VMS__
-   int time2;
-#endif
     wxCHECK_MSG( format, _T(""), _T("NULL format in wxDateTime::Format") );
 
     time_t time = GetTicks();
@@ -1672,11 +1767,11 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
         {
             time += tz.GetOffset();
 
-#ifdef __VMS__ /* time is unsigned so VMS gives a warning on the original */
-          time2 = (int) time;
-          if ( time2 >= 0 )
+#ifdef __VMS__ // time is unsigned so avoid the warning
+            int time2 = (int) time;
+            if ( time2 >= 0 )
 #else
-          if ( time >= 0 )
+            if ( time >= 0 )
 #endif
             {
                 tm = gmtime(&time);
@@ -1931,14 +2026,11 @@ restart:
                 break;
 
             case _T('U'):       // week number in the year (Sunday 1st week day)
-                {
-                    int week = (GetDayOfYear(tz) - tm.GetWeekDay() + 7) / 7;
-                    res += wxString::Format(fmt, week);
-                }
+                res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz));
                 break;
 
             case _T('W'):       // week number in the year (Monday 1st week day)
-                res += wxString::Format(fmt, GetWeekOfYear(tz));
+                res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz));
                 break;
 
             case _T('w'):       // weekday as a number (0-6), Sunday = 0
@@ -2196,7 +2288,7 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
     }
 
     // and now the interesting part: the timezone
-    wxDateTime_t offset;
+    int offset;
     if ( *p == _T('-') || *p == _T('+') )
     {
         // the explicit offset given: it has the form of hhmm
@@ -2293,7 +2385,7 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
 
     // the spec was correct
     Set(day, mon, year, hour, min, sec);
-    MakeTimezone(60*offset);
+    MakeTimezone((wxDateTime_t)(60*offset));
 
     return p;
 }
@@ -2397,7 +2489,7 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date,
 
                     // this is the format which corresponds to ctime() output
                     // and strptime("%c") should parse it, so try it first
-                    static const wxChar *fmtCtime = _T("%a %b %e %H:%M:%S %Y");
+                    static const wxChar *fmtCtime = _T("%a %b %d %H:%M:%S %Y");
 
                     const wxChar *result = dt.ParseFormat(input, fmtCtime);
                     if ( !result )
@@ -2623,18 +2715,27 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date,
                 {
                     wxDateTime dt;
 
-                    wxString fmtDate;
+                    wxString fmtDate, fmtDateAlt;
                     if ( IsWestEuropeanCountry(GetCountry()) ||
                          GetCountry() == Russia )
                     {
                         fmtDate = _T("%d/%m/%y");
+                        fmtDateAlt = _T("%m/%d/%y");
                     }
                     else // assume USA
                     {
                         fmtDate = _T("%m/%d/%y");
+                        fmtDateAlt = _T("%d/%m/%y");
                     }
 
                     const wxChar *result = dt.ParseFormat(input, fmtDate);
+
+                    if ( !result )
+                    {
+                        // ok, be nice and try another one
+                        result = dt.ParseFormat(input, fmtDateAlt);
+                    }
+
                     if ( !result )
                     {
                         // bad luck
@@ -2882,7 +2983,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date)
     int year = 0;
 
     // tokenize the string
-    wxStringTokenizer tok(p, _T(",/-\t "));
+    wxStringTokenizer tok(p, _T(",/-\t\n "));
     while ( tok.HasMoreTokens() )
     {
         wxString token = tok.GetNextToken();
@@ -3211,6 +3312,15 @@ const wxChar *wxDateTime::ParseTime(const wxChar *time)
     return result;
 }
 
+// ----------------------------------------------------------------------------
+// Workdays and holidays support
+// ----------------------------------------------------------------------------
+
+bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const
+{
+    return !wxDateTimeHolidayAuthority::IsHoliday(*this);
+}
+
 // ============================================================================
 // wxTimeSpan
 // ============================================================================
@@ -3236,7 +3346,7 @@ wxString wxTimeSpan::Format(const wxChar *format) const
     wxCHECK_MSG( format, _T(""), _T("NULL format in wxTimeSpan::Format") );
 
     wxString str;
-    str.Alloc(strlen(format));
+    str.Alloc(wxStrlen(format));
 
     for ( const wxChar *pch = format; pch; pch++ )
     {
@@ -3296,3 +3406,121 @@ wxString wxTimeSpan::Format(const wxChar *format) const
 
     return str;
 }
+
+// ============================================================================
+// wxDateTimeHolidayAuthority and related classes
+// ============================================================================
+
+#include "wx/arrimpl.cpp"
+
+WX_DEFINE_OBJARRAY(wxDateTimeArray)
+
+static int wxCMPFUNC_CONV
+wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)
+{
+    wxDateTime dt1 = **first,
+               dt2 = **second;
+
+    return dt1 == dt2 ? 0 : dt1 < dt2 ? -1 : +1;
+}
+
+// ----------------------------------------------------------------------------
+// wxDateTimeHolidayAuthority
+// ----------------------------------------------------------------------------
+
+wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities;
+
+/* static */
+bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt)
+{
+    size_t count = ms_authorities.GetCount();
+    for ( size_t n = 0; n < count; n++ )
+    {
+        if ( ms_authorities[n]->DoIsHoliday(dt) )
+        {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+/* static */
+size_t
+wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart,
+                                               const wxDateTime& dtEnd,
+                                               wxDateTimeArray& holidays)
+{
+    wxDateTimeArray hol;
+
+    holidays.Empty();
+
+    size_t count = ms_authorities.GetCount();
+    for ( size_t n = 0; n < count; n++ )
+    {
+        ms_authorities[n]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
+
+        WX_APPEND_ARRAY(holidays, hol);
+    }
+
+    holidays.Sort(wxDateTimeCompareFunc);
+
+    return holidays.GetCount();
+}
+
+/* static */
+void wxDateTimeHolidayAuthority::ClearAllAuthorities()
+{
+    WX_CLEAR_ARRAY(ms_authorities);
+}
+
+/* static */
+void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
+{
+    ms_authorities.Add(auth);
+}
+
+// ----------------------------------------------------------------------------
+// wxDateTimeWorkDays
+// ----------------------------------------------------------------------------
+
+bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const
+{
+    wxDateTime::WeekDay wd = dt.GetWeekDay();
+
+    return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat);
+}
+
+size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart,
+                                                const wxDateTime& dtEnd,
+                                                wxDateTimeArray& holidays) const
+{
+    if ( dtStart > dtEnd )
+    {
+        wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
+
+        return 0u;
+    }
+
+    holidays.Empty();
+
+    // instead of checking all days, start with the first Sat after dtStart and
+    // end with the last Sun before dtEnd
+    wxDateTime dtSatFirst = dtStart.GetNextWeekDay(wxDateTime::Sat),
+               dtSatLast = dtEnd.GetPrevWeekDay(wxDateTime::Sat),
+               dtSunFirst = dtStart.GetNextWeekDay(wxDateTime::Sun),
+               dtSunLast = dtEnd.GetPrevWeekDay(wxDateTime::Sun),
+               dt;
+
+    for ( dt = dtSatFirst; dt <= dtSatLast; dt += wxDateSpan::Week() )
+    {
+        holidays.Add(dt);
+    }
+
+    for ( dt = dtSunFirst; dt <= dtSunLast; dt += wxDateSpan::Week() )
+    {
+        holidays.Add(dt);
+    }
+
+    return holidays.GetCount();
+}