]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/datetime.cpp
Applied patch for ArtProvider.
[wxWidgets.git] / src / common / datetime.cpp
index 1d85ebbbd81c3946803d88f400a6c7d950f6c97e..b211b4ee2d594689f5f9a08fdf43a75deece0184 100644 (file)
 // Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 
+// TODO: for $DEITY sake, someone please fix the #ifdef __WXWINCE__ everywhere,
+//       the proper way to do it is to implement (subset of) wxStrftime() for
+//       CE instead of this horror!!
+
 /*
  * Implementation notes:
  *
 
 #include <ctype.h>
 
+#ifdef __WINDOWS__
+    #include "wx/msw/wrapwin.h"
+    #include <winnls.h>
+    #ifndef __WXWINCE__
+        #include <locale.h>
+    #endif
+#endif
+
 #include "wx/datetime.h"
 #include "wx/stopwatch.h"           // for wxGetLocalTimeMillis()
 
@@ -120,7 +132,9 @@ wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringCon
 #endif
 
 #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
-    #if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
+    #if defined(__WXPALMOS__)
+        #define WX_GMTOFF_IN_TM
+    #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
         #define WX_TIMEZONE _timezone
     #elif defined(__MWERKS__)
         long wxmw_timezone = 28800;
@@ -233,6 +247,9 @@ static const wxDateTime::wxDateTime_t gs_cumulatedDays[2][MONTHS_IN_YEAR] =
 // global data
 // ----------------------------------------------------------------------------
 
+const wxChar * wxDefaultDateTimeFormat = wxT("%c");
+const wxChar * wxDefaultTimeSpanFormat = wxT("%H:%M:%S");
+
 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
 // indicate an invalid wxDateTime object
 const wxDateTime wxDefaultDateTime;
@@ -342,11 +359,12 @@ static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
             - JDN_OFFSET;
 }
 
+#ifndef __WXWINCE__
 // this function is a wrapper around strftime(3) adding error checking
 static wxString CallStrftime(const wxChar *format, const tm* tm)
 {
     wxChar buf[4096];
-    if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
+       if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
     {
         // buffer is too small?
         wxFAIL_MSG(_T("strftime() failed"));
@@ -354,6 +372,7 @@ static wxString CallStrftime(const wxChar *format, const tm* tm)
 
     return wxString(buf);
 }
+#endif
 
 #ifdef HAVE_STRPTIME
 
@@ -500,7 +519,7 @@ static bool GetNumericToken(size_t len, const wxChar*& p, unsigned long *number)
             break;
     }
 
-    return !s.IsEmpty() && s.ToULong(number);
+    return !s.empty() && s.ToULong(number);
 }
 
 // scans all alphabetic characters and returns the resulting string
@@ -542,8 +561,8 @@ wxDateTime::Tm::Tm(const struct tm& tm, const TimeZone& tz)
     mday = (wxDateTime::wxDateTime_t)tm.tm_mday;
     mon = (wxDateTime::Month)tm.tm_mon;
     year = 1900 + tm.tm_year;
-    wday = tm.tm_wday;
-    yday = tm.tm_yday;
+    wday = (wxDateTime::wxDateTime_t)tm.tm_wday;
+    yday = (wxDateTime::wxDateTime_t)tm.tm_yday;
 }
 
 bool wxDateTime::Tm::IsValid() const
@@ -559,7 +578,7 @@ void wxDateTime::Tm::ComputeWeekDay()
     // compute the week day from day/month/year: we use the dumbest algorithm
     // possible: just compute our JDN and then use the (simple to derive)
     // formula: weekday = (JDN + 1.5) % 7
-    wday = (wxDateTime::WeekDay)(GetTruncatedJDN(mday, mon, year) + 2) % 7;
+    wday = (wxDateTime::wxDateTime_t)((wxDateTime::WeekDay)(GetTruncatedJDN(mday, mon, year) + 2) % 7);
 }
 
 void wxDateTime::Tm::AddMonths(int monDiff)
@@ -800,8 +819,8 @@ wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
 wxString wxDateTime::GetMonthName(wxDateTime::Month month,
                                   wxDateTime::NameFlags flags)
 {
-    wxCHECK_MSG( month != Inv_Month, _T(""), _T("invalid month") );
-
+    wxCHECK_MSG( month != Inv_Month, wxEmptyString, _T("invalid month") );
+#ifndef __WXWINCE__
     // notice that we must set all the fields to avoid confusing libc (GNU one
     // gets confused to a crash if we don't do this)
     tm tm;
@@ -809,14 +828,57 @@ wxString wxDateTime::GetMonthName(wxDateTime::Month month,
     tm.tm_mon = month;
 
     return CallStrftime(flags == Name_Abbr ? _T("%b") : _T("%B"), &tm);
+#else
+       wxString ret;
+       switch(month)
+       {
+               case Jan: 
+                       ret = (flags == Name_Abbr ? wxT("Jan"): wxT("January"));
+                       break;
+               case Feb: 
+                       ret = (flags == Name_Abbr ? wxT("Feb"): wxT("Febuary"));
+                       break;
+               case Mar: 
+                       ret = (flags == Name_Abbr ? wxT("Mar"): wxT("March"));
+                       break;
+               case Apr: 
+                       ret = (flags == Name_Abbr ? wxT("Apr"): wxT("April"));
+                       break;
+               case May: 
+                       ret = (flags == Name_Abbr ? wxT("May"): wxT("May"));
+                       break;
+               case Jun: 
+                       ret = (flags == Name_Abbr ? wxT("Jun"): wxT("June"));
+                       break;
+               case Jul: 
+                       ret = (flags == Name_Abbr ? wxT("Jul"): wxT("July"));
+                       break;
+               case Aug: 
+                       ret = (flags == Name_Abbr ? wxT("Aug"): wxT("August"));
+                       break;
+               case Sep: 
+                       ret = (flags == Name_Abbr ? wxT("Sep"): wxT("September"));
+                       break;
+               case Oct: 
+                       ret = (flags == Name_Abbr ? wxT("Oct"): wxT("October"));
+                       break;
+               case Nov: 
+                       ret = (flags == Name_Abbr ? wxT("Nov"): wxT("November"));
+                       break;
+               case Dec: 
+                       ret = (flags == Name_Abbr ? wxT("Dec"): wxT("December"));
+                       break;
+       }
+       return ret;
+#endif
 }
 
 /* static */
 wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday,
                                     wxDateTime::NameFlags flags)
 {
-    wxCHECK_MSG( wday != Inv_WeekDay, _T(""), _T("invalid weekday") );
-
+    wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, _T("invalid weekday") );
+#ifndef __WXWINCE__
     // take some arbitrary Sunday (but notice that the day should be such that
     // after adding wday to it below we still have a valid date, e.g. don't
     // take 28 here!)
@@ -834,6 +896,35 @@ wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday,
 
     // ... and call strftime()
     return CallStrftime(flags == Name_Abbr ? _T("%a") : _T("%A"), &tm);
+#else
+       wxString ret;
+       switch(wday)
+       {
+               case Sun: 
+                       ret = (flags == Name_Abbr ? wxT("Sun") : wxT("Sunday"));
+                       break;
+               case Mon:
+                       ret = (flags == Name_Abbr ? wxT("Mon") : wxT("Monday"));
+                       break;
+               case Tue:
+                       ret = (flags == Name_Abbr ? wxT("Tue") : wxT("Tuesday"));
+                       break;
+               case Wed:
+                       ret = (flags == Name_Abbr ? wxT("Wed") : wxT("Wednesday"));
+                       break;
+               case Thu:
+                       ret = (flags == Name_Abbr ? wxT("Thu") : wxT("Thursday"));
+                       break;
+               case Fri:
+                       ret = (flags == Name_Abbr ? wxT("Fri") : wxT("Friday"));
+                       break;
+               case Sat:
+                       ret = (flags == Name_Abbr ? wxT("Sat") : wxT("Saturday"));
+                       break;
+       }
+       return ret;
+
+#endif
 }
 
 /* static */
@@ -852,7 +943,7 @@ void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm)
     // assert, even though it is a perfectly legal use.
     if ( am )
     {
-        if (wxStrftime(buffer, sizeof buffer, _T("%p"), &tm) > 0)
+        if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
             *am = wxString(buffer);
         else
             *am = wxString();
@@ -860,7 +951,7 @@ void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm)
     if ( pm )
     {
         tm.tm_hour = 13;
-        if (wxStrftime(buffer, sizeof buffer, _T("%p"), &tm) > 0)
+        if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
             *pm = wxString(buffer);
         else
             *pm = wxString();
@@ -876,7 +967,7 @@ void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm)
 wxDateTime::Country wxDateTime::GetCountry()
 {
     // TODO use LOCALE_ICOUNTRY setting under Win32
-
+#ifndef __WXWINCE__
     if ( ms_country == Country_Unknown )
     {
         // try to guess from the time zone name
@@ -910,6 +1001,9 @@ wxDateTime::Country wxDateTime::GetCountry()
             ms_country = USA;
         }
     }
+#else
+     ms_country = USA;
+#endif
 
     return ms_country;
 }
@@ -1168,16 +1262,11 @@ wxDateTime& wxDateTime::Set(const struct tm& tm)
         // less than timezone - try to make it work for this case
         if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 )
         {
-            // add timezone to make sure that date is in range
-            tm2.tm_sec -= GetTimeZone();
-
-            timet = mktime(&tm2);
-            if ( timet != (time_t)-1 )
-            {
-                timet += GetTimeZone();
-
-                return Set(timet);
-            }
+            return Set((time_t)(
+                       GetTimeZone() +
+                       tm2.tm_hour * MIN_PER_HOUR * SEC_PER_MIN +
+                       tm2.tm_min * SEC_PER_MIN +
+                       tm2.tm_sec));
         }
 
         wxFAIL_MSG( _T("mktime() failed") );
@@ -1210,12 +1299,21 @@ wxDateTime& wxDateTime::Set(wxDateTime_t hour,
 
     wxDATETIME_CHECK( tm, _T("localtime() failed") );
 
+    // make a copy so it isn't clobbered by the call to mktime() below
+    struct tm tm1(*tm);
+
     // adjust the time
-    tm->tm_hour = hour;
-    tm->tm_min = minute;
-    tm->tm_sec = second;
+    tm1.tm_hour = hour;
+    tm1.tm_min = minute;
+    tm1.tm_sec = second;
 
-    (void)Set(*tm);
+    // and the DST in case it changes on this date
+    struct tm tm2(tm1);
+    mktime(&tm2);
+    if ( tm2.tm_isdst != tm1.tm_isdst )
+        tm1.tm_isdst = tm2.tm_isdst;
+
+    (void)Set(tm1);
 
     // and finally adjust milliseconds
     return SetMillisecond(millisec);
@@ -1262,7 +1360,10 @@ wxDateTime& wxDateTime::Set(wxDateTime_t day,
         (void)Set(tm);
 
         // and finally adjust milliseconds
-        return SetMillisecond(millisec);
+        if (IsValid())
+            SetMillisecond(millisec);
+
+        return *this;
     }
     else
     {
@@ -1289,6 +1390,8 @@ wxDateTime& wxDateTime::Set(double jdn)
 
     jdn *= MILLISECONDS_PER_DAY;
 
+    m_time.Assign(jdn);
+
     // JDNs always suppose an UTC date, so bring it back to local time zone
     // (also see GetJulianDayNumber() implementation)
     long tzDiff = GetTimeZone();
@@ -1298,9 +1401,7 @@ wxDateTime& wxDateTime::Set(double jdn)
         tzDiff -= 3600;
     }
 
-    jdn += tzDiff*1000; // tzDiff is in seconds
-
-    m_time.Assign(jdn);
+    m_time += tzDiff*1000; // tzDiff is in seconds
 
     return *this;
 }
@@ -2061,7 +2162,7 @@ wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
 
 wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
 {
-    wxCHECK_MSG( format, _T(""), _T("NULL format in wxDateTime::Format") );
+    wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxDateTime::Format") );
 
     // we have to use our own implementation if the date is out of range of
     // strftime() or if we use non standard specificators
@@ -2099,11 +2200,13 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
                 tm = (struct tm *)NULL;
             }
         }
-
+#ifndef __WXWINCE__
+       //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
         if ( tm )
         {
             return CallStrftime(format, tm);
         }
+#endif
         //else: use generic code below
     }
 
@@ -2182,6 +2285,7 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
 
                 case _T('c'):       // locale default date and time  representation
                 case _T('x'):       // locale default date representation
+#ifndef __WXWINCE__
                     //
                     // the problem: there is no way to know what do these format
                     // specifications correspond to for the current locale.
@@ -2311,6 +2415,11 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
 
                         res += str;
                     }
+#else
+                                       //Use "%m/%d/%y %H:%M:%S" format instead
+                                       res += wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
+                                                       tm.mon+1,tm.mday, tm.year, tm.hour, tm.min, tm.sec);
+#endif
                     break;
 
                 case _T('d'):       // day of a month (01-31)
@@ -2347,7 +2456,11 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
                     break;
 
                 case _T('p'):       // AM or PM string
+#ifndef __WXWINCE__
                     res += CallStrftime(_T("%p"), &tmTimeOnly);
+#else
+                                       res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am");
+#endif
                     break;
 
                 case _T('S'):       // second as a decimal number (00-61)
@@ -2370,7 +2483,11 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
 
                 case _T('X'):       // locale default time representation
                     // just use strftime() to format the time for us
+#ifndef __WXWINCE__
                     res += CallStrftime(_T("%X"), &tmTimeOnly);
+#else
+                                       res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec);
+#endif
                     break;
 
                 case _T('y'):       // year without century (00-99)
@@ -2382,7 +2499,9 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
                     break;
 
                 case _T('Z'):       // timezone name
+#ifndef __WXWINCE__
                     res += CallStrftime(_T("%Z"), &tmTimeOnly);
+#endif
                     break;
 
                 default:
@@ -2394,7 +2513,7 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
                         fmt += *p;
                     }
 
-                    if ( !fmt.IsEmpty() )
+                    if ( !fmt.empty() )
                     {
                         // we've only got the flags and width so far in fmt
                         fmt.Prepend(_T('%'));
@@ -2722,6 +2841,70 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
     return p;
 }
 
+#ifdef __WINDOWS__
+
+// Get's current locale's date formatting string and stores it in fmt if
+// the locale is set; otherwise or in case of failure, leaves fmt unchanged
+static void GetLocaleDateFormat(wxString *fmt)
+{
+    // there is no setlocale() under Windows CE, so just always query the
+    // system there
+#ifndef __WXWINCE__
+    if ( strcmp(setlocale(LC_ALL, NULL), "C") != 0 )
+#endif
+    {
+        // The locale was programatically set to non-C. We assume that this was
+        // done using wxLocale, in which case thread's current locale is also
+        // set to correct LCID value and we can use GetLocaleInfo to determine
+        // the correct formatting string:
+#ifdef __WXWINCE__
+        LCID lcid = LOCALE_USER_DEFAULT;
+#else
+        LCID lcid = GetThreadLocale();
+#endif
+        wxChar delim[5]; // fields deliminer, 4 chars max
+        if ( GetLocaleInfo(lcid, LOCALE_SDATE, delim, 5) )
+        {
+            wxChar centurybuf[2]; // use %y or %Y, 1 char max
+            wxChar century = 'y';
+            if ( GetLocaleInfo(lcid, LOCALE_ICENTURY, centurybuf, 2) )
+            {
+                if ( centurybuf[0] == _T('1') )
+                    century = 'Y';
+                // else 'y' as above
+            }
+
+            wxChar order[2]; // order code, 1 char max
+            if ( GetLocaleInfo(lcid, LOCALE_IDATE, order, 2) )
+            {
+                if ( order[0] == _T('0') ) // M-D-Y
+                {
+                    *fmt = wxString::Format(_T("%%m%s%%d%s%%%c"),
+                                            delim, delim, century);
+                }
+                else if ( order[0] == _T('1') ) // D-M-Y
+                {
+                    *fmt = wxString::Format(_T("%%d%s%%m%s%%%c"),
+                                            delim, delim, century);
+                }
+                else if ( order[0] == _T('2') ) // Y-M-D
+                {
+                    *fmt = wxString::Format(_T("%%%c%s%%m%s%%d"),
+                                            century, delim, delim);
+                }
+                else
+                {
+                    wxFAIL_MSG(_T("unexpected GetLocaleInfo return value"));
+                }
+            }
+        }
+        // if we failed, leave fmtDate value unchanged and
+        // try our luck with the default set above
+    }
+}
+
+#endif // __WINDOWS__
+
 const wxChar *wxDateTime::ParseFormat(const wxChar *date,
                                       const wxChar *format,
                                       const wxDateTime& dateDef)
@@ -2966,7 +3149,7 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date,
                     wxString am, pm, token = GetAlphaToken(input);
 
                     GetAmPmStrings(&am, &pm);
-                    if (am.IsEmpty() && pm.IsEmpty())
+                    if (am.empty() && pm.empty())
                         return (wxChar *)NULL;  // no am/pm strings defined
                     if ( token.CmpNoCase(pm) == 0 )
                     {
@@ -3081,11 +3264,11 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date,
                 }
 #endif // HAVE_STRPTIME
 
-                // TODO query the LOCALE_IDATE setting under Win32
                 {
                     wxDateTime dt;
 
                     wxString fmtDate, fmtDateAlt;
+
                     if ( IsWestEuropeanCountry(GetCountry()) ||
                          GetCountry() == Russia )
                     {
@@ -3098,6 +3281,12 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date,
                         fmtDateAlt = _T("%d/%m/%y");
                     }
 
+#ifdef __WINDOWS__
+                    // The above doesn't work for all locales, try to query
+                    // Windows for the right way of formatting the date:
+                    GetLocaleDateFormat(&fmtDate);
+#endif
+
                     const wxChar *result = dt.ParseFormat(input, fmtDate);
 
                     if ( !result )
@@ -3463,14 +3652,14 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date)
                 }
                 else // may be either day or year
                 {
-                    wxDateTime_t maxDays = (wxDateTime_t)(
+                    wxDateTime_t max_days = (wxDateTime_t)(
                         haveMon
                         ? GetNumOfDaysInMonth(haveYear ? year : Inv_Year, mon)
                         : 31
                     );
 
                     // can it be day?
-                    if ( (val == 0) || (val > (unsigned long)maxDays) )
+                    if ( (val == 0) || (val > (unsigned long)max_days) )
                     {
                         // no
                         isYear = true;
@@ -3792,10 +3981,25 @@ bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const
     return !wxDateTimeHolidayAuthority::IsHoliday(*this);
 }
 
+// ============================================================================
+// wxDateSpan
+// ============================================================================
+
+wxDateSpan WXDLLIMPEXP_BASE operator*(int n, const wxDateSpan& ds)
+{
+    wxDateSpan ds1(ds);
+    return ds1.Multiply(n);
+}
+
 // ============================================================================
 // wxTimeSpan
 // ============================================================================
 
+wxTimeSpan WXDLLIMPEXP_BASE operator*(int n, const wxTimeSpan& ts)
+{
+    return wxTimeSpan(ts).Multiply(n);
+}
+
 // this enum is only used in wxTimeSpan::Format() below but we can't declare
 // it locally to the method as it provokes an internal compiler error in egcs
 // 2.91.60 when building with -O2
@@ -3827,7 +4031,7 @@ enum TimeSpanPart
 //  %l          milliseconds (000 - 999)
 wxString wxTimeSpan::Format(const wxChar *format) const
 {
-    wxCHECK_MSG( format, _T(""), _T("NULL format in wxTimeSpan::Format") );
+    wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxTimeSpan::Format") );
 
     wxString str;
     str.Alloc(wxStrlen(format));
@@ -4030,6 +4234,11 @@ void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
     ms_authorities.push_back(auth);
 }
 
+wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
+{
+    // required here for Darwin
+}
+
 // ----------------------------------------------------------------------------
 // wxDateTimeWorkDays
 // ----------------------------------------------------------------------------
@@ -4075,4 +4284,46 @@ size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart,
     return holidays.GetCount();
 }
 
+// ============================================================================
+// other helper functions
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// iteration helpers: can be used to write a for loop over enum variable like
+// this:
+//  for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
+// ----------------------------------------------------------------------------
+
+WXDLLIMPEXP_BASE void wxNextMonth(wxDateTime::Month& m)
+{
+    wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
+
+    // no wrapping or the for loop above would never end!
+    m = (wxDateTime::Month)(m + 1);
+}
+
+WXDLLIMPEXP_BASE void wxPrevMonth(wxDateTime::Month& m)
+{
+    wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
+
+    m = m == wxDateTime::Jan ? wxDateTime::Inv_Month
+                             : (wxDateTime::Month)(m - 1);
+}
+
+WXDLLIMPEXP_BASE void wxNextWDay(wxDateTime::WeekDay& wd)
+{
+    wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
+
+    // no wrapping or the for loop above would never end!
+    wd = (wxDateTime::WeekDay)(wd + 1);
+}
+
+WXDLLIMPEXP_BASE void wxPrevWDay(wxDateTime::WeekDay& wd)
+{
+    wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
+
+    wd = wd == wxDateTime::Sun ? wxDateTime::Inv_WeekDay
+                               : (wxDateTime::WeekDay)(wd - 1);
+}
+
 #endif // wxUSE_DATETIME