X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/59d454cda2438b6334e2e9241962859aef09da22..dbe0872fc80f7fc9ac37235ab1f8ba8224b0bfa5:/src/common/datetimefmt.cpp diff --git a/src/common/datetimefmt.cpp b/src/common/datetimefmt.cpp index c0bfdf5d55..e6544edd23 100644 --- a/src/common/datetimefmt.cpp +++ b/src/common/datetimefmt.cpp @@ -46,7 +46,6 @@ #endif // WX_PRECOMP #include "wx/thread.h" -#include "wx/tokenzr.h" #include @@ -92,41 +91,41 @@ static const int MIN_PER_HOUR = 60; namespace { -#ifdef HAVE_STRPTIME +// all the functions below taking non-const wxString::const_iterator p advance +// it until the end of the match -#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 - -// 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) +// scans all digits (but no more than len) and returns the resulting number +bool GetNumericToken(size_t len, + wxString::const_iterator& p, + const wxString::const_iterator& end, + unsigned long *number) { - // 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; + size_t n = 1; + wxString s; + while ( p != end && wxIsdigit(*p) ) + { + s += *p++; - // 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(); + if ( len && ++n > len ) + break; + } - return true; + return !s.empty() && s.ToULong(number); } -#endif // HAVE_STRPTIME +// scans all alphabetic characters and returns the resulting string +wxString +GetAlphaToken(wxString::const_iterator& p, + const wxString::const_iterator& end) +{ + wxString s; + while ( p != end && wxIsalpha(*p) ) + { + s += *p++; + } + + return s; +} enum { @@ -141,8 +140,16 @@ enum // 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) +GetMonthFromName(wxString::const_iterator& p, + const wxString::const_iterator& end, + int flags, + int lang) { + const wxString::const_iterator pOrig = p; + const wxString name = GetAlphaToken(p, end); + if ( name.empty() ) + return wxDateTime::Inv_Month; + wxDateTime::Month mon; for ( mon = wxDateTime::Jan; mon < wxDateTime::Inv_Month; wxNextMonth(mon) ) { @@ -176,13 +183,35 @@ GetMonthFromName(const wxString& name, int flags, int lang) if ( lang & DateLang_Local ) { - if ( name.CmpNoCase(wxDateTime::GetMonthName(mon, - wxDateTime::Name_Abbr)) == 0 ) + // some locales (e.g. French one) use periods for the + // abbreviated month names but it's never part of name so + // compare it specially + wxString nameAbbr = wxDateTime::GetMonthName(mon, + wxDateTime::Name_Abbr); + const bool hasPeriod = *nameAbbr.rbegin() == '.'; + if ( hasPeriod ) + nameAbbr.erase(nameAbbr.end() - 1); + + if ( name.CmpNoCase(nameAbbr) == 0 ) + { + if ( hasPeriod ) + { + // skip trailing period if it was part of the match + if ( *p == '.' ) + ++p; + else // no match as no matching period + continue; + } + break; + } } } } + if ( mon == wxDateTime::Inv_Month ) + p = pOrig; + return mon; } @@ -191,8 +220,15 @@ GetMonthFromName(const wxString& name, int flags, int lang) // flags and lang parameters have the same meaning as for GetMonthFromName() // above wxDateTime::WeekDay -GetWeekDayFromName(const wxString& name, int flags, int lang) +GetWeekDayFromName(wxString::const_iterator& p, + const wxString::const_iterator& end, + int flags, int lang) { + const wxString::const_iterator pOrig = p; + const wxString name = GetAlphaToken(p, end); + if ( name.empty() ) + return wxDateTime::Inv_WeekDay; + wxDateTime::WeekDay wd; for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) ) { @@ -231,40 +267,10 @@ GetWeekDayFromName(const wxString& name, int flags, int lang) } } - return wd; -} - -// scans all digits (but no more than len) and returns the resulting 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 ( p != end && wxIsdigit(*p) ) - { - s += *p++; - - if ( len && ++n > len ) - break; - } - - return !s.empty() && s.ToULong(number); -} - -// scans all alphabetic characters and returns the resulting string -wxString -GetAlphaToken(wxString::const_iterator& p, - const wxString::const_iterator& end) -{ - wxString s; - while ( p != end && wxIsalpha(*p) ) - { - s += *p++; - } + if ( wd == wxDateTime::Inv_WeekDay ) + p = pOrig; - return s; + return wd; } // parses string starting at given iterator using the specified format and, @@ -281,15 +287,13 @@ ParseFormatAt(wxString::const_iterator& p, // 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& fmtAlt = wxEmptyString) { 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)) ) + (!fmtAlt.empty() && dt.ParseFormat(str, fmtAlt, &endParse)) ) { p += endParse - str.begin(); } @@ -304,14 +308,20 @@ ParseFormatAt(wxString::const_iterator& p, // wxDateTime to/from text representations // ---------------------------------------------------------------------------- -wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const +wxString wxDateTime::Format(const wxString& formatp, const TimeZone& tz) const { - wxCHECK_MSG( !format.empty(), wxEmptyString, + wxCHECK_MSG( !formatp.empty(), wxEmptyString, _T("NULL format in wxDateTime::Format") ); + wxString format = formatp; +#ifdef __WXOSX__ + format.Replace("%c",wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT)); + format.Replace("%x",wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT)); + 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 -#ifdef HAVE_STRFTIME +#ifdef wxHAS_STRFTIME time_t time = GetTicks(); if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) ) @@ -355,7 +365,7 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const } } //else: use generic code below -#endif // HAVE_STRFTIME +#endif // wxHAS_STRFTIME // we only parse ANSI C format specifications here, no POSIX 2 // complications, no GNU extensions but we do add support for a "%l" format @@ -432,7 +442,7 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const case _T('c'): // locale default date and time representation case _T('x'): // locale default date representation -#ifdef HAVE_STRFTIME +#ifdef wxHAS_STRFTIME // // the problem: there is no way to know what do these format // specifications correspond to for the current locale. @@ -548,11 +558,11 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const res += str; } -#else // !HAVE_STRFTIME +#else // !wxHAS_STRFTIME // Use "%m/%d/%y %H:%M:%S" format instead res += wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"), tm.mon+1,tm.mday, tm.year, tm.hour, tm.min, tm.sec); -#endif // HAVE_STRFTIME/!HAVE_STRFTIME +#endif // wxHAS_STRFTIME/!wxHAS_STRFTIME break; case _T('d'): // day of a month (01-31) @@ -589,11 +599,11 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const break; case _T('p'): // AM or PM string -#ifdef HAVE_STRFTIME +#ifdef wxHAS_STRFTIME res += CallStrftime(_T("%p"), &tmTimeOnly); -#else // !HAVE_STRFTIME +#else // !wxHAS_STRFTIME res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am"); -#endif // HAVE_STRFTIME/!HAVE_STRFTIME +#endif // wxHAS_STRFTIME/!wxHAS_STRFTIME break; case _T('S'): // second as a decimal number (00-61) @@ -616,11 +626,11 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const case _T('X'): // locale default time representation // just use strftime() to format the time for us -#ifdef HAVE_STRFTIME +#ifdef wxHAS_STRFTIME res += CallStrftime(_T("%X"), &tmTimeOnly); -#else // !HAVE_STRFTIME +#else // !wxHAS_STRFTIME res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec); -#endif // HAVE_STRFTIME/!HAVE_STRFTIME +#endif // wxHAS_STRFTIME/!wxHAS_STRFTIME break; case _T('y'): // year without century (00-99) @@ -632,7 +642,7 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const break; case _T('Z'): // timezone name -#ifdef HAVE_STRFTIME +#ifdef wxHAS_STRFTIME res += CallStrftime(_T("%Z"), &tmTimeOnly); #endif break; @@ -688,19 +698,17 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const bool wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end) { + const wxString::const_iterator pEnd = date.end(); wxString::const_iterator p = date.begin(); // 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 ) + const wxDateTime::WeekDay + wd = GetWeekDayFromName(p, pEnd, Name_Abbr, DateLang_English); + if ( wd == Inv_WeekDay ) return false; //else: ignore week day for now, we could also check that it really // corresponds to the specified date - p = endWday; - // 2. separating comma if ( *p++ != ',' || *p++ != ' ' ) return false; @@ -720,15 +728,10 @@ wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end) return false; // 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); + const Month mon = GetMonthFromName(p, pEnd, Name_Abbr, DateLang_English); if ( mon == Inv_Month ) return false; - p = endMonth; - if ( *p++ != ' ' ) return false; @@ -808,7 +811,7 @@ wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end) return false; // 7. now the interesting part: the timezone - int offset wxDUMMY_INITIALIZE(0); + int offset = 0; // just to suppress warnings if ( *p == '-' || *p == '+' ) { // the explicit offset given: it has the form of hhmm @@ -901,387 +904,6 @@ wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end) return true; } -#ifdef __WINDOWS__ - -// returns the string containing strftime() format used for short dates in the -// current locale or an empty string -static wxString GetLocaleDateFormat() -{ - wxString fmtWX; - - // there is no setlocale() under Windows CE, so just always query the - // system there -#ifndef __WXWINCE__ - if ( strcmp(setlocale(LC_ALL, NULL), "C") != 0 ) -#endif - { - // The locale was programatically set to non-C. We assume that this was - // done using wxLocale, in which case thread's current locale is also - // set to correct LCID value and we can use GetLocaleInfo to determine - // the correct formatting string: -#ifdef __WXWINCE__ - LCID lcid = LOCALE_USER_DEFAULT; -#else - LCID lcid = GetThreadLocale(); -#endif - // according to MSDN 80 chars is max allowed for short date format - wxChar fmt[81]; - if ( ::GetLocaleInfo(lcid, LOCALE_SSHORTDATE, fmt, WXSIZEOF(fmt)) ) - { - wxChar chLast = _T('\0'); - size_t lastCount = 0; - for ( const wxChar *p = fmt; /* NUL handled inside */; p++ ) - { - if ( *p == chLast ) - { - lastCount++; - continue; - } - - switch ( *p ) - { - // these characters come in groups, start counting them - case _T('d'): - case _T('M'): - case _T('y'): - case _T('g'): - chLast = *p; - lastCount = 1; - break; - - default: - // first deal with any special characters we have had - if ( lastCount ) - { - switch ( chLast ) - { - case _T('d'): - switch ( lastCount ) - { - case 1: // d - case 2: // dd - // these two are the same as we - // don't distinguish between 1 and - // 2 digits for days - fmtWX += _T("%d"); - break; - - case 3: // ddd - fmtWX += _T("%a"); - break; - - case 4: // dddd - fmtWX += _T("%A"); - break; - - default: - wxFAIL_MSG( _T("too many 'd's") ); - } - break; - - case _T('M'): - switch ( lastCount ) - { - case 1: // M - case 2: // MM - // as for 'd' and 'dd' above - fmtWX += _T("%m"); - break; - - case 3: - fmtWX += _T("%b"); - break; - - case 4: - fmtWX += _T("%B"); - break; - - default: - wxFAIL_MSG( _T("too many 'M's") ); - } - break; - - case _T('y'): - switch ( lastCount ) - { - case 1: // y - case 2: // yy - fmtWX += _T("%y"); - break; - - case 4: // yyyy - fmtWX += _T("%Y"); - break; - - default: - wxFAIL_MSG( _T("wrong number of 'y's") ); - } - break; - - case _T('g'): - // strftime() doesn't have era string, - // ignore this format - wxASSERT_MSG( lastCount <= 2, - _T("too many 'g's") ); - break; - - default: - wxFAIL_MSG( _T("unreachable") ); - } - - chLast = _T('\0'); - lastCount = 0; - } - - // not a special character so must be just a separator, - // treat as is - if ( *p != _T('\0') ) - { - if ( *p == _T('%') ) - { - // this one needs to be escaped - fmtWX += _T('%'); - } - - fmtWX += *p; - } - } - - if ( *p == _T('\0') ) - break; - } - } - //else: GetLocaleInfo() failed, leave fmtDate value unchanged and - // try our luck with the default formats - } - //else: default C locale, default formats should work - - return fmtWX; -} - -#endif // __WINDOWS__ - -#ifdef __WXOSX__ - -#include "wx/osx/private.h" - -// under OSX locale formats are defined using -// http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns -// -// so we need a translation function, bluntly copied from the windows -// version above and enhanced with the additional elements needed - -static wxString TranslateFromUnicodeFormat( const wxString& fmt) -{ - - wxString fmtWX; - - wxChar chLast = _T('\0'); - size_t lastCount = 0; - for ( const wxChar *p = fmt; /* NUL handled inside */; p++ ) - { - if ( *p == chLast ) - { - lastCount++; - continue; - } - - switch ( *p ) - { - // these characters come in groups, start counting them - case _T('d'): - case _T('M'): - case _T('y'): - case _T('g'): - case _T('h'): - case _T('H'): - case _T('m'): - case _T('s'): - chLast = *p; - lastCount = 1; - break; - - default: - // first deal with any special characters we have had - if ( lastCount ) - { - switch ( chLast ) - { - case _T('d'): - switch ( lastCount ) - { - case 1: // d - case 2: // dd - // these two are the same as we - // don't distinguish between 1 and - // 2 digits for days - fmtWX += _T("%d"); - break; - - case 3: // ddd - fmtWX += _T("%a"); - break; - - case 4: // dddd - fmtWX += _T("%A"); - break; - - default: - wxFAIL_MSG( _T("too many 'd's") ); - } - break; - - case _T('M'): - switch ( lastCount ) - { - case 1: // M - case 2: // MM - // as for 'd' and 'dd' above - fmtWX += _T("%m"); - break; - - case 3: - fmtWX += _T("%b"); - break; - - case 4: - fmtWX += _T("%B"); - break; - - default: - wxFAIL_MSG( _T("too many 'M's") ); - } - break; - - case _T('y'): - switch ( lastCount ) - { - case 1: // y - case 2: // yy - fmtWX += _T("%y"); - break; - - case 4: // yyyy - fmtWX += _T("%Y"); - break; - - default: - wxFAIL_MSG( _T("wrong number of 'y's") ); - } - break; - - case _T('H'): - switch ( lastCount ) - { - case 1: // H - case 2: // HH - fmtWX += _T("%H"); - break; - - default: - wxFAIL_MSG( _T("wrong number of 'H's") ); - } - break; - - case _T('h'): - switch ( lastCount ) - { - case 1: // h - case 2: // hh - fmtWX += _T("%h"); - break; - - default: - wxFAIL_MSG( _T("wrong number of 'h's") ); - } - break; - - case _T('m'): - switch ( lastCount ) - { - case 1: // m - case 2: // mm - fmtWX += _T("%M"); - break; - - default: - wxFAIL_MSG( _T("wrong number of 'm's") ); - } - break; - - case _T('s'): - switch ( lastCount ) - { - case 1: // s - case 2: // ss - fmtWX += _T("%S"); - break; - - default: - wxFAIL_MSG( _T("wrong number of 's's") ); - } - break; - - case _T('g'): - // strftime() doesn't have era string, - // ignore this format - wxASSERT_MSG( lastCount <= 2, - _T("too many 'g's") ); - break; - - default: - wxFAIL_MSG( _T("unreachable") ); - } - - chLast = _T('\0'); - lastCount = 0; - } - - // not a special character so must be just a separator, - // treat as is - if ( *p != _T('\0') ) - { - if ( *p == _T('%') ) - { - // this one needs to be escaped - fmtWX += _T('%'); - } - - fmtWX += *p; - } - } - - if ( *p == _T('\0') ) - break; - } - - return fmtWX; -} - -static wxString GetLocaleDateFormat() -{ - wxCFRef currentLocale( CFLocaleCopyCurrent() ); - - wxCFRef dateFormatter( CFDateFormatterCreate - (NULL, currentLocale, kCFDateFormatterShortStyle, kCFDateFormatterNoStyle)); - wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter )); - return TranslateFromUnicodeFormat(cfs.AsString()); -} - -static wxString GetLocaleFullDateFormat() -{ - wxCFRef currentLocale( CFLocaleCopyCurrent() ); - - wxCFRef dateFormatter( CFDateFormatterCreate - (NULL, currentLocale, kCFDateFormatterLongStyle, kCFDateFormatterMediumStyle)); - wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter )); - return TranslateFromUnicodeFormat(cfs.AsString()); -} - - - -#endif // __WXOSX__ - bool wxDateTime::ParseFormat(const wxString& date, const wxString& format, @@ -1329,7 +951,7 @@ wxDateTime::ParseFormat(const wxString& date, { // a white space in the format string matches 0 or more white // spaces in the input - while ( wxIsspace(*input) ) + while ( input != end && wxIsspace(*input) ) { input++; } @@ -1338,7 +960,7 @@ wxDateTime::ParseFormat(const wxString& date, { // any other character (not whitespace, not '%') must be // matched by itself in the input - if ( *input++ != *fmt ) + if ( input == end || *input++ != *fmt ) { // no match return false; @@ -1391,7 +1013,7 @@ wxDateTime::ParseFormat(const wxString& date, { wday = GetWeekDayFromName ( - GetAlphaToken(input, end), + input, end, *fmt == 'a' ? Name_Abbr : Name_Full, DateLang_Local ); @@ -1409,7 +1031,7 @@ wxDateTime::ParseFormat(const wxString& date, { mon = GetMonthFromName ( - GetAlphaToken(input, end), + input, end, *fmt == 'b' ? Name_Abbr : Name_Full, DateLang_Local ); @@ -1424,71 +1046,38 @@ wxDateTime::ParseFormat(const wxString& date, case _T('c'): // locale default date and time representation { -#ifdef HAVE_STRPTIME - struct tm tm; + wxDateTime dt; - // 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 - if ( CallStrptime(date, input, "%c", &tm) ) - { - hour = tm.tm_hour; - min = tm.tm_min; - sec = tm.tm_sec; + const wxString + fmtDateTime = wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT); + if ( !fmtDateTime.empty() ) + dt = ParseFormatAt(input, end, fmtDateTime); - year = 1900 + tm.tm_year; - mon = (Month)tm.tm_mon; - mday = tm.tm_mday; + if ( !dt.IsValid() ) + { + // also try the format which corresponds to ctime() + // output (i.e. the "C" locale default) + dt = ParseFormatAt(input, end, wxS("%a %b %d %H:%M:%S %Y")); } - else // strptime() failed; try generic heuristic code -#endif // HAVE_STRPTIME + + if ( !dt.IsValid() ) { - Tm tm; -#ifdef __WXOSX__ - bool hasValidDate = false; - wxString fmtDate = GetLocaleFullDateFormat(); - if ( !fmtDate.empty() ) - { - const wxDateTime dt = ParseFormatAt - ( - input, - end, - fmtDate - ); - if ( dt.IsValid() ) - { - tm = dt.GetTm(); - hasValidDate = true; - } - } - - if ( !hasValidDate ) -#endif // __WXOSX__ - { - // 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 = dt.GetTm(); - } + // and finally also the two generic date/time formats + dt = ParseFormatAt(input, end, wxS("%x %X"), wxS("%X %x")); + } + if ( !dt.IsValid() ) + return false; - hour = tm.hour; - min = tm.min; - sec = tm.sec; + const Tm tm = dt.GetTm(); - year = tm.year; - mon = tm.mon; - mday = tm.mday; - } + hour = tm.hour; + min = tm.min; + sec = tm.sec; + + year = tm.year; + mon = tm.mon; + mday = tm.mday; haveDay = haveMon = haveYear = haveHour = haveMin = haveSec = true; @@ -1579,25 +1168,25 @@ wxDateTime::ParseFormat(const wxString& date, case _T('p'): // AM or PM string { - wxString am, pm, token = GetAlphaToken(input, end); + wxString am, pm; + GetAmPmStrings(&am, &pm); - // 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; + // we can never match %p in locales which don't use AM/PM + if ( am.empty() || pm.empty() ) + return false; - GetAmPmStrings(&am, &pm); - if (am.empty() && pm.empty()) - return false; // no am/pm strings defined - if ( token.CmpNoCase(pm) == 0 ) + const size_t pos = input - date.begin(); + if ( date.compare(pos, pm.length(), pm) == 0 ) { isPM = true; + input += pm.length(); } - else if ( token.CmpNoCase(am) != 0 ) + else if ( date.compare(pos, am.length(), am) == 0 ) + { + input += am.length(); + } + else // no match { - // no match return false; } } @@ -1612,7 +1201,7 @@ wxDateTime::ParseFormat(const wxString& date, haveHour = haveMin = haveSec = true; - Tm tm = dt.GetTm(); + const Tm tm = dt.GetTm(); hour = tm.hour; min = tm.min; sec = tm.sec; @@ -1629,7 +1218,7 @@ wxDateTime::ParseFormat(const wxString& date, haveHour = haveMin = true; - Tm tm = dt.GetTm(); + const Tm tm = dt.GetTm(); hour = tm.hour; min = tm.min; } @@ -1658,7 +1247,7 @@ wxDateTime::ParseFormat(const wxString& date, haveMin = haveSec = true; - Tm tm = dt.GetTm(); + const Tm tm = dt.GetTm(); hour = tm.hour; min = tm.min; sec = tm.sec; @@ -1678,77 +1267,41 @@ wxDateTime::ParseFormat(const wxString& date, break; case _T('x'): // locale default date representation -#ifdef HAVE_STRPTIME - // 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 { - struct tm tm; - - if ( CallStrptime(date, input, "%x", &tm) ) - { - haveDay = haveMon = haveYear = true; - - year = 1900 + tm.tm_year; - mon = (Month)tm.tm_mon; - mday = tm.tm_mday; + wxString + fmtDate = wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT), + fmtDateAlt = wxLocale::GetInfo(wxLOCALE_LONG_DATE_FMT); - break; - } - } -#endif // HAVE_STRPTIME - - { - wxString fmtDate, - fmtDateAlt; - -#if defined( __WINDOWS__ ) || defined( __WXOSX__ ) - // The above doesn't work for all locales, try to query - // the OS for the right way of formatting the date: - fmtDate = GetLocaleDateFormat(); if ( fmtDate.empty() ) -#endif // __WINDOWS__ { if ( IsWestEuropeanCountry(GetCountry()) || GetCountry() == Russia ) { - fmtDate = _T("%d/%m/%y"); - fmtDateAlt = _T("%m/%d/%y"); + fmtDate = wxS("%d/%m/%Y"); + fmtDateAlt = wxS("%m/%d/%Y"); } else // assume USA { - fmtDate = _T("%m/%d/%y"); - fmtDateAlt = _T("%d/%m/%y"); + fmtDate = wxS("%m/%d/%Y"); + fmtDateAlt = wxS("%d/%m/%Y"); } } - const wxDateTime - dt = ParseFormatAt(input, end, - fmtDate, fmtDateAlt); - Tm tm; - + wxDateTime + dt = ParseFormatAt(input, end, fmtDate, fmtDateAlt); + if ( !dt.IsValid() ) { - wxString fmtDateLong = fmtDate; - wxString fmtDateLongAlt = fmtDateAlt; - + // try with short years too + fmtDate.Replace("%Y","%y"); + fmtDateAlt.Replace("%Y","%y"); + dt = ParseFormatAt(input, end, fmtDate, fmtDateAlt); - if ( !fmtDateLong.empty() ) - { - fmtDateLong.Replace("%y","%Y"); - fmtDateLongAlt.Replace("%y","%Y"); - const wxDateTime dtLong = ParseFormatAt(input, end, - fmtDateLong, fmtDateLongAlt); - if ( !dtLong.IsValid() ) - return false; - - tm = dtLong.GetTm(); - } - else + if ( !dt.IsValid() ) return false; } - else - tm = dt.GetTm(); + + const Tm tm = dt.GetTm(); haveDay = haveMon = @@ -1762,29 +1315,21 @@ wxDateTime::ParseFormat(const wxString& date, break; case _T('X'): // locale default time representation -#ifdef HAVE_STRPTIME { - // use strptime() to do it for us (FIXME !Unicode friendly) - struct tm tm; - if ( !CallStrptime(date, input, "%X", &tm) ) - return false; + wxString fmtTime = wxLocale::GetInfo(wxLOCALE_TIME_FMT), + fmtTimeAlt; - haveHour = haveMin = haveSec = true; + if ( fmtTime.empty() ) + { + // 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 + fmtTime = "%T"; + fmtTimeAlt = "%r"; + } - 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 const wxDateTime - dt = ParseFormatAt(input, end, "%T", "%r"); + dt = ParseFormatAt(input, end, fmtTime, fmtTimeAlt); if ( !dt.IsValid() ) return false; @@ -1792,12 +1337,11 @@ wxDateTime::ParseFormat(const wxString& date, haveMin = haveSec = true; - Tm tm = dt.GetTm(); + const Tm tm = dt.GetTm(); hour = tm.hour; min = tm.min; sec = tm.sec; } -#endif // HAVE_STRPTIME/!HAVE_STRPTIME break; case _T('y'): // year without century (00-99) @@ -1827,7 +1371,9 @@ wxDateTime::ParseFormat(const wxString& date, break; case _T('Z'): // timezone name - wxFAIL_MSG(_T("TODO")); + // FIXME: currently we just ignore everything that looks like a + // time zone here + GetAlphaToken(input, end); break; case _T('%'): // a percent sign @@ -2002,6 +1548,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) // all esoteric constructions ParseDateTime() knows about) const wxString::const_iterator pBegin = date.begin(); + const wxString::const_iterator pEnd = date.end(); wxString::const_iterator p = pBegin; while ( wxIsspace(*p) ) @@ -2019,7 +1566,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) { wxTRANSLATE("tomorrow"), 1 }, }; - const size_t lenRest = date.end() - p; + const size_t lenRest = pEnd - p; for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ ) { const wxString dateStr = wxGetTranslation(literalDates[n].str); @@ -2059,6 +1606,8 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) haveMon = false, // the month? haveYear = false; // the year? + bool monWasNumeric = false; // was month specified as a number? + // and the value of the items we have (init them to get rid of warnings) WeekDay wday = Inv_WeekDay; wxDateTime_t day = 0; @@ -2066,18 +1615,22 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) int year = 0; // tokenize the string - size_t nPosCur = 0; - static const wxStringCharType *dateDelimiters = wxS(".,/-\t\r\n "); - wxStringTokenizer tok(wxString(p, date.end()), dateDelimiters); - while ( tok.HasMoreTokens() ) + while ( p != pEnd ) { - wxString token = tok.GetNextToken(); - if ( !token ) - continue; + // skip white space and date delimiters + while ( wxStrchr(".,/-\t\r\n ", *p) ) + { + ++p; + } + + // modify copy of the iterator as we're not sure if the next token is + // still part of the date at all + wxString::const_iterator pCopy = p; - // is it a number? + // we can have either alphabetic or numeric token, start by testing if + // it's the latter unsigned long val; - if ( token.ToULong(&val) ) + if ( GetNumericToken(10 /* max length */, pCopy, pEnd, &val) ) { // guess what this number is @@ -2141,6 +1694,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) else if ( isMonth ) { haveMon = true; + monWasNumeric = true; mon = (Month)(val - 1); } @@ -2150,7 +1704,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) // be careful not to overwrite the current mon value Month mon2 = GetMonthFromName ( - token, + pCopy, pEnd, Name_Full | Name_Abbr, DateLang_Local | DateLang_English ); @@ -2159,20 +1713,18 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) // it's a month if ( haveMon ) { - // but we already have a month - maybe we guessed wrong? - if ( !haveDay ) - { - // no need to check in month range as always < 12, but - // the days are counted from 1 unlike the months - day = (wxDateTime_t)(mon + 1); - haveDay = true; - } - else - { - // could possible be the year (doesn't the year come - // before the month in the japanese format?) (FIXME) + // but we already have a month - maybe we guessed wrong + // when we had interpreted that numeric value as a month + // and it was the day number instead? + if ( haveDay || !monWasNumeric ) break; - } + + // assume we did and change our mind: reinterpret the month + // value as a day (notice that there is no need to check + // that it is valid as month values are always < 12, but + // the days are counted from 1 unlike the months) + day = (wxDateTime_t)(mon + 1); + haveDay = true; } mon = mon2; @@ -2183,7 +1735,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) { WeekDay wday2 = GetWeekDayFromName ( - token, + pCopy, pEnd, Name_Full | Name_Abbr, DateLang_Local | DateLang_English ); @@ -2191,9 +1743,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) { // a week day if ( haveWDay ) - { break; - } wday = wday2; @@ -2231,8 +1781,11 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) size_t n; for ( n = 0; n < WXSIZEOF(ordinals); n++ ) { - if ( token.CmpNoCase(ordinals[n]) == 0 ) + const wxString ord = wxGetTranslation(ordinals[n]); + const size_t len = ord.length(); + if ( date.compare(p - pBegin, len, ord) == 0 ) { + p += len; break; } } @@ -2259,7 +1812,8 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) } } - nPosCur = tok.GetPosition(); + // advance iterator past a successfully parsed token + p = pCopy; } // either no more tokens or the scan was stopped by something we couldn't @@ -2335,15 +1889,6 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) SetToWeekDayInSameWeek(wday); } - // return the pointer to the first unparsed char - p += nPosCur; - if ( nPosCur && wxStrchr(dateDelimiters, *(p - 1)) ) - { - // if we couldn't parse the token after the delimiter, put back the - // delimiter as well - p--; - } - *end = p; return true;