summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2021-04-23 19:43:44 +0200
committerEdward Welbourne <edward.welbourne@qt.io>2021-04-27 14:12:35 +0200
commit7fa8315eb13ecd99001612fbfe5c0036e25fd344 (patch)
treea4a710833578b530c1896eb26c308ef230d999a8
parent2383e82bcfcb9ef1e81eac3a4810b3860ea3cfb9 (diff)
Fix handling of a POSIX zone rule describing permanent DST
The description necessarily has fake transitions at start and end of the year, potentially outside the year. These transitions should not be reported by QTzTimeZonePrivate as transitions, although its data() must find a "transition" whose data it can use (as in the permanent standard time case, which could potentially be represented the same way, although there's a saner way to do so, that the code already handles) to report the zone's properties. In the process, fix (and make more straightforward) the convoluted decision-making code that was deciding which transitions to include in the returned list. It was assuming invalidMSecs() would be set as the atMSecsSinceEpoch of a transition, although this is computed in a way that makes that value most unlikely, even when the result is invalid. It also rather confusingly mixed < 0 tests as tests for overflow with the one < 0 test that's about ignoring DST before 1970. Also added comments to clarify some of what's going on there. Expanded a recently-added test of a permanent DST zone to verify this now works correctly. Change-Id: Ia8d98f433fb1e479dba5479220a62196c30f0244 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r--src/corelib/time/qtimezoneprivate_tz.cpp61
-rw-r--r--tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp6
2 files changed, 50 insertions, 17 deletions
diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp
index 8f02e3a7d6..300daf70d1 100644
--- a/src/corelib/time/qtimezoneprivate_tz.cpp
+++ b/src/corelib/time/qtimezoneprivate_tz.cpp
@@ -619,34 +619,61 @@ static QList<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArray
Q_ASSERT(startYear <= endYear);
for (int year = startYear; year <= endYear; ++year) {
- QTimeZonePrivate::Data dstData;
+ // Note: std and dst, despite being QDateTime(,, Qt::UTC), have the
+ // date() and time() of the *zone*'s description of the transition
+ // moments; the atMSecsSinceEpoch values computed from them are
+ // correctly offse to be UTC-based.
+
+ QTimeZonePrivate::Data dstData; // Transition to DST
QDateTime dst(calculatePosixDate(dstDateRule, year).startOfDay(Qt::UTC).addSecs(dstTime));
- dstData.atMSecsSinceEpoch = dst.toMSecsSinceEpoch() - (stdZone.offset * 1000);
+ dstData.atMSecsSinceEpoch = dst.toMSecsSinceEpoch() - stdZone.offset * 1000;
dstData.offsetFromUtc = dstZone.offset;
dstData.standardTimeOffset = stdZone.offset;
dstData.daylightTimeOffset = dstZone.offset - stdZone.offset;
dstData.abbreviation = dstZone.name;
- QTimeZonePrivate::Data stdData;
+ QTimeZonePrivate::Data stdData; // Transition to standard time
QDateTime std(calculatePosixDate(stdDateRule, year).startOfDay(Qt::UTC).addSecs(stdTime));
- stdData.atMSecsSinceEpoch = std.toMSecsSinceEpoch() - (dstZone.offset * 1000);
+ stdData.atMSecsSinceEpoch = std.toMSecsSinceEpoch() - dstZone.offset * 1000;
stdData.offsetFromUtc = stdZone.offset;
stdData.standardTimeOffset = stdZone.offset;
stdData.daylightTimeOffset = 0;
stdData.abbreviation = stdZone.name;
- // Part of maxYear will overflow (likewise for minYear, below):
- if (year == maxYear && (dstData.atMSecsSinceEpoch < 0 || stdData.atMSecsSinceEpoch < 0)) {
- if (dstData.atMSecsSinceEpoch > 0) {
- result << dstData;
- } else if (stdData.atMSecsSinceEpoch > 0) {
- result << stdData;
+
+ if (year == startYear) {
+ // Handle the special case of fixed state, which may be represented
+ // by fake transitions at start and end of each year:
+ if (dstData.atMSecsSinceEpoch < stdData.atMSecsSinceEpoch) {
+ if (dst <= QDate(year, 1, 1).startOfDay(Qt::UTC)
+ && std >= QDate(year, 12, 31).endOfDay(Qt::UTC)) {
+ // Permanent DST:
+ dstData.atMSecsSinceEpoch = lastTranMSecs;
+ result << dstData;
+ return result;
+ }
+ } else {
+ if (std <= QDate(year, 1, 1).startOfDay(Qt::UTC)
+ && dst >= QDate(year, 12, 31).endOfDay(Qt::UTC)) {
+ // Permanent Standard time, perversely described:
+ stdData.atMSecsSinceEpoch = lastTranMSecs;
+ result << stdData;
+ return result;
+ }
}
- } else if (year < 1970) { // We ignore DST before the epoch.
- if (year > minYear || stdData.atMSecsSinceEpoch != QTimeZonePrivate::invalidMSecs())
- result << stdData;
- } else if (dst < std) {
- result << dstData << stdData;
- } else {
- result << stdData << dstData;
+ }
+
+ const bool useStd = std.isValid() && std.date().year() == year && !stdZone.name.isEmpty();
+ const bool useDst = dst.isValid() && dst.date().year() == year && !dstZone.name.isEmpty()
+ // We ignore DST before 1970 -- for now.
+ && dstData.atMSecsSinceEpoch >= 0;
+ if (useStd && useDst) {
+ if (dst < std)
+ result << dstData << stdData;
+ else
+ result << stdData << dstData;
+ } else if (useStd) {
+ result << stdData;
+ } else if (useDst) {
+ result << dstData;
}
}
return result;
diff --git a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp
index 19b638cce7..4acbd68f87 100644
--- a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp
+++ b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp
@@ -1152,6 +1152,12 @@ void tst_QTimeZone::tzTest()
// 1am on the next year's Jan 1st; check we don't do that:
QVERIFY(permaDst.isDaylightTime(
QDateTime(QDate(2020, 1, 1), QTime(1, 30), utcP1).toMSecsSinceEpoch()));
+ // It shouldn't have any transitions. QTimeZone::hasTransitions() only says
+ // whether the backend supports them, so ask for transitions in a wide
+ // enough interval that one would show up, if there are any:
+ QVERIFY(permaDst.transitions(QDate(2015, 1, 1).startOfDay(Qt::UTC).toMSecsSinceEpoch(),
+ QDate(2020, 1, 1).startOfDay(Qt::UTC).toMSecsSinceEpoch()
+ ).isEmpty());
QTimeZone tzBrazil("BRT+3"); // parts of Northern Brazil, as a POSIX rule
QVERIFY(tzBrazil.isValid());