+ break;
+
+ case _T('X'): // locale default time representation
+#ifdef HAVE_STRPTIME
+ {
+ // use strptime() to do it for us (FIXME !Unicode friendly)
+ struct tm tm;
+ input = CallStrptime(input, "%X", &tm);
+ if ( !input )
+ {
+ return NULL;
+ }
+
+ haveHour = haveMin = haveSec = true;
+
+ hour = tm.tm_hour;
+ min = tm.tm_min;
+ sec = tm.tm_sec;
+ }
+#else // !HAVE_STRPTIME
+ // TODO under Win32 we can query the LOCALE_ITIME system
+ // setting which says whether the default time format is
+ // 24 or 12 hour
+ {
+ // 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;
+ }
+
+ haveHour =
+ haveMin =
+ haveSec = true;
+
+ Tm tm = dt.GetTm();
+ 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) )
+ {
+ // no match
+ return NULL;
+ }
+
+ haveYear = true;
+
+ // TODO should have an option for roll over date instead of
+ // hard coding it here
+ year = (num > 30 ? 1900 : 2000) + (wxDateTime_t)num;
+ break;
+
+ case _T('Y'): // year with century
+ if ( !GetNumericToken(width, input, &num) )
+ {
+ // no match
+ return NULL;
+ }
+
+ haveYear = true;
+ year = (wxDateTime_t)num;
+ break;
+
+ case _T('Z'): // timezone name
+ wxFAIL_MSG(_T("TODO"));
+ break;
+
+ case _T('%'): // a percent sign
+ if ( *input++ != _T('%') )
+ {
+ // no match
+ return NULL;
+ }
+ break;
+
+ case 0: // the end of string
+ wxFAIL_MSG(_T("unexpected format end"));
+
+ // fall through
+
+ default: // not a known format spec
+ return NULL;
+ }
+ }
+
+ // format matched, try to construct a date from what we have now
+ Tm tmDef;
+ if ( dateDef.IsValid() )
+ {
+ // take this date as default
+ tmDef = dateDef.GetTm();
+ }
+ else if ( IsValid() )
+ {
+ // if this date is valid, don't change it
+ tmDef = GetTm();
+ }
+ else
+ {
+ // no default and this date is invalid - fall back to Today()
+ tmDef = Today().GetTm();
+ }
+
+ Tm tm = tmDef;
+
+ // set the date
+ if ( haveYear )
+ {
+ tm.year = year;
+ }
+
+ // TODO we don't check here that the values are consistent, if both year
+ // day and month/day were found, we just ignore the year day and we
+ // also always ignore the week day
+ if ( haveMon && haveDay )
+ {
+ if ( mday > GetNumOfDaysInMonth(tm.year, mon) )
+ {
+ wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
+
+ return NULL;
+ }
+
+ tm.mon = mon;
+ tm.mday = mday;
+ }
+ else if ( haveYDay )
+ {
+ if ( yday > GetNumberOfDays(tm.year) )
+ {
+ wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
+
+ return NULL;
+ }
+
+ Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm();
+
+ tm.mon = tm2.mon;
+ tm.mday = tm2.mday;
+ }
+
+ // deal with AM/PM
+ if ( haveHour && hourIsIn12hFormat && isPM )
+ {
+ // translate to 24hour format
+ hour += 12;
+ }
+ //else: either already in 24h format or no translation needed
+
+ // set the time
+ if ( haveHour )
+ {
+ tm.hour = hour;
+ }
+
+ if ( haveMin )
+ {
+ tm.min = min;
+ }
+
+ if ( haveSec )
+ {
+ tm.sec = sec;
+ }
+
+ Set(tm);
+
+ // 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;
+ }
+
+ const size_t endpos = input - date.wx_str();
+ if ( end )
+ *end = date.begin() + endpos;
+
+ return date.c_str() + endpos;
+}
+
+const char *
+wxDateTime::ParseDateTime(const wxString& date, wxString::const_iterator *end)
+{
+ // 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();
+
+ wxString::const_iterator
+ endTime,
+ endDate,
+ endBoth;
+
+ // If we got a date in the beginning, see if there is a time specified
+ // after the date
+ if ( dtDate.ParseDate(date, &endDate) )
+ {
+ // Skip spaces, as the ParseTime() function fails on spaces
+ while ( endDate != date.end() && wxIsspace(*endDate) )
+ ++endDate;
+
+ const wxString timestr(endDate, date.end());
+ if ( !dtTime.ParseTime(timestr, &endTime) )
+ return NULL;
+
+ endBoth = endDate + (endTime - timestr.begin());
+ }
+ else // no date in the beginning
+ {
+ // check if we have a time followed by a date
+ if ( !dtTime.ParseTime(date, &endTime) )
+ return NULL;
+
+ while ( endTime != date.end() && wxIsspace(*endTime) )
+ ++endTime;
+
+ const wxString datestr(endTime, date.end());
+ if ( !dtDate.ParseDate(datestr, &endDate) )
+ return NULL;
+
+ endBoth = endTime + (endDate - datestr.begin());
+ }
+
+ Set(dtDate.GetDay(), dtDate.GetMonth(), dtDate.GetYear(),
+ dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(),
+ dtTime.GetMillisecond());
+
+ // Return endpoint of scan
+ if ( end )
+ *end = endBoth;
+
+ return date.c_str() + (endBoth - date.begin());
+}
+
+const char *
+wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
+{
+ // 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();
+ while ( wxIsspace(*p) )
+ p++;
+
+ // some special cases
+ static struct
+ {
+ const char *str;
+ int dayDiffFromToday;
+ } literalDates[] =
+ {
+ { wxTRANSLATE("today"), 0 },
+ { wxTRANSLATE("yesterday"), -1 },
+ { wxTRANSLATE("tomorrow"), 1 },
+ };
+
+ 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 )