diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2016-06-14 13:53:23 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2016-12-12 13:58:26 +0000 |
commit | 6e3f58cbbe313c3b843f69f3110e3c795fef6da8 (patch) | |
tree | d1654bb2305546d9785e1b5dfdd40df4f58bad90 /src/corelib/tools/qdatetime.cpp | |
parent | 57b0f54bb68760958ea315d9ba2000c2a738270d (diff) |
QDateTime, QTimeZone: fix mappings from zone time to UTC
Such mappings are ill-defined in the presence of daylight-savings time
(DST); at its transitions, you need information about whether DST is
active or not to determine the correct UTC value. Existing code did
not have a way to be told that hint, so could not be correct.
Fixing this required changing the (thankfully private) APIs by which
QDateTime accessed QTimeZone's information stipulated by zone time.
In QDateTime, this required propagating the needed hint, when DST
status was known.
QAndroidTimeZonePrivate overloaded QTimeZonePrivate::dataForLocalTime
with an implementation that works whenever !hasTransitions(); the base
implementation handled this case lamely, so I've moved the Android
implementation there, to have only one place for both re-writes.
Amended tst_QDateTime's expected failures; passing a date and time to
the constructor *is* ambiguous when the moment indicated is in a
transition. I have changed which way we resolve that ambiguity.
Added round-trip test of QDateTime's fromMSecs/toMSecs (but as a
QTimeZone test, since that's what's actually getting tested), based on
a test-case from Marko Kangas. Initially failed for various zones,
each at one hour-offset; and, on some platforms, for some zones, at
all offsets. These last revealed that a platform may claim to have
zone information yet, for some zones, lack it (or have very incomplete
information). In each case, despite this, the platform does give
offsetFromUtc(). (The test also found another pre-existing bug on
Linux; fixed in an earlier commit.)
To accommodate these gaps in transition data, the transition-based
code now falls back to the offsetFromUtc()-based code (used when there
are no transitions) if it can't find a previous transition (which, in
any case, it needs to do its job).
Task-number: QTBUG-56460
Task-number: QTBUG-56397
Task-number: QTBUG-52284
Change-Id: I2f7422a9e9d3767940b1901d887c6a2c1f36ac9f
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib/tools/qdatetime.cpp')
-rw-r--r-- | src/corelib/tools/qdatetime.cpp | 24 |
1 files changed, 18 insertions, 6 deletions
diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index 896b4a3840..f81c578bc8 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -2594,7 +2594,7 @@ static void refreshDateTime(QDateTimeData &d) if (!d->m_timeZone.isValid()) status &= ~QDateTimePrivate::ValidDateTime; else - epochMSecs = QDateTimePrivate::zoneMSecsToEpochMSecs(msecs, d->m_timeZone, &testDate, &testTime); + epochMSecs = QDateTimePrivate::zoneMSecsToEpochMSecs(msecs, d->m_timeZone, extractDaylightStatus(status), &testDate, &testTime); } #endif // timezone @@ -2915,11 +2915,13 @@ inline QDateTime::Data QDateTimePrivate::create(const QDate &toDate, const QTime } // Convert a TimeZone time expressed in zone msecs encoding into a UTC epoch msecs +// DST transitions are disambiguated by hint. inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QTimeZone &zone, + DaylightStatus hint, QDate *localDate, QTime *localTime) { // Get the effective data from QTimeZone - QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs); + QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs, int(hint)); // Docs state any LocalTime before 1970-01-01 will *not* have any DST applied // but all affected times afterwards will have DST applied. if (data.atMSecsSinceEpoch >= 0) { @@ -3532,7 +3534,8 @@ qint64 QDateTime::toMSecsSinceEpoch() const #if !QT_CONFIG(timezone) return 0; #else - return QDateTimePrivate::zoneMSecsToEpochMSecs(d->m_msecs, d->m_timeZone); + return QDateTimePrivate::zoneMSecsToEpochMSecs(d->m_msecs, d->m_timeZone, + extractDaylightStatus(getStatus(d))); #endif } Q_UNREACHABLE(); @@ -3631,10 +3634,16 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs) // Docs state any LocalTime before 1970-01-01 will *not* have any DST applied // but all affected times afterwards will have DST applied. d.detach(); - if (msecs >= 0) + if (msecs >= 0) { + status = mergeDaylightStatus(status, + d->m_timeZone.d->isDaylightTime(msecs) + ? QDateTimePrivate::DaylightTime + : QDateTimePrivate::StandardTime); d->m_offsetFromUtc = d->m_timeZone.d->offsetFromUtc(msecs); - else + } else { + status = mergeDaylightStatus(status, QDateTimePrivate::StandardTime); d->m_offsetFromUtc = d->m_timeZone.d->standardTimeOffset(msecs); + } msecs = msecs + (d->m_offsetFromUtc * 1000); status = status | QDateTimePrivate::ValidDate @@ -3922,7 +3931,10 @@ static inline void massageAdjustedDateTime(const QDateTimeData &d, QDate *date, localMSecsToEpochMSecs(timeToMSecs(*date, *time), &status, date, time); #if QT_CONFIG(timezone) } else if (spec == Qt::TimeZone) { - QDateTimePrivate::zoneMSecsToEpochMSecs(timeToMSecs(*date, *time), d->m_timeZone, date, time); + QDateTimePrivate::zoneMSecsToEpochMSecs(timeToMSecs(*date, *time), + d->m_timeZone, + QDateTimePrivate::UnknownDaylightTime, + date, time); #endif // timezone } } |