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>
This commit is contained in:
parent
8af776d414
commit
55f5b29d79
@ -1196,7 +1196,8 @@ public:
|
||||
enum TimeSpec {
|
||||
LocalTime,
|
||||
UTC,
|
||||
OffsetFromUTC
|
||||
OffsetFromUTC,
|
||||
TimeZone
|
||||
};
|
||||
|
||||
enum DayOfWeek {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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(); }
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -145,6 +145,7 @@ private slots:
|
||||
|
||||
void isDaylightTime() const;
|
||||
void daylightTransitions() const;
|
||||
void timeZones() const;
|
||||
|
||||
private:
|
||||
bool europeanTimeZone;
|
||||
@ -547,6 +548,8 @@ void tst_QDateTime::setMSecsSinceEpoch()
|
||||
dt.setMSecsSinceEpoch(msecs);
|
||||
|
||||
QCOMPARE(dt, utc);
|
||||
QCOMPARE(dt.date(), utc.date());
|
||||
QCOMPARE(dt.time(), utc.time());
|
||||
QCOMPARE(dt.timeSpec(), Qt::UTC);
|
||||
|
||||
if (europeanTimeZone) {
|
||||
@ -561,6 +564,23 @@ void tst_QDateTime::setMSecsSinceEpoch()
|
||||
if (msecs != std::numeric_limits<qint64>::max())
|
||||
QCOMPARE(localDt, utc);
|
||||
QCOMPARE(localDt.timeSpec(), Qt::LocalTime);
|
||||
|
||||
// Compare result for LocalTime to TimeZone
|
||||
QTimeZone europe("Europe/Oslo");
|
||||
QDateTime dt2;
|
||||
dt2.setTimeZone(europe);
|
||||
dt2.setMSecsSinceEpoch(msecs);
|
||||
QCOMPARE(dt2.date(), european.date());
|
||||
#ifdef Q_OS_MAC
|
||||
// NSTimeZone doesn't apply DST to high values
|
||||
if (msecs < (Q_INT64_C(123456) << 32))
|
||||
#else
|
||||
// Linux and Win are OK except when they overflow
|
||||
if (msecs != std::numeric_limits<qint64>::max())
|
||||
#endif
|
||||
QCOMPARE(dt2.time(), european.time());
|
||||
QCOMPARE(dt2.timeSpec(), Qt::TimeZone);
|
||||
QCOMPARE(dt2.timeZone(), europe);
|
||||
}
|
||||
|
||||
QCOMPARE(dt.toMSecsSinceEpoch(), msecs);
|
||||
@ -2107,6 +2127,12 @@ void tst_QDateTime::offsetFromUtc()
|
||||
} else {
|
||||
QSKIP("You must test using Central European (CET/CEST) time zone, e.g. TZ=Europe/Oslo");
|
||||
}
|
||||
|
||||
QDateTime dt5(QDate(2013, 1, 1), QTime(0, 0, 0), QTimeZone("Pacific/Auckland"));
|
||||
QCOMPARE(dt5.offsetFromUtc(), 46800);
|
||||
|
||||
QDateTime dt6(QDate(2013, 6, 1), QTime(0, 0, 0), QTimeZone("Pacific/Auckland"));
|
||||
QCOMPARE(dt6.offsetFromUtc(), 43200);
|
||||
}
|
||||
|
||||
void tst_QDateTime::setOffsetFromUtc()
|
||||
@ -2232,6 +2258,17 @@ void tst_QDateTime::timeZoneAbbreviation()
|
||||
} else {
|
||||
QSKIP("You must test using Central European (CET/CEST) time zone, e.g. TZ=Europe/Oslo");
|
||||
}
|
||||
|
||||
QDateTime dt5(QDate(2013, 1, 1), QTime(0, 0, 0), QTimeZone("Europe/Berlin"));
|
||||
#ifdef Q_OS_WIN
|
||||
QEXPECT_FAIL("", "QTimeZone windows backend only returns long name", Continue);
|
||||
#endif
|
||||
QCOMPARE(dt5.timeZoneAbbreviation(), QString("CET"));
|
||||
QDateTime dt6(QDate(2013, 6, 1), QTime(0, 0, 0), QTimeZone("Europe/Berlin"));
|
||||
#ifdef Q_OS_WIN
|
||||
QEXPECT_FAIL("", "QTimeZone windows backend only returns long name", Continue);
|
||||
#endif
|
||||
QCOMPARE(dt6.timeZoneAbbreviation(), QString("CEST"));
|
||||
}
|
||||
|
||||
void tst_QDateTime::getDate()
|
||||
@ -2717,5 +2754,168 @@ void tst_QDateTime::daylightTransitions() const
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::timeZones() const
|
||||
{
|
||||
QTimeZone nzTz = QTimeZone("Pacific/Auckland");
|
||||
|
||||
// During Standard Time NZ is +12:00
|
||||
QDateTime utcStd(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC);
|
||||
QDateTime nzStd(QDate(2012, 6, 1), QTime(12, 0, 0), nzTz);
|
||||
|
||||
QCOMPARE(nzStd.isValid(), true);
|
||||
QCOMPARE(nzStd.timeSpec(), Qt::TimeZone);
|
||||
QCOMPARE(nzStd.date(), QDate(2012, 6, 1));
|
||||
QCOMPARE(nzStd.time(), QTime(12, 0, 0));
|
||||
QVERIFY(nzStd.timeZone() == nzTz);
|
||||
QCOMPARE(nzStd.timeZone().id(), QByteArray("Pacific/Auckland"));
|
||||
QCOMPARE(nzStd.offsetFromUtc(), 43200);
|
||||
QCOMPARE(nzStd.isDaylightTime(), false);
|
||||
QCOMPARE(nzStd.toMSecsSinceEpoch(), utcStd.toMSecsSinceEpoch());
|
||||
|
||||
// During Daylight Time NZ is +13:00
|
||||
QDateTime utcDst(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC);
|
||||
QDateTime nzDst(QDate(2012, 1, 1), QTime(13, 0, 0), nzTz);
|
||||
|
||||
QCOMPARE(nzDst.isValid(), true);
|
||||
QCOMPARE(nzDst.date(), QDate(2012, 1, 1));
|
||||
QCOMPARE(nzDst.time(), QTime(13, 0, 0));
|
||||
QCOMPARE(nzDst.offsetFromUtc(), 46800);
|
||||
QCOMPARE(nzDst.isDaylightTime(), true);
|
||||
QCOMPARE(nzDst.toMSecsSinceEpoch(), utcDst.toMSecsSinceEpoch());
|
||||
|
||||
QDateTime utc = nzStd.toUTC();
|
||||
QCOMPARE(utc.date(), utcStd.date());
|
||||
QCOMPARE(utc.time(), utcStd.time());
|
||||
|
||||
utc = nzDst.toUTC();
|
||||
QCOMPARE(utc.date(), utcDst.date());
|
||||
QCOMPARE(utc.time(), utcDst.time());
|
||||
|
||||
// Sydney is 2 hours behind New Zealand
|
||||
QTimeZone ausTz = QTimeZone("Australia/Sydney");
|
||||
QDateTime aus = nzStd.toTimeZone(ausTz);
|
||||
QCOMPARE(aus.date(), QDate(2012, 6, 1));
|
||||
QCOMPARE(aus.time(), QTime(10, 0, 0));
|
||||
|
||||
QDateTime dt1(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC);
|
||||
QCOMPARE(dt1.timeSpec(), Qt::UTC);
|
||||
dt1.setTimeZone(nzTz);
|
||||
QCOMPARE(dt1.timeSpec(), Qt::TimeZone);
|
||||
QCOMPARE(dt1.date(), QDate(2012, 6, 1));
|
||||
QCOMPARE(dt1.time(), QTime(0, 0, 0));
|
||||
QCOMPARE(dt1.timeZone(), nzTz);
|
||||
|
||||
QDateTime dt2 = QDateTime::fromTime_t(1338465600, nzTz);
|
||||
QCOMPARE(dt2.date(), dt1.date());
|
||||
QCOMPARE(dt2.time(), dt1.time());
|
||||
QCOMPARE(dt2.timeSpec(), dt1.timeSpec());
|
||||
QCOMPARE(dt2.timeZone(), dt1.timeZone());
|
||||
|
||||
QDateTime dt3 = QDateTime::fromMSecsSinceEpoch(1338465600000, nzTz);
|
||||
QCOMPARE(dt3.date(), dt1.date());
|
||||
QCOMPARE(dt3.time(), dt1.time());
|
||||
QCOMPARE(dt3.timeSpec(), dt1.timeSpec());
|
||||
QCOMPARE(dt3.timeZone(), dt1.timeZone());
|
||||
|
||||
// Check datastream serialises the time zone
|
||||
QByteArray tmp;
|
||||
{
|
||||
QDataStream ds(&tmp, QIODevice::WriteOnly);
|
||||
ds << dt1;
|
||||
}
|
||||
QDateTime dt4;
|
||||
{
|
||||
QDataStream ds(&tmp, QIODevice::ReadOnly);
|
||||
ds >> dt4;
|
||||
}
|
||||
QCOMPARE(dt4, dt1);
|
||||
QCOMPARE(dt4.timeSpec(), Qt::TimeZone);
|
||||
QCOMPARE(dt4.timeZone(), nzTz);
|
||||
|
||||
// Check handling of transition times
|
||||
QTimeZone cet("Europe/Oslo");
|
||||
|
||||
// Standard Time to Daylight Time 2013 on 2013-03-31 is 2:00 local time / 1:00 UTC
|
||||
qint64 stdToDstMSecs = 1364691600000;
|
||||
|
||||
// Test MSecs to local
|
||||
// - Test 1 msec before tran = 01:59:59.999
|
||||
QDateTime beforeDst = QDateTime::fromMSecsSinceEpoch(stdToDstMSecs - 1, cet);
|
||||
QCOMPARE(beforeDst.date(), QDate(2013, 3, 31));
|
||||
QCOMPARE(beforeDst.time(), QTime(1, 59, 59, 999));
|
||||
// - Test at tran = 03:00:00
|
||||
QDateTime atDst = QDateTime::fromMSecsSinceEpoch(stdToDstMSecs, cet);
|
||||
QCOMPARE(atDst.date(), QDate(2013, 3, 31));
|
||||
QCOMPARE(atDst.time(), QTime(3, 0, 0));
|
||||
|
||||
// Test local to MSecs
|
||||
// - Test 1 msec before tran = 01:59:59.999
|
||||
beforeDst = QDateTime(QDate(2013, 3, 31), QTime(1, 59, 59, 999), cet);
|
||||
QCOMPARE(beforeDst.toMSecsSinceEpoch(), stdToDstMSecs - 1);
|
||||
// - Test at tran = 03:00:00
|
||||
atDst = QDateTime(QDate(2013, 3, 31), QTime(3, 0, 0), cet);
|
||||
QCOMPARE(atDst.toMSecsSinceEpoch(), stdToDstMSecs);
|
||||
// - Test transition hole, setting 03:00:00 is valid
|
||||
atDst = QDateTime(QDate(2013, 3, 31), QTime(3, 0, 0), cet);
|
||||
QVERIFY(atDst.isValid());
|
||||
QCOMPARE(atDst.date(), QDate(2013, 3, 31));
|
||||
QCOMPARE(atDst.time(), QTime(3, 0, 0));
|
||||
QCOMPARE(atDst.toMSecsSinceEpoch(), stdToDstMSecs);
|
||||
// - Test transition hole, setting 02:00:00 is invalid
|
||||
atDst = QDateTime(QDate(2013, 3, 31), QTime(2, 0, 0), cet);
|
||||
QVERIFY(!atDst.isValid());
|
||||
QCOMPARE(atDst.date(), QDate(2013, 3, 31));
|
||||
QCOMPARE(atDst.time(), QTime(2, 0, 0));
|
||||
// - Test transition hole, setting 02:59:59.999 is invalid
|
||||
atDst = QDateTime(QDate(2013, 3, 31), QTime(2, 59, 59, 999), cet);
|
||||
QVERIFY(!atDst.isValid());
|
||||
QCOMPARE(atDst.date(), QDate(2013, 3, 31));
|
||||
QCOMPARE(atDst.time(), QTime(2, 59, 59, 999));
|
||||
|
||||
// Standard Time to Daylight Time 2013 on 2013-10-27 is 3:00 local time / 1:00 UTC
|
||||
qint64 dstToStdMSecs = 1382835600000;
|
||||
|
||||
// Test MSecs to local
|
||||
// - Test 1 hour before tran = 02:00:00 local first occurrence
|
||||
QDateTime hourBeforeStd = QDateTime::fromMSecsSinceEpoch(dstToStdMSecs - 3600000, cet);
|
||||
QCOMPARE(hourBeforeStd.date(), QDate(2013, 10, 27));
|
||||
QCOMPARE(hourBeforeStd.time(), QTime(2, 0, 0));
|
||||
// - Test 1 msec before tran = 02:59:59.999 local first occurrence
|
||||
QDateTime msecBeforeStd = QDateTime::fromMSecsSinceEpoch(dstToStdMSecs - 1, cet);
|
||||
QCOMPARE(msecBeforeStd.date(), QDate(2013, 10, 27));
|
||||
QCOMPARE(msecBeforeStd.time(), QTime(2, 59, 59, 999));
|
||||
// - Test at tran = 03:00:00 local becomes 02:00:00 local second occurrence
|
||||
QDateTime atStd = QDateTime::fromMSecsSinceEpoch(dstToStdMSecs, cet);
|
||||
QCOMPARE(atStd.date(), QDate(2013, 10, 27));
|
||||
QCOMPARE(atStd.time(), QTime(2, 0, 0));
|
||||
// - Test 59 mins after tran = 02:59:59.999 local second occurrence
|
||||
QDateTime afterStd = QDateTime::fromMSecsSinceEpoch(dstToStdMSecs + 3600000 -1, cet);
|
||||
QCOMPARE(afterStd.date(), QDate(2013, 10, 27));
|
||||
QCOMPARE(afterStd.time(), QTime(2, 59, 59, 999));
|
||||
// - Test 1 hour after tran = 03:00:00 local
|
||||
QDateTime hourAfterStd = QDateTime::fromMSecsSinceEpoch(dstToStdMSecs + 3600000, cet);
|
||||
QCOMPARE(hourAfterStd.date(), QDate(2013, 10, 27));
|
||||
QCOMPARE(hourAfterStd.time(), QTime(3, 00, 00));
|
||||
|
||||
// Test local to MSecs
|
||||
// - Test first occurrence 02:00:00 = 1 hour before tran
|
||||
hourBeforeStd = QDateTime(QDate(2013, 10, 27), QTime(2, 0, 0), cet);
|
||||
QCOMPARE(hourBeforeStd.toMSecsSinceEpoch(), dstToStdMSecs - 3600000);
|
||||
// - Test first occurrence 02:59:59.999 = 1 msec before tran
|
||||
msecBeforeStd = QDateTime(QDate(2013, 10, 27), QTime(2, 59, 59, 999), cet);
|
||||
QCOMPARE(msecBeforeStd.toMSecsSinceEpoch(), dstToStdMSecs - 1);
|
||||
// - Test second occurrence 02:00:00 = at tran
|
||||
atStd = QDateTime(QDate(2013, 10, 27), QTime(2, 0, 0), cet);
|
||||
QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
|
||||
QCOMPARE(atStd.toMSecsSinceEpoch(), dstToStdMSecs);
|
||||
// - Test second occurrence 03:00:00 = 59 mins after tran
|
||||
afterStd = QDateTime(QDate(2013, 10, 27), QTime(2, 59, 59, 999), cet);
|
||||
QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
|
||||
QCOMPARE(afterStd.toMSecsSinceEpoch(), dstToStdMSecs + 3600000 - 1);
|
||||
// - Test 03:00:00 = 1 hour after tran
|
||||
hourAfterStd = QDateTime(QDate(2013, 10, 27), QTime(3, 0, 0), cet);
|
||||
QCOMPARE(hourAfterStd.toMSecsSinceEpoch(), dstToStdMSecs + 3600000);
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QDateTime)
|
||||
#include "tst_qdatetime.moc"
|
||||
|
@ -1303,6 +1303,18 @@ void tst_QLocale::formatTimeZone()
|
||||
QSKIP("You must test using Central European (CET/CEST) time zone, e.g. TZ=Europe/Oslo");
|
||||
}
|
||||
|
||||
QDateTime dt6(QDate(2013, 1, 1), QTime(0, 0, 0), QTimeZone("Europe/Berlin"));
|
||||
#ifdef Q_OS_WIN
|
||||
QEXPECT_FAIL("", "QTimeZone windows backend only returns long name", Continue);
|
||||
#endif
|
||||
QCOMPARE(enUS.toString(dt6, "t"), QString("CET"));
|
||||
|
||||
QDateTime dt7(QDate(2013, 6, 1), QTime(0, 0, 0), QTimeZone("Europe/Berlin"));
|
||||
#ifdef Q_OS_WIN
|
||||
QEXPECT_FAIL("", "QTimeZone windows backend only returns long name", Continue);
|
||||
#endif
|
||||
QCOMPARE(enUS.toString(dt7, "t"), QString("CEST"));
|
||||
|
||||
// Current datetime should return current abbreviation
|
||||
QCOMPARE(enUS.toString(QDateTime::currentDateTime(), "t"),
|
||||
QDateTime::currentDateTime().timeZoneAbbreviation());
|
||||
|
@ -39,7 +39,8 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QLocale>
|
||||
#include <QDateTime>
|
||||
#include <QTimeZone>
|
||||
#include <QTest>
|
||||
#include <qdebug.h>
|
||||
|
||||
@ -71,21 +72,27 @@ private Q_SLOTS:
|
||||
void toMSecsSinceEpoch();
|
||||
void toMSecsSinceEpoch1950();
|
||||
void toMSecsSinceEpoch2050();
|
||||
void toMSecsSinceEpochTz();
|
||||
void toMSecsSinceEpoch1950Tz();
|
||||
void toMSecsSinceEpoch2050Tz();
|
||||
void toTime_t();
|
||||
void setDate();
|
||||
void setTime();
|
||||
void setTimeSpec();
|
||||
void setOffsetFromUtc();
|
||||
void setMSecsSinceEpoch();
|
||||
void setMSecsSinceEpochTz();
|
||||
void setTime_t();
|
||||
void toString();
|
||||
void toStringTextFormat();
|
||||
void toStringIsoFormat();
|
||||
void addDays();
|
||||
void addDaysTz();
|
||||
void addMonths();
|
||||
void addYears();
|
||||
void addSecs();
|
||||
void addMSecs();
|
||||
void addMSecsTz();
|
||||
void toTimeSpec();
|
||||
void toOffsetFromUtc();
|
||||
void daysTo();
|
||||
@ -106,6 +113,7 @@ private Q_SLOTS:
|
||||
void fromTime_t();
|
||||
void fromMSecsSinceEpoch();
|
||||
void fromMSecsSinceEpochUtc();
|
||||
void fromMSecsSinceEpochTz();
|
||||
};
|
||||
|
||||
void tst_QDateTime::create()
|
||||
@ -228,6 +236,42 @@ void tst_QDateTime::toMSecsSinceEpoch2050()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::toMSecsSinceEpochTz()
|
||||
{
|
||||
QTimeZone cet = QTimeZone("Europe/Oslo");
|
||||
QList<QDateTime> list;
|
||||
for (int jd = JULIAN_DAY_2010; jd < JULIAN_DAY_2020; ++jd)
|
||||
list.append(QDateTime(QDate::fromJulianDay(jd), QTime::fromMSecsSinceStartOfDay(0), cet));
|
||||
QBENCHMARK {
|
||||
foreach (const QDateTime &test, list)
|
||||
qint64 result = test.toMSecsSinceEpoch();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::toMSecsSinceEpoch1950Tz()
|
||||
{
|
||||
QTimeZone cet = QTimeZone("Europe/Oslo");
|
||||
QList<QDateTime> list;
|
||||
for (int jd = JULIAN_DAY_1950; jd < JULIAN_DAY_1960; ++jd)
|
||||
list.append(QDateTime(QDate::fromJulianDay(jd), QTime::fromMSecsSinceStartOfDay(0), cet));
|
||||
QBENCHMARK {
|
||||
foreach (const QDateTime &test, list)
|
||||
qint64 result = test.toMSecsSinceEpoch();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::toMSecsSinceEpoch2050Tz()
|
||||
{
|
||||
QTimeZone cet = QTimeZone("Europe/Oslo");
|
||||
QList<QDateTime> list;
|
||||
for (int jd = JULIAN_DAY_2050; jd < JULIAN_DAY_2060; ++jd)
|
||||
list.append(QDateTime(QDate::fromJulianDay(jd), QTime::fromMSecsSinceStartOfDay(0), cet));
|
||||
QBENCHMARK {
|
||||
foreach (const QDateTime &test, list)
|
||||
qint64 result = test.toMSecsSinceEpoch();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::toTime_t()
|
||||
{
|
||||
QList<QDateTime> list;
|
||||
@ -295,6 +339,18 @@ void tst_QDateTime::setMSecsSinceEpoch()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::setMSecsSinceEpochTz()
|
||||
{
|
||||
QTimeZone cet = QTimeZone("Europe/Oslo");
|
||||
QList<QDateTime> list;
|
||||
for (int jd = JULIAN_DAY_2010; jd < JULIAN_DAY_2020; ++jd)
|
||||
list.append(QDateTime(QDate::fromJulianDay(jd), QTime::fromMSecsSinceStartOfDay(0), cet));
|
||||
QBENCHMARK {
|
||||
foreach (QDateTime test, list)
|
||||
test.setMSecsSinceEpoch((JULIAN_DAY_2010 + 180) * MSECS_PER_DAY);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::setTime_t()
|
||||
{
|
||||
time_t secs = time_t(JULIAN_DAY_2010 + 180) * SECS_PER_DAY;
|
||||
@ -351,6 +407,18 @@ void tst_QDateTime::addDays()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::addDaysTz()
|
||||
{
|
||||
QTimeZone cet = QTimeZone("Europe/Oslo");
|
||||
QList<QDateTime> list;
|
||||
for (int jd = JULIAN_DAY_2010; jd < JULIAN_DAY_2020; ++jd)
|
||||
list.append(QDateTime(QDate::fromJulianDay(jd), QTime::fromMSecsSinceStartOfDay(0), cet));
|
||||
QBENCHMARK {
|
||||
foreach (const QDateTime &test, list)
|
||||
QDateTime result = test.addDays(1);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::addMonths()
|
||||
{
|
||||
QList<QDateTime> list;
|
||||
@ -395,6 +463,18 @@ void tst_QDateTime::addMSecs()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::addMSecsTz()
|
||||
{
|
||||
QTimeZone cet = QTimeZone("Europe/Oslo");
|
||||
QList<QDateTime> list;
|
||||
for (int jd = JULIAN_DAY_2010; jd < JULIAN_DAY_2020; ++jd)
|
||||
list.append(QDateTime(QDate::fromJulianDay(jd), QTime::fromMSecsSinceStartOfDay(0), cet));
|
||||
QBENCHMARK {
|
||||
foreach (const QDateTime &test, list)
|
||||
QDateTime result = test.addMSecs(1);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::toTimeSpec()
|
||||
{
|
||||
QList<QDateTime> list;
|
||||
@ -601,6 +681,15 @@ void tst_QDateTime::fromMSecsSinceEpochUtc()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::fromMSecsSinceEpochTz()
|
||||
{
|
||||
QTimeZone cet = QTimeZone("Europe/Oslo");
|
||||
QBENCHMARK {
|
||||
for (int jd = JULIAN_DAY_2010; jd < JULIAN_DAY_2020; ++jd)
|
||||
QDateTime test = QDateTime::fromMSecsSinceEpoch(jd * MSECS_PER_DAY, cet);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QDateTime)
|
||||
|
||||
#include "main.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user