summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2020-09-21 13:46:02 +0200
committerEdward Welbourne <edward.welbourne@qt.io>2020-09-23 16:59:08 +0200
commit1ec67b5e266864fb9d37f04e0239277b8e8978e2 (patch)
tree00e644557b994c8aa99f9d1b34526f60e896e278
parent3bd6901429611636d254bb0140799b019112985d (diff)
Make more consistent use of m_offsetFromUtc()
In QDateTime::toMSecsSinceEpoch(), all non-short date-times know their offsets from UTC, so can use that, saving the TimeZone ones the need to call (costly) zoneMSecsToEpochMSecs() and, when not short, the LocalTime ones the need to call localMSecsToEpochMSecs(). Moved the setting of offset clear of refreshZonedDateTime()'s check that the date and time match the milliseconds being handled, so that a sensible offset value is set even when the date-time comes back invalid due to being in a spring forward. As refreshZonedDateTime() now sets d's m_offsetFromUtc for both specs, it no longer needs QDateTimePrivate::setUtcOffsetByTZ(). In any case, the private time-zone access that provided is equivalent to the difference we already have between msecs and epochMSecs, except in a zone-gap. Add assertions to confirm this in zoneMSecsToEpochMSecs(). This brought to light a problem with whole-day transitions, where QDate::startOfDay() assumed the gap it was hitting would end by end of day. Search backwards from noon tomorrow, instead; likewise, for endOfDay(), search forward from yesterday. Task-number: QTBUG-75585 Change-Id: I6151d1dfccab321605753c98e01152281461bfe2 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r--src/corelib/time/qdatetime.cpp41
-rw-r--r--src/corelib/time/qdatetime_p.h3
2 files changed, 25 insertions, 19 deletions
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp
index c6a41855b0..af77a0ee26 100644
--- a/src/corelib/time/qdatetime.cpp
+++ b/src/corelib/time/qdatetime.cpp
@@ -874,7 +874,10 @@ QDateTime QDate::startOfDay(const QTimeZone &zone) const
// The start of the day must have fallen in a spring-forward's gap; find the spring-forward:
if (zone.hasTransitions()) {
- QTimeZone::OffsetData tran = zone.previousTransition(QDateTime(*this, QTime(23, 59, 59, 999), zone));
+ QTimeZone::OffsetData tran
+ // There's unlikely to be another transition before noon tomorrow.
+ // However, the whole of today may have been skipped !
+ = zone.previousTransition(QDateTime(addDays(1), QTime(12, 0), zone));
const QDateTime &at = tran.atUtc.toTimeZone(zone);
if (at.isValid() && at.date() == *this)
return at;
@@ -997,7 +1000,10 @@ QDateTime QDate::endOfDay(const QTimeZone &zone) const
// The end of the day must have fallen in a spring-forward's gap; find the spring-forward:
if (zone.hasTransitions()) {
- QTimeZone::OffsetData tran = zone.nextTransition(QDateTime(*this, QTime(0, 0), zone));
+ QTimeZone::OffsetData tran
+ // It's unlikely there's been another transition since yesterday noon.
+ // However, the whole of today may have been skipped !
+ = zone.nextTransition(QDateTime(addDays(-1), QTime(12, 0), zone));
const QDateTime &at = tran.atUtc.toTimeZone(zone);
if (at.isValid() && at.date() == *this)
return at;
@@ -2794,13 +2800,6 @@ static inline bool usesSameOffset(const QDateTimeData &a, const QDateTimeData &b
return false;
}
-#if QT_CONFIG(timezone)
-void QDateTimePrivate::setUtcOffsetByTZ(qint64 atMSecsSinceEpoch)
-{
- m_offsetFromUtc = m_timeZone.d->offsetFromUtc(atMSecsSinceEpoch);
-}
-#endif
-
// Refresh the LocalTime or TimeZone validity and offset
static void refreshZonedDateTime(QDateTimeData &d, Qt::TimeSpec spec)
{
@@ -2829,14 +2828,13 @@ static void refreshZonedDateTime(QDateTimeData &d, Qt::TimeSpec spec)
} else if (d->m_timeZone.isValid()) {
epochMSecs = QDateTimePrivate::zoneMSecsToEpochMSecs(
msecs, d->m_timeZone, extractDaylightStatus(status), &testDate, &testTime);
- d->setUtcOffsetByTZ(epochMSecs);
#endif // timezone
} // else: testDate, testTime haven't been set, so are invalid.
+ // Cache the offset to use in offsetFromUtc() &c.
+ offsetFromUtc = (msecs - epochMSecs) / 1000;
if (testDate.isValid() && testTime.isValid()
&& timeToMSecs(testDate, testTime) == msecs) {
status |= QDateTimePrivate::ValidDateTime;
- // Cache the offset to use in offsetFromUtc()
- offsetFromUtc = (msecs - epochMSecs) / 1000;
} else {
status &= ~QDateTimePrivate::ValidDateTime;
}
@@ -3155,6 +3153,12 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT
Q_ASSERT(zone.isValid());
// Get the effective data from QTimeZone
QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs, int(hint));
+ Q_ASSERT(zone.d->offsetFromUtc(data.atMSecsSinceEpoch) == data.offsetFromUtc);
+ Q_ASSERT((zoneMSecs - data.atMSecsSinceEpoch) / 1000 == data.offsetFromUtc
+ // If zoneMSecs fell in a spring-forward's gap, we get this instead:
+ || (zoneMSecs - data.atMSecsSinceEpoch) / 1000 == data.standardTimeOffset
+ // If it fell in a skipped day (Pacific date-line crossings), this happens:
+ || (data.offsetFromUtc - (zoneMSecs - data.atMSecsSinceEpoch) / 1000) % 86400 == 0);
// Docs state any time before 1970-01-01 will *not* have any DST applied
// but all affected times afterwards will have DST applied.
if (data.atMSecsSinceEpoch < 0) {
@@ -3772,20 +3776,25 @@ qint64 QDateTime::toMSecsSinceEpoch() const
return getMSecs(d);
case Qt::OffsetFromUTC:
+ Q_ASSERT(!d.isShort());
return d->m_msecs - (d->m_offsetFromUtc * 1000);
case Qt::LocalTime: {
// recalculate the local timezone
auto status = extractDaylightStatus(getStatus(d));
+ // If short, use offset saved by refreshZonedDateTime() on creation:
+ if (!d.isShort())
+ return d->m_msecs - d->m_offsetFromUtc * 1000;
+ // Offset from UTC not recorded: need to recompute.
return localMSecsToEpochMSecs(getMSecs(d), &status);
}
case Qt::TimeZone:
+ Q_ASSERT(!d.isShort());
#if QT_CONFIG(timezone)
- if (d->m_timeZone.isValid()) {
- return QDateTimePrivate::zoneMSecsToEpochMSecs(d->m_msecs, d->m_timeZone,
- extractDaylightStatus(getStatus(d)));
- }
+ // Use offset refreshZonedDateTime() saved creation:
+ if (d->m_timeZone.isValid())
+ return d->m_msecs - d->m_offsetFromUtc * 1000;
#endif
return 0;
}
diff --git a/src/corelib/time/qdatetime_p.h b/src/corelib/time/qdatetime_p.h
index c500b023c4..2b690841ee 100644
--- a/src/corelib/time/qdatetime_p.h
+++ b/src/corelib/time/qdatetime_p.h
@@ -128,9 +128,6 @@ public:
static qint64 zoneMSecsToEpochMSecs(qint64 msecs, const QTimeZone &zone,
DaylightStatus hint = UnknownDaylightTime,
QDate *localDate = nullptr, QTime *localTime = nullptr);
-
- // Inlined for its one caller in qdatetime.cpp
- inline void setUtcOffsetByTZ(qint64 atMSecsSinceEpoch);
#endif // timezone
};