summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/corelib/tools/qdatetime.cpp174
-rw-r--r--src/corelib/tools/qdatetime_p.h10
2 files changed, 153 insertions, 31 deletions
diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp
index c3ce5b2540..4f7da33991 100644
--- a/src/corelib/tools/qdatetime.cpp
+++ b/src/corelib/tools/qdatetime.cpp
@@ -307,10 +307,25 @@ static qint64 qt_mktime(QDate *date, QTime *time, QDateTimePrivate::DaylightStat
local.tm_wday = 0;
local.tm_yday = 0;
local.tm_isdst = -1;
- const time_t secsSinceEpoch = mktime(&local);
+#if defined(Q_OS_WIN)
+ int hh = local.tm_hour;
+#endif // Q_OS_WIN
+ time_t secsSinceEpoch = mktime(&local);
if (secsSinceEpoch != time_t(-1)) {
*date = QDate(local.tm_year + 1900, local.tm_mon + 1, local.tm_mday);
*time = QTime(local.tm_hour, local.tm_min, local.tm_sec, msec);
+#if defined(Q_OS_WIN)
+ // Windows mktime for the missing hour subtracts 1 hour from the time
+ // instead of adding 1 hour. If time differs and is standard time then
+ // this has happened, so add 2 hours to the time and 1 hour to the msecs
+ if (local.tm_isdst == 0 && local.tm_hour != hh) {
+ if (time->hour() >= 22)
+ *date = date->addDays(1);
+ *time = time->addSecs(2 * SECS_PER_HOUR);
+ secsSinceEpoch += SECS_PER_HOUR;
+ local.tm_isdst = 1;
+ }
+#endif // Q_OS_WIN
if (local.tm_isdst >= 1) {
if (daylightStatus)
*daylightStatus = QDateTimePrivate::DaylightTime;
@@ -2502,6 +2517,8 @@ static qint64 localMSecsToEpochMSecs(qint64 localMsecs, QDate *localDate = 0, QT
void QDateTimePrivate::setTimeSpec(Qt::TimeSpec spec, int offsetSeconds)
{
+ clearValidDateTime();
+
switch (spec) {
case Qt::OffsetFromUTC:
if (offsetSeconds == 0) {
@@ -2550,6 +2567,9 @@ void QDateTimePrivate::setDateTime(const QDate &date, const QTime &time)
// Set msecs serial value
m_msecs = (days * MSECS_PER_DAY) + ds;
+
+ // Set if date and time are valid
+ checkValidDateTime();
}
void QDateTimePrivate::getDateTime(QDate *date, QTime *time) const
@@ -2563,6 +2583,58 @@ void QDateTimePrivate::getDateTime(QDate *date, QTime *time) const
*time = QTime();
}
+// Check the UTC / offsetFromUTC validity
+void QDateTimePrivate::checkValidDateTime()
+{
+ switch (m_spec) {
+ case Qt::OffsetFromUTC:
+ case Qt::UTC:
+ if (isValidDate() && isValidTime())
+ setValidDateTime();
+ else
+ clearValidDateTime();
+ break;
+ case Qt::LocalTime:
+ // Defer checking until required as can be expensive
+ clearValidDateTime();
+ m_offsetFromUtc = 0;
+ break;
+ }
+}
+
+// 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)
+ return;
+
+ // If not valid date and time then is invalid
+ if (!isValidDate() || !isValidTime()) {
+ clearValidDateTime();
+ m_offsetFromUtc = 0;
+ return;
+ }
+
+ // We have a valid date and time and a Qt::LocalTime that needs calculating
+ QDate date;
+ QTime time;
+ getDateTime(&date, &time);
+ // LocalTime and TimeZone might fall into "missing" DaylightTime transition hour
+ // Calling toEpochMSecs will adjust the returned date/time if it does
+ QDate testDate;
+ QTime testTime;
+ qint64 epochMSecs = localMSecsToEpochMSecs(m_msecs, &testDate, &testTime);
+ if (testDate == date && testTime == time) {
+ setValidDateTime();
+ // Cache the offset to use in toMSecsSinceEpoch()
+ m_offsetFromUtc = (m_msecs - epochMSecs) / 1000;
+ } else {
+ clearValidDateTime();
+ m_offsetFromUtc = 0;
+ }
+}
+
/*****************************************************************************
QDateTime member functions
*****************************************************************************/
@@ -2659,6 +2731,12 @@ void QDateTimePrivate::getDateTime(QDate *date, QTime *time) const
time zone before 1970, even if the system's time zone database
supports that information.
+ QDateTime takes into consideration the Standard Time to Daylight Time
+ transition. For example if the transition is at 2am and the clock goes
+ forward to 3am, then there is a "missing" hour from 02:00:00 to 02:59:59.999
+ which QDateTime considers to be invalid. Any date maths performed
+ will take this missing hour into account and return a valid result.
+
\section2 Offset From UTC
A Qt::TimeSpec of Qt::OffsetFromUTC is also supported. This allows you
@@ -2800,15 +2878,21 @@ bool QDateTime::isNull() const
}
/*!
- Returns true if both the date and the time are valid; otherwise
- returns false.
+ 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
+ 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.
\sa QDate::isValid(), QTime::isValid()
*/
bool QDateTime::isValid() const
{
- return d->isValidDate() && d->isValidTime();
+ d->refreshDateTime();
+ return (d->isValidDateTime());
}
/*!
@@ -2867,16 +2951,8 @@ Qt::TimeSpec QDateTime::timeSpec() const
int QDateTime::offsetFromUtc() const
{
- switch (d->m_spec) {
- case Qt::OffsetFromUTC:
- return d->m_offsetFromUtc;
- case Qt::UTC:
- return 0;
- case Qt::LocalTime:
- if (isValid())
- return (d->m_msecs - toMSecsSinceEpoch()) / 1000;
- }
- return 0;
+ d->refreshDateTime();
+ return d->m_offsetFromUtc;
}
/*!
@@ -2958,6 +3034,7 @@ void QDateTime::setTimeSpec(Qt::TimeSpec spec)
{
detach();
d->setTimeSpec(spec, 0);
+ d->checkValidDateTime();
}
/*!
@@ -2979,6 +3056,7 @@ void QDateTime::setOffsetFromUtc(int offsetSeconds)
{
detach();
d->setTimeSpec(Qt::OffsetFromUTC, offsetSeconds);
+ d->checkValidDateTime();
}
/*!
@@ -2998,15 +3076,8 @@ void QDateTime::setOffsetFromUtc(int offsetSeconds)
*/
qint64 QDateTime::toMSecsSinceEpoch() const
{
- switch (d->m_spec) {
- case Qt::UTC:
- return d->m_msecs;
- case Qt::OffsetFromUTC:
- return d->m_msecs - (d->m_offsetFromUtc * 1000);
- case Qt::LocalTime:
- return localMSecsToEpochMSecs(d->m_msecs);
- }
- return 0;
+ d->refreshDateTime();
+ return d->toMSecsSinceEpoch();
}
/*!
@@ -3033,7 +3104,9 @@ qint64 QDateTime::toMSecsSinceEpoch() const
uint QDateTime::toTime_t() const
{
- qint64 retval = toMSecsSinceEpoch() / 1000;
+ if (!isValid())
+ return uint(-1);
+ qint64 retval = d->toMSecsSinceEpoch() / 1000;
if (quint64(retval) >= Q_UINT64_C(0xFFFFFFFF))
return uint(-1);
return uint(retval);
@@ -3061,11 +3134,17 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs)
switch (d->m_spec) {
case Qt::UTC:
d->m_msecs = msecs;
- d->m_status = d->m_status | QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime;
+ d->m_status = d->m_status
+ | QDateTimePrivate::ValidDate
+ | QDateTimePrivate::ValidTime
+ | QDateTimePrivate::ValidDateTime;
break;
case Qt::OffsetFromUTC:
d->m_msecs = msecs + (d->m_offsetFromUtc * 1000);
- d->m_status = d->m_status | QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime;
+ d->m_status = d->m_status
+ | QDateTimePrivate::ValidDate
+ | QDateTimePrivate::ValidTime
+ | QDateTimePrivate::ValidDateTime;
break;
case Qt::LocalTime: {
QDate dt;
@@ -3311,6 +3390,12 @@ QString QDateTime::toString(const QString& format) const
later than the datetime of this object (or earlier if \a ndays is
negative).
+ If the timeSpec() is Qt::LocalTime and the resulting
+ date and time fall in the Standard Time to Daylight Time transition
+ hour then the result will be adjusted accordingly, i.e. if the transition
+ is at 2am and the clock goes forward to 3am and the result falls between
+ 2am and 3am then the result will be adjusted to fall after 3am.
+
\sa daysTo(), addMonths(), addYears(), addSecs()
*/
@@ -3321,7 +3406,12 @@ QDateTime QDateTime::addDays(qint64 ndays) const
QDate date;
QTime time;
d->getDateTime(&date, &time);
- dt.d->setDateTime(date.addDays(ndays), time);
+ date = date.addDays(ndays);
+ // Result might fall into "missing" DaylightTime transition hour,
+ // so call conversion and use the adjusted returned time
+ if (d->m_spec == Qt::LocalTime)
+ localMSecsToEpochMSecs(timeToMSecs(date, time), &date, &time);
+ dt.d->setDateTime(date, time);
return dt;
}
@@ -3330,6 +3420,12 @@ QDateTime QDateTime::addDays(qint64 ndays) const
later than the datetime of this object (or earlier if \a nmonths
is negative).
+ If the timeSpec() is Qt::LocalTime and the resulting
+ date and time fall in the Standard Time to Daylight Time transition
+ hour then the result will be adjusted accordingly, i.e. if the transition
+ is at 2am and the clock goes forward to 3am and the result falls between
+ 2am and 3am then the result will be adjusted to fall after 3am.
+
\sa daysTo(), addDays(), addYears(), addSecs()
*/
@@ -3340,7 +3436,12 @@ QDateTime QDateTime::addMonths(int nmonths) const
QDate date;
QTime time;
d->getDateTime(&date, &time);
- dt.d->setDateTime(date.addMonths(nmonths), time);
+ date = date.addMonths(nmonths);
+ // Result might fall into "missing" DaylightTime transition hour,
+ // so call conversion and use the adjusted returned time
+ if (d->m_spec == Qt::LocalTime)
+ localMSecsToEpochMSecs(timeToMSecs(date, time), &date, &time);
+ dt.d->setDateTime(date, time);
return dt;
}
@@ -3349,6 +3450,12 @@ QDateTime QDateTime::addMonths(int nmonths) const
later than the datetime of this object (or earlier if \a nyears is
negative).
+ If the timeSpec() is Qt::LocalTime and the resulting
+ date and time fall in the Standard Time to Daylight Time transition
+ hour then the result will be adjusted accordingly, i.e. if the transition
+ is at 2am and the clock goes forward to 3am and the result falls between
+ 2am and 3am then the result will be adjusted to fall after 3am.
+
\sa daysTo(), addDays(), addMonths(), addSecs()
*/
@@ -3359,7 +3466,12 @@ QDateTime QDateTime::addYears(int nyears) const
QDate date;
QTime time;
d->getDateTime(&date, &time);
- dt.d->setDateTime(date.addYears(nyears), time);
+ date = date.addYears(nyears);
+ // Result might fall into "missing" DaylightTime transition hour,
+ // so call conversion and use the adjusted returned time
+ if (d->m_spec == Qt::LocalTime)
+ localMSecsToEpochMSecs(timeToMSecs(date, time), &date, &time);
+ dt.d->setDateTime(date, time);
return dt;
}
@@ -3396,7 +3508,7 @@ QDateTime QDateTime::addMSecs(qint64 msecs) const
dt.detach();
if (d->m_spec == Qt::LocalTime)
// Convert to real UTC first in case crosses daylight transition
- dt.setMSecsSinceEpoch(toMSecsSinceEpoch() + msecs);
+ dt.setMSecsSinceEpoch(d->toMSecsSinceEpoch() + msecs);
else
// No need to convert, just add on
dt.d->m_msecs = dt.d->m_msecs + msecs;
@@ -3465,7 +3577,7 @@ qint64 QDateTime::msecsTo(const QDateTime &other) const
if (!isValid() || !other.isValid())
return 0;
- return other.toMSecsSinceEpoch() - toMSecsSinceEpoch();
+ return other.d->toMSecsSinceEpoch() - d->toMSecsSinceEpoch();
}
/*!
diff --git a/src/corelib/tools/qdatetime_p.h b/src/corelib/tools/qdatetime_p.h
index 124bd98a31..7fd36bc4e0 100644
--- a/src/corelib/tools/qdatetime_p.h
+++ b/src/corelib/tools/qdatetime_p.h
@@ -86,6 +86,7 @@ public:
NullTime = 0x02,
ValidDate = 0x04,
ValidTime = 0x08,
+ ValidDateTime = 0x10
};
Q_DECLARE_FLAGS(StatusFlags, StatusFlag)
@@ -114,11 +115,20 @@ public:
void setDateTime(const QDate &date, const QTime &time);
void getDateTime(QDate *date, QTime *time) const;
+ // Returns msecs since epoch, assumes offset value is current
+ inline qint64 toMSecsSinceEpoch() const { return (m_msecs - (m_offsetFromUtc * 1000)); }
+
+ void checkValidDateTime();
+ void refreshDateTime();
+
// Get/set date and time status
inline bool isNullDate() const { return (m_status & NullDate) == NullDate; }
inline bool isNullTime() const { return (m_status & NullTime) == NullTime; }
inline bool isValidDate() const { return (m_status & ValidDate) == ValidDate; }
inline bool isValidTime() const { return (m_status & ValidTime) == ValidTime; }
+ 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; }
static inline qint64 minJd() { return QDate::minJd(); }
static inline qint64 maxJd() { return QDate::maxJd(); }