summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qdatetime.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools/qdatetime.cpp')
-rw-r--r--src/corelib/tools/qdatetime.cpp309
1 files changed, 297 insertions, 12 deletions
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;