]> git.saurik.com Git - wxWidgets.git/commitdiff
Consistently handle DST start time in wxDateTime::Set().
authorVadim Zeitlin <vadim@wxwidgets.org>
Sun, 8 Sep 2013 18:00:58 +0000 (18:00 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sun, 8 Sep 2013 18:00:58 +0000 (18:00 +0000)
Always move the dates invalid due to DST (i.e. falling into the "missing" hour
on the DST start date) forward, as GNU libc does, even when using a different
CRT implementation, such as MSVC one which moves the invalid dates backwards.

This seems more expected and also fixes an especially bad problem which
happened due to moving the date backwards in Brazilian time zone where DST
starts at midnight as doing this changed the day and totally broke ParseDate()
assumption that setting wxDateTime to 00:00:00 at the given date really did
set it to this date.

Closes #15419.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74777 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
interface/wx/datetime.h
src/common/datetime.cpp
tests/datetime/datetimetest.cpp

index 8fa6d43046f4aeb8f1eb33e7917084ce132e9254..17df9bf9733039b5bf325e762485d07a54c78889 100644 (file)
@@ -562,6 +562,7 @@ Major new features in this release
 
 All:
 
+- Adjust dates invalid due to DST consistently under all platforms in wxDateTime.
 - Allow using custom HTTP methods with wxHTTP (Kolya Kosenko).
 - Add wxFileName::SetPermissions() (Catalin Raceanu).
 - Fix build with wxUSE_FFILE==0 (jroemmler).
index c181b9c75ea17db7bea124be54d529f3929b4264..f7f1906921470b04090aa747cf48beef9c0d02a6 100644 (file)
@@ -389,11 +389,25 @@ public:
     /**
         Sets the date to be equal to Today() and the time from supplied
         parameters.
+
+        See the full Set() overload for the remarks about DST.
     */
     wxDateTime& Set(wxDateTime_t hour, wxDateTime_t minute = 0,
                     wxDateTime_t second = 0, wxDateTime_t millisec = 0);
     /**
         Sets the date and time from the parameters.
+
+        If the function parameters are invalid, e.g. @a month is February and
+        @a day is 30, the object is left in an invalid state, i.e. IsValid()
+        method will return @false.
+
+        If the specified time moment is invalid due to DST, i.e. it falls into
+        the "missing" hour on the date on which the DST starts, a valid
+        wxDateTime object is still constructed but its hour component is moved
+        forward to ensure that it corresponds to a valid moment in the local
+        time zone. For example, in the CET time zone the DST started on
+        2013-03-31T02:00:00 in 2013 and so setting the object to 2:30 at this
+        date actually sets the hour to 3, and not 2.
     */
     wxDateTime& Set(wxDateTime_t day, Month month,
                     int year = Inv_Year, wxDateTime_t hour = 0,
index 48818ea622545e620457547c5c0e48015c45f4c0..669d0a3269690dc02ec828c9ced617171a86022b 100644 (file)
@@ -1129,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,
index 8eefcb862f4b4f528ee52d6cb1c6b096a04e727f..112e3291a6d289d90dbb34c88708fb6f60c25492 100644 (file)
@@ -1335,6 +1335,19 @@ void DateTimeTestCase::TestDSTBug()
     CPPUNIT_ASSERT_EQUAL(0, (int)dt2.GetSecond());
     CPPUNIT_ASSERT_EQUAL(0, (int)dt2.GetMillisecond());
 #endif // CHANGE_SYSTEM_DATE
+
+    // Verify that setting the date to the beginning of the DST period moves it
+    // forward (as this date on its own would be invalid). The problem here is
+    // that our GetBeginDST() is far from being trustworthy, so just try a
+    // couple of dates for the common time zones and check that all of them are
+    // either unchanged or moved forward.
+    wxDateTime dtDST(10, wxDateTime::Mar, 2013, 2, 0, 0);
+    if ( dtDST.GetHour() != 2 )
+        CPPUNIT_ASSERT_EQUAL( 3, dtDST.GetHour() );
+
+    dtDST = wxDateTime(31, wxDateTime::Mar, 2013, 2, 0, 0);
+    if ( dtDST.GetHour() != 2 )
+        CPPUNIT_ASSERT_EQUAL( 3, dtDST.GetHour() );
 }
 
 void DateTimeTestCase::TestDateOnly()