From: Vadim Zeitlin Date: Sun, 28 Aug 2005 13:06:36 +0000 (+0000) Subject: Fixed several bugs in wxDateTime timezone handling: X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/d26adb9df7d83c7c1280afdc22679978fba3ff90 Fixed several bugs in wxDateTime timezone handling: - ToTimezone() and MakeTimezone() now work as expected - added and documented FromTimezone() and MakeFromTimezone() - Set(double jdn) interprets jdn always in UTC - updated ParseRfc822Date() timezone handling - removed workarounds for old bugs from the test git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@35334 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/docs/changes.txt b/docs/changes.txt index b5941a9ab7..ae373127d7 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -16,6 +16,7 @@ All: - Fixed compilation with IBM xlC compiler. - wxABI_VERSION, see 'Backward Compatibility' topic overview in the manual. - Added wxLongLong::ToDouble() +- Added wxDateTime::[Make]FromTimezone(), fixed several TZ-related bugs All (GUI): diff --git a/docs/latex/wx/datetime.tex b/docs/latex/wx/datetime.tex index c77282bc17..ba26086069 100644 --- a/docs/latex/wx/datetime.tex +++ b/docs/latex/wx/datetime.tex @@ -406,10 +406,12 @@ provided. You can construct a wxDateTime object from a Please see the \helpref{time zone overview}{tdatetimezones} for more information about time zones. Normally, these functions should be rarely used. +\helpref{FromTimezone}{wxdatetimefromtimezone}\\ \helpref{ToTimezone}{wxdatetimetotimezone}\\ \helpref{MakeTimezone}{wxdatetimemaketimezone}\\ -\helpref{ToGMT}{wxdatetimetogmt}\\ -\helpref{MakeGMT}{wxdatetimemakegmt}\\ +\helpref{MakeFromTimezone}{wxdatetimemakefromtimezone}\\ +\helpref{ToUTC}{wxdatetimetoutc}\\ +\helpref{MakeUTC}{wxdatetimemakeutc}\\ \helpref{GetBeginDST}{wxdatetimegetbegindst}\\ \helpref{GetEndDST}{wxdatetimegetenddst}\\ \helpref{IsDST}{wxdatetimeisdst} @@ -1461,6 +1463,16 @@ year 1 is Rata Die day 1. %%%%%%%%%%%%%%%%%%%%%%%%%%% timezone and DST %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\membersection{wxDateTime::FromTimezone}\label{wxdatetimefromtimezone} + +\constfunc{wxDateTime}{FromTimezone}{\param{const TimeZone\& }{tz}, \param{bool }{noDST = false}} + +Transform the date from the given time zone to the local one. If {\it noDST} is +{\tt true}, no DST adjustments will be made. + +Returns the date in the local time zone. + + \membersection{wxDateTime::ToTimezone}\label{wxdatetimetotimezone} \constfunc{wxDateTime}{ToTimezone}{\param{const TimeZone\& }{tz}, \param{bool }{noDST = false}} @@ -1479,17 +1491,25 @@ Modifies the object in place to represent the date in another time zone. If {\it noDST} is {\tt true}, no DST adjustments will be made. -\membersection{wxDateTime::ToGMT}\label{wxdatetimetogmt} +\membersection{wxDateTime::MakeFromTimezone}\label{wxdatetimemakefromtimezone} + +\func{wxDateTime\&}{MakeFromTimezone}{\param{const TimeZone\& }{tz}, \param{bool }{noDST = false}} + +Same as \helpref{FromTimezone}{wxdatetimefromtimezone} but modifies the object +in place. + + +\membersection{wxDateTime::ToUTC}\label{wxdatetimetoutc} -\constfunc{wxDateTime}{ToGMT}{\param{bool }{noDST = false}} +\constfunc{wxDateTime}{ToUTC}{\param{bool }{noDST = false}} This is the same as calling \helpref{ToTimezone}{wxdatetimetotimezone} with the argument {\tt GMT0}. -\membersection{wxDateTime::MakeGMT}\label{wxdatetimemakegmt} +\membersection{wxDateTime::MakeUTC}\label{wxdatetimemakeutc} -\func{wxDateTime\&}{MakeGMT}{\param{bool }{noDST = false}} +\func{wxDateTime\&}{MakeUTC}{\param{bool }{noDST = false}} This is the same as calling \helpref{MakeTimezone}{wxdatetimemaketimezone} with the argument {\tt GMT0}. diff --git a/include/wx/datetime.h b/include/wx/datetime.h index 16bf40e690..5e6f63d698 100644 --- a/include/wx/datetime.h +++ b/include/wx/datetime.h @@ -757,26 +757,42 @@ public: // religious holidays (Easter...) or moon/solar eclipses? Some // algorithms can be found in the calendar FAQ - // timezone stuff: a wxDateTime object constructed using given - // day/month/year/hour/min/sec values correspond to this moment in local - // time. Using the functions below, it may be converted to another time - // zone (for example, the Unix epoch is wxDateTime(1, Jan, 1970).ToGMT()) + + // Timezone stuff: a wxDateTime object constructed using given + // day/month/year/hour/min/sec values is interpreted as this moment in + // local time. Using the functions below, it may be converted to another + // time zone (e.g., the Unix epoch is wxDateTime(1, Jan, 1970).ToGMT()). // - // these functions try to handle DST internally, but there is no magical + // These functions try to handle DST internally, but there is no magical // way to know all rules for it in all countries in the world, so if the // program can handle it itself (or doesn't want to handle it at all for // whatever reason), the DST handling can be disabled with noDST. - // - // Converting to the local time zone doesn't do anything. // ------------------------------------------------------------------------ // transform to any given timezone inline wxDateTime ToTimezone(const TimeZone& tz, bool noDST = false) const; wxDateTime& MakeTimezone(const TimeZone& tz, bool noDST = false); - // transform to GMT/UTC - wxDateTime ToGMT(bool noDST = false) const { return ToTimezone(GMT0, noDST); } - wxDateTime& MakeGMT(bool noDST = false) { return MakeTimezone(GMT0, noDST); } +#if wxABI_VERSION >= 20602 + // interpret current value as being in another timezone and transform + // it to local one + inline wxDateTime FromTimezone(const TimeZone& tz, bool noDST = false) const; + wxDateTime& MakeFromTimezone(const TimeZone& tz, bool noDST = false); +#endif // ABI >= 2.6.2 + + // transform to/from GMT/UTC + wxDateTime ToUTC(bool noDST = false) const { return ToTimezone(UTC, noDST); } + wxDateTime& MakeUTC(bool noDST = false) { return MakeTimezone(UTC, noDST); } + + wxDateTime ToGMT(bool noDST = false) const { return ToUTC(noDST); } + wxDateTime& MakeGMT(bool noDST = false) { return MakeUTC(noDST); } + +#if wxABI_VERSION >= 20602 + wxDateTime FromUTC(bool noDST = false) const + { return FromTimezone(UTC, noDST); } + wxDateTime& MakeFromUTC(bool noDST = false) + { return MakeFromTimezone(UTC, noDST); } +#endif // ABI >= 2.6.2 // is daylight savings time in effect at this moment according to the // rules of the specified country? @@ -785,6 +801,7 @@ public: // the information is not available (this is compatible with ANSI C) int IsDST(Country country = Country_Default) const; + // accessors: many of them take the timezone parameter which indicates the // timezone for which to make the calculations and the default value means // to do it for the current timezone of this machine (even if the function @@ -1839,12 +1856,22 @@ inline wxDateTime& wxDateTime::operator+=(const wxDateSpan& diff) // wxDateTime and timezones // ---------------------------------------------------------------------------- -inline wxDateTime wxDateTime::ToTimezone(const wxDateTime::TimeZone& tz, - bool noDST) const +inline wxDateTime +wxDateTime::ToTimezone(const wxDateTime::TimeZone& tz, bool noDST) const { MODIFY_AND_RETURN( MakeTimezone(tz, noDST) ); } +#if wxABI_VERSION >= 20602 + +inline wxDateTime +wxDateTime::FromTimezone(const wxDateTime::TimeZone& tz, bool noDST) const +{ + MODIFY_AND_RETURN( MakeFromTimezone(tz, noDST) ); +} + +#endif // ABI >= 2.6.2 + // ---------------------------------------------------------------------------- // wxTimeSpan construction // ---------------------------------------------------------------------------- diff --git a/src/common/datetime.cpp b/src/common/datetime.cpp index b09fe1ef75..7ee3917095 100644 --- a/src/common/datetime.cpp +++ b/src/common/datetime.cpp @@ -692,7 +692,7 @@ wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz) case wxDateTime::A_CST: // Central Standard Time in use in Australia = UTC + 9.5 - m_offset = 60l*(9*60 + 30); + m_offset = 60l*(9*MIN_PER_HOUR + MIN_PER_HOUR/2); break; default: @@ -1405,20 +1405,9 @@ wxDateTime& wxDateTime::Set(double jdn) // EPOCH_JDN + 0.5 jdn -= EPOCH_JDN + 0.5; - jdn *= MILLISECONDS_PER_DAY; + m_time.Assign(jdn*MILLISECONDS_PER_DAY); - m_time.Assign(jdn); - - // JDNs always suppose an UTC date, so bring it back to local time zone - // (also see GetJulianDayNumber() implementation) - long tzDiff = GetTimeZone(); - if ( IsDST() == 1 ) - { - // FIXME: again, we suppose that DST is always one hour - tzDiff -= 3600; - } - - m_time += tzDiff*1000; // tzDiff is in seconds + // JDNs always are in UTC, so we don't need any adjustments for time zone return *this; } @@ -1637,14 +1626,14 @@ wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const timeOnly -= tm.msec; timeOnly /= 1000; // now we have time in seconds - tm.sec = (wxDateTime_t)(timeOnly % 60); + tm.sec = (wxDateTime_t)(timeOnly % SEC_PER_MIN); timeOnly -= tm.sec; - timeOnly /= 60; // now we have time in minutes + timeOnly /= SEC_PER_MIN; // now we have time in minutes - tm.min = (wxDateTime_t)(timeOnly % 60); + tm.min = (wxDateTime_t)(timeOnly % MIN_PER_HOUR); timeOnly -= tm.min; - tm.hour = (wxDateTime_t)(timeOnly / 60); + tm.hour = (wxDateTime_t)(timeOnly / MIN_PER_HOUR); return tm; } @@ -2110,16 +2099,7 @@ wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday) double wxDateTime::GetJulianDayNumber() const { - // JDN are always expressed for the UTC dates - Tm tm(ToTimezone(UTC).GetTm(UTC)); - - double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year); - - // add the part GetTruncatedJDN() neglected - result += 0.5; - - // and now add the time: 86400 sec = 1 JDN - return result + ((double)(60*(60*tm.hour + tm.min) + tm.sec)) / 86400; + return m_time.ToDouble() / MILLISECONDS_PER_DAY + EPOCH_JDN + 0.5; } double wxDateTime::GetRataDie() const @@ -2173,6 +2153,21 @@ wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST) secDiff -= 3600; } + return Add(wxTimeSpan::Seconds(secDiff)); +} + +wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST) +{ + long secDiff = GetTimeZone() + tz.GetOffset(); + + // we need to know whether DST is or not in effect for this date unless + // the test disabled by the caller + if ( !noDST && (IsDST() == 1) ) + { + // FIXME we assume that the DST is always shifted by 1 hour + secDiff -= 3600; + } + return Subtract(wxTimeSpan::Seconds(secDiff)); } @@ -2771,7 +2766,7 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date) } // hours - offset = 60*(10*(*p - _T('0')) + (*(p + 1) - _T('0'))); + offset = MIN_PER_HOUR*(10*(*p - _T('0')) + (*(p + 1) - _T('0'))); p += 2; @@ -2851,12 +2846,12 @@ const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date) } // make it minutes - offset *= 60; + offset *= MIN_PER_HOUR; } - // the spec was correct + // the spec was correct, construct the date from the values we found Set(day, mon, year, hour, min, sec); - MakeTimezone((wxDateTime_t)(60*offset)); + MakeFromTimezone(offset*SEC_PER_MIN); return p; } diff --git a/tests/datetime/datetimetest.cpp b/tests/datetime/datetimetest.cpp index 44924b868d..29209eef6c 100644 --- a/tests/datetime/datetimetest.cpp +++ b/tests/datetime/datetimetest.cpp @@ -257,7 +257,9 @@ void DateTimeTestCase::TestTimeJDN() { const Date& d = testDates[n]; wxDateTime dt(d.day, d.month, d.year, d.hour, d.min, d.sec); - double jdn = dt.GetJulianDayNumber(); + + // JDNs must be computed for UTC times + double jdn = dt.FromUTC().GetJulianDayNumber(); CPPUNIT_ASSERT( jdn == d.jdn ); @@ -662,7 +664,7 @@ void DateTimeTestCase::TestTimeTicks() long ticks = (dt.GetValue() / 1000).ToLong(); CPPUNIT_ASSERT( ticks == d.ticks ); - dt = d.DT().ToTimezone(wxDateTime::GMT0); + dt = d.DT().FromTimezone(wxDateTime::GMT0); ticks = (dt.GetValue() / 1000).ToLong(); CPPUNIT_ASSERT( ticks == d.gmticks ); } @@ -674,12 +676,25 @@ void DateTimeTestCase::TestTimeParse() static const struct ParseTestData { const wxChar *format; - Date date; + Date date; // NB: this should be in UTC bool good; } parseTestDates[] = { - { _T("Sat, 18 Dec 1999 00:46:40 +0100"), { 18, wxDateTime::Dec, 1999, 00, 46, 40, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, true }, - { _T("Wed, 1 Dec 1999 05:17:20 +0300"), { 1, wxDateTime::Dec, 1999, 03, 17, 20, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, true }, + { + _T("Sat, 18 Dec 1999 00:46:40 +0100"), + { 17, wxDateTime::Dec, 1999, 23, 46, 40, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, + true + }, + { + _T("Wed, 1 Dec 1999 05:17:20 +0300"), + { 1, wxDateTime::Dec, 1999, 2, 17, 20, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, + true + }, + { + _T("Sun, 28 Aug 2005 03:31:30 +0200"), + { 28, wxDateTime::Aug, 2005, 1, 31, 30, 0.0, wxDateTime::Inv_WeekDay, 0, 0 }, + true + }, }; for ( size_t n = 0; n < WXSIZEOF(parseTestDates); n++ ) @@ -691,10 +706,7 @@ void DateTimeTestCase::TestTimeParse() { CPPUNIT_ASSERT( parseTestDates[n].good ); - wxDateTime dtReal = parseTestDates[n].date.DT(); - //RN: We need this because the tests are based on - //a non-GMT time zone - dtReal.MakeTimezone(wxDateTime::WEST, true); + wxDateTime dtReal = parseTestDates[n].date.DT().FromUTC(); CPPUNIT_ASSERT( dt == dtReal ); } else // failed to parse