summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/tools/qdatetime_p.h4
-rw-r--r--src/corelib/tools/qtimezoneprivate_tz.cpp55
-rw-r--r--tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp7
3 files changed, 43 insertions, 23 deletions
diff --git a/src/corelib/tools/qdatetime_p.h b/src/corelib/tools/qdatetime_p.h
index 4d30d4192b..b3d00745d0 100644
--- a/src/corelib/tools/qdatetime_p.h
+++ b/src/corelib/tools/qdatetime_p.h
@@ -140,6 +140,10 @@ public:
// Inlined for its one caller in qdatetime.cpp
inline void setUtcOffsetByTZ(qint64 atMSecsSinceEpoch);
#endif // timezone
+
+ // ### Qt 5.14: expose publicly in QDateTime
+ // The first and last years of which QDateTime can represent some part:
+ enum class YearRange : qint32 { First = -292275056, Last = +292278994 };
};
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qtimezoneprivate_tz.cpp b/src/corelib/tools/qtimezoneprivate_tz.cpp
index f75a61977d..7d85bc077d 100644
--- a/src/corelib/tools/qtimezoneprivate_tz.cpp
+++ b/src/corelib/tools/qtimezoneprivate_tz.cpp
@@ -39,6 +39,7 @@
#include "qtimezone.h"
#include "qtimezoneprivate_p.h"
+#include "qdatetime_p.h" // ### Qt 5.14: remove once YearRange is on QDateTime
#include <QtCore/QFile>
#include <QtCore/QHash>
@@ -520,19 +521,14 @@ PosixZone PosixZone::parse(const char *&pos, const char *end)
static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArray &posixRule,
int startYear, int endYear,
- int lastTranMSecs)
+ qint64 lastTranMSecs)
{
QVector<QTimeZonePrivate::Data> result;
- // Limit year by qint64 max size for msecs
- if (startYear > 292278994)
- startYear = 292278994;
- if (endYear > 292278994)
- endYear = 292278994;
-
// POSIX Format is like "TZ=CST6CDT,M3.2.0/2:00:00,M11.1.0/2:00:00"
// i.e. "std offset dst [offset],start[/time],end[/time]"
- // See the section about TZ at http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
+ // See the section about TZ at
+ // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
QList<QByteArray> parts = posixRule.split(',');
PosixZone stdZone, dstZone = PosixZone::invalid();
@@ -583,6 +579,13 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra
else
stdTime = QTime(2, 0, 0);
+ // Limit year to the range QDateTime can represent:
+ const int minYear = int(QDateTimePrivate::YearRange::First);
+ const int maxYear = int(QDateTimePrivate::YearRange::Last);
+ startYear = qBound(minYear, startYear, maxYear);
+ endYear = qBound(minYear, endYear, maxYear);
+ Q_ASSERT(startYear <= endYear);
+
for (int year = startYear; year <= endYear; ++year) {
QTimeZonePrivate::Data dstData;
QDateTime dst(calculatePosixDate(dstDateRule, year), dstTime, Qt::UTC);
@@ -598,13 +601,16 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra
stdData.standardTimeOffset = stdZone.offset;
stdData.daylightTimeOffset = 0;
stdData.abbreviation = stdZone.name;
- // Part of the high year will overflow
- if (year == 292278994 && (dstData.atMSecsSinceEpoch < 0 || stdData.atMSecsSinceEpoch < 0)) {
+ // 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;
}
+ } 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 {
@@ -794,6 +800,8 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId)
tran.atMSecsSinceEpoch = tz_tran.tz_time * 1000;
m_tranTimes.append(tran);
}
+ if (m_tranTimes.isEmpty() && m_posixRule.isEmpty())
+ return; // Invalid after all !
if (ianaId.isEmpty())
m_id = systemTimeZoneId();
@@ -954,22 +962,25 @@ QVector<QTimeZonePrivate::Data> QTzTimeZonePrivate::getPosixTransitions(qint64 m
QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
{
// If the required time is after the last transition (or there were none)
- // and we have a POSIX rule then use it:
- if ((m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < forMSecsSinceEpoch)
- && !m_posixRule.isEmpty() && forMSecsSinceEpoch >= 0) {
+ // and we have a POSIX rule, then use it:
+ if (!m_posixRule.isEmpty()
+ && (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < forMSecsSinceEpoch)) {
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(forMSecsSinceEpoch);
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
[forMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
});
- if (it > posixTrans.cbegin()) {
- QTimeZonePrivate::Data data = *--it;
+ // Use most recent, if any in the past; or the first if we have no other rules:
+ if (it > posixTrans.cbegin() || (m_tranTimes.isEmpty() && it < posixTrans.cend())) {
+ QTimeZonePrivate::Data data = *(it > posixTrans.cbegin() ? it - 1 : it);
data.atMSecsSinceEpoch = forMSecsSinceEpoch;
return data;
}
}
+ if (m_tranTimes.isEmpty()) // Only possible if !isValid()
+ return invalidData();
- // Otherwise, if we can find a valid tran, then use its rule:
+ // Otherwise, use the rule for the most recent or first transition:
auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
[forMSecsSinceEpoch] (const QTzTransitionTime &at) {
return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
@@ -989,9 +1000,9 @@ bool QTzTimeZonePrivate::hasTransitions() const
QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const
{
// If the required time is after the last transition (or there were none)
- // and we have a POSIX rule then use it:
- if ((m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < afterMSecsSinceEpoch)
- && !m_posixRule.isEmpty() && afterMSecsSinceEpoch >= 0) {
+ // and we have a POSIX rule, then use it:
+ if (!m_posixRule.isEmpty()
+ && (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < afterMSecsSinceEpoch)) {
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(afterMSecsSinceEpoch);
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
[afterMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
@@ -1012,9 +1023,9 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSince
QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
{
// If the required time is after the last transition (or there were none)
- // and we have a POSIX rule then use it:
- if ((m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < beforeMSecsSinceEpoch)
- && !m_posixRule.isEmpty() && beforeMSecsSinceEpoch > 0) {
+ // and we have a POSIX rule, then use it:
+ if (!m_posixRule.isEmpty()
+ && (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < beforeMSecsSinceEpoch)) {
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(beforeMSecsSinceEpoch);
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
[beforeMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
diff --git a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp
index eff9835776..bb6c48a2ed 100644
--- a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp
+++ b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp
@@ -539,8 +539,13 @@ void tst_QTimeZone::checkOffset_data()
int year, month, day, hour, min, sec;
int std, dst;
} table[] = {
- // Zone with no transitions (QTBUG-74614, when TZ backend uses minimalist data)
+ // Zone with no transitions (QTBUG-74614, QTBUG-74666, when TZ backend uses minimal data)
{ "Etc/UTC", "epoch", 1970, 1, 1, 0, 0, 0, 0, 0 },
+ { "Etc/UTC", "pre_int32", 1901, 12, 13, 20, 45, 51, 0, 0 },
+ { "Etc/UTC", "post_int32", 2038, 1, 19, 3, 14, 9, 0, 0 },
+ { "Etc/UTC", "post_uint32", 2106, 2, 7, 6, 28, 17, 0, 0 },
+ { "Etc/UTC", "initial", -292275056, 5, 16, 16, 47, 5, 0, 0 },
+ { "Etc/UTC", "final", 292278994, 8, 17, 7, 12, 55, 0, 0 },
// Kiev: regression test for QTBUG-64122 (on MS):
{ "Europe/Kiev", "summer", 2017, 10, 27, 12, 0, 0, 2 * 3600, 3600 },
{ "Europe/Kiev", "winter", 2017, 10, 29, 12, 0, 0, 2 * 3600, 0 }