X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/9ffaedfcd05fbaf839bc16b6717660a475386ffe..82c126e50158efee5a1f46e1200be380c5199f8d:/src/common/datetime.cpp diff --git a/src/common/datetime.cpp b/src/common/datetime.cpp index 5dcdc4e208..d1ce06ab39 100644 --- a/src/common/datetime.cpp +++ b/src/common/datetime.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Name: wx/datetime.h +// Name: src/common/datetime.cpp // Purpose: implementation of time/date related classes // Author: Vadim Zeitlin // Modified by: @@ -16,10 +16,6 @@ // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// -// TODO: for $DEITY sake, someone please fix the #ifdef __WXWINCE__ everywhere, -// the proper way to do it is to implement (subset of) wxStrftime() for -// CE instead of this horror!! - /* * Implementation notes: * @@ -56,10 +52,6 @@ // headers // ---------------------------------------------------------------------------- -#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) - #pragma implementation "datetime.h" -#endif - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" @@ -70,19 +62,23 @@ #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME #ifndef WX_PRECOMP + #ifdef __WXMSW__ + #include "wx/msw/wrapwin.h" + #endif #include "wx/string.h" #include "wx/log.h" + #include "wx/intl.h" + #include "wx/stopwatch.h" // for wxGetLocalTimeMillis() + #include "wx/module.h" + #include "wx/crt.h" #endif // WX_PRECOMP -#include "wx/intl.h" #include "wx/thread.h" #include "wx/tokenzr.h" -#include "wx/module.h" #include #ifdef __WINDOWS__ - #include "wx/msw/wrapwin.h" #include #ifndef __WXWINCE__ #include @@ -90,7 +86,6 @@ #endif #include "wx/datetime.h" -#include "wx/stopwatch.h" // for wxGetLocalTimeMillis() const long wxDateTime::TIME_T_FACTOR = 1000l; @@ -143,6 +138,13 @@ wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter , wxFromStringCon #include #endif +// define a special symbol for VC8 instead of writing tests for 1400 repeatedly +#ifdef __VISUALC__ + #if __VISUALC__ >= 1400 + #define __VISUALC8__ + #endif +#endif + #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM) #if defined(__WXPALMOS__) #define WX_GMTOFF_IN_TM @@ -168,23 +170,127 @@ wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter , wxFromStringCon #define WX_TIMEZONE wxGetTimeZone() #elif defined(__DARWIN__) #define WX_GMTOFF_IN_TM + #elif defined(__WXWINCE__) && defined(__VISUALC8__) + // _timezone is not present in dynamic run-time library + #if 0 + // Solution (1): use the function equivalent of _timezone + static long wxGetTimeZone() + { + static long s_Timezone = MAXLONG; // invalid timezone + if (s_Timezone == MAXLONG) + { + int t; + _get_timezone(& t); + s_Timezone = (long) t; + } + return s_Timezone; + } + #define WX_TIMEZONE wxGetTimeZone() + #elif 1 + // Solution (2): using GetTimeZoneInformation + static long wxGetTimeZone() + { + static long timezone = MAXLONG; // invalid timezone + if (timezone == MAXLONG) + { + TIME_ZONE_INFORMATION tzi; + ::GetTimeZoneInformation(&tzi); + timezone = tzi.Bias; + } + return timezone; + } + #define WX_TIMEZONE wxGetTimeZone() + #else + // Old method using _timezone: this symbol doesn't exist in the dynamic run-time library (i.e. using /MD) + #define WX_TIMEZONE _timezone + #endif #else // unknown platform - try timezone #define WX_TIMEZONE timezone #endif #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM +// everyone has strftime except Win CE unless VC8 is used +#if !defined(__WXWINCE__) || defined(__VISUALC8__) + #define HAVE_STRFTIME +#endif + +// NB: VC8 safe time functions could/should be used for wxMSW as well probably +#if defined(__WXWINCE__) && defined(__VISUALC8__) + +struct tm *wxLocaltime_r(const time_t *t, struct tm* tm) +{ + __time64_t t64 = *t; + return _localtime64_s(tm, &t64) == 0 ? tm : NULL; +} + +struct tm *wxGmtime_r(const time_t* t, struct tm* tm) +{ + __time64_t t64 = *t; + return _gmtime64_s(tm, &t64) == 0 ? tm : NULL; +} + +#else // !wxWinCE with VC8 + +#if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__) +static wxMutex timeLock; +#endif + +#ifndef HAVE_LOCALTIME_R +struct tm *wxLocaltime_r(const time_t* ticks, struct tm* temp) +{ +#if wxUSE_THREADS && !defined(__WINDOWS__) + // No need to waste time with a mutex on windows since it's using + // thread local storage for localtime anyway. + wxMutexLocker locker(timeLock); +#endif + + // Borland CRT crashes when passed 0 ticks for some reason, see SF bug 1704438 +#ifdef __BORLANDC__ + if ( !*ticks ) + return NULL; +#endif + + const tm * const t = localtime(ticks); + if ( !t ) + return NULL; + + memcpy(temp, t, sizeof(struct tm)); + return temp; +} +#endif // !HAVE_LOCALTIME_R + +#ifndef HAVE_GMTIME_R +struct tm *wxGmtime_r(const time_t* ticks, struct tm* temp) +{ +#if wxUSE_THREADS && !defined(__WINDOWS__) + // No need to waste time with a mutex on windows since it's + // using thread local storage for gmtime anyway. + wxMutexLocker locker(timeLock); +#endif + +#ifdef __BORLANDC__ + if ( !*ticks ) + return NULL; +#endif + + const tm * const t = gmtime(ticks); + if ( !t ) + return NULL; + + memcpy(temp, gmtime(ticks), sizeof(struct tm)); + return temp; +} +#endif // !HAVE_GMTIME_R + +#endif // wxWinCE with VC8/other platforms + // ---------------------------------------------------------------------------- // macros // ---------------------------------------------------------------------------- // debugging helper: just a convenient replacement of wxCHECK() -#define wxDATETIME_CHECK(expr, msg) \ - if ( !(expr) ) \ - { \ - wxFAIL_MSG(msg); \ - *this = wxInvalidDateTime; \ - return *this; \ - } +#define wxDATETIME_CHECK(expr, msg) \ + wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg) // ---------------------------------------------------------------------------- // private classes @@ -306,32 +412,32 @@ wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month) // (in seconds) static int GetTimeZone() { -#ifdef WX_GMTOFF_IN_TM // set to true when the timezone is set static bool s_timezoneSet = false; static long gmtoffset = LONG_MAX; // invalid timezone - // ensure that the timezone variable is set by calling localtime + // ensure that the timezone variable is set by calling wxLocaltime_r if ( !s_timezoneSet ) { - // just call localtime() instead of figuring out whether this system - // supports tzset(), _tzset() or something else + // just call wxLocaltime_r() instead of figuring out whether this + // system supports tzset(), _tzset() or something else time_t t = 0; - struct tm *tm; + struct tm tm; - tm = localtime(&t); + wxLocaltime_r(&t, &tm); s_timezoneSet = true; +#ifdef WX_GMTOFF_IN_TM // note that GMT offset is the opposite of time zone and so to return // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM // cases we have to negate it - gmtoffset = -tm->tm_gmtoff; + gmtoffset = -tm.tm_gmtoff; +#else // !WX_GMTOFF_IN_TM + gmtoffset = WX_TIMEZONE; +#endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM } return (int)gmtoffset; -#else // !WX_GMTOFF_IN_TM - return (int)WX_TIMEZONE; -#endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM } // return the integral part of the JDN for the midnight of the given date (to @@ -374,9 +480,10 @@ static long GetTruncatedJDN(wxDateTime::wxDateTime_t day, - JDN_OFFSET; } -#ifndef __WXWINCE__ +#ifdef HAVE_STRFTIME + // this function is a wrapper around strftime(3) adding error checking -static wxString CallStrftime(const wxChar *format, const tm* tm) +static wxString CallStrftime(const wxString& format, const tm* tm) { wxChar buf[4096]; // Create temp wxString here to work around mingw/cygwin bug 1046059 @@ -392,13 +499,14 @@ static wxString CallStrftime(const wxChar *format, const tm* tm) s = buf; return s; } -#endif + +#endif // HAVE_STRFTIME #ifdef HAVE_STRPTIME -// glibc2 doesn't define this in the headers unless _XOPEN_SOURCE is defined -// which, unfortunately, wreaks havoc elsewhere -#if defined(__GLIBC__) && (__GLIBC__ == 2) +#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 @@ -434,10 +542,11 @@ static void ReplaceDefaultYearMonthWithCurrent(int *year, wxDateTime::Month *month) { struct tm *tmNow = NULL; + struct tm tmstruct; if ( *year == wxDateTime::Inv_Year ) { - tmNow = wxDateTime::GetTmNow(); + tmNow = wxDateTime::GetTmNow(&tmstruct); *year = 1900 + tmNow->tm_year; } @@ -445,7 +554,7 @@ static void ReplaceDefaultYearMonthWithCurrent(int *year, if ( *month == wxDateTime::Inv_Month ) { if ( !tmNow ) - tmNow = wxDateTime::GetTmNow(); + tmNow = wxDateTime::GetTmNow(&tmstruct); *month = (wxDateTime::Month)tmNow->tm_mon; } @@ -526,6 +635,13 @@ 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 wxChar*& p, unsigned long *number) { @@ -598,7 +714,7 @@ void wxDateTime::Tm::ComputeWeekDay() // compute the week day from day/month/year: we use the dumbest algorithm // possible: just compute our JDN and then use the (simple to derive) // formula: weekday = (JDN + 1.5) % 7 - wday = (wxDateTime::wxDateTime_t)((wxDateTime::WeekDay)(GetTruncatedJDN(mday, mon, year) + 2) % 7); + wday = (wxDateTime::wxDateTime_t)((GetTruncatedJDN(mday, mon, year) + 2) % 7); } void wxDateTime::Tm::AddMonths(int monDiff) @@ -690,6 +806,7 @@ wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz) case wxDateTime::GMT10: case wxDateTime::GMT11: case wxDateTime::GMT12: + case wxDateTime::GMT13: m_offset = 3600*(tz - wxDateTime::GMT0); break; @@ -840,7 +957,7 @@ wxString wxDateTime::GetMonthName(wxDateTime::Month month, wxDateTime::NameFlags flags) { wxCHECK_MSG( month != Inv_Month, wxEmptyString, _T("invalid month") ); -#ifndef __WXWINCE__ +#ifdef HAVE_STRFTIME // notice that we must set all the fields to avoid confusing libc (GNU one // gets confused to a crash if we don't do this) tm tm; @@ -848,7 +965,7 @@ wxString wxDateTime::GetMonthName(wxDateTime::Month month, tm.tm_mon = month; return CallStrftime(flags == Name_Abbr ? _T("%b") : _T("%B"), &tm); -#else +#else // !HAVE_STRFTIME wxString ret; switch(month) { @@ -890,7 +1007,7 @@ wxString wxDateTime::GetMonthName(wxDateTime::Month month, break; } return ret; -#endif +#endif // HAVE_STRFTIME/!HAVE_STRFTIME } /* static */ @@ -898,7 +1015,7 @@ wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday, wxDateTime::NameFlags flags) { wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, _T("invalid weekday") ); -#ifndef __WXWINCE__ +#ifdef HAVE_STRFTIME // take some arbitrary Sunday (but notice that the day should be such that // after adding wday to it below we still have a valid date, e.g. don't // take 28 here!) @@ -916,7 +1033,7 @@ wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday, // ... and call strftime() return CallStrftime(flags == Name_Abbr ? _T("%a") : _T("%A"), &tm); -#else +#else // !HAVE_STRFTIME wxString ret; switch(wday) { @@ -943,8 +1060,7 @@ wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday, break; } return ret; - -#endif +#endif // HAVE_STRFTIME/!HAVE_STRFTIME } /* static */ @@ -992,7 +1108,8 @@ wxDateTime::Country wxDateTime::GetCountry() { // try to guess from the time zone name time_t t = time(NULL); - struct tm *tm = localtime(&t); + struct tm tmstruct; + struct tm *tm = wxLocaltime_r(&t, &tmstruct); wxString tz = CallStrftime(_T("%Z"), tm); if ( tz == _T("WET") || tz == _T("WEST") ) @@ -1021,9 +1138,9 @@ wxDateTime::Country wxDateTime::GetCountry() ms_country = USA; } } -#else +#else // __WXWINCE__ ms_country = USA; -#endif +#endif // !__WXWINCE__/__WXWINCE__ return ms_country; } @@ -1114,9 +1231,6 @@ wxDateTime wxDateTime::GetBeginDST(int year, Country country) } dt += wxTimeSpan::Hours(1); - - // disable DST tests because it could result in an infinite recursion! - dt.MakeGMT(true); } else switch ( country ) { @@ -1215,9 +1329,6 @@ wxDateTime wxDateTime::GetEndDST(int year, Country country) } dt += wxTimeSpan::Hours(1); - - // disable DST tests because it could result in an infinite recursion! - dt.MakeGMT(true); } else switch ( country ) { @@ -1315,9 +1426,10 @@ wxDateTime& wxDateTime::Set(wxDateTime_t hour, _T("Invalid time in wxDateTime::Set()") ); // get the current date from system - struct tm *tm = GetTmNow(); + struct tm tmstruct; + struct tm *tm = GetTmNow(&tmstruct); - wxDATETIME_CHECK( tm, _T("localtime() failed") ); + wxDATETIME_CHECK( tm, _T("wxLocaltime_r() failed") ); // make a copy so it isn't clobbered by the call to mktime() below struct tm tm1(*tm); @@ -1432,6 +1544,16 @@ wxDateTime& wxDateTime::ResetTime() return *this; } +wxDateTime wxDateTime::GetDateOnly() const +{ + Tm tm = GetTm(); + tm.msec = + tm.sec = + tm.min = + tm.hour = 0; + return wxDateTime(tm); +} + // ---------------------------------------------------------------------------- // DOS Date and Time Format functions // ---------------------------------------------------------------------------- @@ -1483,7 +1605,9 @@ unsigned long wxDateTime::GetAsDOS() const { unsigned long ddt; time_t ticks = GetTicks(); - struct tm *tm = localtime(&ticks); + struct tm tmstruct; + struct tm *tm = wxLocaltime_r(&ticks, &tmstruct); + wxCHECK_MSG( tm, ULONG_MAX, _T("time can't be represented in DOS format") ); long year = tm->tm_year; year -= 80; @@ -1521,14 +1645,15 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const if ( time != (time_t)-1 ) { // use C RTL functions + struct tm tmstruct; tm *tm; if ( tz.GetOffset() == -GetTimeZone() ) { // we are working with local time - tm = localtime(&time); + tm = wxLocaltime_r(&time, &tmstruct); // should never happen - wxCHECK_MSG( tm, Tm(), _T("localtime() failed") ); + wxCHECK_MSG( tm, Tm(), _T("wxLocaltime_r() failed") ); } else { @@ -1540,10 +1665,10 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const if ( time >= 0 ) #endif { - tm = gmtime(&time); + tm = wxGmtime_r(&time, &tmstruct); // should never happen - wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") ); + wxCHECK_MSG( tm, Tm(), _T("wxGmtime_r() failed") ); } else { @@ -1777,6 +1902,7 @@ wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd) return dt; } +#if WXWIN_COMPATIBILITY_2_6 // use a separate function to avoid warnings about using deprecated // SetToTheWeek in GetWeek below static wxDateTime @@ -1814,6 +1940,7 @@ wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek, { return ::SetToTheWeek(GetYear(), numWeek, weekday, flags); } +#endif // WXWIN_COMPATIBILITY_2_6 wxDateTime& wxDateTime::SetToLastMonthDay(Month month, int year) @@ -2124,9 +2251,10 @@ int wxDateTime::IsDST(wxDateTime::Country country) const time_t timet = GetTicks(); if ( timet != (time_t)-1 ) { - tm *tm = localtime(&timet); + struct tm tmstruct; + tm *tm = wxLocaltime_r(&timet, &tmstruct); - wxCHECK_MSG( tm, -1, _T("localtime() failed") ); + wxCHECK_MSG( tm, -1, _T("wxLocaltime_r() failed") ); return tm->tm_isdst; } @@ -2178,24 +2306,28 @@ wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST) // wxDateTime to/from text representations // ---------------------------------------------------------------------------- -wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const +wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const { - wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxDateTime::Format") ); + wxCHECK_MSG( !format.empty(), wxEmptyString, + _T("NULL format in wxDateTime::Format") ); // 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 time_t time = GetTicks(); + if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) ) { // use strftime() - tm *tm; + struct tm tmstruct; + struct tm *tm; if ( tz.GetOffset() == -GetTimeZone() ) { // we are working with local time - tm = localtime(&time); + tm = wxLocaltime_r(&time, &tmstruct); // should never happen - wxCHECK_MSG( tm, wxEmptyString, _T("localtime() failed") ); + wxCHECK_MSG( tm, wxEmptyString, _T("wxLocaltime_r() failed") ); } else { @@ -2208,25 +2340,24 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const if ( time >= 0 ) #endif { - tm = gmtime(&time); + tm = wxGmtime_r(&time, &tmstruct); // should never happen - wxCHECK_MSG( tm, wxEmptyString, _T("gmtime() failed") ); + wxCHECK_MSG( tm, wxEmptyString, _T("wxGmtime_r() failed") ); } else { tm = (struct tm *)NULL; } } -#ifndef __WXWINCE__ - //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation + if ( tm ) { return CallStrftime(format, tm); } -#endif - //else: use generic code below } + //else: use generic code below +#endif // HAVE_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 @@ -2246,7 +2377,7 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const tmTimeOnly.tm_isdst = 0; // no DST, we adjust for tz ourselves wxString tmp, res, fmt; - for ( const wxChar *p = format; *p; p++ ) + for ( wxString::const_iterator p = format.begin(); p != format.end(); ++p ) { if ( *p != _T('%') ) { @@ -2257,7 +2388,7 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const } // set the default format - switch ( *++p ) + switch ( (*++p).GetValue() ) { case _T('Y'): // year has 4 digits fmt = _T("%04d"); @@ -2286,7 +2417,7 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const restart = false; // start of the format specification - switch ( *p ) + switch ( (*p).GetValue() ) { case _T('a'): // a weekday name case _T('A'): @@ -2303,7 +2434,7 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const case _T('c'): // locale default date and time representation case _T('x'): // locale default date representation -#ifndef __WXWINCE__ +#ifdef HAVE_STRFTIME // // the problem: there is no way to know what do these format // specifications correspond to for the current locale. @@ -2325,7 +2456,7 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const // will change if one of these years is leap and the other one // is not! { - // find the YEAR: normally, for any year X, Jan 1 or the + // find the YEAR: normally, for any year X, Jan 1 of the // year X + 28 is the same weekday as Jan 1 of X (because // the weekday advances by 1 for each normal X and by 2 // for each leap X, hence by 5 every 4 years or by 35 @@ -2367,33 +2498,15 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const nLostWeekDays += year++ % 4 ? 1 : 2; } - // at any rate, we couldn't go further than 1988 + 9 + 28! - wxASSERT_MSG( year < 2030, - _T("logic error in wxDateTime::Format") ); - - wxString strYear, strYear2; - strYear.Printf(_T("%d"), year); - strYear2.Printf(_T("%d"), year % 100); + // finally move the year below 2000 so that the 2-digit + // year number can never match the month or day of the + // month when we do the replacements below + if ( year >= 2000 ) + year -= 28; - // find two strings not occurring in format (this is surely - // not the optimal way of doing it... improvements welcome!) - wxString fmt = format; - wxString replacement = (wxChar)-1; - while ( fmt.Find(replacement) != wxNOT_FOUND ) - { - replacement << (wxChar)-1; - } - - wxString replacement2 = (wxChar)-2; - while ( fmt.Find(replacement) != wxNOT_FOUND ) - { - replacement << (wxChar)-2; - } + wxASSERT_MSG( year >= 1970 && year < 2000, + _T("logic error in wxDateTime::Format") ); - // replace all occurrences of year with it - bool wasReplaced = fmt.Replace(strYear, replacement) > 0; - if ( !wasReplaced ) - wasReplaced = fmt.Replace(strYear2, replacement2) > 0; // use strftime() to format the same date but in supported // year @@ -2417,27 +2530,31 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const : _T("%x"), &tmAdjusted); - // now replace the occurrence of 1999 with the real year - wxString strYearReal, strYearReal2; - strYearReal.Printf(_T("%04d"), yearReal); - strYearReal2.Printf(_T("%02d"), yearReal % 100); - str.Replace(strYear, strYearReal); - str.Replace(strYear2, strYearReal2); - - // and replace back all occurrences of replacement string - if ( wasReplaced ) - { - str.Replace(replacement2, strYear2); - str.Replace(replacement, strYear); - } + // now replace the replacement year with the real year: + // notice that we have to replace the 4 digit year with + // a unique string not appearing in strftime() output + // first to prevent the 2 digit year from matching any + // substring of the 4 digit year (but any day, month, + // hours or minutes components should be safe because + // they are never in 70-99 range) + wxString replacement("|"); + while ( str.find(replacement) != wxString::npos ) + replacement += '|'; + + str.Replace(wxString::Format("%d", year), + replacement); + str.Replace(wxString::Format("%d", year % 100), + wxString::Format("%d", yearReal % 100)); + str.Replace(replacement, + wxString::Format("%d", yearReal)); res += str; } -#else - //Use "%m/%d/%y %H:%M:%S" format instead +#else // !HAVE_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 +#endif // HAVE_STRFTIME/!HAVE_STRFTIME break; case _T('d'): // day of a month (01-31) @@ -2474,11 +2591,11 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const break; case _T('p'): // AM or PM string -#ifndef __WXWINCE__ +#ifdef HAVE_STRFTIME res += CallStrftime(_T("%p"), &tmTimeOnly); -#else +#else // !HAVE_STRFTIME res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am"); -#endif +#endif // HAVE_STRFTIME/!HAVE_STRFTIME break; case _T('S'): // second as a decimal number (00-61) @@ -2501,11 +2618,11 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const case _T('X'): // locale default time representation // just use strftime() to format the time for us -#ifndef __WXWINCE__ +#ifdef HAVE_STRFTIME res += CallStrftime(_T("%X"), &tmTimeOnly); -#else +#else // !HAVE_STRFTIME res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec); -#endif +#endif // HAVE_STRFTIME/!HAVE_STRFTIME break; case _T('y'): // year without century (00-99) @@ -2517,7 +2634,7 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const break; case _T('Z'): // timezone name -#ifndef __WXWINCE__ +#ifdef HAVE_STRFTIME res += CallStrftime(_T("%Z"), &tmTimeOnly); #endif break; @@ -2847,7 +2964,7 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date) p += tz.length(); } - + // make it minutes offset *= MIN_PER_HOUR; } @@ -3020,10 +3137,10 @@ static wxString GetLocaleDateFormat() #endif // __WINDOWS__ const wxChar *wxDateTime::ParseFormat(const wxChar *date, - const wxChar *format, + const wxString& format, const wxDateTime& dateDef) { - wxCHECK_MSG( date && format, (wxChar *)NULL, + wxCHECK_MSG( date && !format.empty(), (wxChar *)NULL, _T("NULL pointer in wxDateTime::ParseFormat()") ); wxString str; @@ -3053,7 +3170,7 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date, int year = 0; const wxChar *input = date; - for ( const wxChar *fmt = format; *fmt; fmt++ ) + for ( wxString::const_iterator fmt = format.begin(); fmt != format.end(); ++fmt ) { if ( *fmt != _T('%') ) { @@ -3094,7 +3211,7 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date, // the default widths for the various fields if ( !width ) { - switch ( *fmt ) + switch ( (*fmt).GetValue() ) { case _T('Y'): // year has 4 digits width = 4; @@ -3116,7 +3233,7 @@ const wxChar *wxDateTime::ParseFormat(const wxChar *date, } // then the format itself - switch ( *fmt ) + switch ( (*fmt).GetValue() ) { case _T('a'): // a weekday name case _T('A'): @@ -3685,7 +3802,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) // some special cases static struct { - const wxChar *str; + const char *str; int dayDiffFromToday; } literalDates[] = { @@ -3696,12 +3813,12 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ ) { - wxString date = wxGetTranslation(literalDates[n].str); - size_t len = date.length(); + const wxString dateStr = wxGetTranslation(literalDates[n].str); + size_t len = dateStr.length(); if ( wxStrlen(p) >= len ) { wxString str(p, len); - if ( str.CmpNoCase(date) == 0 ) + if ( str.CmpNoCase(dateStr) == 0 ) { // nothing can follow this, so stop here p += len; @@ -3769,9 +3886,11 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) } 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 : Inv_Year, mon) + ? GetNumOfDaysInMonth(haveYear ? year : 1976, mon) : 31 ); @@ -3858,7 +3977,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) else // not a valid weekday name { // try the ordinals - static const wxChar *ordinals[] = + static const char *ordinals[] = { wxTRANSLATE("first"), wxTRANSLATE("second"), @@ -3924,7 +4043,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) { wxLogDebug(_T("ParseDate: no day, no weekday hence no date.")); - return (wxChar *)NULL; + return NULL; } if ( haveWDay && (haveMon || haveYear || haveDay) && @@ -3933,7 +4052,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) // 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 (wxChar *)NULL; + return NULL; } if ( !haveWDay && haveYear && !(haveDay && haveMon) ) @@ -3963,7 +4082,7 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) // 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 (wxChar *)NULL; + return NULL; } } @@ -3979,6 +4098,11 @@ const wxChar *wxDateTime::ParseDate(const wxChar *date) if ( haveDay ) { + // 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 > GetNumOfDaysInMonth(year, mon) ) + return NULL; + Set(day, mon, year); if ( haveWDay ) @@ -4019,7 +4143,7 @@ const wxChar *wxDateTime::ParseTime(const wxChar *time) // first try some extra things static const struct { - const wxChar *name; + const char *name; wxDateTime_t hour; } stdTimes[] = { @@ -4146,12 +4270,13 @@ enum TimeSpanPart // And, to be better than MFC :-), we also have // %E number of wEeks // %l milliseconds (000 - 999) -wxString wxTimeSpan::Format(const wxChar *format) const +wxString wxTimeSpan::Format(const wxString& format) const { - wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxTimeSpan::Format") ); + wxCHECK_MSG( !format.empty(), wxEmptyString, + _T("NULL format in wxTimeSpan::Format") ); wxString str; - str.Alloc(wxStrlen(format)); + str.Alloc(format.length()); // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */) // @@ -4169,18 +4294,21 @@ wxString wxTimeSpan::Format(const wxChar *format) const // we remember the most important unit found so far TimeSpanPart partBiggest = Part_MSec; - for ( const wxChar *pch = format; *pch; pch++ ) + for ( wxString::const_iterator pch = format.begin(); pch != format.end(); ++pch ) { wxChar ch = *pch; if ( ch == _T('%') ) { // the start of the format specification of the printf() below - wxString fmtPrefix = _T('%'); + wxString fmtPrefix(_T('%')); // the number long n; + // the number of digits for the format string, 0 if unused + unsigned digits = 0; + ch = *++pch; // get the format spec char switch ( ch ) { @@ -4215,6 +4343,13 @@ wxString wxTimeSpan::Format(const wxChar *format) const n = GetHours(); if ( partBiggest < Part_Hour ) { + if ( n < 0 ) + { + // the sign has already been taken into account + // when outputting the biggest part + n = -n; + } + n %= HOURS_PER_DAY; } else @@ -4222,25 +4357,31 @@ wxString wxTimeSpan::Format(const wxChar *format) const partBiggest = Part_Hour; } - fmtPrefix += _T("02"); + digits = 2; break; case _T('l'): n = GetMilliseconds().ToLong(); if ( partBiggest < Part_MSec ) { + if ( n < 0 ) + n = -n; + n %= 1000; } //else: no need to reset partBiggest to Part_MSec, it is // the least significant one anyhow - fmtPrefix += _T("03"); + digits = 3; break; case _T('M'): n = GetMinutes(); if ( partBiggest < Part_Min ) { + if ( n < 0 ) + n = -n; + n %= MIN_PER_HOUR; } else @@ -4248,13 +4389,16 @@ wxString wxTimeSpan::Format(const wxChar *format) const partBiggest = Part_Min; } - fmtPrefix += _T("02"); + digits = 2; break; case _T('S'): n = GetSeconds().ToLong(); if ( partBiggest < Part_Sec ) { + if ( n < 0 ) + n = -n; + n %= SEC_PER_MIN; } else @@ -4262,10 +4406,19 @@ wxString wxTimeSpan::Format(const wxChar *format) const partBiggest = Part_Sec; } - fmtPrefix += _T("02"); + digits = 2; break; } + if ( digits ) + { + // negative numbers need one extra position for '-' display + if ( n < 0 ) + digits++; + + fmtPrefix << _T("0") << digits; + } + str += wxString::Format(fmtPrefix + _T("ld"), n); } else @@ -4284,7 +4437,7 @@ wxString wxTimeSpan::Format(const wxChar *format) const #include "wx/arrimpl.cpp" -WX_DEFINE_OBJARRAY(wxDateTimeArray); +WX_DEFINE_OBJARRAY(wxDateTimeArray) static int wxCMPFUNC_CONV wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second) @@ -4326,8 +4479,8 @@ wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart, holidays.Clear(); - size_t count = ms_authorities.size(); - for ( size_t nAuth = 0; nAuth < count; nAuth++ ) + const size_t countAuth = ms_authorities.size(); + for ( size_t nAuth = 0; nAuth < countAuth; nAuth++ ) { ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);