]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/datetime.cpp
Stupid bug: using LastError() instead of Error()
[wxWidgets.git] / src / common / datetime.cpp
index 1f48f3022dcec6f73941ce3d9c454092ddffef14..eafbeeb5863cedd1db4bf5f3ddc309b419878eb0 100644 (file)
 
 #include "wx/thread.h"
 #include "wx/tokenzr.h"
+#include "wx/module.h"
 
 #define wxDEFINE_TIME_CONSTANTS // before including datetime.h
 
+#include <ctype.h>
+
 #include "wx/datetime.h"
 
 // ----------------------------------------------------------------------------
 #ifndef WX_TIMEZONE
     #if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
         #define WX_TIMEZONE _timezone
+    #elif defined(__MWERKS__)
+       long wxmw_timezone = 28800;
+       #define WX_TIMEZONE wxmw_timezone ;
     #else // unknown platform - try timezone
         #define WX_TIMEZONE timezone
     #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();
+        wxDateTimeHolidayAuthority::ms_authorities.Clear();
+    }
+
+private:
+    DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule)
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule)
+
 // ----------------------------------------------------------------------------
 // constants
 // ----------------------------------------------------------------------------
@@ -160,6 +192,18 @@ wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
 // private functions
 // ----------------------------------------------------------------------------
 
+// debugger helper: shows what the date really is
+#ifdef __WXDEBUG__
+extern const wxChar *wxDumpDate(const wxDateTime* dt)
+{
+    static wxChar buf[128];
+
+    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)
@@ -437,6 +481,9 @@ 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)
@@ -1147,9 +1194,6 @@ wxDateTime& wxDateTime::ResetTime()
 
 wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
 {
-#ifdef __VMS__
-   int time2;
-#endif
     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
 
     time_t time = GetTicks();
@@ -1167,9 +1211,9 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
         }
         else
         {
-            time += tz.GetOffset();
+            time += (time_t)tz.GetOffset();
 #ifdef __VMS__ // time is unsigned so avoid warning
-            time2 = (int) time;
+            int time2 = (int) time;
             if ( time2 >= 0 )
 #else
             if ( time >= 0 )
@@ -1220,18 +1264,18 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
     wxASSERT_MSG( jdn > -2, _T("JDN out of range") );
 
     // calculate the century
-    int temp = (jdn + JDN_OFFSET) * 4 - 1;
-    int century = temp / DAYS_PER_400_YEARS;
+    long temp = (jdn + JDN_OFFSET) * 4 - 1;
+    long century = temp / DAYS_PER_400_YEARS;
 
     // then the year and day of year (1 <= dayOfYear <= 366)
     temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
-    int year = (century * 100) + (temp / DAYS_PER_4_YEARS);
-    int dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
+    long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
+    long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
 
     // and finally the month and day of the month
     temp = dayOfYear * 5 - 3;
-    int month = temp / DAYS_PER_5_MONTHS;
-    int day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
+    long month = temp / DAYS_PER_5_MONTHS;
+    long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
 
     // month is counted from March - convert to normal
     if ( month < 10 )
@@ -1258,18 +1302,18 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
     tm.year = (int)year;
     tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
     tm.mday = (wxDateTime_t)day;
-    tm.msec = timeOnly % 1000;
+    tm.msec = (wxDateTime_t)(timeOnly % 1000);
     timeOnly -= tm.msec;
     timeOnly /= 1000;               // now we have time in seconds
 
-    tm.sec = timeOnly % 60;
+    tm.sec = (wxDateTime_t)(timeOnly % 60);
     timeOnly -= tm.sec;
     timeOnly /= 60;                 // now we have time in minutes
 
-    tm.min = timeOnly % 60;
+    tm.min = (wxDateTime_t)(timeOnly % 60);
     timeOnly -= tm.min;
 
-    tm.hour = timeOnly / 60;
+    tm.hour = (wxDateTime_t)(timeOnly / 60);
 
     return tm;
 }
@@ -1361,6 +1405,20 @@ 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);
@@ -1375,6 +1433,23 @@ 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)
 {
@@ -1399,11 +1474,11 @@ 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));
     }
 }
 
@@ -1428,7 +1503,7 @@ wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
         diff = weekday - wdayThis;
     }
 
-    return Add(wxTimeSpan::Days(diff));
+    return Add(wxDateSpan::Days(diff));
 }
 
 wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
@@ -1452,7 +1527,7 @@ wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
         diff = wdayThis - weekday;
     }
 
-    return Substract(wxTimeSpan::Days(diff));
+    return Substract(wxDateSpan::Days(diff));
 }
 
 bool wxDateTime::SetToWeekDay(WeekDay weekday,
@@ -1567,7 +1642,7 @@ wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
 {
     Tm tm = GetTm(tz);
     wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year);
-    size_t nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
+    int nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
     if ( nWeek < 0 )
     {
         // this may happen for January when Jan, 1 is the last week of the
@@ -1575,7 +1650,7 @@ wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
         nWeek += IsLeapYear(tm.year - 1) ? 53 : 52;
     }
 
-    return nWeek;
+    return (wxDateTime::wxDateTime_t)nWeek;
 }
 
 wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
@@ -1660,7 +1735,7 @@ int wxDateTime::IsDST(wxDateTime::Country country) const
 
 wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
 {
-    int secDiff = GetTimeZone() + tz.GetOffset();
+    long secDiff = GetTimeZone() + tz.GetOffset();
 
     // we need to know whether DST is or not in effect for this date unless
     // the test disabled by the caller
@@ -1679,9 +1754,6 @@ wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
 
 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();
@@ -1699,10 +1771,10 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
         }
         else
         {
-            time += tz.GetOffset();
+            time += (int)tz.GetOffset();
 
-#ifdef __VMS__ /* time is unsigned so VMS gives a warning on the original */
-            time2 = (int) time;
+#ifdef __VMS__ // time is unsigned so avoid the warning
+            int time2 = (int) time;
             if ( time2 >= 0 )
 #else
             if ( time >= 0 )
@@ -2492,7 +2564,7 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date,
 
                 haveHour = TRUE;
                 hourIsIn12hFormat = TRUE;
-                hour = num % 12;        // 12 should be 0
+                hour = (wxDateTime_t)(num % 12);        // 12 should be 0
                 break;
 
             case _T('j'):       // day of the year
@@ -2748,7 +2820,7 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date,
                 }
 
                 haveYear = TRUE;
-                year = 1900 + num;
+                year = 1900 + (wxDateTime_t)num;
                 break;
 
             case _T('Y'):       // year with century
@@ -2892,16 +2964,36 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date)
     while ( wxIsspace(*p) )
         p++;
 
-    wxString today = _T("today");
-    size_t len = today.length();
-    if ( wxString(p, len).CmpNoCase(today) == 0 )
+    // some special cases
+    static struct
     {
-        // nothing can follow this, so stop here
-        p += len;
+        const wxChar *str;
+        int dayDiffFromToday;
+    } literalDates[] =
+    {
+        { wxTRANSLATE("today"),             0 },
+        { wxTRANSLATE("yesterday"),        -1 },
+        { wxTRANSLATE("tomorrow"),          1 },
+    };
 
-        *this = Today();
+    for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
+    {
+        wxString date = wxGetTranslation(literalDates[n].str);
+        size_t len = date.length();
+        if ( wxString(p, len).CmpNoCase(date) == 0 )
+        {
+            // nothing can follow this, so stop here
+            p += len;
 
-        return p;
+            int dayDiffFromToday = literalDates[n].dayDiffFromToday;
+            *this = Today();
+            if ( dayDiffFromToday )
+            {
+                *this += wxDateSpan::Days(dayDiffFromToday);
+            }
+
+            return p;
+        }
     }
 
     // what do we have?
@@ -2988,7 +3080,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date)
                 haveYear = TRUE;
 
                 // no roll over - 99 means 99, not 1999 for us
-                year = val;
+                year = (wxDateTime_t)val;
             }
             else if ( isMonth )
             {
@@ -3032,7 +3124,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date)
 
                 haveDay = TRUE;
 
-                day = val;
+                day = (wxDateTime_t)val;
             }
         }
         else // not a number
@@ -3246,6 +3338,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
 // ============================================================================
@@ -3271,7 +3372,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++ )
     {
@@ -3331,3 +3432,123 @@ 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 nAuth = 0; nAuth < count; nAuth++ )
+    {
+        ms_authorities[nAuth]->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();
+}
+
+