X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/694cb6c5819c59bc104619d3dc3b6c0f18e80ea9..439c7eae9b77cc6c0414ccae52e688d626c86851:/src/common/datetimefmt.cpp diff --git a/src/common/datetimefmt.cpp b/src/common/datetimefmt.cpp index 91d6452981..f9404d8642 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,6 +91,42 @@ static const int MIN_PER_HOUR = 60; namespace { +// all the functions below taking non-const wxString::const_iterator p advance +// it until the end of the match + +// 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++; + } + + return s; +} + enum { DateLang_English = 1, @@ -105,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) ) { @@ -140,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; } @@ -155,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) ) { @@ -195,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, @@ -266,17 +308,23 @@ 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, - _T("NULL format in wxDateTime::Format") ); - + wxCHECK_MSG( !formatp.empty(), wxEmptyString, + wxT("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")) ) + if ( (time != (time_t)-1) && !wxStrstr(format, wxT("%l")) ) { // use strftime() struct tm tmstruct; @@ -287,7 +335,7 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const tm = wxLocaltime_r(&time, &tmstruct); // should never happen - wxCHECK_MSG( tm, wxEmptyString, _T("wxLocaltime_r() failed") ); + wxCHECK_MSG( tm, wxEmptyString, wxT("wxLocaltime_r() failed") ); } else { @@ -303,7 +351,7 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const tm = wxGmtime_r(&time, &tmstruct); // should never happen - wxCHECK_MSG( tm, wxEmptyString, _T("wxGmtime_r() failed") ); + wxCHECK_MSG( tm, wxEmptyString, wxT("wxGmtime_r() failed") ); } else { @@ -317,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 @@ -339,7 +387,7 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const wxString tmp, res, fmt; for ( wxString::const_iterator p = format.begin(); p != format.end(); ++p ) { - if ( *p != _T('%') ) + if ( *p != wxT('%') ) { // copy as is res += *p; @@ -350,17 +398,17 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const // set the default format switch ( (*++p).GetValue() ) { - case _T('Y'): // year has 4 digits - fmt = _T("%04d"); + case wxT('Y'): // year has 4 digits + fmt = wxT("%04d"); break; - case _T('j'): // day of year has 3 digits - case _T('l'): // milliseconds have 3 digits - fmt = _T("%03d"); + case wxT('j'): // day of year has 3 digits + case wxT('l'): // milliseconds have 3 digits + fmt = wxT("%03d"); break; - case _T('w'): // week day as number has only one - fmt = _T("%d"); + case wxT('w'): // week day as number has only one + fmt = wxT("%d"); break; default: @@ -368,7 +416,7 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const // the format is "%02d" (for all the rest) or we have the // field width preceding the format in which case it will // override the default format anyhow - fmt = _T("%02d"); + fmt = wxT("%02d"); } bool restart = true; @@ -379,22 +427,22 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const // start of the format specification switch ( (*p).GetValue() ) { - case _T('a'): // a weekday name - case _T('A'): + case wxT('a'): // a weekday name + case wxT('A'): // second parameter should be true for abbreviated names res += GetWeekDayName(tm.GetWeekDay(), - *p == _T('a') ? Name_Abbr : Name_Full); + *p == wxT('a') ? Name_Abbr : Name_Full); break; - case _T('b'): // a month name - case _T('B'): + case wxT('b'): // a month name + case wxT('B'): res += GetMonthName(tm.mon, - *p == _T('b') ? Name_Abbr : Name_Full); + *p == wxT('b') ? Name_Abbr : Name_Full); break; - case _T('c'): // locale default date and time representation - case _T('x'): // locale default date representation -#ifdef HAVE_STRFTIME + case wxT('c'): // locale default date and time representation + case wxT('x'): // locale default date representation +#ifdef wxHAS_STRFTIME // // the problem: there is no way to know what do these format // specifications correspond to for the current locale. @@ -465,7 +513,7 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const year -= 28; wxASSERT_MSG( year >= 1970 && year < 2000, - _T("logic error in wxDateTime::Format") ); + wxT("logic error in wxDateTime::Format") ); // use strftime() to format the same date but in supported @@ -486,8 +534,8 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const tmAdjusted.tm_mon = tm.mon; tmAdjusted.tm_year = year - 1900; tmAdjusted.tm_isdst = 0; // no DST, already adjusted - wxString str = CallStrftime(*p == _T('c') ? _T("%c") - : _T("%x"), + wxString str = CallStrftime(*p == wxT('c') ? wxT("%c") + : wxT("%x"), &tmAdjusted); // now replace the replacement year with the real year: @@ -510,22 +558,22 @@ 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) + case wxT('d'): // day of a month (01-31) res += wxString::Format(fmt, tm.mday); break; - case _T('H'): // hour in 24h format (00-23) + case wxT('H'): // hour in 24h format (00-23) res += wxString::Format(fmt, tm.hour); break; - case _T('I'): // hour in 12h format (01-12) + case wxT('I'): // hour in 12h format (01-12) { // 24h -> 12h, 0h -> 12h too int hour12 = tm.hour > 12 ? tm.hour - 12 @@ -534,76 +582,76 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const } break; - case _T('j'): // day of the year + case wxT('j'): // day of the year res += wxString::Format(fmt, GetDayOfYear(tz)); break; - case _T('l'): // milliseconds (NOT STANDARD) + case wxT('l'): // milliseconds (NOT STANDARD) res += wxString::Format(fmt, GetMillisecond(tz)); break; - case _T('m'): // month as a number (01-12) + case wxT('m'): // month as a number (01-12) res += wxString::Format(fmt, tm.mon + 1); break; - case _T('M'): // minute as a decimal number (00-59) + case wxT('M'): // minute as a decimal number (00-59) res += wxString::Format(fmt, tm.min); break; - case _T('p'): // AM or PM string -#ifdef HAVE_STRFTIME - res += CallStrftime(_T("%p"), &tmTimeOnly); -#else // !HAVE_STRFTIME + case wxT('p'): // AM or PM string +#ifdef wxHAS_STRFTIME + res += CallStrftime(wxT("%p"), &tmTimeOnly); +#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) + case wxT('S'): // second as a decimal number (00-61) res += wxString::Format(fmt, tm.sec); break; - case _T('U'): // week number in the year (Sunday 1st week day) + case wxT('U'): // week number in the year (Sunday 1st week day) res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz)); break; - case _T('W'): // week number in the year (Monday 1st week day) + case wxT('W'): // week number in the year (Monday 1st week day) res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz)); break; - case _T('w'): // weekday as a number (0-6), Sunday = 0 + case wxT('w'): // weekday as a number (0-6), Sunday = 0 res += wxString::Format(fmt, tm.GetWeekDay()); break; - // case _T('x'): -- handled with "%c" + // case wxT('x'): -- handled with "%c" - case _T('X'): // locale default time representation + case wxT('X'): // locale default time representation // just use strftime() to format the time for us -#ifdef HAVE_STRFTIME - res += CallStrftime(_T("%X"), &tmTimeOnly); -#else // !HAVE_STRFTIME +#ifdef wxHAS_STRFTIME + res += CallStrftime(wxT("%X"), &tmTimeOnly); +#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) + case wxT('y'): // year without century (00-99) res += wxString::Format(fmt, tm.year % 100); break; - case _T('Y'): // year with century + case wxT('Y'): // year with century res += wxString::Format(fmt, tm.year); break; - case _T('Z'): // timezone name -#ifdef HAVE_STRFTIME - res += CallStrftime(_T("%Z"), &tmTimeOnly); + case wxT('Z'): // timezone name +#ifdef wxHAS_STRFTIME + res += CallStrftime(wxT("%Z"), &tmTimeOnly); #endif break; default: // is it the format width? fmt.Empty(); - while ( *p == _T('-') || *p == _T('+') || - *p == _T(' ') || wxIsdigit(*p) ) + while ( *p == wxT('-') || *p == wxT('+') || + *p == wxT(' ') || wxIsdigit(*p) ) { fmt += *p; } @@ -611,8 +659,8 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const if ( !fmt.empty() ) { // we've only got the flags and width so far in fmt - fmt.Prepend(_T('%')); - fmt.Append(_T('d')); + fmt.Prepend(wxT('%')); + fmt.Append(wxT('d')); restart = true; @@ -620,19 +668,19 @@ wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const } // no, it wasn't the width - wxFAIL_MSG(_T("unknown format specificator")); + wxFAIL_MSG(wxT("unknown format specificator")); // fall through and just copy it nevertheless - case _T('%'): // a percent sign + case wxT('%'): // a percent sign res += *p; break; case 0: // the end of string - wxFAIL_MSG(_T("missing format at the end of string")); + wxFAIL_MSG(wxT("missing format at the end of string")); // just put the '%' which was the last char in format - res += _T('%'); + res += wxT('%'); break; } } @@ -650,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; @@ -682,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; @@ -770,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 @@ -811,7 +852,7 @@ wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end) +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0 }; - if ( *p < _T('A') || *p > _T('Z') || *p == _T('J') ) + if ( *p < wxT('A') || *p > wxT('Z') || *p == wxT('J') ) return false; offset = offsets[*p++ - 'A']; @@ -820,27 +861,27 @@ wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end) { // abbreviation const wxString tz(p, date.end()); - if ( tz == _T("UT") || tz == _T("UTC") || tz == _T("GMT") ) + if ( tz == wxT("UT") || tz == wxT("UTC") || tz == wxT("GMT") ) offset = 0; - else if ( tz == _T("AST") ) + else if ( tz == wxT("AST") ) offset = AST - GMT0; - else if ( tz == _T("ADT") ) + else if ( tz == wxT("ADT") ) offset = ADT - GMT0; - else if ( tz == _T("EST") ) + else if ( tz == wxT("EST") ) offset = EST - GMT0; - else if ( tz == _T("EDT") ) + else if ( tz == wxT("EDT") ) offset = EDT - GMT0; - else if ( tz == _T("CST") ) + else if ( tz == wxT("CST") ) offset = CST - GMT0; - else if ( tz == _T("CDT") ) + else if ( tz == wxT("CDT") ) offset = CDT - GMT0; - else if ( tz == _T("MST") ) + else if ( tz == wxT("MST") ) offset = MST - GMT0; - else if ( tz == _T("MDT") ) + else if ( tz == wxT("MDT") ) offset = MDT - GMT0; - else if ( tz == _T("PST") ) + else if ( tz == wxT("PST") ) offset = PST - GMT0; - else if ( tz == _T("PDT") ) + else if ( tz == wxT("PDT") ) offset = PDT - GMT0; else return false; @@ -904,7 +945,7 @@ wxDateTime::ParseFormat(const wxString& date, const wxString::const_iterator end = date.end(); for ( wxString::const_iterator fmt = format.begin(); fmt != format.end(); ++fmt ) { - if ( *fmt != _T('%') ) + if ( *fmt != wxT('%') ) { if ( wxIsspace(*fmt) ) { @@ -945,16 +986,16 @@ wxDateTime::ParseFormat(const wxString& date, { switch ( (*fmt).GetValue() ) { - case _T('Y'): // year has 4 digits + case wxT('Y'): // year has 4 digits width = 4; break; - case _T('j'): // day of year has 3 digits - case _T('l'): // milliseconds have 3 digits + case wxT('j'): // day of year has 3 digits + case wxT('l'): // milliseconds have 3 digits width = 3; break; - case _T('w'): // week day as number has only one + case wxT('w'): // week day as number has only one width = 1; break; @@ -967,12 +1008,12 @@ wxDateTime::ParseFormat(const wxString& date, // then the format itself switch ( (*fmt).GetValue() ) { - case _T('a'): // a weekday name - case _T('A'): + case wxT('a'): // a weekday name + case wxT('A'): { wday = GetWeekDayFromName ( - GetAlphaToken(input, end), + input, end, *fmt == 'a' ? Name_Abbr : Name_Full, DateLang_Local ); @@ -985,12 +1026,12 @@ wxDateTime::ParseFormat(const wxString& date, haveWDay = true; break; - case _T('b'): // a month name - case _T('B'): + case wxT('b'): // a month name + case wxT('B'): { mon = GetMonthFromName ( - GetAlphaToken(input, end), + input, end, *fmt == 'b' ? Name_Abbr : Name_Full, DateLang_Local ); @@ -1003,15 +1044,16 @@ wxDateTime::ParseFormat(const wxString& date, haveMon = true; break; - case _T('c'): // locale default date and time representation + case wxT('c'): // locale default date and time representation { wxDateTime dt; +#if wxUSE_INTL const wxString fmtDateTime = wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT); if ( !fmtDateTime.empty() ) dt = ParseFormatAt(input, end, fmtDateTime); - +#endif // wxUSE_INTL if ( !dt.IsValid() ) { // also try the format which corresponds to ctime() @@ -1043,7 +1085,8 @@ wxDateTime::ParseFormat(const wxString& date, } break; - case _T('d'): // day of a month (01-31) + case wxT('d'): // day of a month (01-31) + case 'e': // day of a month (1-31) (GNU extension) if ( !GetNumericToken(width, input, end, &num) || (num > 31) || (num < 1) ) { @@ -1057,7 +1100,7 @@ wxDateTime::ParseFormat(const wxString& date, mday = (wxDateTime_t)num; break; - case _T('H'): // hour in 24h format (00-23) + case wxT('H'): // hour in 24h format (00-23) if ( !GetNumericToken(width, input, end, &num) || (num > 23) ) { // no match @@ -1068,7 +1111,7 @@ wxDateTime::ParseFormat(const wxString& date, hour = (wxDateTime_t)num; break; - case _T('I'): // hour in 12h format (01-12) + case wxT('I'): // hour in 12h format (01-12) if ( !GetNumericToken(width, input, end, &num) || !num || (num > 12) ) { @@ -1081,7 +1124,7 @@ wxDateTime::ParseFormat(const wxString& date, hour = (wxDateTime_t)(num % 12); // 12 should be 0 break; - case _T('j'): // day of the year + case wxT('j'): // day of the year if ( !GetNumericToken(width, input, end, &num) || !num || (num > 366) ) { @@ -1093,7 +1136,7 @@ wxDateTime::ParseFormat(const wxString& date, yday = (wxDateTime_t)num; break; - case _T('l'): // milliseconds (0-999) + case wxT('l'): // milliseconds (0-999) if ( !GetNumericToken(width, input, end, &num) ) return false; @@ -1101,7 +1144,7 @@ wxDateTime::ParseFormat(const wxString& date, msec = (wxDateTime_t)num; break; - case _T('m'): // month as a number (01-12) + case wxT('m'): // month as a number (01-12) if ( !GetNumericToken(width, input, end, &num) || !num || (num > 12) ) { @@ -1113,7 +1156,7 @@ wxDateTime::ParseFormat(const wxString& date, mon = (Month)(num - 1); break; - case _T('M'): // minute as a decimal number (00-59) + case wxT('M'): // minute as a decimal number (00-59) if ( !GetNumericToken(width, input, end, &num) || (num > 59) ) { @@ -1125,33 +1168,33 @@ wxDateTime::ParseFormat(const wxString& date, min = (wxDateTime_t)num; break; - case _T('p'): // AM or PM string + case wxT('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; } } break; - case _T('r'): // time as %I:%M:%S %p + case wxT('r'): // time as %I:%M:%S %p { wxDateTime dt; if ( !dt.ParseFormat(wxString(input, end), @@ -1167,7 +1210,7 @@ wxDateTime::ParseFormat(const wxString& date, } break; - case _T('R'): // time as %H:%M + case wxT('R'): // time as %H:%M { const wxDateTime dt = ParseFormatAt(input, end, wxS("%H:%M")); @@ -1183,7 +1226,7 @@ wxDateTime::ParseFormat(const wxString& date, } break; - case _T('S'): // second as a decimal number (00-61) + case wxT('S'): // second as a decimal number (00-61) if ( !GetNumericToken(width, input, end, &num) || (num > 61) ) { @@ -1195,7 +1238,7 @@ wxDateTime::ParseFormat(const wxString& date, sec = (wxDateTime_t)num; break; - case _T('T'): // time as %H:%M:%S + case wxT('T'): // time as %H:%M:%S { const wxDateTime dt = ParseFormatAt(input, end, wxS("%H:%M:%S")); @@ -1213,7 +1256,7 @@ wxDateTime::ParseFormat(const wxString& date, } break; - case _T('w'): // weekday as a number (0-6), Sunday = 0 + case wxT('w'): // weekday as a number (0-6), Sunday = 0 if ( !GetNumericToken(width, input, end, &num) || (wday > 6) ) { @@ -1225,12 +1268,15 @@ wxDateTime::ParseFormat(const wxString& date, wday = (WeekDay)num; break; - case _T('x'): // locale default date representation + case wxT('x'): // locale default date representation { +#if wxUSE_INTL wxString fmtDate = wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT), fmtDateAlt = wxLocale::GetInfo(wxLOCALE_LONG_DATE_FMT); - +#else // !wxUSE_INTL + wxString fmtDate, fmtDateAlt; +#endif // wxUSE_INTL/!wxUSE_INTL if ( fmtDate.empty() ) { if ( IsWestEuropeanCountry(GetCountry()) || @@ -1273,11 +1319,14 @@ wxDateTime::ParseFormat(const wxString& date, break; - case _T('X'): // locale default time representation + case wxT('X'): // locale default time representation { +#if wxUSE_INTL wxString fmtTime = wxLocale::GetInfo(wxLOCALE_TIME_FMT), fmtTimeAlt; - +#else // !wxUSE_INTL + wxString fmtTime, fmtTimeAlt; +#endif // wxUSE_INTL/!wxUSE_INTL if ( fmtTime.empty() ) { // try to parse what follows as "%H:%M:%S" and, if this @@ -1303,7 +1352,7 @@ wxDateTime::ParseFormat(const wxString& date, } break; - case _T('y'): // year without century (00-99) + case wxT('y'): // year without century (00-99) if ( !GetNumericToken(width, input, end, &num) || (num > 99) ) { @@ -1318,7 +1367,7 @@ wxDateTime::ParseFormat(const wxString& date, year = (num > 30 ? 1900 : 2000) + (wxDateTime_t)num; break; - case _T('Y'): // year with century + case wxT('Y'): // year with century if ( !GetNumericToken(width, input, end, &num) ) { // no match @@ -1329,14 +1378,14 @@ wxDateTime::ParseFormat(const wxString& date, year = (wxDateTime_t)num; break; - case _T('Z'): // timezone name + case wxT('Z'): // timezone name // FIXME: currently we just ignore everything that looks like a // time zone here GetAlphaToken(input, end); break; - case _T('%'): // a percent sign - if ( *input++ != _T('%') ) + case wxT('%'): // a percent sign + if ( *input++ != wxT('%') ) { // no match return false; @@ -1344,7 +1393,7 @@ wxDateTime::ParseFormat(const wxString& date, break; case 0: // the end of string - wxFAIL_MSG(_T("unexpected format end")); + wxFAIL_MSG(wxT("unexpected format end")); // fall through @@ -1507,9 +1556,10 @@ 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) ) + while ( p != pEnd && wxIsspace(*p) ) p++; // some special cases @@ -1524,7 +1574,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); @@ -1564,6 +1614,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; @@ -1571,18 +1623,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; + } - // is it a number? + // 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; + + // 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 @@ -1646,6 +1702,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) else if ( isMonth ) { haveMon = true; + monWasNumeric = true; mon = (Month)(val - 1); } @@ -1655,7 +1712,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 ); @@ -1664,20 +1721,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; @@ -1688,7 +1743,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) { WeekDay wday2 = GetWeekDayFromName ( - token, + pCopy, pEnd, Name_Full | Name_Abbr, DateLang_Local | DateLang_English ); @@ -1696,9 +1751,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) { // a week day if ( haveWDay ) - { break; - } wday = wday2; @@ -1707,7 +1760,7 @@ wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end) else // not a valid weekday name { // try the ordinals - static const char *ordinals[] = + static const char *const ordinals[] = { wxTRANSLATE("first"), wxTRANSLATE("second"), @@ -1736,8 +1789,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; } } @@ -1764,7 +1820,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 @@ -1840,15 +1897,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; @@ -1874,14 +1922,13 @@ wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end) for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ ) { const wxString timeString = wxGetTranslation(stdTimes[n].name); - const wxString::const_iterator p = time.begin() + timeString.length(); - if ( timeString.CmpNoCase(wxString(time.begin(), p)) == 0 ) + if ( timeString.CmpNoCase(wxString(time, timeString.length())) == 0 ) { // casts required by DigitalMars Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0)); if ( end ) - *end = p; + *end = time.begin() + timeString.length(); return true; } @@ -1889,12 +1936,12 @@ wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end) // try all time formats we may think about in the order from longest to // shortest - static const char *timeFormats[] = + static const char *const timeFormats[] = { "%I:%M:%S %p", // 12hour with AM/PM "%H:%M:%S", // could be the same or 24 hour one so try it too "%I:%M %p", // 12hour with AM/PM but without seconds - "%H:%M:%S", // and a possibly 24 hour version without seconds + "%H:%M", // and a possibly 24 hour version without seconds "%X", // possibly something from above or maybe something // completely different -- try it last @@ -1978,7 +2025,7 @@ wxString wxTimeSpan::Format(const wxString& format) const } wxCHECK_MSG( !format.empty(), wxEmptyString, - _T("NULL format in wxTimeSpan::Format") ); + wxT("NULL format in wxTimeSpan::Format") ); wxString str; str.Alloc(format.length()); @@ -2003,10 +2050,10 @@ wxString wxTimeSpan::Format(const wxString& format) const { wxChar ch = *pch; - if ( ch == _T('%') ) + if ( ch == wxT('%') ) { // the start of the format specification of the printf() below - wxString fmtPrefix(_T('%')); + wxString fmtPrefix(wxT('%')); // the number long n; @@ -2018,16 +2065,16 @@ wxString wxTimeSpan::Format(const wxString& format) const switch ( ch ) { default: - wxFAIL_MSG( _T("invalid format character") ); + wxFAIL_MSG( wxT("invalid format character") ); // fall through - case _T('%'): + case wxT('%'): str += ch; // skip the part below switch continue; - case _T('D'): + case wxT('D'): n = GetDays(); if ( partBiggest < Part_Day ) { @@ -2039,12 +2086,12 @@ wxString wxTimeSpan::Format(const wxString& format) const } break; - case _T('E'): + case wxT('E'): partBiggest = Part_Week; n = GetWeeks(); break; - case _T('H'): + case wxT('H'): n = GetHours(); if ( partBiggest < Part_Hour ) { @@ -2058,7 +2105,7 @@ wxString wxTimeSpan::Format(const wxString& format) const digits = 2; break; - case _T('l'): + case wxT('l'): n = GetMilliseconds().ToLong(); if ( partBiggest < Part_MSec ) { @@ -2070,7 +2117,7 @@ wxString wxTimeSpan::Format(const wxString& format) const digits = 3; break; - case _T('M'): + case wxT('M'): n = GetMinutes(); if ( partBiggest < Part_Min ) { @@ -2084,7 +2131,7 @@ wxString wxTimeSpan::Format(const wxString& format) const digits = 2; break; - case _T('S'): + case wxT('S'): n = GetSeconds().ToLong(); if ( partBiggest < Part_Sec ) { @@ -2101,10 +2148,10 @@ wxString wxTimeSpan::Format(const wxString& format) const if ( digits ) { - fmtPrefix << _T("0") << digits; + fmtPrefix << wxT("0") << digits; } - str += wxString::Format(fmtPrefix + _T("ld"), n); + str += wxString::Format(fmtPrefix + wxT("ld"), n); } else {