]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/datetime.cpp
Somehow, setting a tint color makes gauge work :/.
[wxWidgets.git] / src / common / datetime.cpp
index 2d60ee2d981a06e3e9d580889b2510a0b0a588e6..669d0a3269690dc02ec828c9ced617171a86022b 100644 (file)
@@ -5,7 +5,6 @@
 // Author:      Vadim Zeitlin
 // Modified by:
 // Created:     11.05.99
-// RCS-ID:      $Id$
 // Copyright:   (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
 //              parts of code taken from sndcal library by Scott E. Lee:
 //
@@ -63,7 +62,7 @@
 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
 
 #ifndef WX_PRECOMP
-    #ifdef __WXMSW__
+    #ifdef __WINDOWS__
         #include "wx/msw/wrapwin.h"
     #endif
     #include "wx/string.h"
@@ -75,6 +74,7 @@
 #endif // WX_PRECOMP
 
 #include "wx/thread.h"
+#include "wx/time.h"
 #include "wx/tokenzr.h"
 
 #include <ctype.h>
@@ -108,124 +108,6 @@ wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringCon
 
 #endif // wxUSE_EXTENDED_RTTI
 
-
-// ----------------------------------------------------------------------------
-// conditional compilation
-// ----------------------------------------------------------------------------
-
-#if defined(__MWERKS__) && wxUSE_UNICODE
-    #include <wtime.h>
-#endif
-
-#if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
-    #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;
-        #define WX_TIMEZONE wxmw_timezone
-    #elif defined(__DJGPP__) || defined(__WINE__)
-        #include <sys/timeb.h>
-        #include <values.h>
-        static long wxGetTimeZone()
-        {
-            struct timeb tb;
-            ftime(&tb);
-            return tb.timezone;
-        }
-        #define WX_TIMEZONE wxGetTimeZone()
-    #elif defined(__DARWIN__)
-        #define WX_GMTOFF_IN_TM
-    #elif wxCHECK_VISUALC_VERSION(8)
-        // While _timezone is still present in (some versions of) VC CRT, it's
-        // deprecated and _get_timezone() should be used instead.
-        static long wxGetTimeZone()
-        {
-            // We must initialize the time zone information before using it
-            // (this will be done only once internally).
-            _tzset();
-
-            long t;
-            _get_timezone(&t);
-            return t;
-        }
-        #define WX_TIMEZONE wxGetTimeZone()
-    #else // unknown platform - try timezone
-        #define WX_TIMEZONE timezone
-    #endif
-#endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
-
-// NB: VC8 safe time functions could/should be used for wxMSW as well probably
-#if defined(__WXWINCE__) && defined(__VISUALC8__)
-
-struct tm *wxLocaltime_r(const time_t *t, struct tm* tm)
-{
-    __time64_t t64 = *t;
-    return _localtime64_s(tm, &t64) == 0 ? tm : NULL;
-}
-
-struct tm *wxGmtime_r(const time_t* t, struct tm* tm)
-{
-    __time64_t t64 = *t;
-    return _gmtime64_s(tm, &t64) == 0 ? tm : NULL;
-}
-
-#else // !wxWinCE with VC8
-
-#if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__)
-static wxMutex timeLock;
-#endif
-
-#ifndef HAVE_LOCALTIME_R
-struct tm *wxLocaltime_r(const time_t* ticks, struct tm* temp)
-{
-#if wxUSE_THREADS && !defined(__WINDOWS__)
-  // No need to waste time with a mutex on windows since it's using
-  // thread local storage for localtime anyway.
-  wxMutexLocker locker(timeLock);
-#endif
-
-  // Borland CRT crashes when passed 0 ticks for some reason, see SF bug 1704438
-#ifdef __BORLANDC__
-  if ( !*ticks )
-      return NULL;
-#endif
-
-  const tm * const t = localtime(ticks);
-  if ( !t )
-      return NULL;
-
-  memcpy(temp, t, sizeof(struct tm));
-  return temp;
-}
-#endif // !HAVE_LOCALTIME_R
-
-#ifndef HAVE_GMTIME_R
-struct tm *wxGmtime_r(const time_t* ticks, struct tm* temp)
-{
-#if wxUSE_THREADS && !defined(__WINDOWS__)
-  // No need to waste time with a mutex on windows since it's
-  // using thread local storage for gmtime anyway.
-  wxMutexLocker locker(timeLock);
-#endif
-
-#ifdef __BORLANDC__
-  if ( !*ticks )
-      return NULL;
-#endif
-
-  const tm * const t = gmtime(ticks);
-  if ( !t )
-      return NULL;
-
-  memcpy(temp, gmtime(ticks), sizeof(struct tm));
-  return temp;
-}
-#endif // !HAVE_GMTIME_R
-
-#endif // wxWinCE with VC8/other platforms
-
 // ----------------------------------------------------------------------------
 // macros
 // ----------------------------------------------------------------------------
@@ -353,38 +235,6 @@ wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
     return daysInMonth[wxDateTime::IsLeapYear(year)][month];
 }
 
-// returns the time zone in the C sense, i.e. the difference UTC - local
-// (in seconds)
-// NOTE: not static because used by datetimefmt.cpp
-int GetTimeZone()
-{
-#ifdef WX_GMTOFF_IN_TM
-    // set to true when the timezone is set
-    static bool s_timezoneSet = false;
-    static long gmtoffset = LONG_MAX; // invalid timezone
-
-    // ensure that the timezone variable is set by calling wxLocaltime_r
-    if ( !s_timezoneSet )
-    {
-        // just call wxLocaltime_r() instead of figuring out whether this
-        // system supports tzset(), _tzset() or something else
-        time_t t = 0;
-        struct tm tm;
-
-        wxLocaltime_r(&t, &tm);
-        s_timezoneSet = true;
-
-        // note that GMT offset is the opposite of time zone and so to return
-        // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
-        // cases we have to negate it
-        gmtoffset = -tm.tm_gmtoff;
-    }
-    return (int)gmtoffset;
-#else // !WX_GMTOFF_IN_TM
-    return WX_TIMEZONE;
-#endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
-}
-
 // return the integral part of the JDN for the midnight of the given date (to
 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
 // noon of the previous day)
@@ -438,8 +288,15 @@ wxString CallStrftime(const wxString& format, const tm* tm)
 
     if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
     {
-        // if the format is valid, buffer must be too small?
-        wxFAIL_MSG(wxT("strftime() failed"));
+        // There is one special case in which strftime() can return 0 without
+        // indicating an error: "%p" may give empty string depending on the
+        // locale, so check for it explicitly. Apparently it's really the only
+        // exception.
+        if ( format != wxS("%p") )
+        {
+            // if the format is valid, buffer must be too small?
+            wxFAIL_MSG(wxT("strftime() failed"));
+        }
 
         buf[0] = '\0';
     }
@@ -603,7 +460,7 @@ wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
         case wxDateTime::Local:
             // get the offset from C RTL: it returns the difference GMT-local
             // while we want to have the offset _from_ GMT, hence the '-'
-            m_offset = -GetTimeZone();
+            m_offset = -wxGetTimeZone();
             break;
 
         case wxDateTime::GMT_12:
@@ -1244,7 +1101,7 @@ wxDateTime wxDateTime::GetEndDST(int year, Country country)
 // return the current time with ms precision
 /* static */ wxDateTime wxDateTime::UNow()
 {
-    return wxDateTime(wxGetLocalTimeMillis());
+    return wxDateTime(wxGetUTCTimeMillis());
 }
 
 // the values in the tm structure contain the local time
@@ -1260,7 +1117,7 @@ wxDateTime& wxDateTime::Set(const struct tm& tm)
         if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 )
         {
             return Set((time_t)(
-                       GetTimeZone() +
+                       wxGetTimeZone() +
                        tm2.tm_hour * MIN_PER_HOUR * SEC_PER_MIN +
                        tm2.tm_min * SEC_PER_MIN +
                        tm2.tm_sec));
@@ -1272,10 +1129,37 @@ wxDateTime& wxDateTime::Set(const struct tm& tm)
 
         return *this;
     }
-    else
-    {
-        return Set(timet);
+
+    // mktime() only adjusts tm_wday, tm_yday and tm_isdst fields normally, if
+    // it changed anything else, it must have performed the DST adjustment. But
+    // the trouble with this is that different implementations do it
+    // differently, e.g. GNU libc moves the time forward if the specified time
+    // is invalid in the local time zone, while MSVC CRT moves it backwards
+    // which is especially pernicious as it can change the date if the DST
+    // starts at midnight, as it does in some time zones (see #15419), and this
+    // is completely unexpected for the code working with dates only.
+    //
+    // So standardize on moving the time forwards to have consistent behaviour
+    // under all platforms and to avoid the problem above.
+    if ( tm2.tm_hour != tm.tm_hour )
+    {
+        tm2 = tm;
+        tm2.tm_hour++;
+        if ( tm2.tm_hour == 24 )
+        {
+            // This shouldn't normally happen as the DST never starts at 23:00
+            // but if it does, we have a problem as we need to adjust the day
+            // as well. However we stop here, i.e. we don't adjust the month
+            // (or the year) because mktime() is supposed to take care of this
+            // for us.
+            tm2.tm_hour = 0;
+            tm2.tm_mday++;
+        }
+
+        timet = mktime(&tm2);
     }
+
+    return Set(timet);
 }
 
 wxDateTime& wxDateTime::Set(wxDateTime_t hour,
@@ -1374,7 +1258,7 @@ wxDateTime& wxDateTime::Set(wxDateTime_t day,
         m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;
 
         // JDN corresponds to GMT, we take localtime
-        Add(wxTimeSpan(hour, minute, second + GetTimeZone(), millisec));
+        Add(wxTimeSpan(hour, minute, second + wxGetTimeZone(), millisec));
     }
 
     return *this;
@@ -1513,7 +1397,7 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
         // use C RTL functions
         struct tm tmstruct;
         tm *tm;
-        if ( tz.GetOffset() == -GetTimeZone() )
+        if ( tz.GetOffset() == -wxGetTimeZone() )
         {
             // we are working with local time
             tm = wxLocaltime_r(&time, &tmstruct);
@@ -1744,6 +1628,56 @@ wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
     return *this;
 }
 
+wxDateSpan wxDateTime::DiffAsDateSpan(const wxDateTime& dt) const
+{
+    wxASSERT_MSG( IsValid() && dt.IsValid(), wxT("invalid wxDateTime"));
+
+    // If dt is larger than this, calculations below needs to be inverted.
+    int inv = 1;
+    if ( dt > *this )
+        inv = -1;
+
+    int y = GetYear() - dt.GetYear();
+    int m = GetMonth() - dt.GetMonth();
+    int d = GetDay() - dt.GetDay();
+
+    // If month diff is negative, dt is the year before, so decrease year
+    // and set month diff to its inverse, e.g. January - December should be 1,
+    // not -11.
+    if ( m * inv < 0 || (m == 0 && d * inv < 0))
+    {
+        m += inv * MONTHS_IN_YEAR;
+        y -= inv;
+    }
+
+    // Same logic for days as for months above.
+    if ( d * inv < 0 )
+    {
+        // Use number of days in month from the month which end date we're
+        // crossing. That is month before this for positive diff, and this
+        // month for negative diff.
+        // If we're on january and using previous month, we get december
+        // previous year, but don't care, december has same amount of days
+        // every year.
+        wxDateTime::Month monthfordays = GetMonth();
+        if (inv > 0 && monthfordays == wxDateTime::Jan)
+            monthfordays = wxDateTime::Dec;
+        else if (inv > 0)
+            monthfordays = static_cast<wxDateTime::Month>(monthfordays - 1);
+
+        d += inv * wxDateTime::GetNumberOfDays(monthfordays, GetYear());
+        m -= inv;
+    }
+
+    int w =  d / DAYS_PER_WEEK;
+
+    // Remove weeks from d, since wxDateSpan only keep days as the ones
+    // not in complete weeks
+    d -= w * DAYS_PER_WEEK;
+
+    return wxDateSpan(y, m, w, d);
+}
+
 // ----------------------------------------------------------------------------
 // Weekday and monthday stuff
 // ----------------------------------------------------------------------------
@@ -2013,7 +1947,6 @@ wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const
     {
         // adjust the weekdays to non-US style.
         wdYearStart = ConvertWeekDayToMondayBase(wdYearStart);
-        wdTarget = ConvertWeekDayToMondayBase(wdTarget);
 
         // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
         //
@@ -2029,22 +1962,24 @@ wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const
         //
 
         // if Jan 1 is Thursday or less, it is in the first week of this year
-        if ( wdYearStart < 4 )
-        {
-            // count the number of entire weeks between Jan 1 and this date
-            week = (nDayInYear + wdYearStart + 6 - wdTarget)/7;
+        int dayCountFix = wdYearStart < 4 ? 6 : -1;
 
-            // be careful to check for overflow in the next year
-            if ( week == 53 && tm.mday - wdTarget > 28 )
-                    week = 1;
+        // count the number of week
+        week = (nDayInYear + wdYearStart + dayCountFix) / DAYS_PER_WEEK;
+
+        // check if we happen to be at the last week of previous year:
+        if ( week == 0 )
+        {
+            week = wxDateTime(31, Dec, GetYear() - 1).GetWeekOfYear();
         }
-        else // Jan 1 is in the last week of the previous year
+        else if ( week == 53 )
         {
-            // check if we happen to be at the last week of previous year:
-            if ( tm.mon == Jan && tm.mday < 8 - wdYearStart )
-                week = wxDateTime(31, Dec, GetYear()-1).GetWeekOfYear();
-            else
-                week = (nDayInYear + wdYearStart - 1 - wdTarget)/7;
+            int wdYearEnd = (wdYearStart + 364 + IsLeapYear(GetYear()))
+                                % DAYS_PER_WEEK;
+
+            // Week 53 only if last day of year is Thursday or later.
+            if ( wdYearEnd < 3 )
+                week = 1;
         }
     }
 
@@ -2147,7 +2082,7 @@ int wxDateTime::IsDST(wxDateTime::Country country) const
 
 wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
 {
-    long secDiff = GetTimeZone() + tz.GetOffset();
+    long secDiff = wxGetTimeZone() + tz.GetOffset();
 
     // we need to know whether DST is or not in effect for this date unless
     // the test disabled by the caller
@@ -2162,7 +2097,7 @@ wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
 
 wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST)
 {
-    long secDiff = GetTimeZone() + tz.GetOffset();
+    long secDiff = wxGetTimeZone() + tz.GetOffset();
 
     // we need to know whether DST is or not in effect for this date unless
     // the test disabled by the caller
@@ -2340,7 +2275,7 @@ WXDLLIMPEXP_BASE void wxPrevWDay(wxDateTime::WeekDay& wd)
                                : (wxDateTime::WeekDay)(wd - 1);
 }
 
-#ifdef __WXMSW__
+#ifdef __WINDOWS__
 
 wxDateTime& wxDateTime::SetFromMSWSysTime(const SYSTEMTIME& st)
 {
@@ -2388,6 +2323,6 @@ void wxDateTime::GetAsMSWSysDate(SYSTEMTIME* st) const
     st->wMilliseconds = 0;
 }
 
-#endif // __WXMSW__
+#endif // __WINDOWS__
 
 #endif // wxUSE_DATETIME