From 543c522cb8ee7b46ae68af914bfa7caabcd06636 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 29 Nov 2017 22:58:06 +0100 Subject: [PATCH] Explicitly disambiguate local time zone from UTC Don't rely on time zone offset to check whether it is local as this doesn't, and can't, work for the local time zone in Great Britain which uses the same offset as UTC, but does use DST, unlike the latter. Add a unit test (albeit disabled by default) checking that the code that previously didn't work correctly in BST does work now (run the tests using "TZ=Europe/London ./test wxDateTime-BST-bugs" under Unix to test). Closes #14317, #17220. See #10445. --- include/wx/datetime.h | 4 +++- interface/wx/datetime.h | 11 +++++++++++ src/common/datetime.cpp | 14 ++++++++++---- tests/datetime/datetimetest.cpp | 25 ++++++++++++++++++++++++- 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/include/wx/datetime.h b/include/wx/datetime.h index e6b271c60c..8ed8dfcbda 100644 --- a/include/wx/datetime.h +++ b/include/wx/datetime.h @@ -306,7 +306,9 @@ public: return tz; } - long GetOffset() const { return m_offset; } + bool IsLocal() const { return m_offset == -1; } + + long GetOffset() const; private: // offset for this timezone from GMT in seconds diff --git a/interface/wx/datetime.h b/interface/wx/datetime.h index 0f9eb55966..289ac1da48 100644 --- a/interface/wx/datetime.h +++ b/interface/wx/datetime.h @@ -254,6 +254,17 @@ public: /// Create a time zone with the given offset in seconds. static TimeZone Make(long offset); + /** + Return true if this is the local time zone. + + This method can be useful for distinguishing between UTC time zone + and local time zone in Great Britain, which use the same offset as + UTC (i.e. 0), but do use DST. + + @since 3.1.1 + */ + bool IsLocal() const; + /// Return the offset of this time zone from UTC, in seconds. long GetOffset() const; }; diff --git a/src/common/datetime.cpp b/src/common/datetime.cpp index 1fda366782..5b6013b8a8 100644 --- a/src/common/datetime.cpp +++ b/src/common/datetime.cpp @@ -456,9 +456,8 @@ wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz) switch ( 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 = -wxGetTimeZone(); + // Use a special value for local time zone. + m_offset = -1; break; case wxDateTime::GMT_12: @@ -503,6 +502,13 @@ wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz) } } +long wxDateTime::TimeZone::GetOffset() const +{ + // get the offset from C RTL: it returns the difference GMT-local + // while we want to have the offset _from_ GMT, hence the '-' + return m_offset == -1 ? -wxGetTimeZone() : m_offset; +} + // ---------------------------------------------------------------------------- // static functions // ---------------------------------------------------------------------------- @@ -1432,7 +1438,7 @@ unsigned long wxDateTime::GetAsDOS() const const tm* wxTryGetTm(tm& tmstruct, time_t t, const wxDateTime::TimeZone& tz) { - if ( tz.GetOffset() == -wxGetTimeZone() ) + if ( tz.IsLocal() ) { // we are working with local time return wxLocaltime_r(&t, &tmstruct); diff --git a/tests/datetime/datetimetest.cpp b/tests/datetime/datetimetest.cpp index fdcd9f70cb..0ae4321900 100644 --- a/tests/datetime/datetimetest.cpp +++ b/tests/datetime/datetimetest.cpp @@ -683,7 +683,7 @@ void DateTimeTestCase::TestTimeFormat() const long timeZonesOffsets[] = { - wxDateTime::TimeZone(wxDateTime::Local).GetOffset(), + -1, // This is pseudo-offset used for local time zone // Fictitious TimeZone offsets to ensure time zone formating and // interpretation works @@ -1638,4 +1638,27 @@ TEST_CASE("wxDateTime::SetOnDST", "[datetime][dst]") } } +// Tests random problems that used to appear in BST time zone during DST. +// This test is disabled by default as it only passes in BST time zone, due to +// the times hard-coded in it. +TEST_CASE("wxDateTime-BST-bugs", "[datetime][dst][BST][.]") +{ + SECTION("bug-17220") + { + wxDateTime dt; + dt.Set(22, wxDateTime::Oct, 2015, 10, 10, 10, 10); + REQUIRE( dt.IsDST() ); + + CHECK( dt.GetTm().hour == 10 ); + CHECK( dt.GetTm(wxDateTime::UTC).hour == 9 ); + + CHECK( dt.Format("%Y-%m-%d %H:%M:%S", wxDateTime::Local ) == "2015-10-22 10:10:10" ); + CHECK( dt.Format("%Y-%m-%d %H:%M:%S", wxDateTime::UTC ) == "2015-10-22 09:10:10" ); + + dt.MakeFromUTC(); + CHECK( dt.Format("%Y-%m-%d %H:%M:%S", wxDateTime::Local ) == "2015-10-22 11:10:10" ); + CHECK( dt.Format("%Y-%m-%d %H:%M:%S", wxDateTime::UTC ) == "2015-10-22 10:10:10" ); + } +} + #endif // wxUSE_DATETIME