]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/datetimefmt.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / datetimefmt.cpp
index c624b6c6f0b5a39c2741bc88d145656e9960942b..fda6c0ad52925ba98a1b0ef2264c3a0cbe8b1f53 100644 (file)
@@ -34,7 +34,7 @@
 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
 
 #ifndef WX_PRECOMP
-    #ifdef __WXMSW__
+    #ifdef __WINDOWS__
         #include "wx/msw/wrapwin.h"
     #endif
     #include "wx/string.h"
@@ -57,6 +57,7 @@
 #endif
 
 #include "wx/datetime.h"
+#include "wx/time.h"
 
 // ============================================================================
 // implementation of wxDateTime
@@ -68,8 +69,6 @@
 
 extern void InitTm(struct tm& tm);
 
-extern int GetTimeZone();
-
 extern wxString CallStrftime(const wxString& format, const tm* tm);
 
 // ----------------------------------------------------------------------------
@@ -292,8 +291,14 @@ ParseFormatAt(wxString::const_iterator& p,
     const wxString str(p, end);
     wxString::const_iterator endParse;
     wxDateTime dt;
-    if ( dt.ParseFormat(str, fmt, &endParse) ||
-            (!fmtAlt.empty() && dt.ParseFormat(str, fmtAlt, &endParse)) )
+
+    // Use a default date outside of the DST period to avoid problems with
+    // parsing the time differently depending on the today's date (which is used
+    // as the fall back date if none is explicitly specified).
+    static const wxDateTime dtDef(1, wxDateTime::Jan, 2012);
+
+    if ( dt.ParseFormat(str, fmt, dtDef, &endParse) ||
+            (!fmtAlt.empty() && dt.ParseFormat(str, fmtAlt, dtDef, &endParse)) )
     {
         p += endParse - str.begin();
     }
@@ -320,16 +325,22 @@ wxString wxDateTime::Format(const wxString& formatp, const TimeZone& tz) const
     format.Replace("%X",wxLocale::GetInfo(wxLOCALE_TIME_FMT));
 #endif
     // we have to use our own implementation if the date is out of range of
-    // strftime() or if we use non standard specificators
+    // strftime() or if we use non standard specifiers (notice that "%z" is
+    // special because it is de facto standard under Unix but is not supported
+    // under Windows)
 #ifdef wxHAS_STRFTIME
     time_t time = GetTicks();
 
-    if ( (time != (time_t)-1) && !wxStrstr(format, wxT("%l")) )
+    if ( (time != (time_t)-1) && !wxStrstr(format, wxT("%l"))
+#ifdef __WINDOWS__
+            && !wxStrstr(format, wxT("%z"))
+#endif
+       )
     {
         // use strftime()
         struct tm tmstruct;
         struct tm *tm;
-        if ( tz.GetOffset() == -GetTimeZone() )
+        if ( tz.GetOffset() == -wxGetTimeZone() )
         {
             // we are working with local time
             tm = wxLocaltime_r(&time, &tmstruct);
@@ -374,12 +385,11 @@ wxString wxDateTime::Format(const wxString& formatp, const TimeZone& tz) const
 
     // used for calls to strftime() when we only deal with time
     struct tm tmTimeOnly;
+    memset(&tmTimeOnly, 0, sizeof(tmTimeOnly));
     tmTimeOnly.tm_hour = tm.hour;
     tmTimeOnly.tm_min = tm.min;
     tmTimeOnly.tm_sec = tm.sec;
-    tmTimeOnly.tm_wday = 0;
-    tmTimeOnly.tm_yday = 0;
-    tmTimeOnly.tm_mday = 1;         // any date will do
+    tmTimeOnly.tm_mday = 1;         // any date will do, use 1976-01-01
     tmTimeOnly.tm_mon = 0;
     tmTimeOnly.tm_year = 76;
     tmTimeOnly.tm_isdst = 0;        // no DST, we adjust for tz ourselves
@@ -399,6 +409,7 @@ wxString wxDateTime::Format(const wxString& formatp, const TimeZone& tz) const
         switch ( (*++p).GetValue() )
         {
             case wxT('Y'):               // year has 4 digits
+            case wxT('z'):               // time zone as well
                 fmt = wxT("%04d");
                 break;
 
@@ -503,7 +514,7 @@ wxString wxDateTime::Format(const wxString& formatp, const TimeZone& tz) const
                         // (indirectly) set the year correctly
                         while ( (nLostWeekDays % 7) != 0 )
                         {
-                            nLostWeekDays += year++ % 4 ? 1 : 2;
+                            nLostWeekDays += (year++ % 4) ? 1 : 2;
                         }
 
                         // finally move the year below 2000 so that the 2-digit
@@ -641,6 +652,25 @@ wxString wxDateTime::Format(const wxString& formatp, const TimeZone& tz) const
                     res += wxString::Format(fmt, tm.year);
                     break;
 
+                case wxT('z'):       // time zone as [-+]HHMM
+                    {
+                        int ofs = tz.GetOffset();
+                        if ( ofs < 0 )
+                        {
+                            res += '-';
+                            ofs = -ofs;
+                        }
+                        else
+                        {
+                            res += '+';
+                        }
+
+                        // Converts seconds to HHMM representation.
+                        res += wxString::Format(fmt,
+                                                100*(ofs/3600) + (ofs/60)%60);
+                    }
+                    break;
+
                 case wxT('Z'):       // timezone name
 #ifdef wxHAS_STRFTIME
                     res += CallStrftime(wxT("%Z"), &tmTimeOnly);
@@ -649,9 +679,10 @@ wxString wxDateTime::Format(const wxString& formatp, const TimeZone& tz) const
 
                 default:
                     // is it the format width?
-                    fmt.Empty();
-                    while ( *p == wxT('-') || *p == wxT('+') ||
-                            *p == wxT(' ') || wxIsdigit(*p) )
+                    for ( fmt.clear();
+                          *p == wxT('-') || *p == wxT('+') ||
+                            *p == wxT(' ') || wxIsdigit(*p);
+                          ++p )
                     {
                         fmt += *p;
                     }
@@ -668,7 +699,7 @@ wxString wxDateTime::Format(const wxString& formatp, const TimeZone& tz) const
                     }
 
                     // no, it wasn't the width
-                    wxFAIL_MSG(wxT("unknown format specificator"));
+                    wxFAIL_MSG(wxT("unknown format specifier"));
 
                     // fall through and just copy it nevertheless
 
@@ -904,6 +935,26 @@ wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end)
     return true;
 }
 
+const char* wxDateTime::ParseRfc822Date(const char* date)
+{
+    wxString::const_iterator end;
+    wxString dateStr(date);
+    if ( !ParseRfc822Date(dateStr, &end) )
+        return NULL;
+
+    return date + dateStr.IterOffsetInMBStr(end);
+}
+
+const wchar_t* wxDateTime::ParseRfc822Date(const wchar_t* date)
+{
+    wxString::const_iterator end;
+    wxString dateStr(date);
+    if ( !ParseRfc822Date(dateStr, &end) )
+        return NULL;
+
+    return date + (end - dateStr.begin());
+}
+
 bool
 wxDateTime::ParseFormat(const wxString& date,
                         const wxString& format,
@@ -930,6 +981,8 @@ wxDateTime::ParseFormat(const wxString& date,
     bool hourIsIn12hFormat = false, // or in 24h one?
          isPM = false;              // AM by default
 
+    bool haveTimeZone = false;
+
     // and the value of the items we have (init them to get rid of warnings)
     wxDateTime_t msec = 0,
                  sec = 0,
@@ -940,6 +993,7 @@ wxDateTime::ParseFormat(const wxString& date,
                  mday = 0;
     wxDateTime::Month mon = Inv_Month;
     int year = 0;
+    long timeZone = 0;  // time zone in seconds as expected in Tm structure
 
     wxString::const_iterator input = date.begin();
     const wxString::const_iterator end = date.end();
@@ -1378,6 +1432,41 @@ wxDateTime::ParseFormat(const wxString& date,
                 year = (wxDateTime_t)num;
                 break;
 
+            case wxT('z'):
+                {
+                    // check that we have something here at all
+                    if ( input == end )
+                        return false;
+
+                    // and then check that it's either plus or minus sign
+                    bool minusFound;
+                    if ( *input == wxT('-') )
+                        minusFound = true;
+                    else if ( *input == wxT('+') )
+                        minusFound = false;
+                    else
+                        return false;   // no match
+
+                    // here should follow 4 digits HHMM
+                    ++input;
+                    unsigned long tzHourMin;
+                    if ( !GetNumericToken(4, input, end, &tzHourMin) )
+                        return false;   // no match
+
+                    const unsigned hours = tzHourMin / 100;
+                    const unsigned minutes = tzHourMin % 100;
+
+                    if ( hours > 12 || minutes > 59 )
+                        return false;   // bad format
+
+                    timeZone = 3600*hours + 60*minutes;
+                    if ( minusFound )
+                        timeZone = -timeZone;
+
+                    haveTimeZone = true;
+                }
+                break;
+
             case wxT('Z'):       // timezone name
                 // FIXME: currently we just ignore everything that looks like a
                 //        time zone here
@@ -1385,7 +1474,7 @@ wxDateTime::ParseFormat(const wxString& date,
                 break;
 
             case wxT('%'):       // a percent sign
-                if ( *input++ != wxT('%') )
+                if ( input == end || *input++ != wxT('%') )
                 {
                     // no match
                     return false;
@@ -1483,6 +1572,14 @@ wxDateTime::ParseFormat(const wxString& date,
 
     Set(tm);
 
+    // If a time zone was specified and it is not the local time zone, we need
+    // to shift the time accordingly.
+    //
+    // Note that avoiding the call to MakeFromTimeZone is necessary to avoid
+    // DST problems.
+    if ( haveTimeZone && timeZone != -wxGetTimeZone() )
+        MakeFromTimezone(timeZone);
+
     // finally check that the week day is consistent -- if we had it
     if ( haveWDay && GetWeekDay() != wday )
         return false;
@@ -1492,15 +1589,40 @@ wxDateTime::ParseFormat(const wxString& date,
     return true;
 }
 
+const char*
+wxDateTime::ParseFormat(const char* date,
+                        const wxString& format,
+                        const wxDateTime& dateDef)
+{
+    wxString::const_iterator end;
+    wxString dateStr(date);
+    if ( !ParseFormat(dateStr, format, dateDef, &end) )
+        return NULL;
+
+    return date + dateStr.IterOffsetInMBStr(end);
+}
+
+const wchar_t*
+wxDateTime::ParseFormat(const wchar_t* date,
+                        const wxString& format,
+                        const wxDateTime& dateDef)
+{
+    wxString::const_iterator end;
+    wxString dateStr(date);
+    if ( !ParseFormat(dateStr, format, dateDef, &end) )
+        return NULL;
+
+    return date + (end - dateStr.begin());
+}
+
 bool
 wxDateTime::ParseDateTime(const wxString& date, wxString::const_iterator *end)
 {
     wxCHECK_MSG( end, false, "end iterator pointer must be specified" );
 
-    // Set to current day and hour, so strings like '14:00' becomes today at
-    // 14, not some other random date
-    wxDateTime dtDate = wxDateTime::Today();
-    wxDateTime dtTime = wxDateTime::Today();
+    wxDateTime
+        dtDate,
+        dtTime;
 
     wxString::const_iterator
         endTime,
@@ -1546,6 +1668,26 @@ wxDateTime::ParseDateTime(const wxString& date, wxString::const_iterator *end)
     return true;
 }
 
+const char* wxDateTime::ParseDateTime(const char* date)
+{
+    wxString::const_iterator end;
+    wxString dateStr(date);
+    if ( !ParseDateTime(dateStr, &end) )
+        return NULL;
+
+    return date + dateStr.IterOffsetInMBStr(end);
+}
+
+const wchar_t* wxDateTime::ParseDateTime(const wchar_t* date)
+{
+    wxString::const_iterator end;
+    wxString dateStr(date);
+    if ( !ParseDateTime(dateStr, &end) )
+        return NULL;
+
+    return date + (end - dateStr.begin());
+}
+
 bool
 wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
 {
@@ -1559,7 +1701,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
     const wxString::const_iterator pEnd = date.end();
 
     wxString::const_iterator p = pBegin;
-    while ( wxIsspace(*p) )
+    while ( p != pEnd && wxIsspace(*p) )
         p++;
 
     // some special cases
@@ -1583,12 +1725,12 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
         if ( len > lenRest )
             continue;
 
-        const wxString::const_iterator pEnd = p + len;
-        if ( wxString(p, pEnd).CmpNoCase(dateStr) == 0 )
+        const wxString::const_iterator pEndStr = p + len;
+        if ( wxString(p, pEndStr).CmpNoCase(dateStr) == 0 )
         {
             // nothing can follow this, so stop here
 
-            p = pEnd;
+            p = pEndStr;
 
             int dayDiffFromToday = literalDates[n].dayDiffFromToday;
             *this = Today();
@@ -1597,7 +1739,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
                 *this += wxDateSpan::Days(dayDiffFromToday);
             }
 
-            *end = pEnd;
+            *end = pEndStr;
 
             return true;
         }
@@ -1626,9 +1768,10 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
     while ( p != pEnd )
     {
         // skip white space and date delimiters
-        while ( wxStrchr(".,/-\t\r\n ", *p) )
+        if ( wxStrchr(".,/-\t\r\n ", *p) )
         {
             ++p;
+            continue;
         }
 
         // modify copy of the iterator as we're not sure if the next token is
@@ -1760,7 +1903,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
                 else // not a valid weekday name
                 {
                     // try the ordinals
-                    static const char *ordinals[] =
+                    static const char *const ordinals[] =
                     {
                         wxTRANSLATE("first"),
                         wxTRANSLATE("second"),
@@ -1902,6 +2045,26 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
     return true;
 }
 
+const char* wxDateTime::ParseDate(const char* date)
+{
+    wxString::const_iterator end;
+    wxString dateStr(date);
+    if ( !ParseDate(dateStr, &end) )
+        return NULL;
+
+    return date + dateStr.IterOffsetInMBStr(end);
+}
+
+const wchar_t* wxDateTime::ParseDate(const wchar_t* date)
+{
+    wxString::const_iterator end;
+    wxString dateStr(date);
+    if ( !ParseDate(dateStr, &end) )
+        return NULL;
+
+    return date + (end - dateStr.begin());
+}
+
 bool
 wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end)
 {
@@ -1922,14 +2085,13 @@ wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end)
     for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ )
     {
         const wxString timeString = wxGetTranslation(stdTimes[n].name);
-        const wxString::const_iterator p = time.begin() + timeString.length();
-        if ( timeString.CmpNoCase(wxString(time.begin(), p)) == 0 )
+        if ( timeString.CmpNoCase(wxString(time, timeString.length())) == 0 )
         {
             // casts required by DigitalMars
             Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0));
 
             if ( end )
-                *end = p;
+                *end = time.begin() + timeString.length();
 
             return true;
         }
@@ -1937,12 +2099,12 @@ wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end)
 
     // try all time formats we may think about in the order from longest to
     // shortest
-    static const char *timeFormats[] =
+    static const char *const timeFormats[] =
     {
         "%I:%M:%S %p",  // 12hour with AM/PM
         "%H:%M:%S",     // could be the same or 24 hour one so try it too
         "%I:%M %p",     // 12hour with AM/PM but without seconds
-        "%H:%M:%S",     // and a possibly 24 hour version without seconds
+        "%H:%M",        // and a possibly 24 hour version without seconds
         "%X",           // possibly something from above or maybe something
                         // completely different -- try it last
 
@@ -1958,6 +2120,26 @@ wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end)
     return false;
 }
 
+const char* wxDateTime::ParseTime(const char* date)
+{
+    wxString::const_iterator end;
+    wxString dateStr(date);
+    if ( !ParseTime(dateStr, &end) )
+        return NULL;
+
+    return date + dateStr.IterOffsetInMBStr(end);
+}
+
+const wchar_t* wxDateTime::ParseTime(const wchar_t* date)
+{
+    wxString::const_iterator end;
+    wxString dateStr(date);
+    if ( !ParseTime(dateStr, &end) )
+        return NULL;
+
+    return date + (end - dateStr.begin());
+}
+
 // ----------------------------------------------------------------------------
 // Workdays and holidays support
 // ----------------------------------------------------------------------------