summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
authorJohn Layt <jlayt@kde.org>2013-02-11 13:44:37 +0000
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-23 01:14:03 +0200
commit55f5b29d7975d7e642ec258e44ca23726e1dfa0d (patch)
treea962c946ed58690d67bba1982556f3cb97545969 /src/corelib
parent8af776d4140c19baccb8e2767ed8b57cc79ccb06 (diff)
QDateTime - Add QTimeZone support
Add support to QDateTime for time zones using the new QTimeZone class. [ChangeLog][QtCore][QDateTime] Add support for a new Qt::TimeZone spec to be used with QTimeZone to define times in a specific time zone. Change-Id: I21bfa52a8ba8989b55bb74e025d1f2b2b623b2a7 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/global/qnamespace.h3
-rw-r--r--src/corelib/global/qnamespace.qdoc1
-rw-r--r--src/corelib/tools/qdatetime.cpp309
-rw-r--r--src/corelib/tools/qdatetime.h19
-rw-r--r--src/corelib/tools/qdatetime_p.h26
-rw-r--r--src/corelib/tools/qtimezone.h2
-rw-r--r--src/corelib/tools/qtimezoneprivate.cpp65
-rw-r--r--src/corelib/tools/qtimezoneprivate_p.h1
8 files changed, 411 insertions, 15 deletions
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index 3d83c2a646..420721cb54 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -1196,7 +1196,8 @@ public:
enum TimeSpec {
LocalTime,
UTC,
- OffsetFromUTC
+ OffsetFromUTC,
+ TimeZone
};
enum DayOfWeek {
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index 5398740316..1cd2321ef6 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -604,6 +604,7 @@
\value LocalTime Locale dependent time (Timezones and Daylight Savings Time).
\value UTC Coordinated Universal Time, replaces Greenwich Mean Time.
\value OffsetFromUTC An offset in seconds from Coordinated Universal Time.
+ \value TimeZone A named time zone using a specific set of Daylight Savings rules.
*/
/*!
diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp
index 997f0e5ff2..ac9639385a 100644
--- a/src/corelib/tools/qdatetime.cpp
+++ b/src/corelib/tools/qdatetime.cpp
@@ -47,6 +47,7 @@
#include "qset.h"
#include "qlocale.h"
#include "qdatetime.h"
+#include "qtimezoneprivate_p.h"
#include "qregexp.h"
#include "qdebug.h"
#ifndef Q_OS_WIN
@@ -2597,9 +2598,26 @@ QDateTimePrivate::QDateTimePrivate(const QDate &toDate, const QTime &toTime, Qt:
setDateTime(toDate, toTime);
}
+#ifndef QT_BOOTSTRAPPED
+QDateTimePrivate::QDateTimePrivate(const QDate &toDate, const QTime &toTime,
+ const QTimeZone &toTimeZone)
+ : m_spec(Qt::TimeZone),
+ m_offsetFromUtc(0),
+ m_timeZone(toTimeZone),
+ m_status(0)
+{
+ setDateTime(toDate, toTime);
+}
+#endif // QT_BOOTSTRAPPED
+
void QDateTimePrivate::setTimeSpec(Qt::TimeSpec spec, int offsetSeconds)
{
clearValidDateTime();
+ clearTimeZoneCached();
+
+#ifndef QT_BOOTSTRAPPED
+ m_timeZone = QTimeZone();
+#endif // QT_BOOTSTRAPPED
switch (spec) {
case Qt::OffsetFromUTC:
@@ -2611,6 +2629,11 @@ void QDateTimePrivate::setTimeSpec(Qt::TimeSpec spec, int offsetSeconds)
m_offsetFromUtc = offsetSeconds;
}
break;
+ case Qt::TimeZone:
+ // Use system time zone instead
+ m_spec = Qt::LocalTime;
+ m_offsetFromUtc = 0;
+ break;
case Qt::UTC:
case Qt::LocalTime:
m_spec = spec;
@@ -2676,6 +2699,12 @@ void QDateTimePrivate::checkValidDateTime()
else
clearValidDateTime();
break;
+ case Qt::TimeZone:
+ // Defer checking until required as can be expensive
+ clearValidDateTime();
+ clearTimeZoneCached();
+ m_offsetFromUtc = 0;
+ break;
case Qt::LocalTime:
// Defer checking until required as can be expensive
clearValidDateTime();
@@ -2687,9 +2716,21 @@ void QDateTimePrivate::checkValidDateTime()
// Refresh the LocalTime validity and offset
void QDateTimePrivate::refreshDateTime()
{
- // Always set by setDateTime so just return
- if (m_spec == Qt::UTC || m_spec == Qt::OffsetFromUTC)
+ switch (m_spec) {
+ case Qt::OffsetFromUTC:
+ case Qt::UTC:
+ // Always set by setDateTime so just return
return;
+ case Qt::TimeZone:
+ // If already cached then don't need to refresh as tz won't change
+ if (isTimeZoneCached())
+ return;
+ // Flag that will have a cached result after calculations
+ setTimeZoneCached();
+ break;
+ case Qt::LocalTime:
+ break;
+ }
// If not valid date and time then is invalid
if (!isValidDate() || !isValidTime()) {
@@ -2698,7 +2739,7 @@ void QDateTimePrivate::refreshDateTime()
return;
}
- // We have a valid date and time and a Qt::LocalTime that needs calculating
+ // We have a valid date and time and a Qt::LocalTime or Qt::TimeZone that needs calculating
QDate date;
QTime time;
getDateTime(&date, &time);
@@ -2706,7 +2747,13 @@ void QDateTimePrivate::refreshDateTime()
// Calling toEpochMSecs will adjust the returned date/time if it does
QDate testDate;
QTime testTime;
- qint64 epochMSecs = localMSecsToEpochMSecs(m_msecs, &testDate, &testTime);
+ qint64 epochMSecs = 0;
+#ifndef QT_BOOTSTRAPPED
+ if (m_spec == Qt::TimeZone)
+ epochMSecs = zoneMSecsToEpochMSecs(m_msecs, m_timeZone, &testDate, &testTime);
+ else
+#endif // QT_BOOTSTRAPPED
+ epochMSecs = localMSecsToEpochMSecs(m_msecs, &testDate, &testTime);
if (testDate == date && testTime == time) {
setValidDateTime();
// Cache the offset to use in toMSecsSinceEpoch()
@@ -2717,6 +2764,25 @@ void QDateTimePrivate::refreshDateTime()
}
}
+#ifndef QT_BOOTSTRAPPED
+// Convert a TimeZone time expressed in zone msecs encoding into a UTC epoch msecs
+qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QTimeZone &zone,
+ QDate *localDate, QTime *localTime)
+{
+ // Get the effective data from QTimeZone
+ QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs);
+ // Docs state any LocalTime before 1970-01-01 will *not* have any Daylight Time applied
+ // but all times afterwards will have Daylight Time applied.
+ if (data.atMSecsSinceEpoch >= 0) {
+ msecsToTime(data.atMSecsSinceEpoch + (data.offsetFromUtc * 1000), localDate, localTime);
+ return data.atMSecsSinceEpoch;
+ } else {
+ msecsToTime(zoneMSecs, localDate, localTime);
+ return zoneMSecs - (data.standardTimeOffset * 1000);
+ }
+}
+#endif // QT_BOOTSTRAPPED
+
/*****************************************************************************
QDateTime member functions
*****************************************************************************/
@@ -2833,7 +2899,17 @@ void QDateTimePrivate::refreshDateTime()
to +/- 99 hours and 59 minutes and whole minutes only. Note that currently
no time zone lies outside the range of +/- 14 hours.
- \sa QDate, QTime, QDateTimeEdit
+ \section2 Time Zone Support
+
+ A Qt::TimeSpec of Qt::TimeZone is also supported in conjunction with the
+ QTimeZone class. This allows you to define a datetime in a named time zone
+ adhering to a consistent set of daylight savings transition rules. For
+ example a time zone of "Europe/Berlin" will apply the daylight savings
+ rules as used in Germany since 1970. Note that the transition rules
+ applied depend on the platform support. See the QTimeZone documentation
+ for more details.
+
+ \sa QDate, QTime, QDateTimeEdit, QTimeZone
*/
/*!
@@ -2867,6 +2943,10 @@ QDateTime::QDateTime(const QDate &date)
If \a spec is Qt::OffsetFromUTC then it will be set to Qt::UTC, i.e. an
offset of 0 seconds. To create a Qt::OffsetFromUTC datetime use the
correct constructor.
+
+ If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime,
+ i.e. the current system time zone. To create a Qt::TimeZone datetime
+ use the correct constructor.
*/
QDateTime::QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec)
@@ -2886,6 +2966,10 @@ QDateTime::QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec)
If the \a spec is Qt::OffsetFromUTC and \a offsetSeconds is 0 then the
timeSpec() will be set to Qt::UTC, i.e. an offset of 0 seconds.
+
+ If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime,
+ i.e. the current system time zone. To create a Qt::TimeZone datetime
+ use the correct constructor.
*/
QDateTime::QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec, int offsetSeconds)
@@ -2893,6 +2977,24 @@ QDateTime::QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec, in
{
}
+#ifndef QT_BOOTSTRAPPED
+/*!
+ \since 5.2
+
+ Constructs a datetime with the given \a date and \a time, using
+ the Time Zone specified by \a timeZone.
+
+ If \a date is valid and \a time is not, the time will be set to 00:00:00.
+
+ If \a timeZone is invalid then the datetime will be invalid.
+*/
+
+QDateTime::QDateTime(const QDate &date, const QTime &time, const QTimeZone &timeZone)
+ : d(new QDateTimePrivate(date, time, timeZone))
+{
+}
+#endif // QT_BOOTSTRAPPED
+
/*!
Constructs a copy of the \a other datetime.
*/
@@ -2943,7 +3045,7 @@ bool QDateTime::isNull() const
Returns true if both the date and the time are valid and they are valid in
the current Qt::TimeSpec, otherwise returns false.
- If the timeSpec() is Qt::LocalTime then the date and time are
+ If the timeSpec() is Qt::LocalTime or Qt::TimeZone then the date and time are
checked to see if they fall in the Standard Time to Daylight Time transition
hour, i.e. if the transition is at 2am and the clock goes forward to 3am
then the time from 02:00:00 to 02:59:59.999 is considered to be invalid.
@@ -2996,6 +3098,39 @@ Qt::TimeSpec QDateTime::timeSpec() const
return d->m_spec;
}
+#ifndef QT_BOOTSTRAPPED
+/*!
+ \since 5.2
+
+ Returns the time zone of the datetime.
+
+ If the timeSpec() is Qt::LocalTime then an instance of the current system
+ time zone will be returned. Note however that if you copy this time zone
+ the instance will not remain in sync if the system time zone changes.
+
+ \sa setTimeZone(), Qt::TimeSpec
+*/
+
+QTimeZone QDateTime::timeZone() const
+{
+ switch (d->m_spec) {
+ case Qt::OffsetFromUTC:
+ if (!d->m_timeZone.isValid())
+ d->m_timeZone = QTimeZone(d->m_offsetFromUtc);
+ return d->m_timeZone;
+ case Qt::UTC:
+ if (!d->m_timeZone.isValid())
+ d->m_timeZone = QTimeZone("UTC");
+ return d->m_timeZone;
+ case Qt::TimeZone :
+ return d->m_timeZone;
+ case Qt::LocalTime:
+ return QTimeZone(QTimeZone::systemTimeZoneId());
+ }
+ return QTimeZone();
+}
+#endif // QT_BOOTSTRAPPED
+
/*!
\since 5.2
@@ -3003,6 +3138,9 @@ Qt::TimeSpec QDateTime::timeSpec() const
If the timeSpec() is Qt::OffsetFromUTC this will be the value originally set.
+ If the timeSpec() is Qt::TimeZone this will be the offset effective in the
+ Time Zone including any Daylight Saving Offset.
+
If the timeSpec() is Qt::LocalTime this will be the difference between the
Local Time and UTC including any Daylight Saving Offset.
@@ -3045,6 +3183,10 @@ QString QDateTime::timeZoneAbbreviation() const
return QStringLiteral("UTC");
case Qt::OffsetFromUTC:
return QLatin1String("UTC") + toOffsetString(Qt::ISODate, d->m_offsetFromUtc);
+ case Qt::TimeZone:
+#ifndef QT_BOOTSTRAPPED
+ return d->m_timeZone.d->abbreviation(d->toMSecsSinceEpoch());
+#endif // QT_BOOTSTRAPPED
case Qt::LocalTime: {
QString abbrev;
localMSecsToEpochMSecs(d->m_msecs, 0, 0, 0, &abbrev);
@@ -3059,7 +3201,7 @@ QString QDateTime::timeZoneAbbreviation() const
Returns if this datetime falls in Daylight Savings Time.
- If the Qt::TimeSpec is not Qt::LocalTime then will always
+ If the Qt::TimeSpec is not Qt::LocalTime or Qt::TimeZone then will always
return false.
\sa timeSpec()
@@ -3071,6 +3213,10 @@ bool QDateTime::isDaylightTime() const
case Qt::UTC:
case Qt::OffsetFromUTC:
return false;
+ case Qt::TimeZone:
+#ifndef QT_BOOTSTRAPPED
+ return d->m_timeZone.d->isDaylightTime(toMSecsSinceEpoch());
+#endif // QT_BOOTSTRAPPED
case Qt::LocalTime: {
QDateTimePrivate::DaylightStatus status;
localMSecsToEpochMSecs(d->m_msecs, 0, 0, &status, 0);
@@ -3112,10 +3258,13 @@ void QDateTime::setTime(const QTime &time)
If \a spec is Qt::OffsetFromUTC then the timeSpec() will be set
to Qt::UTC, i.e. an effective offset of 0.
+ If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime,
+ i.e. the current system time zone.
+
Example:
\snippet code/src_corelib_tools_qdatetime.cpp 19
- \sa timeSpec(), setDate(), setTime(), Qt::TimeSpec
+ \sa timeSpec(), setDate(), setTime(), setTimeZone(), Qt::TimeSpec
*/
void QDateTime::setTimeSpec(Qt::TimeSpec spec)
@@ -3147,6 +3296,28 @@ void QDateTime::setOffsetFromUtc(int offsetSeconds)
d->checkValidDateTime();
}
+#ifndef QT_BOOTSTRAPPED
+/*!
+ \since 5.2
+
+ Sets the time zone used in this datetime to \a toZone.
+ The datetime will refer to a different point in time.
+
+ If \a toZone is invalid then the datetime will be invalid.
+
+ \sa timeZone(), Qt::TimeSpec
+*/
+
+void QDateTime::setTimeZone(const QTimeZone &toZone)
+{
+ detach();
+ d->m_spec = Qt::TimeZone;
+ d->m_offsetFromUtc = 0;
+ d->m_timeZone = toZone;
+ d->m_status = d->m_status & ~QDateTimePrivate::ValidDateTime & ~QDateTimePrivate::TimeZoneCached;
+}
+#endif // QT_BOOTSTRAPPED
+
/*!
\since 4.7
@@ -3234,6 +3405,22 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs)
| QDateTimePrivate::ValidTime
| QDateTimePrivate::ValidDateTime;
break;
+ case Qt::TimeZone:
+#ifndef QT_BOOTSTRAPPED
+ // Docs state any LocalTime before 1970-01-01 will *not* have any Daylight Time applied
+ // but all times afterwards will have Daylight Time applied.
+ if (msecs >= 0)
+ d->m_offsetFromUtc = d->m_timeZone.d->offsetFromUtc(msecs);
+ else
+ d->m_offsetFromUtc = d->m_timeZone.d->standardTimeOffset(msecs);
+ d->m_msecs = msecs + (d->m_offsetFromUtc * 1000);
+ d->m_status = d->m_status
+ | QDateTimePrivate::ValidDate
+ | QDateTimePrivate::ValidTime
+ | QDateTimePrivate::ValidDateTime
+ | QDateTimePrivate::TimeZoneCached;
+#endif // QT_BOOTSTRAPPED
+ break;
case Qt::LocalTime: {
QDate dt;
QTime tm;
@@ -3499,6 +3686,10 @@ QDateTime QDateTime::addDays(qint64 ndays) const
// so call conversion and use the adjusted returned time
if (d->m_spec == Qt::LocalTime)
localMSecsToEpochMSecs(timeToMSecs(date, time), &date, &time);
+#ifndef QT_BOOTSTRAPPED
+ else if (d->m_spec == Qt::TimeZone)
+ d->zoneMSecsToEpochMSecs(timeToMSecs(date, time), d->m_timeZone, &date, &time);
+#endif // QT_BOOTSTRAPPED
dt.d->setDateTime(date, time);
return dt;
}
@@ -3529,6 +3720,10 @@ QDateTime QDateTime::addMonths(int nmonths) const
// so call conversion and use the adjusted returned time
if (d->m_spec == Qt::LocalTime)
localMSecsToEpochMSecs(timeToMSecs(date, time), &date, &time);
+#ifndef QT_BOOTSTRAPPED
+ else if (d->m_spec == Qt::TimeZone)
+ d->zoneMSecsToEpochMSecs(timeToMSecs(date, time), d->m_timeZone, &date, &time);
+#endif // QT_BOOTSTRAPPED
dt.d->setDateTime(date, time);
return dt;
}
@@ -3559,6 +3754,10 @@ QDateTime QDateTime::addYears(int nyears) const
// so call conversion and use the adjusted returned time
if (d->m_spec == Qt::LocalTime)
localMSecsToEpochMSecs(timeToMSecs(date, time), &date, &time);
+#ifndef QT_BOOTSTRAPPED
+ else if (d->m_spec == Qt::TimeZone)
+ d->zoneMSecsToEpochMSecs(timeToMSecs(date, time), d->m_timeZone, &date, &time);
+#endif // QT_BOOTSTRAPPED
dt.d->setDateTime(date, time);
return dt;
}
@@ -3594,7 +3793,7 @@ QDateTime QDateTime::addMSecs(qint64 msecs) const
QDateTime dt(*this);
dt.detach();
- if (d->m_spec == Qt::LocalTime)
+ if (d->m_spec == Qt::LocalTime || d->m_spec == Qt::TimeZone)
// Convert to real UTC first in case crosses daylight transition
dt.setMSecsSinceEpoch(d->toMSecsSinceEpoch() + msecs);
else
@@ -3677,10 +3876,13 @@ qint64 QDateTime::msecsTo(const QDateTime &other) const
If \a spec is Qt::OffsetFromUTC then it is set to Qt::UTC. To set to a
spec of Qt::OffsetFromUTC use toOffsetFromUtc().
+ If \a spec is Qt::TimeZone then it is set to Qt::LocalTime,
+ i.e. the local Time Zone.
+
Example:
\snippet code/src_corelib_tools_qdatetime.cpp 16
- \sa timeSpec(), toUTC(), toLocalTime()
+ \sa timeSpec(), toTimeZone(), toUTC(), toLocalTime()
*/
QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const
@@ -3706,6 +3908,21 @@ QDateTime QDateTime::toOffsetFromUtc(int offsetSeconds) const
return fromMSecsSinceEpoch(toMSecsSinceEpoch(), Qt::OffsetFromUTC, offsetSeconds);
}
+#ifndef QT_BOOTSTRAPPED
+/*!
+ \since 5.2
+
+ Returns a copy of this datetime converted to the given \a timeZone
+
+ \sa timeZone(), toTimeSpec()
+*/
+
+QDateTime QDateTime::toTimeZone(const QTimeZone &timeZone) const
+{
+ return fromMSecsSinceEpoch(toMSecsSinceEpoch(), timeZone);
+}
+#endif // QT_BOOTSTRAPPED
+
/*!
Returns true if this datetime is equal to the \a other datetime;
otherwise returns false.
@@ -3934,6 +4151,22 @@ QDateTime QDateTime::fromTime_t(uint seconds, Qt::TimeSpec spec, int offsetSecon
return fromMSecsSinceEpoch((qint64)seconds * 1000, spec, offsetSeconds);
}
+#ifndef QT_BOOTSTRAPPED
+/*!
+ \since 5.2
+
+ Returns a datetime whose date and time are the number of \a seconds
+ that have passed since 1970-01-01T00:00:00, Coordinated Universal
+ Time (Qt::UTC) and with the given \a timeZone.
+
+ \sa toTime_t(), setTime_t()
+*/
+QDateTime QDateTime::fromTime_t(uint seconds, const QTimeZone &timeZone)
+{
+ return fromMSecsSinceEpoch((qint64)seconds * 1000, timeZone);
+}
+#endif
+
/*!
\since 4.7
@@ -3968,6 +4201,9 @@ QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs)
ignored. If the \a spec is Qt::OffsetFromUTC and the \a offsetSeconds is 0
then the spec will be set to Qt::UTC, i.e. an offset of 0 seconds.
+ If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime,
+ i.e. the current system time zone.
+
\sa fromTime_t()
*/
QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec, int offsetSeconds)
@@ -3979,6 +4215,25 @@ QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec, int of
return dt;
}
+#ifndef QT_BOOTSTRAPPED
+/*!
+ \since 5.2
+
+ Returns a datetime whose date and time are the number of milliseconds \a msecs
+ that have passed since 1970-01-01T00:00:00.000, Coordinated Universal
+ Time (Qt::UTC) and with the given \a timeZone.
+
+ \sa fromTime_t()
+*/
+QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone)
+{
+ QDateTime dt;
+ dt.setTimeZone(timeZone);
+ dt.setMSecsSinceEpoch(msecs);
+ return dt;
+}
+#endif
+
#if QT_DEPRECATED_SINCE(5, 2)
/*!
\since 4.4
@@ -4480,6 +4735,10 @@ QDataStream &operator<<(QDataStream &out, const QDateTime &dateTime)
out << dt << tm << qint8(dateTime.timeSpec());
if (dateTime.timeSpec() == Qt::OffsetFromUTC)
out << qint32(dateTime.offsetFromUtc());
+#ifndef QT_BOOTSTRAPPED
+ else if (dateTime.timeSpec() == Qt::TimeZone)
+ out << dateTime.timeZone();
+#endif // QT_BOOTSTRAPPED
} else if (out.version() == QDataStream::Qt_5_0) {
@@ -4506,6 +4765,11 @@ QDataStream &operator<<(QDataStream &out, const QDateTime &dateTime)
case Qt::OffsetFromUTC:
out << (qint8)QDateTimePrivate::OffsetFromUTC;
break;
+ case Qt::TimeZone:
+#ifndef QT_BOOTSTRAPPED
+ out << (qint8)QDateTimePrivate::TimeZone;
+ break;
+#endif // QT_BOOTSTRAPPED
case Qt::LocalTime:
out << (qint8)QDateTimePrivate::LocalUnknown;
break;
@@ -4540,15 +4804,26 @@ QDataStream &operator>>(QDataStream &in, QDateTime &dateTime)
qint8 ts = 0;
Qt::TimeSpec spec = Qt::LocalTime;
qint32 offset = 0;
+#ifndef QT_BOOTSTRAPPED
+ QTimeZone tz;
+#endif // QT_BOOTSTRAPPED
if (in.version() >= QDataStream::Qt_5_2) {
// In 5.2 we switched to using Qt::TimeSpec and added offset support
in >> dt >> tm >> ts;
spec = static_cast<Qt::TimeSpec>(ts);
- if (spec == Qt::OffsetFromUTC)
+ if (spec == Qt::OffsetFromUTC) {
in >> offset;
- dateTime = QDateTime(dt, tm, spec, offset);
+ dateTime = QDateTime(dt, tm, spec, offset);
+#ifndef QT_BOOTSTRAPPED
+ } else if (spec == Qt::TimeZone) {
+ in >> tz;
+ dateTime = QDateTime(dt, tm, tz);
+#endif // QT_BOOTSTRAPPED
+ } else {
+ dateTime = QDateTime(dt, tm, spec);
+ }
} else if (in.version() == QDataStream::Qt_5_0) {
@@ -4569,6 +4844,11 @@ QDataStream &operator>>(QDataStream &in, QDateTime &dateTime)
case QDateTimePrivate::OffsetFromUTC:
spec = Qt::OffsetFromUTC;
break;
+ case QDateTimePrivate::TimeZone:
+#ifndef QT_BOOTSTRAPPED
+ spec = Qt::TimeZone;
+ break;
+#endif // QT_BOOTSTRAPPED
case QDateTimePrivate::LocalUnknown:
case QDateTimePrivate::LocalStandard:
case QDateTimePrivate::LocalDST:
@@ -4616,6 +4896,11 @@ QDebug operator<<(QDebug dbg, const QDateTime &date)
case Qt::OffsetFromUTC:
spec = QString::fromUtf8(" Qt::OffsetFromUTC %1s").arg(date.offsetFromUtc());
break;
+ case Qt::TimeZone:
+#ifndef QT_BOOTSTRAPPED
+ spec = QStringLiteral(" Qt::TimeZone ") + date.timeZone().id();
+ break;
+#endif // QT_BOOTSTRAPPED
case Qt::LocalTime:
spec = QStringLiteral(" Qt::LocalTime");
break;
diff --git a/src/corelib/tools/qdatetime.h b/src/corelib/tools/qdatetime.h
index 4b97cd797d..b9f6995155 100644
--- a/src/corelib/tools/qdatetime.h
+++ b/src/corelib/tools/qdatetime.h
@@ -50,6 +50,7 @@
QT_BEGIN_NAMESPACE
+class QTimeZone;
class Q_CORE_EXPORT QDate
{
@@ -208,6 +209,9 @@ public:
QDateTime(const QDate &, const QTime &, Qt::TimeSpec spec = Qt::LocalTime);
// ### Qt 6: Merge with above with default offsetSeconds = 0
QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec, int offsetSeconds);
+#ifndef QT_BOOTSTRAPPED
+ QDateTime(const QDate &date, const QTime &time, const QTimeZone &timeZone);
+#endif // QT_BOOTSTRAPPED
QDateTime(const QDateTime &other);
~QDateTime();
@@ -222,6 +226,9 @@ public:
QTime time() const;
Qt::TimeSpec timeSpec() const;
int offsetFromUtc() const;
+#ifndef QT_BOOTSTRAPPED
+ QTimeZone timeZone() const;
+#endif // QT_BOOTSTRAPPED
QString timeZoneAbbreviation() const;
bool isDaylightTime() const;
@@ -233,6 +240,9 @@ public:
void setTime(const QTime &time);
void setTimeSpec(Qt::TimeSpec spec);
void setOffsetFromUtc(int offsetSeconds);
+#ifndef QT_BOOTSTRAPPED
+ void setTimeZone(const QTimeZone &toZone);
+#endif // QT_BOOTSTRAPPED
void setMSecsSinceEpoch(qint64 msecs);
// ### Qt 6: use quint64 instead of uint
void setTime_t(uint secsSince1Jan1970UTC);
@@ -251,6 +261,9 @@ public:
inline QDateTime toLocalTime() const { return toTimeSpec(Qt::LocalTime); }
inline QDateTime toUTC() const { return toTimeSpec(Qt::UTC); }
QDateTime toOffsetFromUtc(int offsetSeconds) const;
+#ifndef QT_BOOTSTRAPPED
+ QDateTime toTimeZone(const QTimeZone &toZone) const;
+#endif // QT_BOOTSTRAPPED
qint64 daysTo(const QDateTime &) const;
qint64 secsTo(const QDateTime &) const;
@@ -279,9 +292,15 @@ public:
// ### Qt 6: Merge with above with default spec = Qt::LocalTime
static QDateTime fromTime_t(uint secsSince1Jan1970UTC, Qt::TimeSpec spec,
int offsetFromUtc = 0);
+#ifndef QT_BOOTSTRAPPED
+ static QDateTime fromTime_t(uint secsSince1Jan1970UTC, const QTimeZone &timeZone);
+#endif
static QDateTime fromMSecsSinceEpoch(qint64 msecs);
// ### Qt 6: Merge with above with default spec = Qt::LocalTime
static QDateTime fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec, int offsetFromUtc = 0);
+#ifndef QT_BOOTSTRAPPED
+ static QDateTime fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone);
+#endif
static qint64 currentMSecsSinceEpoch() Q_DECL_NOTHROW;
private:
diff --git a/src/corelib/tools/qdatetime_p.h b/src/corelib/tools/qdatetime_p.h
index 7fd36bc4e0..f52108d764 100644
--- a/src/corelib/tools/qdatetime_p.h
+++ b/src/corelib/tools/qdatetime_p.h
@@ -57,6 +57,8 @@
#include "QtCore/qatomic.h"
#include "QtCore/qdatetime.h"
+#include "qtimezone.h"
+
QT_BEGIN_NAMESPACE
class QDateTimePrivate : public QSharedData
@@ -69,7 +71,8 @@ public:
LocalStandard = 0,
LocalDST = 1,
UTC = 2,
- OffsetFromUTC = 3
+ OffsetFromUTC = 3,
+ TimeZone = 4
};
// Daylight Time Status
@@ -86,7 +89,8 @@ public:
NullTime = 0x02,
ValidDate = 0x04,
ValidTime = 0x08,
- ValidDateTime = 0x10
+ ValidDateTime = 0x10,
+ TimeZoneCached = 0x20
};
Q_DECLARE_FLAGS(StatusFlags, StatusFlag)
@@ -99,16 +103,26 @@ public:
QDateTimePrivate(const QDate &toDate, const QTime &toTime, Qt::TimeSpec toSpec,
int offsetSeconds);
+#ifndef QT_BOOTSTRAPPED
+ QDateTimePrivate(const QDate &toDate, const QTime &toTime, const QTimeZone & timeZone);
+#endif // QT_BOOTSTRAPPED
+
QDateTimePrivate(const QDateTimePrivate &other) : QSharedData(other),
m_msecs(other.m_msecs),
m_spec(other.m_spec),
m_offsetFromUtc(other.m_offsetFromUtc),
+#ifndef QT_BOOTSTRAPPED
+ m_timeZone(other.m_timeZone),
+#endif // QT_BOOTSTRAPPED
m_status(other.m_status)
{}
qint64 m_msecs;
Qt::TimeSpec m_spec;
int m_offsetFromUtc;
+#ifndef QT_BOOTSTRAPPED
+ QTimeZone m_timeZone;
+#endif // QT_BOOTSTRAPPED
StatusFlags m_status;
void setTimeSpec(Qt::TimeSpec spec, int offsetSeconds);
@@ -129,6 +143,14 @@ public:
inline bool isValidDateTime() const { return (m_status & ValidDateTime) == ValidDateTime; }
inline void setValidDateTime() { m_status = m_status | ValidDateTime; }
inline void clearValidDateTime() { m_status = m_status & ~ValidDateTime; }
+ inline bool isTimeZoneCached() const { return (m_status & TimeZoneCached) == TimeZoneCached; }
+ inline void setTimeZoneCached() { m_status = m_status | TimeZoneCached; }
+ inline void clearTimeZoneCached() { m_status = m_status & ~TimeZoneCached; }
+
+#ifndef QT_BOOTSTRAPPED
+ static qint64 zoneMSecsToEpochMSecs(qint64 msecs, const QTimeZone &zone,
+ QDate *localDate, QTime *localTime);
+#endif // QT_BOOTSTRAPPED
static inline qint64 minJd() { return QDate::minJd(); }
static inline qint64 maxJd() { return QDate::maxJd(); }
diff --git a/src/corelib/tools/qtimezone.h b/src/corelib/tools/qtimezone.h
index 5eeb22f8f3..1a6a923cf4 100644
--- a/src/corelib/tools/qtimezone.h
+++ b/src/corelib/tools/qtimezone.h
@@ -143,6 +143,8 @@ private:
friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz);
#endif
friend class QTimeZonePrivate;
+ friend class QDateTime;
+ friend class QDateTimePrivate;
QSharedDataPointer<QTimeZonePrivate> d;
};
diff --git a/src/corelib/tools/qtimezoneprivate.cpp b/src/corelib/tools/qtimezoneprivate.cpp
index 4a8d891759..8d5d60bf37 100644
--- a/src/corelib/tools/qtimezoneprivate.cpp
+++ b/src/corelib/tools/qtimezoneprivate.cpp
@@ -48,6 +48,10 @@
QT_BEGIN_NAMESPACE
+enum {
+ MSECS_TRAN_WINDOW = 21600000 // 6 hour window for possible recent transitions
+};
+
/*
Static utilities for looking up Windows ID tables
*/
@@ -242,6 +246,67 @@ QTimeZonePrivate::Data QTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
return invalidData();
}
+// Private only method for use by QDateTime to convert local msecs to epoch msecs
+// TODO Could be platform optimised if needed
+QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs) const
+{
+ if (!hasDaylightTime() ||!hasTransitions()) {
+ // No daylight time means same offset for all local msecs
+ // Having daylight time but no transitions means we can't calculate, so use nearest
+ return data(forLocalMSecs - (standardTimeOffset(forLocalMSecs) * 1000));
+ }
+
+ // Get the transition for the local msecs which most of the time should be the right one
+ // Only around the transition times might it not be the right one
+ Data tran = previousTransition(forLocalMSecs);
+ Data nextTran;
+
+ // If the local msecs is less than the real local time of the transition
+ // then get the previous transition to use instead
+ if (forLocalMSecs < tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000)) {
+ while (forLocalMSecs < tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000)) {
+ nextTran = tran;
+ tran = previousTransition(tran.atMSecsSinceEpoch);
+ }
+ } else {
+ // The zone msecs is after the transition, so check it is before the next tran
+ // If not try use the next transition instead
+ nextTran = nextTransition(tran.atMSecsSinceEpoch);
+ while (forLocalMSecs >= nextTran.atMSecsSinceEpoch + (nextTran.offsetFromUtc * 1000)) {
+ tran = nextTran;
+ nextTran = nextTransition(tran.atMSecsSinceEpoch);
+ }
+ }
+
+ if (tran.daylightTimeOffset == 0) {
+ // If tran is in StandardTime, then need to check if falls close either daylight transition
+ // If it does, then it may need adjusting for missing hour or for second occurrence
+ qint64 diffPrevTran = forLocalMSecs
+ - (tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000));
+ qint64 diffNextTran = nextTran.atMSecsSinceEpoch + (nextTran.offsetFromUtc * 1000)
+ - forLocalMSecs;
+ if (diffPrevTran >= 0 && diffPrevTran < MSECS_TRAN_WINDOW) {
+ // If tran picked is for standard time check if changed from daylight in last 6 hours,
+ // as the local msecs may be ambiguous and represent two valid utc msecs.
+ // If in last 6 hours then get prev tran and if diff falls within the daylight offset
+ // then use the prev tran as we default to the FirstOccurrence
+ // TODO Check if faster to just always get prev tran, or if faster using 6 hour check.
+ Data dstTran = previousTransition(tran.atMSecsSinceEpoch);
+ if (dstTran.daylightTimeOffset > 0 && diffPrevTran < (dstTran.daylightTimeOffset * 1000))
+ tran = dstTran;
+ } else if (diffNextTran >= 0 && diffNextTran <= (nextTran.daylightTimeOffset * 1000)) {
+ // If time falls within last hour of standard time then is actually the missing hour
+ // So return the next tran instead and adjust the local time to be valid
+ tran = nextTran;
+ forLocalMSecs = forLocalMSecs + (nextTran.daylightTimeOffset * 1000);
+ }
+ }
+
+ // tran should now hold the right transition offset to use
+ tran.atMSecsSinceEpoch = forLocalMSecs - (tran.offsetFromUtc * 1000);
+ return tran;
+}
+
bool QTimeZonePrivate::hasTransitions() const
{
return false;
diff --git a/src/corelib/tools/qtimezoneprivate_p.h b/src/corelib/tools/qtimezoneprivate_p.h
index b4e4090c0b..9f99f49fcf 100644
--- a/src/corelib/tools/qtimezoneprivate_p.h
+++ b/src/corelib/tools/qtimezoneprivate_p.h
@@ -120,6 +120,7 @@ public:
virtual bool isDaylightTime(qint64 atMSecsSinceEpoch) const;
virtual Data data(qint64 forMSecsSinceEpoch) const;
+ virtual Data dataForLocalTime(qint64 forLocalMSecs) const;
virtual bool hasTransitions() const;
virtual Data nextTransition(qint64 afterMSecsSinceEpoch) const;