summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qdatetime.cpp
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2016-06-14 13:53:23 +0200
committerEdward Welbourne <edward.welbourne@qt.io>2016-12-12 13:58:26 +0000
commit6e3f58cbbe313c3b843f69f3110e3c795fef6da8 (patch)
treed1654bb2305546d9785e1b5dfdd40df4f58bad90 /src/corelib/tools/qdatetime.cpp
parent57b0f54bb68760958ea315d9ba2000c2a738270d (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.cpp24
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
}
}