X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a452689b23cf8366caba853ce26b7ad5a4fc269e..7c081bd21c8f2a6331eae678328f23e45224b917:/src/common/datetime.cpp diff --git a/src/common/datetime.cpp b/src/common/datetime.cpp index 8448eb1a5e..43b767754e 100644 --- a/src/common/datetime.cpp +++ b/src/common/datetime.cpp @@ -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: * @@ -66,19 +62,22 @@ #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" #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 @@ -86,7 +85,6 @@ #endif #include "wx/datetime.h" -#include "wx/stopwatch.h" // for wxGetLocalTimeMillis() const long wxDateTime::TIME_T_FACTOR = 1000l; @@ -139,6 +137,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 @@ -164,23 +169,74 @@ wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter , wxFromStringCon #define WX_TIMEZONE wxGetTimeZone() #elif defined(__DARWIN__) #define WX_GMTOFF_IN_TM + #elif defined(__WXWINCE__) && defined(__VISUALC8__) + #define WX_TIMEZONE _timezone #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 + memcpy(temp, localtime(ticks), 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 + 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 @@ -302,7 +358,6 @@ 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 @@ -313,22 +368,22 @@ static int GetTimeZone() // 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 tmstruct; + struct tm tm; - tm = wxLocaltime_r(&t, &tmstruct); + 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 @@ -371,7 +426,8 @@ 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) { @@ -389,7 +445,8 @@ static wxString CallStrftime(const wxChar *format, const tm* tm) s = buf; return s; } -#endif + +#endif // HAVE_STRFTIME #ifdef HAVE_STRPTIME @@ -695,6 +752,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; @@ -845,7 +903,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; @@ -853,7 +911,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) { @@ -895,7 +953,7 @@ wxString wxDateTime::GetMonthName(wxDateTime::Month month, break; } return ret; -#endif +#endif // HAVE_STRFTIME/!HAVE_STRFTIME } /* static */ @@ -903,7 +961,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!) @@ -921,7 +979,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) { @@ -948,8 +1006,7 @@ wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday, break; } return ret; - -#endif +#endif // HAVE_STRFTIME/!HAVE_STRFTIME } /* static */ @@ -1027,9 +1084,9 @@ wxDateTime::Country wxDateTime::GetCountry() ms_country = USA; } } -#else +#else // __WXWINCE__ ms_country = USA; -#endif +#endif // !__WXWINCE__/__WXWINCE__ return ms_country; } @@ -1786,6 +1843,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 @@ -1823,6 +1881,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) @@ -2192,9 +2251,11 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const { wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxDateTime::Format") ); + time_t time = GetTicks(); + // we have to use our own implementation if the date is out of range of // strftime() or if we use non standard specificators - time_t time = GetTicks(); +#ifdef HAVE_STRFTIME if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) ) { // use strftime() @@ -2229,15 +2290,14 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const 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 @@ -2314,7 +2374,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. @@ -2378,6 +2438,9 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const nLostWeekDays += year++ % 4 ? 1 : 2; } + // Keep year below 2000 so the 2digit year number + // can never match the month or day of the month + if (year>=2000) year-=28; // at any rate, we couldn't go further than 1988 + 9 + 28! wxASSERT_MSG( year < 2030, _T("logic error in wxDateTime::Format") ); @@ -2386,25 +2449,30 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const strYear.Printf(_T("%d"), year); strYear2.Printf(_T("%d"), year % 100); - // find two strings not occurring in format (this is surely + // find four strings not occurring in format (this is surely // not the optimal way of doing it... improvements welcome!) wxString fmt2 = format; - wxString replacement = (wxChar)-1; - while ( fmt2.Find(replacement) != wxNOT_FOUND ) + wxString replacement,replacement2,replacement3,replacement4; + for (int rnr=1; rnr<5 ; rnr++) { - replacement << (wxChar)-1; - } + wxString r = (wxChar)-rnr; + while ( fmt2.Find(r) != wxNOT_FOUND ) + { + r << (wxChar)-rnr; + } - wxString replacement2 = (wxChar)-2; - while ( fmt2.Find(replacement) != wxNOT_FOUND ) - { - replacement << (wxChar)-2; + switch (rnr) + { + case 1: replacement=r; break; + case 2: replacement2=r; break; + case 3: replacement3=r; break; + case 4: replacement4=r; break; + } } - // replace all occurrences of year with it bool wasReplaced = fmt2.Replace(strYear, replacement) > 0; - if ( !wasReplaced ) - wasReplaced = fmt2.Replace(strYear2, replacement2) > 0; + // evaluation order ensures we always attempt the replacement. + wasReplaced = (fmt2.Replace(strYear2, replacement2) > 0) | wasReplaced ; // use strftime() to format the same date but in supported // year @@ -2429,11 +2497,17 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const &tmAdjusted); // now replace the occurrence of 1999 with the real year + // we do this in two stages to stop the 2 digit year + // matching any substring of the 4 digit year. + // Any day,month hours and minutes components should be safe due + // to ensuring the range of the years. wxString strYearReal, strYearReal2; strYearReal.Printf(_T("%04d"), yearReal); strYearReal2.Printf(_T("%02d"), yearReal % 100); - str.Replace(strYear, strYearReal); - str.Replace(strYear2, strYearReal2); + str.Replace(strYear, replacement3); + str.Replace(strYear2,replacement4); + str.Replace(replacement3, strYearReal); + str.Replace(replacement4, strYearReal2); // and replace back all occurrences of replacement string if ( wasReplaced ) @@ -2444,11 +2518,11 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const 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) @@ -2485,11 +2559,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) @@ -2512,11 +2586,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) @@ -2528,7 +2602,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; @@ -3780,9 +3854,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 ); @@ -3935,7 +4011,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) && @@ -3944,7 +4020,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) ) @@ -3974,7 +4050,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; } } @@ -3990,6 +4066,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 ) @@ -4187,11 +4268,14 @@ wxString wxTimeSpan::Format(const wxChar *format) const 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 ) { @@ -4226,6 +4310,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 @@ -4233,25 +4324,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 @@ -4259,13 +4356,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 @@ -4273,10 +4373,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