+
+ 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;
+ }
+ }
+
+ Tm tm = dt.GetTm();
+
+ haveDay =
+ haveMon =
+ haveYear = true;
+
+ year = tm.year;
+ mon = tm.mon;
+ mday = tm.mday;
+
+ input += endDate - indate.begin();
+ }
+
+ 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 ( haveMon )
+ {
+ tm.mon = mon;
+ }
+
+ 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 ( haveDay )
+ {
+ if ( mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
+ {
+ wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
+
+ return NULL;
+ }
+
+ 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 )
+ {
+ // nothing can follow this, so stop here
+ p += len;
+
+ int dayDiffFromToday = literalDates[n].dayDiffFromToday;
+ *this = Today();
+ if ( dayDiffFromToday )
+ {
+ *this += wxDateSpan::Days(dayDiffFromToday);
+ }
+
+ const size_t endpos = p - date.wx_str();
+
+ if ( end )
+ *end = date.begin() + endpos;
+ return date.c_str() + endpos;
+ }
+ }
+ }
+
+ // We try to guess what we have here: for each new (numeric) token, we
+ // determine if it can be a month, day or a year. Of course, there is an
+ // ambiguity as some numbers may be days as well as months, so we also
+ // have the ability to back track.
+
+ // what do we have?
+ bool haveDay = false, // the months day?
+ haveWDay = false, // the day of week?
+ haveMon = false, // the month?
+ haveYear = false; // the year?
+
+ // and the value of the items we have (init them to get rid of warnings)
+ WeekDay wday = Inv_WeekDay;
+ wxDateTime_t day = 0;
+ wxDateTime::Month mon = Inv_Month;
+ int year = 0;
+
+ // tokenize the string
+ size_t nPosCur = 0;
+ static const wxStringCharType *dateDelimiters = wxS(".,/-\t\r\n ");
+ wxStringTokenizer tok(p, dateDelimiters);
+ while ( tok.HasMoreTokens() )
+ {
+ wxString token = tok.GetNextToken();
+ if ( !token )
+ continue;
+
+ // is it a number?
+ unsigned long val;
+ if ( token.ToULong(&val) )
+ {
+ // guess what this number is
+
+ bool isDay = false,
+ isMonth = false,
+ isYear = false;
+
+ if ( !haveMon && val > 0 && val <= 12 )
+ {
+ // assume it is month
+ isMonth = true;
+ }
+ else // not the month
+ {
+ if ( haveDay )
+ {
+ // this can only be the year
+ isYear = true;
+ }
+ 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 : 1976, mon)
+ : 31
+ );
+
+ // can it be day?
+ if ( (val == 0) || (val > (unsigned long)max_days) )
+ {
+ // no
+ isYear = true;
+ }
+ else // yes, suppose it's the day
+ {
+ isDay = true;
+ }