X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e4d0689515f282838013f74525ac5d37c53fa518..f363cee882672e147340240cf0bb194a68e37833:/src/common/datetime.cpp?ds=inline diff --git a/src/common/datetime.cpp b/src/common/datetime.cpp index e9f1935bb3..669d0a3269 100644 --- a/src/common/datetime.cpp +++ b/src/common/datetime.cpp @@ -5,7 +5,6 @@ // Author: Vadim Zeitlin // Modified by: // Created: 11.05.99 -// RCS-ID: $Id$ // Copyright: (c) 1999 Vadim Zeitlin // parts of code taken from sndcal library by Scott E. Lee: // @@ -63,7 +62,7 @@ #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME #ifndef WX_PRECOMP - #ifdef __WXMSW__ + #ifdef __WINDOWS__ #include "wx/msw/wrapwin.h" #endif #include "wx/string.h" @@ -75,6 +74,7 @@ #endif // WX_PRECOMP #include "wx/thread.h" +#include "wx/time.h" #include "wx/tokenzr.h" #include @@ -108,134 +108,6 @@ wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter , wxFromStringCon #endif // wxUSE_EXTENDED_RTTI - -// ---------------------------------------------------------------------------- -// conditional compilation -// ---------------------------------------------------------------------------- - -#if defined(__MWERKS__) && wxUSE_UNICODE - #include -#endif - -#if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM) - #if defined(__WXPALMOS__) - #define WX_GMTOFF_IN_TM - #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__) - #define WX_TIMEZONE _timezone - #elif defined(__MWERKS__) - long wxmw_timezone = 28800; - #define WX_TIMEZONE wxmw_timezone - #elif defined(__DJGPP__) || defined(__WINE__) - #include - #include - static long wxGetTimeZone() - { - struct timeb tb; - ftime(&tb); - return tb.timezone; - } - #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() - { - long t; - _get_timezone(& t); - return t; - } - #define WX_TIMEZONE wxGetTimeZone() - #elif 1 - // Solution (2): using GetTimeZoneInformation - static long wxGetTimeZone() - { - TIME_ZONE_INFORMATION tzi; - ::GetTimeZoneInformation(&tzi); - return tzi.Bias; // x 60 - } - #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 - -// 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 // ---------------------------------------------------------------------------- @@ -363,38 +235,6 @@ wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month) return daysInMonth[wxDateTime::IsLeapYear(year)][month]; } -// returns the time zone in the C sense, i.e. the difference UTC - local -// (in seconds) -// NOTE: not static because used by datetimefmt.cpp -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 wxLocaltime_r - if ( !s_timezoneSet ) - { - // just call wxLocaltime_r() instead of figuring out whether this - // system supports tzset(), _tzset() or something else - time_t t = 0; - struct tm tm; - - wxLocaltime_r(&t, &tm); - s_timezoneSet = true; - - // 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; - } - return (int)gmtoffset; -#else // !WX_GMTOFF_IN_TM - return 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 // get the real JDN you need to add 0.5, this is, in fact, JDN of the // noon of the previous day) @@ -448,8 +288,15 @@ wxString CallStrftime(const wxString& format, const tm* tm) if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) ) { - // if the format is valid, buffer must be too small? - wxFAIL_MSG(wxT("strftime() failed")); + // There is one special case in which strftime() can return 0 without + // indicating an error: "%p" may give empty string depending on the + // locale, so check for it explicitly. Apparently it's really the only + // exception. + if ( format != wxS("%p") ) + { + // if the format is valid, buffer must be too small? + wxFAIL_MSG(wxT("strftime() failed")); + } buf[0] = '\0'; } @@ -508,8 +355,12 @@ wxDateTime::Tm::Tm() { year = (wxDateTime_t)wxDateTime::Inv_Year; mon = wxDateTime::Inv_Month; - mday = 0; - hour = min = sec = msec = 0; + mday = + yday = 0; + hour = + min = + sec = + msec = 0; wday = wxDateTime::Inv_WeekDay; } @@ -529,9 +380,17 @@ wxDateTime::Tm::Tm(const struct tm& tm, const TimeZone& tz) bool wxDateTime::Tm::IsValid() const { + if ( mon == wxDateTime::Inv_Month ) + return false; + + // We need to check this here to avoid crashing in GetNumOfDaysInMonth() if + // somebody passed us "(wxDateTime::Month)1000". + wxCHECK_MSG( mon >= wxDateTime::Jan && mon < wxDateTime::Inv_Month, false, + wxS("Invalid month value") ); + // we allow for the leap seconds, although we don't use them (yet) return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) && - (mday <= GetNumOfDaysInMonth(year, mon)) && + (mday > 0 && mday <= GetNumOfDaysInMonth(year, mon)) && (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000); } @@ -601,7 +460,7 @@ wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz) case wxDateTime::Local: // get the offset from C RTL: it returns the difference GMT-local // while we want to have the offset _from_ GMT, hence the '-' - m_offset = -GetTimeZone(); + m_offset = -wxGetTimeZone(); break; case wxDateTime::GMT_12: @@ -1242,7 +1101,7 @@ wxDateTime wxDateTime::GetEndDST(int year, Country country) // return the current time with ms precision /* static */ wxDateTime wxDateTime::UNow() { - return wxDateTime(wxGetLocalTimeMillis()); + return wxDateTime(wxGetUTCTimeMillis()); } // the values in the tm structure contain the local time @@ -1258,7 +1117,7 @@ wxDateTime& wxDateTime::Set(const struct tm& tm) if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 ) { return Set((time_t)( - GetTimeZone() + + wxGetTimeZone() + tm2.tm_hour * MIN_PER_HOUR * SEC_PER_MIN + tm2.tm_min * SEC_PER_MIN + tm2.tm_sec)); @@ -1270,10 +1129,37 @@ wxDateTime& wxDateTime::Set(const struct tm& tm) return *this; } - else - { - return Set(timet); + + // mktime() only adjusts tm_wday, tm_yday and tm_isdst fields normally, if + // it changed anything else, it must have performed the DST adjustment. But + // the trouble with this is that different implementations do it + // differently, e.g. GNU libc moves the time forward if the specified time + // is invalid in the local time zone, while MSVC CRT moves it backwards + // which is especially pernicious as it can change the date if the DST + // starts at midnight, as it does in some time zones (see #15419), and this + // is completely unexpected for the code working with dates only. + // + // So standardize on moving the time forwards to have consistent behaviour + // under all platforms and to avoid the problem above. + if ( tm2.tm_hour != tm.tm_hour ) + { + tm2 = tm; + tm2.tm_hour++; + if ( tm2.tm_hour == 24 ) + { + // This shouldn't normally happen as the DST never starts at 23:00 + // but if it does, we have a problem as we need to adjust the day + // as well. However we stop here, i.e. we don't adjust the month + // (or the year) because mktime() is supposed to take care of this + // for us. + tm2.tm_hour = 0; + tm2.tm_mday++; + } + + timet = mktime(&tm2); } + + return Set(timet); } wxDateTime& wxDateTime::Set(wxDateTime_t hour, @@ -1372,7 +1258,7 @@ wxDateTime& wxDateTime::Set(wxDateTime_t day, m_time *= SECONDS_PER_DAY * TIME_T_FACTOR; // JDN corresponds to GMT, we take localtime - Add(wxTimeSpan(hour, minute, second + GetTimeZone(), millisec)); + Add(wxTimeSpan(hour, minute, second + wxGetTimeZone(), millisec)); } return *this; @@ -1511,7 +1397,7 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const // use C RTL functions struct tm tmstruct; tm *tm; - if ( tz.GetOffset() == -GetTimeZone() ) + if ( tz.GetOffset() == -wxGetTimeZone() ) { // we are working with local time tm = wxLocaltime_r(&time, &tmstruct); @@ -1612,6 +1498,7 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const // construct Tm from these values Tm tm; tm.year = (int)year; + tm.yday = (wxDateTime_t)(dayOfYear - 1); // use C convention for day number tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0 tm.mday = (wxDateTime_t)day; tm.msec = (wxDateTime_t)(timeOnly % 1000); @@ -1741,6 +1628,56 @@ wxDateTime& wxDateTime::Add(const wxDateSpan& diff) return *this; } +wxDateSpan wxDateTime::DiffAsDateSpan(const wxDateTime& dt) const +{ + wxASSERT_MSG( IsValid() && dt.IsValid(), wxT("invalid wxDateTime")); + + // If dt is larger than this, calculations below needs to be inverted. + int inv = 1; + if ( dt > *this ) + inv = -1; + + int y = GetYear() - dt.GetYear(); + int m = GetMonth() - dt.GetMonth(); + int d = GetDay() - dt.GetDay(); + + // If month diff is negative, dt is the year before, so decrease year + // and set month diff to its inverse, e.g. January - December should be 1, + // not -11. + if ( m * inv < 0 || (m == 0 && d * inv < 0)) + { + m += inv * MONTHS_IN_YEAR; + y -= inv; + } + + // Same logic for days as for months above. + if ( d * inv < 0 ) + { + // Use number of days in month from the month which end date we're + // crossing. That is month before this for positive diff, and this + // month for negative diff. + // If we're on january and using previous month, we get december + // previous year, but don't care, december has same amount of days + // every year. + wxDateTime::Month monthfordays = GetMonth(); + if (inv > 0 && monthfordays == wxDateTime::Jan) + monthfordays = wxDateTime::Dec; + else if (inv > 0) + monthfordays = static_cast(monthfordays - 1); + + d += inv * wxDateTime::GetNumberOfDays(monthfordays, GetYear()); + m -= inv; + } + + int w = d / DAYS_PER_WEEK; + + // Remove weeks from d, since wxDateSpan only keep days as the ones + // not in complete weeks + d -= w * DAYS_PER_WEEK; + + return wxDateSpan(y, m, w, d); +} + // ---------------------------------------------------------------------------- // Weekday and monthday stuff // ---------------------------------------------------------------------------- @@ -2010,7 +1947,6 @@ wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const { // adjust the weekdays to non-US style. wdYearStart = ConvertWeekDayToMondayBase(wdYearStart); - wdTarget = ConvertWeekDayToMondayBase(wdTarget); // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html: // @@ -2026,22 +1962,24 @@ wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const // // if Jan 1 is Thursday or less, it is in the first week of this year - if ( wdYearStart < 4 ) - { - // count the number of entire weeks between Jan 1 and this date - week = (nDayInYear + wdYearStart + 6 - wdTarget)/7; + int dayCountFix = wdYearStart < 4 ? 6 : -1; - // be careful to check for overflow in the next year - if ( week == 53 && tm.mday - wdTarget > 28 ) - week = 1; + // count the number of week + week = (nDayInYear + wdYearStart + dayCountFix) / DAYS_PER_WEEK; + + // check if we happen to be at the last week of previous year: + if ( week == 0 ) + { + week = wxDateTime(31, Dec, GetYear() - 1).GetWeekOfYear(); } - else // Jan 1 is in the last week of the previous year + else if ( week == 53 ) { - // check if we happen to be at the last week of previous year: - if ( tm.mon == Jan && tm.mday < 8 - wdYearStart ) - week = wxDateTime(31, Dec, GetYear()-1).GetWeekOfYear(); - else - week = (nDayInYear + wdYearStart - 1 - wdTarget)/7; + int wdYearEnd = (wdYearStart + 364 + IsLeapYear(GetYear())) + % DAYS_PER_WEEK; + + // Week 53 only if last day of year is Thursday or later. + if ( wdYearEnd < 3 ) + week = 1; } } @@ -2144,7 +2082,7 @@ int wxDateTime::IsDST(wxDateTime::Country country) const wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST) { - long secDiff = GetTimeZone() + tz.GetOffset(); + long secDiff = wxGetTimeZone() + tz.GetOffset(); // we need to know whether DST is or not in effect for this date unless // the test disabled by the caller @@ -2159,7 +2097,7 @@ wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST) wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST) { - long secDiff = GetTimeZone() + tz.GetOffset(); + long secDiff = wxGetTimeZone() + tz.GetOffset(); // we need to know whether DST is or not in effect for this date unless // the test disabled by the caller @@ -2337,7 +2275,7 @@ WXDLLIMPEXP_BASE void wxPrevWDay(wxDateTime::WeekDay& wd) : (wxDateTime::WeekDay)(wd - 1); } -#ifdef __WXMSW__ +#ifdef __WINDOWS__ wxDateTime& wxDateTime::SetFromMSWSysTime(const SYSTEMTIME& st) { @@ -2347,6 +2285,14 @@ wxDateTime& wxDateTime::SetFromMSWSysTime(const SYSTEMTIME& st) st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); } +wxDateTime& wxDateTime::SetFromMSWSysDate(const SYSTEMTIME& st) +{ + return Set(st.wDay, + static_cast(wxDateTime::Jan + st.wMonth - 1), + st.wYear, + 0, 0, 0, 0); +} + void wxDateTime::GetAsMSWSysTime(SYSTEMTIME* st) const { const wxDateTime::Tm tm(GetTm()); @@ -2361,6 +2307,22 @@ void wxDateTime::GetAsMSWSysTime(SYSTEMTIME* st) const st->wSecond = tm.sec; st->wMilliseconds = tm.msec; } -#endif // __WXMSW__ + +void wxDateTime::GetAsMSWSysDate(SYSTEMTIME* st) const +{ + const wxDateTime::Tm tm(GetTm()); + + st->wYear = (WXWORD)tm.year; + st->wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1); + st->wDay = tm.mday; + + st->wDayOfWeek = + st->wHour = + st->wMinute = + st->wSecond = + st->wMilliseconds = 0; +} + +#endif // __WINDOWS__ #endif // wxUSE_DATETIME