]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/datetime.cpp
define wxEventLoopBase::ms_activeLoop in appcmn.cpp instead of doing it in all platfo...
[wxWidgets.git] / src / common / datetime.cpp
index b09fe1ef75c694a91d067104276475d089d0a09e..c797fc0144ee085109bdde0917d4e821306d416f 100644 (file)
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////////
-// Name:        wx/datetime.h
+// Name:        src/common/datetime.cpp
 // Purpose:     implementation of time/date related classes
 // Author:      Vadim Zeitlin
 // Modified by:
 // headers
 // ----------------------------------------------------------------------------
 
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
-    #pragma implementation "datetime.h"
-#endif
-
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
@@ -173,6 +169,36 @@ wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringCon
     #endif
 #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
 
+#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
+  memcpy(temp, localtime(ticks), sizeof(struct tm));
+  return temp;
+}
+#endif
+
+#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
+  memcpy(temp, gmtime(ticks), sizeof(struct tm));
+  return temp;
+}
+#endif
+
 // ----------------------------------------------------------------------------
 // macros
 // ----------------------------------------------------------------------------
@@ -235,11 +261,14 @@ static const long MILLISECONDS_PER_DAY = 86400000l;
 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
 static const long EPOCH_JDN = 2440587l;
 
+// used only in asserts
+#ifdef __WXDEBUG__
 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
 // reference date for us) is Nov 24, 4714BC
 static const int JDN_0_YEAR = -4713;
 static const int JDN_0_MONTH = wxDateTime::Nov;
 static const int JDN_0_DAY = 24;
+#endif // __WXDEBUG__
 
 // the constants used for JDN calculations
 static const long JDN_OFFSET         = 32046l;
@@ -308,15 +337,16 @@ static int GetTimeZone()
     static bool s_timezoneSet = false;
     static long gmtoffset = LONG_MAX; // invalid timezone
 
-    // ensure that the timezone variable is set by calling localtime
+    // ensure that the timezone variable is set by calling wxLocaltime_r
     if ( !s_timezoneSet )
     {
-        // just call localtime() instead of figuring out whether this system
-        // supports tzset(), _tzset() or something else
+        // just call wxLocaltime_r() instead of figuring out whether this
+        // system supports tzset(), _tzset() or something else
         time_t t = 0;
         struct tm *tm;
+        struct tm tmstruct;
 
-        tm = localtime(&t);
+        tm = wxLocaltime_r(&t, &tmstruct);
         s_timezoneSet = true;
 
         // note that GMT offset is the opposite of time zone and so to return
@@ -393,9 +423,9 @@ static wxString CallStrftime(const wxChar *format, const tm* tm)
 
 #ifdef HAVE_STRPTIME
 
-// glibc2 doesn't define this in the headers unless _XOPEN_SOURCE is defined
-// which, unfortunately, wreaks havoc elsewhere
-#if defined(__GLIBC__) && (__GLIBC__ == 2)
+#if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
+    // configure detected that we had strptime() but not its declaration,
+    // provide it ourselves
     extern "C" char *strptime(const char *, const char *, struct tm *);
 #endif
 
@@ -431,10 +461,11 @@ static void ReplaceDefaultYearMonthWithCurrent(int *year,
                                                wxDateTime::Month *month)
 {
     struct tm *tmNow = NULL;
+    struct tm tmstruct;
 
     if ( *year == wxDateTime::Inv_Year )
     {
-        tmNow = wxDateTime::GetTmNow();
+        tmNow = wxDateTime::GetTmNow(&tmstruct);
 
         *year = 1900 + tmNow->tm_year;
     }
@@ -442,7 +473,7 @@ static void ReplaceDefaultYearMonthWithCurrent(int *year,
     if ( *month == wxDateTime::Inv_Month )
     {
         if ( !tmNow )
-            tmNow = wxDateTime::GetTmNow();
+            tmNow = wxDateTime::GetTmNow(&tmstruct);
 
         *month = (wxDateTime::Month)tmNow->tm_mon;
     }
@@ -523,6 +554,13 @@ static wxDateTime::WeekDay GetWeekDayFromName(const wxString& name, int flags)
     return wd;
 }
 
+/* static */
+struct tm *wxDateTime::GetTmNow(struct tm *tmstruct)
+{
+    time_t t = GetTimeNow();
+    return wxLocaltime_r(&t, tmstruct);
+}
+
 // scans all digits (but no more than len) and returns the resulting number
 static bool GetNumericToken(size_t len, const wxChar*& p, unsigned long *number)
 {
@@ -692,7 +730,7 @@ wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
 
         case wxDateTime::A_CST:
             // Central Standard Time in use in Australia = UTC + 9.5
-            m_offset = 60l*(9*60 + 30);
+            m_offset = 60l*(9*MIN_PER_HOUR + MIN_PER_HOUR/2);
             break;
 
         default:
@@ -989,7 +1027,8 @@ wxDateTime::Country wxDateTime::GetCountry()
     {
         // try to guess from the time zone name
         time_t t = time(NULL);
-        struct tm *tm = localtime(&t);
+        struct tm tmstruct;
+        struct tm *tm = wxLocaltime_r(&t, &tmstruct);
 
         wxString tz = CallStrftime(_T("%Z"), tm);
         if ( tz == _T("WET") || tz == _T("WEST") )
@@ -1312,9 +1351,10 @@ wxDateTime& wxDateTime::Set(wxDateTime_t hour,
                       _T("Invalid time in wxDateTime::Set()") );
 
     // get the current date from system
-    struct tm *tm = GetTmNow();
+    struct tm tmstruct;
+    struct tm *tm = GetTmNow(&tmstruct);
 
-    wxDATETIME_CHECK( tm, _T("localtime() failed") );
+    wxDATETIME_CHECK( tm, _T("wxLocaltime_r() failed") );
 
     // make a copy so it isn't clobbered by the call to mktime() below
     struct tm tm1(*tm);
@@ -1405,20 +1445,9 @@ wxDateTime& wxDateTime::Set(double jdn)
     // EPOCH_JDN + 0.5
     jdn -= EPOCH_JDN + 0.5;
 
-    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();
-    if ( IsDST() == 1 )
-    {
-        // FIXME: again, we suppose that DST is always one hour
-        tzDiff -= 3600;
-    }
+    m_time.Assign(jdn*MILLISECONDS_PER_DAY);
 
-    m_time += tzDiff*1000; // tzDiff is in seconds
+    // JDNs always are in UTC, so we don't need any adjustments for time zone
 
     return *this;
 }
@@ -1491,7 +1520,8 @@ unsigned long wxDateTime::GetAsDOS() const
 {
     unsigned long ddt;
     time_t ticks = GetTicks();
-    struct tm *tm = localtime(&ticks);
+    struct tm tmstruct;
+    struct tm *tm = wxLocaltime_r(&ticks, &tmstruct);
 
     long year = tm->tm_year;
     year -= 80;
@@ -1529,14 +1559,15 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
     if ( time != (time_t)-1 )
     {
         // use C RTL functions
+        struct tm tmstruct;
         tm *tm;
         if ( tz.GetOffset() == -GetTimeZone() )
         {
             // we are working with local time
-            tm = localtime(&time);
+            tm = wxLocaltime_r(&time, &tmstruct);
 
             // should never happen
-            wxCHECK_MSG( tm, Tm(), _T("localtime() failed") );
+            wxCHECK_MSG( tm, Tm(), _T("wxLocaltime_r() failed") );
         }
         else
         {
@@ -1548,10 +1579,10 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
             if ( time >= 0 )
 #endif
             {
-                tm = gmtime(&time);
+                tm = wxGmtime_r(&time, &tmstruct);
 
                 // should never happen
-                wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") );
+                wxCHECK_MSG( tm, Tm(), _T("wxGmtime_r() failed") );
             }
             else
             {
@@ -1637,14 +1668,14 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
     timeOnly -= tm.msec;
     timeOnly /= 1000;               // now we have time in seconds
 
-    tm.sec = (wxDateTime_t)(timeOnly % 60);
+    tm.sec = (wxDateTime_t)(timeOnly % SEC_PER_MIN);
     timeOnly -= tm.sec;
-    timeOnly /= 60;                 // now we have time in minutes
+    timeOnly /= SEC_PER_MIN;        // now we have time in minutes
 
-    tm.min = (wxDateTime_t)(timeOnly % 60);
+    tm.min = (wxDateTime_t)(timeOnly % MIN_PER_HOUR);
     timeOnly -= tm.min;
 
-    tm.hour = (wxDateTime_t)(timeOnly / 60);
+    tm.hour = (wxDateTime_t)(timeOnly / MIN_PER_HOUR);
 
     return tm;
 }
@@ -2110,16 +2141,7 @@ wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
 
 double wxDateTime::GetJulianDayNumber() const
 {
-    // JDN are always expressed for the UTC dates
-    Tm tm(ToTimezone(UTC).GetTm(UTC));
-
-    double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year);
-
-    // add the part GetTruncatedJDN() neglected
-    result += 0.5;
-
-    // and now add the time: 86400 sec = 1 JDN
-    return result + ((double)(60*(60*tm.hour + tm.min) + tm.sec)) / 86400;
+    return m_time.ToDouble() / MILLISECONDS_PER_DAY + EPOCH_JDN + 0.5;
 }
 
 double wxDateTime::GetRataDie() const
@@ -2141,9 +2163,10 @@ int wxDateTime::IsDST(wxDateTime::Country country) const
     time_t timet = GetTicks();
     if ( timet != (time_t)-1 )
     {
-        tm *tm = localtime(&timet);
+        struct tm tmstruct;
+        tm *tm = wxLocaltime_r(&timet, &tmstruct);
 
-        wxCHECK_MSG( tm, -1, _T("localtime() failed") );
+        wxCHECK_MSG( tm, -1, _T("wxLocaltime_r() failed") );
 
         return tm->tm_isdst;
     }
@@ -2173,6 +2196,21 @@ wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
         secDiff -= 3600;
     }
 
+    return Add(wxTimeSpan::Seconds(secDiff));
+}
+
+wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST)
+{
+    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
+    if ( !noDST && (IsDST() == 1) )
+    {
+        // FIXME we assume that the DST is always shifted by 1 hour
+        secDiff -= 3600;
+    }
+
     return Subtract(wxTimeSpan::Seconds(secDiff));
 }
 
@@ -2190,14 +2228,15 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
     if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) )
     {
         // use strftime()
-        tm *tm;
+        struct tm tmstruct;
+        struct tm *tm;
         if ( tz.GetOffset() == -GetTimeZone() )
         {
             // we are working with local time
-            tm = localtime(&time);
+            tm = wxLocaltime_r(&time, &tmstruct);
 
             // should never happen
-            wxCHECK_MSG( tm, wxEmptyString, _T("localtime() failed") );
+            wxCHECK_MSG( tm, wxEmptyString, _T("wxLocaltime_r() failed") );
         }
         else
         {
@@ -2210,10 +2249,10 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
             if ( time >= 0 )
 #endif
             {
-                tm = gmtime(&time);
+                tm = wxGmtime_r(&time, &tmstruct);
 
                 // should never happen
-                wxCHECK_MSG( tm, wxEmptyString, _T("gmtime() failed") );
+                wxCHECK_MSG( tm, wxEmptyString, _T("wxGmtime_r() failed") );
             }
             else
             {
@@ -2379,23 +2418,23 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
 
                         // find two strings not occurring in format (this is surely
                         // not the optimal way of doing it... improvements welcome!)
-                        wxString fmt = format;
+                        wxString fmt2 = format;
                         wxString replacement = (wxChar)-1;
-                        while ( fmt.Find(replacement) != wxNOT_FOUND )
+                        while ( fmt2.Find(replacement) != wxNOT_FOUND )
                         {
                             replacement << (wxChar)-1;
                         }
 
                         wxString replacement2 = (wxChar)-2;
-                        while ( fmt.Find(replacement) != wxNOT_FOUND )
+                        while ( fmt2.Find(replacement) != wxNOT_FOUND )
                         {
                             replacement << (wxChar)-2;
                         }
 
                         // replace all occurrences of year with it
-                        bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
+                        bool wasReplaced = fmt2.Replace(strYear, replacement) > 0;
                         if ( !wasReplaced )
-                            wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
+                            wasReplaced = fmt2.Replace(strYear2, replacement2) > 0;
 
                         // use strftime() to format the same date but in supported
                         // year
@@ -2771,7 +2810,7 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
         }
 
         // hours
-        offset = 60*(10*(*p - _T('0')) + (*(p + 1) - _T('0')));
+        offset = MIN_PER_HOUR*(10*(*p - _T('0')) + (*(p + 1) - _T('0')));
 
         p += 2;
 
@@ -2851,12 +2890,12 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
         }
 
         // make it minutes
-        offset *= 60;
+        offset *= MIN_PER_HOUR;
     }
 
-    // the spec was correct
+    // the spec was correct, construct the date from the values we found
     Set(day, mon, year, hour, min, sec);
-    MakeTimezone((wxDateTime_t)(60*offset));
+    MakeFromTimezone(TimeZone((wxDateTime_t)(offset*SEC_PER_MIN)));
 
     return p;
 }
@@ -3698,12 +3737,12 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date)
 
     for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
     {
-        wxString date = wxGetTranslation(literalDates[n].str);
-        size_t len = date.length();
+        const wxString dateStr = wxGetTranslation(literalDates[n].str);
+        size_t len = dateStr.length();
         if ( wxStrlen(p) >= len )
         {
             wxString str(p, len);
-            if ( str.CmpNoCase(date) == 0 )
+            if ( str.CmpNoCase(dateStr) == 0 )
             {
                 // nothing can follow this, so stop here
                 p += len;
@@ -4286,7 +4325,7 @@ wxString wxTimeSpan::Format(const wxChar *format) const
 
 #include "wx/arrimpl.cpp"
 
-WX_DEFINE_OBJARRAY(wxDateTimeArray);
+WX_DEFINE_OBJARRAY(wxDateTimeArray)
 
 static int wxCMPFUNC_CONV
 wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)
@@ -4328,8 +4367,8 @@ wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart,
 
     holidays.Clear();
 
-    size_t count = ms_authorities.size();
-    for ( size_t nAuth = 0; nAuth < count; nAuth++ )
+    const size_t countAuth = ms_authorities.size();
+    for ( size_t nAuth = 0; nAuth < countAuth; nAuth++ )
     {
         ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);