]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/datetimefmt.cpp
making sure NULs stay in the result, even if the string is not nul terminated
[wxWidgets.git] / src / common / datetimefmt.cpp
index 845d8ead326d37bc0c7380b0854c32044507276d..ece5bff59f98882092c3bcb846fdc375b149d68f 100644 (file)
 // implementation of wxDateTime
 // ============================================================================
 
+// ----------------------------------------------------------------------------
+// helpers shared between datetime.cpp and datetimefmt.cpp
+// ----------------------------------------------------------------------------
+
+extern void InitTm(struct tm& tm);
+
+extern int GetTimeZone();
+
+extern wxString CallStrftime(const wxString& format, const tm* tm);
+
 // ----------------------------------------------------------------------------
 // constants (see also datetime.cpp)
 // ----------------------------------------------------------------------------
@@ -79,6 +89,9 @@ static const int MIN_PER_HOUR = 60;
 // parsing helpers
 // ----------------------------------------------------------------------------
 
+namespace
+{
+
 #ifdef HAVE_STRPTIME
 
 #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
@@ -87,35 +100,48 @@ static const int MIN_PER_HOUR = 60;
     extern "C" char *strptime(const char *, const char *, struct tm *);
 #endif
 
-// Unicode-friendly strptime() wrapper
-static const wxStringCharType *
-CallStrptime(const wxStringCharType *input, const char *fmt, tm *tm)
+// strptime() wrapper: call strptime() for the string starting at the given
+// iterator and fill output tm struct with the results and modify input to
+// point to the end of the string consumed by strptime() if successful,
+// otherwise return false and don't modify anything
+bool
+CallStrptime(const wxString& str,
+             wxString::const_iterator& p,
+             const char *fmt,
+             tm *tm)
 {
-    // the problem here is that strptime() returns pointer into the string we
-    // passed to it while we're really interested in the pointer into the
-    // original, Unicode, string so we try to transform the pointer back
-#if wxUSE_UNICODE_WCHAR
-    wxCharBuffer inputMB(wxConvertWX2MB(input));
-#else // ASCII
-    const char * const inputMB = input;
-#endif // Unicode/Ascii
-
-    const char *result = strptime(inputMB, fmt, tm);
-    if ( !result )
-        return NULL;
-
-#if wxUSE_UNICODE_WCHAR
-    // FIXME: this is wrong in presence of surrogates &c
-    return input + (result - inputMB.data());
-#else // ASCII
-    return result;
-#endif // Unicode/Ascii
+    // convert from iterator to char pointer: this is simple as wxCStrData
+    // already supports this
+    const char * const start = str.c_str() + (p - str.begin());
+
+    const char * const end = strptime(start, fmt, tm);
+    if ( !end )
+        return false;
+
+    // convert back from char pointer to iterator: unfortunately we have no way
+    // to do it efficiently currently so create a temporary string just to
+    // compute the number of characters between start and end
+    p += wxString(start, end - start).length();
+
+    return true;
 }
 
 #endif // HAVE_STRPTIME
 
+enum
+{
+    DateLang_English = 1,
+    DateLang_Local   = 2
+};
+
 // return the month if the string is a month name or Inv_Month otherwise
-static wxDateTime::Month GetMonthFromName(const wxString& name, int flags)
+//
+// flags can contain wxDateTime::Name_Abbr/Name_Full or both of them and lang
+// can be either DateLang_Local (default) to interpret string as a localized
+// month name or DateLang_English to parse it as a standard English name or
+// their combination to interpret it in any way
+wxDateTime::Month
+GetMonthFromName(const wxString& name, int flags, int lang)
 {
     wxDateTime::Month mon;
     for ( mon = wxDateTime::Jan; mon < wxDateTime::Inv_Month; wxNextMonth(mon) )
@@ -124,19 +150,35 @@ static wxDateTime::Month GetMonthFromName(const wxString& name, int flags)
         // and not versions
         if ( flags & wxDateTime::Name_Full )
         {
-            if ( name.CmpNoCase(wxDateTime::
-                        GetMonthName(mon, wxDateTime::Name_Full)) == 0 )
+            if ( lang & DateLang_English )
             {
-                break;
+                if ( name.CmpNoCase(wxDateTime::GetEnglishMonthName(mon,
+                        wxDateTime::Name_Full)) == 0 )
+                    break;
+            }
+
+            if ( lang & DateLang_Local )
+            {
+                if ( name.CmpNoCase(wxDateTime::GetMonthName(mon,
+                        wxDateTime::Name_Full)) == 0 )
+                    break;
             }
         }
 
         if ( flags & wxDateTime::Name_Abbr )
         {
-            if ( name.CmpNoCase(wxDateTime::
-                        GetMonthName(mon, wxDateTime::Name_Abbr)) == 0 )
+            if ( lang & DateLang_English )
             {
-                break;
+                if ( name.CmpNoCase(wxDateTime::GetEnglishMonthName(mon,
+                        wxDateTime::Name_Abbr)) == 0 )
+                    break;
+            }
+
+            if ( lang & DateLang_Local )
+            {
+                if ( name.CmpNoCase(wxDateTime::GetMonthName(mon,
+                        wxDateTime::Name_Abbr)) == 0 )
+                    break;
             }
         }
     }
@@ -145,28 +187,46 @@ static wxDateTime::Month GetMonthFromName(const wxString& name, int flags)
 }
 
 // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
-static wxDateTime::WeekDay GetWeekDayFromName(const wxString& name, int flags)
+//
+// flags and lang parameters have the same meaning as for GetMonthFromName()
+// above
+wxDateTime::WeekDay
+GetWeekDayFromName(const wxString& name, int flags, int lang)
 {
     wxDateTime::WeekDay wd;
     for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
     {
-        // case-insensitive comparison either one of or with both abbreviated
-        // and not versions
         if ( flags & wxDateTime::Name_Full )
         {
-            if ( name.CmpNoCase(wxDateTime::
-                        GetWeekDayName(wd, wxDateTime::Name_Full)) == 0 )
+            if ( lang & DateLang_English )
             {
-                break;
+                if ( name.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd,
+                        wxDateTime::Name_Full)) == 0 )
+                    break;
+            }
+
+            if ( lang & DateLang_Local )
+            {
+                if ( name.CmpNoCase(wxDateTime::GetWeekDayName(wd,
+                        wxDateTime::Name_Full)) == 0 )
+                    break;
             }
         }
 
         if ( flags & wxDateTime::Name_Abbr )
         {
-            if ( name.CmpNoCase(wxDateTime::
-                        GetWeekDayName(wd, wxDateTime::Name_Abbr)) == 0 )
+            if ( lang & DateLang_English )
             {
-                break;
+                if ( name.CmpNoCase(wxDateTime::GetEnglishWeekDayName(wd,
+                        wxDateTime::Name_Abbr)) == 0 )
+                    break;
+            }
+
+            if ( lang & DateLang_Local )
+            {
+                if ( name.CmpNoCase(wxDateTime::GetWeekDayName(wd,
+                        wxDateTime::Name_Abbr)) == 0 )
+                    break;
             }
         }
     }
@@ -174,21 +234,15 @@ 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 wxStringCharType*& p,
-                            unsigned long *number)
+bool GetNumericToken(size_t len,
+                     wxString::const_iterator& p,
+                     const wxString::const_iterator& end,
+                     unsigned long *number)
 {
     size_t n = 1;
     wxString s;
-    while ( wxIsdigit(*p) )
+    while ( p != end && wxIsdigit(*p) )
     {
         s += *p++;
 
@@ -200,10 +254,12 @@ static bool GetNumericToken(size_t len,
 }
 
 // scans all alphabetic characters and returns the resulting string
-static wxString GetAlphaToken(const wxStringCharType*& p)
+wxString
+GetAlphaToken(wxString::const_iterator& p,
+              const wxString::const_iterator& end)
 {
     wxString s;
-    while ( wxIsalpha(*p) )
+    while ( p != end && wxIsalpha(*p) )
     {
         s += *p++;
     }
@@ -211,6 +267,39 @@ static wxString GetAlphaToken(const wxStringCharType*& p)
     return s;
 }
 
+// parses string starting at given iterator using the specified format and,
+// optionally, a fall back format (and optionally another one... but it stops
+// there, really)
+//
+// if unsuccessful, returns invalid wxDateTime without changing p; otherwise
+// advance p to the end of the match and returns wxDateTime containing the
+// results of the parsing
+wxDateTime
+ParseFormatAt(wxString::const_iterator& p,
+              const wxString::const_iterator& end,
+              const wxString& fmt,
+              // FIXME-VC6: using wxString() instead of wxEmptyString in the
+              //            line below results in error C2062: type 'class
+              //            wxString (__cdecl *)(void)' unexpected
+              const wxString& fmtAlt = wxEmptyString,
+              const wxString& fmtAlt2 = wxString())
+{
+    const wxString str(p, end);
+    wxString::const_iterator endParse;
+    wxDateTime dt;
+    if ( dt.ParseFormat(str, fmt, &endParse) ||
+            (!fmtAlt.empty() && dt.ParseFormat(str, fmtAlt, &endParse)) ||
+                (!fmtAlt2.empty() && dt.ParseFormat(str, fmtAlt2, &endParse)) )
+    {
+        p += endParse - str.begin();
+    }
+    //else: all formats failed
+
+    return dt;
+}
+
+} // anonymous namespace
+
 // ----------------------------------------------------------------------------
 // wxDateTime to/from text representations
 // ----------------------------------------------------------------------------
@@ -596,226 +685,156 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const
 //
 // this function is "strict" by design - it must reject anything except true
 // RFC822 time specs.
-//
-// TODO a great candidate for using reg exps
-const char *
+bool
 wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end)
 {
-    // TODO: rewrite using iterators instead of wxChar pointers
-    const wxStringCharType *p = date.wx_str();
-    const wxStringCharType *comma = wxStrchr(p, wxS(','));
-    if ( comma )
-    {
-        // the part before comma is the weekday
+    wxString::const_iterator p = date.begin();
 
-        // skip it for now - we don't use but might check that it really
-        // corresponds to the specfied date
-        p = comma + 1;
+    // 1. week day
+    static const int WDAY_LEN = 3;
+    const wxString::const_iterator endWday = p + WDAY_LEN;
+    const wxString wday(p, endWday);
+    if ( GetWeekDayFromName(wday, Name_Abbr, DateLang_English) == Inv_WeekDay )
+        return false;
+    //else: ignore week day for now, we could also check that it really
+    //      corresponds to the specified date
 
-        if ( *p != _T(' ') )
-        {
-            wxLogDebug(_T("no space after weekday in RFC822 time spec"));
-
-            return NULL;
-        }
+    p = endWday;
 
-        p++; // skip space
-    }
+    // 2. separating comma
+    if ( *p++ != ',' || *p++ != ' ' )
+        return false;
 
-    // the following 1 or 2 digits are the day number
+    // 3. day number
     if ( !wxIsdigit(*p) )
-    {
-        wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
-
-        return NULL;
-    }
+        return false;
 
-    wxDateTime_t day = (wxDateTime_t)(*p++ - _T('0'));
+    wxDateTime_t day = (wxDateTime_t)(*p++ - '0');
     if ( wxIsdigit(*p) )
     {
         day *= 10;
-        day = (wxDateTime_t)(day + (*p++ - _T('0')));
-    }
-
-    if ( *p++ != _T(' ') )
-    {
-        return NULL;
+        day = (wxDateTime_t)(day + (*p++ - '0'));
     }
 
-    // the following 3 letters specify the month
-    wxString monName(p, 3);
-    Month mon;
-    if ( monName == _T("Jan") )
-        mon = Jan;
-    else if ( monName == _T("Feb") )
-        mon = Feb;
-    else if ( monName == _T("Mar") )
-        mon = Mar;
-    else if ( monName == _T("Apr") )
-        mon = Apr;
-    else if ( monName == _T("May") )
-        mon = May;
-    else if ( monName == _T("Jun") )
-        mon = Jun;
-    else if ( monName == _T("Jul") )
-        mon = Jul;
-    else if ( monName == _T("Aug") )
-        mon = Aug;
-    else if ( monName == _T("Sep") )
-        mon = Sep;
-    else if ( monName == _T("Oct") )
-        mon = Oct;
-    else if ( monName == _T("Nov") )
-        mon = Nov;
-    else if ( monName == _T("Dec") )
-        mon = Dec;
-    else
-    {
-        wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName.c_str());
+    if ( *p++ != ' ' )
+        return false;
 
-        return NULL;
-    }
+    // 4. month name
+    static const int MONTH_LEN = 3;
+    const wxString::const_iterator endMonth = p + MONTH_LEN;
+    const wxString monName(p, endMonth);
+    Month mon = GetMonthFromName(monName, Name_Abbr, DateLang_English);
+    if ( mon == Inv_Month )
+        return false;
 
-    p += 3;
+    p = endMonth;
 
-    if ( *p++ != _T(' ') )
-    {
-        return NULL;
-    }
+    if ( *p++ != ' ' )
+        return false;
 
-    // next is the year
+    // 5. year
     if ( !wxIsdigit(*p) )
-    {
-        // no year?
-        return NULL;
-    }
-
-    int year = *p++ - _T('0');
+        return false;
 
-    if ( !wxIsdigit(*p) )
-    {
-        // should have at least 2 digits in the year
-        return NULL;
-    }
+    int year = *p++ - '0';
+    if ( !wxIsdigit(*p) ) // should have at least 2 digits in the year
+        return false;
 
     year *= 10;
-    year += *p++ - _T('0');
+    year += *p++ - '0';
 
     // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
     if ( wxIsdigit(*p) )
     {
         year *= 10;
-        year += *p++ - _T('0');
+        year += *p++ - '0';
 
         if ( !wxIsdigit(*p) )
         {
             // no 3 digit years please
-            return NULL;
+            return false;
         }
 
         year *= 10;
-        year += *p++ - _T('0');
+        year += *p++ - '0';
     }
 
-    if ( *p++ != _T(' ') )
-    {
-        return NULL;
-    }
+    if ( *p++ != ' ' )
+        return false;
 
-    // time is in the format hh:mm:ss and seconds are optional
+    // 6. time in hh:mm:ss format with seconds being optional
     if ( !wxIsdigit(*p) )
-    {
-        return NULL;
-    }
+        return false;
 
-    wxDateTime_t hour = (wxDateTime_t)(*p++ - _T('0'));
+    wxDateTime_t hour = (wxDateTime_t)(*p++ - '0');
 
     if ( !wxIsdigit(*p) )
-    {
-        return NULL;
-    }
+        return false;
 
     hour *= 10;
-    hour = (wxDateTime_t)(hour + (*p++ - _T('0')));
+    hour = (wxDateTime_t)(hour + (*p++ - '0'));
 
-    if ( *p++ != _T(':') )
-    {
-        return NULL;
-    }
+    if ( *p++ != ':' )
+        return false;
 
     if ( !wxIsdigit(*p) )
-    {
-        return NULL;
-    }
+        return false;
 
-    wxDateTime_t min = (wxDateTime_t)(*p++ - _T('0'));
+    wxDateTime_t min = (wxDateTime_t)(*p++ - '0');
 
     if ( !wxIsdigit(*p) )
-    {
-        return NULL;
-    }
+        return false;
 
     min *= 10;
-    min = (wxDateTime_t)(min + *p++ - _T('0'));
+    min += (wxDateTime_t)(*p++ - '0');
 
     wxDateTime_t sec = 0;
-    if ( *p == _T(':') )
+    if ( *p == ':' )
     {
         p++;
         if ( !wxIsdigit(*p) )
-        {
-            return NULL;
-        }
+            return false;
 
-        sec = (wxDateTime_t)(*p++ - _T('0'));
+        sec = (wxDateTime_t)(*p++ - '0');
 
         if ( !wxIsdigit(*p) )
-        {
-            return NULL;
-        }
+            return false;
 
         sec *= 10;
-        sec = (wxDateTime_t)(sec + *p++ - _T('0'));
+        sec += (wxDateTime_t)(*p++ - '0');
     }
 
-    if ( *p++ != _T(' ') )
-    {
-        return NULL;
-    }
+    if ( *p++ != ' ' )
+        return false;
 
-    // and now the interesting part: the timezone
+    // 7. now the interesting part: the timezone
     int offset wxDUMMY_INITIALIZE(0);
-    if ( *p == _T('-') || *p == _T('+') )
+    if ( *p == '-' || *p == '+' )
     {
         // the explicit offset given: it has the form of hhmm
-        bool plus = *p++ == _T('+');
+        bool plus = *p++ == '+';
 
         if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
-        {
-            return NULL;
-        }
+            return false;
+
 
         // hours
-        offset = MIN_PER_HOUR*(10*(*p - _T('0')) + (*(p + 1) - _T('0')));
+        offset = MIN_PER_HOUR*(10*(*p - '0') + (*(p + 1) - '0'));
 
         p += 2;
 
         if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
-        {
-            return NULL;
-        }
+            return false;
 
         // minutes
-        offset += 10*(*p - _T('0')) + (*(p + 1) - _T('0'));
+        offset += 10*(*p - '0') + (*(p + 1) - '0');
 
         if ( !plus )
-        {
             offset = -offset;
-        }
 
         p += 2;
     }
-    else
+    else // not numeric
     {
         // the symbolic timezone given: may be either military timezone or one
         // of standard abbreviations
@@ -831,18 +850,14 @@ wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end)
             };
 
             if ( *p < _T('A') || *p > _T('Z') || *p == _T('J') )
-            {
-                wxLogDebug(_T("Invalid militaty timezone '%c'"), *p);
+                return false;
 
-                return NULL;
-            }
-
-            offset = offsets[*p++ - _T('A')];
+            offset = offsets[*p++ - 'A'];
         }
         else
         {
             // abbreviation
-            wxString tz = p;
+            const wxString tz(p, date.end());
             if ( tz == _T("UT") || tz == _T("UTC") || tz == _T("GMT") )
                 offset = 0;
             else if ( tz == _T("AST") )
@@ -866,11 +881,7 @@ wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end)
             else if ( tz == _T("PDT") )
                 offset = PDT - GMT0;
             else
-            {
-                wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p);
-
-                return NULL;
-            }
+                return false;
 
             p += tz.length();
         }
@@ -879,15 +890,15 @@ wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end)
         offset *= MIN_PER_HOUR;
     }
 
+
     // the spec was correct, construct the date from the values we found
     Set(day, mon, year, hour, min, sec);
     MakeFromTimezone(TimeZone::Make(offset*SEC_PER_MIN));
 
-    const size_t endpos = p - date.wx_str();
     if ( end )
-        *end = date.begin() + endpos;
+        *end = p;
 
-    return date.c_str() + endpos;
+    return true;
 }
 
 #ifdef __WINDOWS__
@@ -1050,13 +1061,14 @@ static wxString GetLocaleDateFormat()
 
 #endif // __WINDOWS__
 
-const char *
+bool
 wxDateTime::ParseFormat(const wxString& date,
                         const wxString& format,
                         const wxDateTime& dateDef,
-                        wxString::const_iterator *end)
+                        wxString::const_iterator *endParse)
 {
-    wxCHECK_MSG( !format.empty(), NULL, "format can't be empty" );
+    wxCHECK_MSG( !format.empty(), false, "format can't be empty" );
+    wxCHECK_MSG( endParse, false, "end iterator pointer must be specified" );
 
     wxString str;
     unsigned long num;
@@ -1086,7 +1098,8 @@ wxDateTime::ParseFormat(const wxString& date,
     wxDateTime::Month mon = Inv_Month;
     int year = 0;
 
-    const wxStringCharType *input = date.wx_str();
+    wxString::const_iterator input = date.begin();
+    const wxString::const_iterator end = date.end();
     for ( wxString::const_iterator fmt = format.begin(); fmt != format.end(); ++fmt )
     {
         if ( *fmt != _T('%') )
@@ -1107,7 +1120,7 @@ wxDateTime::ParseFormat(const wxString& date,
                 if ( *input++ != *fmt )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
             }
 
@@ -1122,7 +1135,7 @@ wxDateTime::ParseFormat(const wxString& date,
         while ( wxIsdigit(*++fmt) )
         {
             width *= 10;
-            width += *fmt - _T('0');
+            width += *fmt - '0';
         }
 
         // the default widths for the various fields
@@ -1155,12 +1168,16 @@ wxDateTime::ParseFormat(const wxString& date,
             case _T('a'):       // a weekday name
             case _T('A'):
                 {
-                    int flag = *fmt == _T('a') ? Name_Abbr : Name_Full;
-                    wday = GetWeekDayFromName(GetAlphaToken(input), flag);
+                    wday = GetWeekDayFromName
+                           (
+                            GetAlphaToken(input, end),
+                            *fmt == 'a' ? Name_Abbr : Name_Full,
+                            DateLang_Local
+                           );
                     if ( wday == Inv_WeekDay )
                     {
                         // no match
-                        return NULL;
+                        return false;
                     }
                 }
                 haveWDay = true;
@@ -1169,12 +1186,16 @@ wxDateTime::ParseFormat(const wxString& date,
             case _T('b'):       // a month name
             case _T('B'):
                 {
-                    int flag = *fmt == _T('b') ? Name_Abbr : Name_Full;
-                    mon = GetMonthFromName(GetAlphaToken(input), flag);
+                    mon = GetMonthFromName
+                          (
+                            GetAlphaToken(input, end),
+                            *fmt == 'b' ? Name_Abbr : Name_Full,
+                            DateLang_Local
+                          );
                     if ( mon == Inv_Month )
                     {
                         // no match
-                        return NULL;
+                        return false;
                     }
                 }
                 haveMon = true;
@@ -1182,24 +1203,14 @@ wxDateTime::ParseFormat(const wxString& date,
 
             case _T('c'):       // locale default date and time  representation
                 {
-                    wxDateTime dt;
-
-                    const wxString inc(input);
-
-                    // NOTE: %c is locale-dependent; try strptime
 #ifdef HAVE_STRPTIME
                     struct tm tm;
 
                     // try using strptime() -- it may fail even if the input is
                     // correct but the date is out of range, so we will fall back
                     // to our generic code anyhow
-                    const wxStringCharType *
-                        result = CallStrptime(input, "%c", &tm);
-                    if ( result )
+                    if ( CallStrptime(date, input, "%c", &tm) )
                     {
-                        haveDay = haveMon = haveYear =
-                        haveHour = haveMin = haveSec = true;
-
                         hour = tm.tm_hour;
                         min = tm.tm_min;
                         sec = tm.tm_sec;
@@ -1207,28 +1218,25 @@ wxDateTime::ParseFormat(const wxString& date,
                         year = 1900 + tm.tm_year;
                         mon = (Month)tm.tm_mon;
                         mday = tm.tm_mday;
-                        
-                        input = result;     // proceed where strptime() ended
                     }
-                    else
-                    {
-                        // strptime() failed; try generic heuristic code
+                    else // strptime() failed; try generic heuristic code
 #endif // HAVE_STRPTIME
+                    {
 
-                        // try the format which corresponds to ctime() output first
-                        wxString::const_iterator endc;
-                        if ( !dt.ParseFormat(inc, wxS("%a %b %d %H:%M:%S %Y"), &endc) &&
-                                !dt.ParseFormat(inc, wxS("%x %X"), &endc) &&
-                                    !dt.ParseFormat(inc, wxS("%X %x"), &endc) )
-                        {
-                            // we've tried everything and still no match
-                            return NULL;
-                        }
+                        // try the format which corresponds to ctime() output
+                        // first, then the generic date/time formats
+                        const wxDateTime dt = ParseFormatAt
+                                              (
+                                                input,
+                                                end,
+                                                wxS("%a %b %d %H:%M:%S %Y"),
+                                                wxS("%x %X"),
+                                                wxS("%X %x")
+                                              );
+                        if ( !dt.IsValid() )
+                            return false;
 
                         Tm tm = dt.GetTm();
-                        
-                        haveDay = haveMon = haveYear =
-                        haveHour = haveMin = haveSec = true;
 
                         hour = tm.hour;
                         min = tm.min;
@@ -1237,20 +1245,19 @@ wxDateTime::ParseFormat(const wxString& date,
                         year = tm.year;
                         mon = tm.mon;
                         mday = tm.mday;
-                        
-                        input += endc - inc.begin();
-#ifdef HAVE_STRPTIME
                     }
-#endif // HAVE_STRPTIME
+
+                    haveDay = haveMon = haveYear =
+                    haveHour = haveMin = haveSec = true;
                 }
                 break;
 
             case _T('d'):       // day of a month (01-31)
-                if ( !GetNumericToken(width, input, &num) ||
+                if ( !GetNumericToken(width, input, end, &num) ||
                         (num > 31) || (num < 1) )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
 
                 // we can't check whether the day range is correct yet, will
@@ -1260,10 +1267,10 @@ wxDateTime::ParseFormat(const wxString& date,
                 break;
 
             case _T('H'):       // hour in 24h format (00-23)
-                if ( !GetNumericToken(width, input, &num) || (num > 23) )
+                if ( !GetNumericToken(width, input, end, &num) || (num > 23) )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
 
                 haveHour = true;
@@ -1271,10 +1278,11 @@ wxDateTime::ParseFormat(const wxString& date,
                 break;
 
             case _T('I'):       // hour in 12h format (01-12)
-                if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
+                if ( !GetNumericToken(width, input, end, &num) ||
+                        !num || (num > 12) )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
 
                 haveHour = true;
@@ -1283,10 +1291,11 @@ wxDateTime::ParseFormat(const wxString& date,
                 break;
 
             case _T('j'):       // day of the year
-                if ( !GetNumericToken(width, input, &num) || !num || (num > 366) )
+                if ( !GetNumericToken(width, input, end, &num) ||
+                        !num || (num > 366) )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
 
                 haveYDay = true;
@@ -1294,18 +1303,19 @@ wxDateTime::ParseFormat(const wxString& date,
                 break;
 
             case _T('l'):       // milliseconds (0-999)
-                if ( !GetNumericToken(width, input, &num) )
-                    return NULL;
+                if ( !GetNumericToken(width, input, end, &num) )
+                    return false;
 
                 haveMsec = true;
                 msec = (wxDateTime_t)num;
                 break;
 
             case _T('m'):       // month as a number (01-12)
-                if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
+                if ( !GetNumericToken(width, input, end, &num) ||
+                        !num || (num > 12) )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
 
                 haveMon = true;
@@ -1313,10 +1323,11 @@ wxDateTime::ParseFormat(const wxString& date,
                 break;
 
             case _T('M'):       // minute as a decimal number (00-59)
-                if ( !GetNumericToken(width, input, &num) || (num > 59) )
+                if ( !GetNumericToken(width, input, end, &num) ||
+                        (num > 59) )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
 
                 haveMin = true;
@@ -1325,11 +1336,18 @@ wxDateTime::ParseFormat(const wxString& date,
 
             case _T('p'):       // AM or PM string
                 {
-                    wxString am, pm, token = GetAlphaToken(input);
+                    wxString am, pm, token = GetAlphaToken(input, end);
+
+                    // some locales have empty AM/PM tokens and thus when formatting
+                    // dates with the %p specifier nothing is generated; when trying to
+                    // parse them back, we get an empty token here... but that's not
+                    // an error.
+                    if (token.empty())
+                        break;
 
                     GetAmPmStrings(&am, &pm);
                     if (am.empty() && pm.empty())
-                        return NULL;  // no am/pm strings defined
+                        return false;  // no am/pm strings defined
                     if ( token.CmpNoCase(pm) == 0 )
                     {
                         isPM = true;
@@ -1337,7 +1355,7 @@ wxDateTime::ParseFormat(const wxString& date,
                     else if ( token.CmpNoCase(am) != 0 )
                     {
                         // no match
-                        return NULL;
+                        return false;
                     }
                 }
                 break;
@@ -1345,12 +1363,9 @@ wxDateTime::ParseFormat(const wxString& date,
             case _T('r'):       // time as %I:%M:%S %p
                 {
                     wxDateTime dt;
-                    input = dt.ParseFormat(input, wxS("%I:%M:%S %p"));
-                    if ( !input )
-                    {
-                        // no match
-                        return NULL;
-                    }
+                    if ( !dt.ParseFormat(wxString(input, end),
+                                         wxS("%I:%M:%S %p"), &input) )
+                        return false;
 
                     haveHour = haveMin = haveSec = true;
 
@@ -1363,15 +1378,13 @@ wxDateTime::ParseFormat(const wxString& date,
 
             case _T('R'):       // time as %H:%M
                 {
-                    wxDateTime dt;
-                    input = dt.ParseFormat(input, wxS("%H:%M"));
-                    if ( !input )
-                    {
-                        // no match
-                        return NULL;
-                    }
+                    const wxDateTime
+                        dt = ParseFormatAt(input, end, wxS("%H:%M"));
+                    if ( !dt.IsValid() )
+                        return false;
 
-                    haveHour = haveMin = true;
+                    haveHour =
+                    haveMin = true;
 
                     Tm tm = dt.GetTm();
                     hour = tm.hour;
@@ -1380,10 +1393,11 @@ wxDateTime::ParseFormat(const wxString& date,
                 break;
 
             case _T('S'):       // second as a decimal number (00-61)
-                if ( !GetNumericToken(width, input, &num) || (num > 61) )
+                if ( !GetNumericToken(width, input, end, &num) ||
+                        (num > 61) )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
 
                 haveSec = true;
@@ -1392,15 +1406,14 @@ wxDateTime::ParseFormat(const wxString& date,
 
             case _T('T'):       // time as %H:%M:%S
                 {
-                    wxDateTime dt;
-                    input = dt.ParseFormat(input, _T("%H:%M:%S"));
-                    if ( !input )
-                    {
-                        // no match
-                        return NULL;
-                    }
+                    const wxDateTime
+                        dt = ParseFormatAt(input, end, wxS("%H:%M:%S"));
+                    if ( !dt.IsValid() )
+                        return false;
 
-                    haveHour = haveMin = haveSec = true;
+                    haveHour =
+                    haveMin =
+                    haveSec = true;
 
                     Tm tm = dt.GetTm();
                     hour = tm.hour;
@@ -1410,10 +1423,11 @@ wxDateTime::ParseFormat(const wxString& date,
                 break;
 
             case _T('w'):       // weekday as a number (0-6), Sunday = 0
-                if ( !GetNumericToken(width, input, &num) || (wday > 6) )
+                if ( !GetNumericToken(width, input, end, &num) ||
+                        (wday > 6) )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
 
                 haveWDay = true;
@@ -1428,12 +1442,8 @@ wxDateTime::ParseFormat(const wxString& date,
                 {
                     struct tm tm;
 
-                    const wxStringCharType *
-                        result = CallStrptime(input, "%x", &tm);
-                    if ( result )
+                    if ( CallStrptime(date, input, "%x", &tm) )
                     {
-                        input = result;
-
                         haveDay = haveMon = haveYear = true;
 
                         year = 1900 + tm.tm_year;
@@ -1446,7 +1456,6 @@ wxDateTime::ParseFormat(const wxString& date,
 #endif // HAVE_STRPTIME
 
                 {
-                    wxDateTime dt;
                     wxString fmtDate,
                              fmtDateAlt;
 
@@ -1470,17 +1479,11 @@ wxDateTime::ParseFormat(const wxString& date,
                         }
                     }
 
-                    const wxString indate(input);
-                    wxString::const_iterator endDate;
-                    if ( !dt.ParseFormat(indate, fmtDate, &endDate) )
-                    {
-                        // try another one if we have it
-                        if ( fmtDateAlt.empty() ||
-                                !dt.ParseFormat(indate, fmtDateAlt, &endDate) )
-                        {
-                            return NULL;
-                        }
-                    }
+                    const wxDateTime
+                        dt = ParseFormatAt(input, end,
+                                           fmtDate, fmtDateAlt);
+                    if ( !dt.IsValid() )
+                        return false;
 
                     Tm tm = dt.GetTm();
 
@@ -1491,8 +1494,6 @@ wxDateTime::ParseFormat(const wxString& date,
                     year = tm.year;
                     mon = tm.mon;
                     mday = tm.mday;
-
-                    input += endDate - indate.begin();
                 }
 
                 break;
@@ -1502,11 +1503,8 @@ wxDateTime::ParseFormat(const wxString& date,
                 {
                     // use strptime() to do it for us (FIXME !Unicode friendly)
                     struct tm tm;
-                    input = CallStrptime(input, "%X", &tm);
-                    if ( !input )
-                    {
-                        return NULL;
-                    }
+                    if ( !CallStrptime(date, input, "%X", &tm) )
+                        return false;
 
                     haveHour = haveMin = haveSec = true;
 
@@ -1522,20 +1520,10 @@ wxDateTime::ParseFormat(const wxString& date,
                     // try to parse what follows as "%H:%M:%S" and, if this
                     // fails, as "%I:%M:%S %p" - this should catch the most
                     // common cases
-                    wxDateTime dt;
-
-                    const wxStringCharType *
-                        result = dt.ParseFormat(input, wxS("%T"));
-                    if ( !result )
-                    {
-                        result = dt.ParseFormat(input, wxS("%r"));
-                    }
-
-                    if ( !result )
-                    {
-                        // no match
-                        return NULL;
-                    }
+                    const wxDateTime
+                        dt = ParseFormatAt(input, end, "%T", "%r");
+                    if ( !dt.IsValid() )
+                        return false;
 
                     haveHour =
                     haveMin =
@@ -1545,17 +1533,16 @@ wxDateTime::ParseFormat(const wxString& date,
                     hour = tm.hour;
                     min = tm.min;
                     sec = tm.sec;
-
-                    input = result;
                 }
 #endif // HAVE_STRPTIME/!HAVE_STRPTIME
                 break;
 
             case _T('y'):       // year without century (00-99)
-                if ( !GetNumericToken(width, input, &num) || (num > 99) )
+                if ( !GetNumericToken(width, input, end, &num) ||
+                        (num > 99) )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
 
                 haveYear = true;
@@ -1566,10 +1553,10 @@ wxDateTime::ParseFormat(const wxString& date,
                 break;
 
             case _T('Y'):       // year with century
-                if ( !GetNumericToken(width, input, &num) )
+                if ( !GetNumericToken(width, input, end, &num) )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
 
                 haveYear = true;
@@ -1584,7 +1571,7 @@ wxDateTime::ParseFormat(const wxString& date,
                 if ( *input++ != _T('%') )
                 {
                     // no match
-                    return NULL;
+                    return false;
                 }
                 break;
 
@@ -1594,7 +1581,7 @@ wxDateTime::ParseFormat(const wxString& date,
                 // fall through
 
             default:            // not a known format spec
-                return NULL;
+                return false;
         }
     }
 
@@ -1635,22 +1622,14 @@ wxDateTime::ParseFormat(const wxString& date,
     if ( haveDay )
     {
         if ( mday > GetNumberOfDays(tm.mon, tm.year) )
-        {
-            wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
-
-            return NULL;
-        }
+            return false;
 
         tm.mday = mday;
     }
     else if ( haveYDay )
     {
         if ( yday > GetNumberOfDays(tm.year) )
-        {
-            wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
-
-            return NULL;
-        }
+            return false;
 
         Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm();
 
@@ -1689,22 +1668,18 @@ wxDateTime::ParseFormat(const wxString& date,
 
     // finally check that the week day is consistent -- if we had it
     if ( haveWDay && GetWeekDay() != wday )
-    {
-        wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
-
-        return NULL;
-    }
+        return false;
 
-    const size_t endpos = input - date.wx_str();
-    if ( end )
-        *end = date.begin() + endpos;
+    *endParse = input;
 
-    return date.c_str() + endpos;
+    return true;
 }
 
-const char *
+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();
@@ -1725,7 +1700,7 @@ wxDateTime::ParseDateTime(const wxString& date, wxString::const_iterator *end)
 
         const wxString timestr(endDate, date.end());
         if ( !dtTime.ParseTime(timestr, &endTime) )
-            return NULL;
+            return false;
 
         endBoth = endDate + (endTime - timestr.begin());
     }
@@ -1733,14 +1708,14 @@ wxDateTime::ParseDateTime(const wxString& date, wxString::const_iterator *end)
     {
         // check if we have a time followed by a date
         if ( !dtTime.ParseTime(date, &endTime) )
-            return NULL;
+            return false;
 
         while ( endTime != date.end() && wxIsspace(*endTime) )
             ++endTime;
 
         const wxString datestr(endTime, date.end());
         if ( !dtDate.ParseDate(datestr, &endDate) )
-            return NULL;
+            return false;
 
         endBoth = endTime + (endDate - datestr.begin());
     }
@@ -1749,21 +1724,23 @@ wxDateTime::ParseDateTime(const wxString& date, wxString::const_iterator *end)
         dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(),
         dtTime.GetMillisecond());
 
-    // Return endpoint of scan
-    if ( end )
-        *end = endBoth;
+    *end = endBoth;
 
-    return date.c_str() + (endBoth - date.begin());
+    return true;
 }
 
-const char *
+bool
 wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
 {
+    wxCHECK_MSG( end, false, "end iterator pointer must be specified" );
+
     // this is a simplified version of ParseDateTime() which understands only
     // "today" (for wxDate compatibility) and digits only otherwise (and not
     // all esoteric constructions ParseDateTime() knows about)
 
-    const wxStringCharType *p = date.wx_str();
+    const wxString::const_iterator pBegin = date.begin();
+
+    wxString::const_iterator p = pBegin;
     while ( wxIsspace(*p) )
         p++;
 
@@ -1779,31 +1756,32 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
         { wxTRANSLATE("tomorrow"),          1 },
     };
 
+    const size_t lenRest = date.end() - p;
     for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
     {
         const wxString dateStr = wxGetTranslation(literalDates[n].str);
         size_t len = dateStr.length();
-        if ( wxStrlen(p) >= len )
-        {
-            wxString str(p, len);
-            if ( str.CmpNoCase(dateStr) == 0 )
-            {
-                // nothing can follow this, so stop here
-                p += len;
 
-                int dayDiffFromToday = literalDates[n].dayDiffFromToday;
-                *this = Today();
-                if ( dayDiffFromToday )
-                {
-                    *this += wxDateSpan::Days(dayDiffFromToday);
-                }
+        if ( len > lenRest )
+            continue;
+
+        const wxString::const_iterator pEnd = p + len;
+        if ( wxString(p, pEnd).CmpNoCase(dateStr) == 0 )
+        {
+            // nothing can follow this, so stop here
 
-                const size_t endpos = p - date.wx_str();
+            p = pEnd;
 
-                if ( end )
-                    *end = date.begin() + endpos;
-                return date.c_str() + endpos;
+            int dayDiffFromToday = literalDates[n].dayDiffFromToday;
+            *this = Today();
+            if ( dayDiffFromToday )
+            {
+                *this += wxDateSpan::Days(dayDiffFromToday);
             }
+
+            *end = pEnd;
+
+            return true;
         }
     }
 
@@ -1827,7 +1805,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
     // tokenize the string
     size_t nPosCur = 0;
     static const wxStringCharType *dateDelimiters = wxS(".,/-\t\r\n ");
-    wxStringTokenizer tok(p, dateDelimiters);
+    wxStringTokenizer tok(wxString(p, date.end()), dateDelimiters);
     while ( tok.HasMoreTokens() )
     {
         wxString token = tok.GetNextToken();
@@ -1907,7 +1885,12 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
         else // not a number
         {
             // be careful not to overwrite the current mon value
-            Month mon2 = GetMonthFromName(token, Name_Full | Name_Abbr);
+            Month mon2 = GetMonthFromName
+                         (
+                            token,
+                            Name_Full | Name_Abbr,
+                            DateLang_Local | DateLang_English
+                         );
             if ( mon2 != Inv_Month )
             {
                 // it's a month
@@ -1935,7 +1918,12 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
             }
             else // not a valid month name
             {
-                WeekDay wday2 = GetWeekDayFromName(token, Name_Full | Name_Abbr);
+                WeekDay wday2 = GetWeekDayFromName
+                                (
+                                    token,
+                                    Name_Full | Name_Abbr,
+                                    DateLang_Local | DateLang_English
+                                );
                 if ( wday2 != Inv_WeekDay )
                 {
                     // a week day
@@ -2014,11 +2002,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
     // either no more tokens or the scan was stopped by something we couldn't
     // parse - in any case, see if we can construct a date from what we have
     if ( !haveDay && !haveWDay )
-    {
-        wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
-
-        return NULL;
-    }
+        return false;
 
     if ( haveWDay && (haveMon || haveYear || haveDay) &&
          !(haveDay && haveMon && haveYear) )
@@ -2026,7 +2010,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
         // 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 NULL;
+        return false;
     }
 
     if ( !haveWDay && haveYear && !(haveDay && haveMon) )
@@ -2052,12 +2036,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
         }
 
         if ( !haveMon )
-        {
-            // 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 NULL;
-        }
+            return false;
     }
 
     if ( !haveMon )
@@ -2075,7 +2054,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
         // 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 > GetNumberOfDays(mon, year) )
-            return NULL;
+            return false;
 
         Set(day, mon, year);
 
@@ -2083,12 +2062,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
         {
             // check that it is really the same
             if ( GetWeekDay() != wday )
-            {
-                // inconsistency detected
-                wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
-
-                return NULL;
-            }
+                return false;
         }
     }
     else // haveWDay
@@ -2107,21 +2081,21 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
         p--;
     }
 
-    const size_t endpos = p - date.wx_str();
-    if ( end )
-        *end = date.begin() + endpos;
+    *end = p;
 
-    return date.c_str() + endpos;
+    return true;
 }
 
-const char *
+bool
 wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end)
 {
+    wxCHECK_MSG( end, false, "end iterator pointer must be specified" );
+
     // first try some extra things
     static const struct
     {
         const char *name;
-        wxDateTime_t  hour;
+        wxDateTime_t hour;
     } stdTimes[] =
     {
         { wxTRANSLATE("noon"),      12 },
@@ -2131,17 +2105,17 @@ wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end)
 
     for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ )
     {
-        wxString timeString = wxGetTranslation(stdTimes[n].name);
-        size_t len = timeString.length();
-        if ( timeString.CmpNoCase(wxString(time, len)) == 0 )
+        const wxString timeString = wxGetTranslation(stdTimes[n].name);
+        const wxString::const_iterator p = time.begin() + timeString.length();
+        if ( timeString.CmpNoCase(wxString(time.begin(), p)) == 0 )
         {
             // casts required by DigitalMars
             Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0));
 
             if ( end )
-                *end = time.begin() + len;
+                *end = p;
 
-            return time.c_str() + len;
+            return true;
         }
     }
 
@@ -2161,12 +2135,11 @@ wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end)
 
     for ( size_t nFmt = 0; nFmt < WXSIZEOF(timeFormats); nFmt++ )
     {
-        const char *result = ParseFormat(time, timeFormats[nFmt], end);
-        if ( result )
-            return result;
+        if ( ParseFormat(time, timeFormats[nFmt], end) )
+            return true;
     }
 
-    return NULL;
+    return false;
 }
 
 // ----------------------------------------------------------------------------