]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/datetime.cpp
fix recently introduced memory leak of m_conv (bug 1466559)
[wxWidgets.git] / src / common / datetime.cpp
index c4425617ed0fcd4d2255a0aab1cf28569e507287..5f7f6931e296a30a0c903ea0d4822c5c6a1ec3cb 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:
@@ -169,18 +169,43 @@ 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
 // ----------------------------------------------------------------------------
 
 // debugging helper: just a convenient replacement of wxCHECK()
-#define wxDATETIME_CHECK(expr, msg)     \
-        if ( !(expr) )                  \
-        {                               \
-            wxFAIL_MSG(msg);            \
-            *this = wxInvalidDateTime;  \
-            return *this;               \
-        }
+#define wxDATETIME_CHECK(expr, msg) \
+    wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg)
 
 // ----------------------------------------------------------------------------
 // private classes
@@ -307,15 +332,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
@@ -392,9 +418,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
 
@@ -430,10 +456,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;
     }
@@ -441,7 +468,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;
     }
@@ -522,6 +549,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)
 {
@@ -988,7 +1022,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") )
@@ -1311,9 +1346,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);
@@ -1479,7 +1515,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;
@@ -1517,14 +1554,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
         {
@@ -1536,10 +1574,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
             {
@@ -1773,6 +1811,7 @@ wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd)
     return dt;
 }
 
+#if WXWIN_COMPATIBILITY_2_6
 // use a separate function to avoid warnings about using deprecated
 // SetToTheWeek in GetWeek below
 static wxDateTime
@@ -1810,6 +1849,7 @@ wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek,
 {
     return ::SetToTheWeek(GetYear(), numWeek, weekday, flags);
 }
+#endif // WXWIN_COMPATIBILITY_2_6
 
 wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
                                           int year)
@@ -2120,9 +2160,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;
     }
@@ -2184,14 +2225,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
         {
@@ -2204,10 +2246,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
             {
@@ -2362,34 +2404,40 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
                         {
                             nLostWeekDays += year++ % 4 ? 1 : 2;
                         }
-
+                       
+                       // Keep year below 2000 so the 2digit year number
+                       // can never match the month or day of the month
+                       if (year>=2000) year-=28;
                         // at any rate, we couldn't go further than 1988 + 9 + 28!
                         wxASSERT_MSG( year < 2030,
                                       _T("logic error in wxDateTime::Format") );
 
-                        wxString strYear, strYear2;
+                       wxString strYear, strYear2;
                         strYear.Printf(_T("%d"), year);
                         strYear2.Printf(_T("%d"), year % 100);
-
-                        // find two strings not occurring in format (this is surely
+       
+                        // find four strings not occurring in format (this is surely
                         // not the optimal way of doing it... improvements welcome!)
-                        wxString fmt = format;
-                        wxString replacement = (wxChar)-1;
-                        while ( fmt.Find(replacement) != wxNOT_FOUND )
-                        {
-                            replacement << (wxChar)-1;
-                        }
-
-                        wxString replacement2 = (wxChar)-2;
-                        while ( fmt.Find(replacement) != wxNOT_FOUND )
-                        {
-                            replacement << (wxChar)-2;
-                        }
-
+                        wxString fmt2 = format;
+                       wxString replacement,replacement2,replacement3,replacement4;
+                       for (int rnr=1; rnr<5 ; rnr++) {
+                               wxString r = (wxChar)-rnr;
+                               while ( fmt2.Find(r) != wxNOT_FOUND )
+                               {
+                                   r << (wxChar)-rnr;
+                               }
+
+                               switch (rnr) {
+                                       case 1: replacement=r; break;
+                                       case 2: replacement2=r; break;
+                                       case 3: replacement3=r; break;
+                                       case 4: replacement4=r; break;
+                               }
+                       }
                         // replace all occurrences of year with it
-                        bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
-                        if ( !wasReplaced )
-                            wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
+                        bool wasReplaced = fmt2.Replace(strYear, replacement) > 0;
+                        // evaluation order ensures we always attempt the replacement.
+                       wasReplaced = (fmt2.Replace(strYear2, replacement2) > 0) | wasReplaced ;
 
                         // use strftime() to format the same date but in supported
                         // year
@@ -2414,11 +2462,17 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
                                                     &tmAdjusted);
 
                         // now replace the occurrence of 1999 with the real year
+                       // we do this in two stages to stop the 2 digit year
+                       // matching any substring of the 4 digit year.
+                       // Any day,month hours and minutes components should be safe due
+                       // to ensuring the range of the years.
                         wxString strYearReal, strYearReal2;
                         strYearReal.Printf(_T("%04d"), yearReal);
                         strYearReal2.Printf(_T("%02d"), yearReal % 100);
-                        str.Replace(strYear, strYearReal);
-                        str.Replace(strYear2, strYearReal2);
+                        str.Replace(strYear, replacement3);
+                        str.Replace(strYear2,replacement4);
+                        str.Replace(replacement3, strYearReal);
+                        str.Replace(replacement4, strYearReal2);
 
                         // and replace back all occurrences of replacement string
                         if ( wasReplaced )
@@ -2843,7 +2897,7 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
 
             p += tz.length();
         }
-        
+
         // make it minutes
         offset *= MIN_PER_HOUR;
     }
@@ -3765,9 +3819,11 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date)
                 }
                 else // may be either day or year
                 {
+                    // use a leap year if we don't have the year yet to allow
+                    // dates like 2/29/1976 which would be rejected otherwise
                     wxDateTime_t max_days = (wxDateTime_t)(
                         haveMon
-                        ? GetNumOfDaysInMonth(haveYear ? year : Inv_Year, mon)
+                        ? GetNumOfDaysInMonth(haveYear ? year : 1976, mon)
                         : 31
                     );
 
@@ -3920,7 +3976,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date)
     {
         wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
 
-        return (wxChar *)NULL;
+        return NULL;
     }
 
     if ( haveWDay && (haveMon || haveYear || haveDay) &&
@@ -3929,7 +3985,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date)
         // without adjectives (which we don't support here) the week day only
         // makes sense completely separately or with the full date
         // specification (what would "Wed 1999" mean?)
-        return (wxChar *)NULL;
+        return NULL;
     }
 
     if ( !haveWDay && haveYear && !(haveDay && haveMon) )
@@ -3959,7 +4015,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date)
             // if we give the year, month and day must be given too
             wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
 
-            return (wxChar *)NULL;
+            return NULL;
         }
     }
 
@@ -3975,6 +4031,11 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date)
 
     if ( haveDay )
     {
+        // normally we check the day above but the check is optimistic in case
+        // we find the day before its month/year so we have to redo it now
+        if ( day > GetNumOfDaysInMonth(year, mon) )
+            return NULL;
+
         Set(day, mon, year);
 
         if ( haveWDay )