summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/kernel/qfunctions_wince.h2
-rw-r--r--src/corelib/tools/qdatetime.cpp872
-rw-r--r--src/corelib/tools/qdatetime_p.h45
3 files changed, 560 insertions, 359 deletions
diff --git a/src/corelib/kernel/qfunctions_wince.h b/src/corelib/kernel/qfunctions_wince.h
index ab7bbe3f99..c71bd6a142 100644
--- a/src/corelib/kernel/qfunctions_wince.h
+++ b/src/corelib/kernel/qfunctions_wince.h
@@ -108,6 +108,7 @@ struct tm {
#endif // _TM_DEFINED
FILETIME qt_wince_time_tToFt( time_t tt );
+time_t qt_wince_ftToTime_t( const FILETIME ft );
// File I/O ---------------------------------------------------------
#define _O_RDONLY 0x0001
@@ -433,6 +434,7 @@ generate_inline_return_func4(getenv_s, errno_t, size_t *, char *, size_t, const
generate_inline_return_func2(_putenv_s, errno_t, const char *, const char *)
generate_inline_return_func0(_getpid, int)
generate_inline_return_func1(time_tToFt, FILETIME, time_t)
+generate_inline_return_func1(ftToTime_t, time_t, FILETIME)
generate_inline_return_func0(_getdrive, int)
generate_inline_return_func2(_waccess, int, const wchar_t *, int)
generate_inline_return_func3(_wopen, int, const wchar_t *, int, int)
diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp
index ff96fbf811..c3ce5b2540 100644
--- a/src/corelib/tools/qdatetime.cpp
+++ b/src/corelib/tools/qdatetime.cpp
@@ -75,6 +75,7 @@ enum {
MSECS_PER_HOUR = 3600000,
SECS_PER_MIN = 60,
MSECS_PER_MIN = 60000,
+ TIME_T_MAX = 2145916799, // int maximum 2037-12-31T23:59:59 UTC
JULIAN_DAY_FOR_EPOCH = 2440588 // result of julianDayFromDate(1970, 1, 1)
};
@@ -164,9 +165,6 @@ int qt_monthNumberFromShortName(const QString &shortName)
#ifndef QT_NO_DATESTRING
static void rfcDateImpl(const QString &s, QDate *dd = 0, QTime *dt = 0, int *utfcOffset = 0);
#endif
-static QDateTimePrivate::Spec utcToLocal(QDate &date, QTime &time);
-static void utcToOffset(QDate *date, QTime *time, qint32 offset);
-static QDate adjustDate(QDate date);
// Return offset in [+-]HH:MM format
// Qt::ISODate puts : between the hours and minutes, but Qt:TextDate does not
@@ -218,19 +216,87 @@ static int fromOffsetString(const QString &offsetString, bool *valid)
return ((hour * 60) + minute) * 60;
}
-#if !defined(Q_OS_WINCE)
+// Returns the tzname, assume tzset has been called already
+static QString qt_tzname(QDateTimePrivate::DaylightStatus daylightStatus)
+{
+#if defined(Q_OS_WINCE)
+ TIME_ZONE_INFORMATION tzi;
+ DWORD res = GetTimeZoneInformation(&tzi);
+ if (res == TIME_ZONE_ID_UNKNOWN)
+ return QString();
+ else if (daylightStatus == QDateTimePrivate::DaylightTime)
+ return QString::fromWCharArray(tzi.DaylightName);
+ else
+ return QString::fromWCharArray(tzi.StandardName);
+#else
+ int isDst = (daylightStatus == QDateTimePrivate::DaylightTime) ? 1 : 0;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ size_t s = 0;
+ char name[512];
+ if (_get_tzname(&s, name, 512, isDst))
+ return QString();
+ return QString::fromLocal8Bit(name);
+#else
+ return QString::fromLocal8Bit(tzname[isDst]);
+#endif // Q_OS_WIN
+#endif // Q_OS_WINCE
+}
+
// Calls the platform variant of mktime for the given date and time,
// and updates the date, time, daylightStatus and abbreviation with the returned values
// If the date falls outside the 1970 to 2037 range supported by mktime / time_t
-// then null date/time will be returned, you should call adjustDate() first if
+// then null date/time will be returned, you should adjust the date first if
// you need a guaranteed result.
-static time_t qt_mktime(QDate *date, QTime *time, QDateTimePrivate::DaylightStatus *daylightStatus,
+static qint64 qt_mktime(QDate *date, QTime *time, QDateTimePrivate::DaylightStatus *daylightStatus,
QString *abbreviation, bool *ok)
{
- if (ok)
- *ok = false;
+ const qint64 msec = time->msec();
int yy, mm, dd;
date->getDate(&yy, &mm, &dd);
+
+#if defined(Q_OS_WINCE)
+ // WinCE doesn't provide standard C library time functions
+ SYSTEMTIME st;
+ memset(&st, 0, sizeof(SYSTEMTIME));
+ st.wSecond = time->second();
+ st.wMinute = time->minute();
+ st.wHour = time->hour();
+ st.wDay = dd;
+ st.wMonth = mm;
+ st.wYear = yy;
+ FILETIME lft;
+ bool valid = SystemTimeToFileTime(&st, &lft);
+ FILETIME ft;
+ if (valid)
+ valid = LocalFileTimeToFileTime(&lft, &ft);
+ const time_t secsSinceEpoch = ftToTime_t(ft);
+ const time_t localSecs = ftToTime_t(lft);
+ TIME_ZONE_INFORMATION tzi;
+ GetTimeZoneInformation(&tzi);
+ bool isDaylight = false;
+ // Check for overflow
+ qint64 localDiff = qAbs(localSecs - secsSinceEpoch);
+ int daylightOffset = qAbs(tzi.Bias + tzi.DaylightBias) * 60;
+ if (localDiff > daylightOffset)
+ valid = false;
+ else
+ isDaylight = (localDiff == daylightOffset);
+ if (daylightStatus) {
+ if (isDaylight)
+ *daylightStatus = QDateTimePrivate::DaylightTime;
+ else
+ *daylightStatus = QDateTimePrivate::StandardTime;
+ }
+ if (abbreviation) {
+ if (isDaylight)
+ *abbreviation = QString::fromWCharArray(tzi.DaylightName);
+ else
+ *abbreviation = QString::fromWCharArray(tzi.StandardName);
+ }
+ if (ok)
+ *ok = valid;
+#else
+ // All other platforms provide standard C library time functions
tm local;
local.tm_sec = time->second();
local.tm_min = time->minute();
@@ -241,30 +307,25 @@ static time_t qt_mktime(QDate *date, QTime *time, QDateTimePrivate::DaylightStat
local.tm_wday = 0;
local.tm_yday = 0;
local.tm_isdst = -1;
-#if defined(Q_OS_WIN)
- _tzset();
-#else
- tzset();
-#endif // Q_OS_WIN
const 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, time->msec());
- if (local.tm_isdst == 1) {
+ *time = QTime(local.tm_hour, local.tm_min, local.tm_sec, msec);
+ if (local.tm_isdst >= 1) {
if (daylightStatus)
*daylightStatus = QDateTimePrivate::DaylightTime;
if (abbreviation)
- *abbreviation = QString::fromLocal8Bit(tzname[1]);
+ *abbreviation = qt_tzname(QDateTimePrivate::DaylightTime);
} else if (local.tm_isdst == 0) {
if (daylightStatus)
*daylightStatus = QDateTimePrivate::StandardTime;
if (abbreviation)
- *abbreviation = QString::fromLocal8Bit(tzname[0]);
+ *abbreviation = qt_tzname(QDateTimePrivate::StandardTime);
} else {
if (daylightStatus)
*daylightStatus = QDateTimePrivate::UnknownDaylightTime;
if (abbreviation)
- *abbreviation = QString::fromLocal8Bit(tzname[0]);
+ *abbreviation = qt_tzname(QDateTimePrivate::StandardTime);
}
if (ok)
*ok = true;
@@ -275,10 +336,13 @@ static time_t qt_mktime(QDate *date, QTime *time, QDateTimePrivate::DaylightStat
*daylightStatus = QDateTimePrivate::UnknownDaylightTime;
if (abbreviation)
*abbreviation = QString();
+ if (ok)
+ *ok = false;
}
- return secsSinceEpoch;
+#endif // Q_OS_WINCE
+
+ return ((qint64)secsSinceEpoch * 1000) + msec;
}
-#endif // !Q_OS_WINCE
/*****************************************************************************
QDate member functions
@@ -2153,6 +2217,286 @@ int QTime::elapsed() const
}
/*****************************************************************************
+ QDateTimePrivate private helper functions
+ *****************************************************************************/
+
+// Calls the platform variant of tzset
+static void qt_tzset()
+{
+#if defined(Q_OS_WINCE)
+ // WinCE doesn't use tzset
+ return;
+#elif defined(Q_OS_WIN)
+ _tzset();
+#else
+ tzset();
+#endif // Q_OS_WIN
+}
+
+// Returns the platform variant of timezone, i.e. the standard time offset
+// The timezone external variable is documented as always holding the
+// Standard Time offset as seconds west of Greenwich, i.e. UTC+01:00 is -3600
+// Note this may not be historicaly accurate.
+// Relies on tzset, mktime, or localtime having been called to populate timezone
+static int qt_timezone()
+{
+#if defined(Q_OS_WINCE)
+ TIME_ZONE_INFORMATION tzi;
+ GetTimeZoneInformation(&tzi);
+ // Expressed in minutes, convert to seconds
+ return (tzi.Bias + tzi.StandardBias) * 60;
+#elif defined(_MSC_VER) && _MSC_VER >= 1400
+ long offset;
+ _get_timezone(&offset);
+ return offset;
+#else
+ return timezone;
+#endif // Q_OS_WIN
+}
+
+// Calls the platform variant of localtime for the given msecs, and updates
+// the date, time, and daylight status with the returned values.
+static bool qt_localtime(qint64 msecsSinceEpoch, QDate *localDate, QTime *localTime,
+ QDateTimePrivate::DaylightStatus *daylightStatus)
+{
+ const time_t secsSinceEpoch = msecsSinceEpoch / 1000;
+ const int msec = msecsSinceEpoch % 1000;
+
+ tm local;
+ bool valid = false;
+
+#if defined(Q_OS_WINCE)
+ FILETIME utcTime = time_tToFt(secsSinceEpoch);
+ FILETIME resultTime;
+ valid = FileTimeToLocalFileTime(&utcTime , &resultTime);
+ SYSTEMTIME sysTime;
+ if (valid)
+ valid = FileTimeToSystemTime(&resultTime , &sysTime);
+
+ if (valid) {
+ local.tm_sec = sysTime.wSecond;
+ local.tm_min = sysTime.wMinute;
+ local.tm_hour = sysTime.wHour;
+ local.tm_mday = sysTime.wDay;
+ local.tm_mon = sysTime.wMonth - 1;
+ local.tm_year = sysTime.wYear - 1900;
+ }
+#elif !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
+ // Use the reentrant version of localtime() where available
+ // as is thread-safe and doesn't use a shared static data area
+ tm *res = 0;
+ res = localtime_r(&secsSinceEpoch, &local);
+ if (res)
+ valid = true;
+#elif defined(_MSC_VER) && _MSC_VER >= 1400
+ if (!_localtime64_s(&local, &secsSinceEpoch))
+ valid = true;
+#else
+ // Returns shared static data which may be overwritten at any time
+ // So copy the result asap
+ tm *res = 0;
+ res = localtime(&secsSinceEpoch);
+ if (res) {
+ local = *res;
+ valid = true;
+ }
+#endif
+ if (valid) {
+ *localDate = QDate(local.tm_year + 1900, local.tm_mon + 1, local.tm_mday);
+ *localTime = QTime(local.tm_hour, local.tm_min, local.tm_sec, msec);
+ if (daylightStatus) {
+ if (local.tm_isdst > 0)
+ *daylightStatus = QDateTimePrivate::DaylightTime;
+ else if (local.tm_isdst < 0)
+ *daylightStatus = QDateTimePrivate::UnknownDaylightTime;
+ else
+ *daylightStatus = QDateTimePrivate::StandardTime;
+ }
+ return true;
+ } else {
+ *localDate = QDate();
+ *localTime = QTime();
+ if (daylightStatus)
+ *daylightStatus = QDateTimePrivate::UnknownDaylightTime;
+ return false;
+ }
+}
+
+// Converts an msecs value into a date and time
+static void msecsToTime(qint64 msecs, QDate *date, QTime *time)
+{
+ qint64 jd = JULIAN_DAY_FOR_EPOCH;
+ qint64 ds = 0;
+
+ if (qAbs(msecs) >= MSECS_PER_DAY) {
+ jd += (msecs / MSECS_PER_DAY);
+ msecs %= MSECS_PER_DAY;
+ }
+
+ if (msecs < 0) {
+ ds = MSECS_PER_DAY - msecs - 1;
+ jd -= ds / MSECS_PER_DAY;
+ ds = ds % MSECS_PER_DAY;
+ ds = MSECS_PER_DAY - ds - 1;
+ } else {
+ ds = msecs;
+ }
+
+ if (date)
+ *date = QDate::fromJulianDay(jd);
+ if (time)
+ *time = QTime::fromMSecsSinceStartOfDay(ds);
+}
+
+// Converts a date/time value into msecs
+static qint64 timeToMSecs(const QDate &date, const QTime &time)
+{
+ return ((date.toJulianDay() - JULIAN_DAY_FOR_EPOCH) * MSECS_PER_DAY)
+ + time.msecsSinceStartOfDay();
+}
+
+// Convert an MSecs Since Epoch into Local Time
+static bool epochMSecsToLocalTime(qint64 msecs, QDate *localDate, QTime *localTime,
+ QDateTimePrivate::DaylightStatus *daylightStatus = 0)
+{
+ if (msecs < 0) {
+ // Docs state any LocalTime before 1970-01-01 will *not* have any Daylight Time applied
+ // Instead just use the standard offset from UTC to convert to UTC time
+ qt_tzset();
+ msecsToTime(msecs - qt_timezone() * 1000, localDate, localTime);
+ if (daylightStatus)
+ *daylightStatus = QDateTimePrivate::StandardTime;
+ return true;
+ } else if (msecs > (qint64(TIME_T_MAX) * 1000)) {
+ // Docs state any LocalTime after 2037-12-31 *will* have any Daylight Time applied
+ // but this may fall outside the supported time_t range, so need to fake it.
+ // Use existing method to fake the conversion, but this is deeply flawed as it may
+ // apply the conversion from the wrong day number, e.g. if rule is last Sunday of month
+ // TODO Use QTimeZone when available to apply the future rule correctly
+ QDate utcDate;
+ QTime utcTime;
+ msecsToTime(msecs, &utcDate, &utcTime);
+ int year, month, day;
+ utcDate.getDate(&year, &month, &day);
+ // 2037 is not a leap year, so make sure date isn't Feb 29
+ if (month == 2 && day == 29)
+ --day;
+ QDate fakeDate(2037, month, day);
+ qint64 fakeMsecs = QDateTime(fakeDate, utcTime, Qt::UTC).toMSecsSinceEpoch();
+ bool res = qt_localtime(fakeMsecs, localDate, localTime, daylightStatus);
+ *localDate = localDate->addDays(fakeDate.daysTo(utcDate));
+ return res;
+ } else {
+ // Falls inside time_t suported range so can use localtime
+ return qt_localtime(msecs, localDate, localTime, daylightStatus);
+ }
+}
+
+// Convert a LocalTime expressed in local msecs encoding into a UTC epoch msecs
+// Optionally populate the returned values from mktime for the adjusted local
+// date and time and daylight status
+static qint64 localMSecsToEpochMSecs(qint64 localMsecs, QDate *localDate = 0, QTime *localTime = 0,
+ QDateTimePrivate::DaylightStatus *daylightStatus = 0,
+ QString *abbreviation = 0, bool *ok = 0)
+{
+ QDate dt;
+ QTime tm;
+ msecsToTime(localMsecs, &dt, &tm);
+
+ qint64 msecsMax = qint64(TIME_T_MAX) * 1000;
+
+ if (localMsecs <= qint64(MSECS_PER_DAY)) {
+
+ // Docs state any LocalTime before 1970-01-01 will *not* have any Daylight Time applied
+
+ // First, if localMsecs is within +/- 1 day of minimum time_t try mktime in case it does
+ // fall after minimum and needs proper daylight conversion
+ if (localMsecs >= -qint64(MSECS_PER_DAY)) {
+ bool valid;
+ qint64 utcMsecs = qt_mktime(&dt, &tm, daylightStatus, abbreviation, &valid);
+ if (valid && utcMsecs >= 0) {
+ // mktime worked and falls in valid range, so use it
+ if (localDate)
+ *localDate = dt;
+ if (localTime)
+ *localTime = tm;
+ if (ok)
+ *ok = true;
+ return utcMsecs;
+ }
+ } else {
+ // If we don't call mktime then need to call tzset to get offset
+ qt_tzset();
+ }
+ // Time is clearly before 1970-01-01 so just use standard offset to convert
+ qint64 utcMsecs = localMsecs + qt_timezone() * 1000;
+ if (localDate || localTime)
+ msecsToTime(localMsecs, localDate, localTime);
+ if (daylightStatus)
+ *daylightStatus = QDateTimePrivate::StandardTime;
+ if (abbreviation)
+ *abbreviation = qt_tzname(QDateTimePrivate::StandardTime);
+ if (ok)
+ *ok = true;
+ return utcMsecs;
+
+ } else if (localMsecs >= msecsMax - MSECS_PER_DAY) {
+
+ // Docs state any LocalTime after 2037-12-31 *will* have any Daylight Time applied
+ // but this may fall outside the supported time_t range, so need to fake it.
+
+ // First, if localMsecs is within +/- 1 day of maximum time_t try mktime in case it does
+ // fall before maximum and can use proper daylight conversion
+ if (localMsecs <= msecsMax + MSECS_PER_DAY) {
+ bool valid;
+ qint64 utcMsecs = qt_mktime(&dt, &tm, daylightStatus, abbreviation, &valid);
+ if (valid && utcMsecs <= msecsMax) {
+ // mktime worked and falls in valid range, so use it
+ if (localDate)
+ *localDate = dt;
+ if (localTime)
+ *localTime = tm;
+ if (ok)
+ *ok = true;
+ return utcMsecs;
+ }
+ }
+ // Use existing method to fake the conversion, but this is deeply flawed as it may
+ // apply the conversion from the wrong day number, e.g. if rule is last Sunday of month
+ // TODO Use QTimeZone when available to apply the future rule correctly
+ int year, month, day;
+ dt.getDate(&year, &month, &day);
+ // 2037 is not a leap year, so make sure date isn't Feb 29
+ if (month == 2 && day == 29)
+ --day;
+ QDate fakeDate(2037, month, day);
+ qint64 fakeDiff = fakeDate.daysTo(dt);
+ qint64 utcMsecs = qt_mktime(&fakeDate, &tm, daylightStatus, abbreviation, ok);
+ if (localDate)
+ *localDate = fakeDate.addDays(fakeDiff);
+ if (localTime)
+ *localTime = tm;
+ QDate utcDate;
+ QTime utcTime;
+ msecsToTime(utcMsecs, &utcDate, &utcTime);
+ utcDate = utcDate.addDays(fakeDiff);
+ utcMsecs = timeToMSecs(utcDate, utcTime);
+ return utcMsecs;
+
+ } else {
+
+ // Clearly falls inside 1970-2037 suported range so can use mktime
+ qint64 utcMsecs = qt_mktime(&dt, &tm, daylightStatus, abbreviation, ok);
+ if (localDate)
+ *localDate = dt;
+ if (localTime)
+ *localTime = tm;
+ return utcMsecs;
+
+ }
+}
+
+/*****************************************************************************
QDateTimePrivate member functions
*****************************************************************************/
@@ -2176,6 +2520,49 @@ void QDateTimePrivate::setTimeSpec(Qt::TimeSpec spec, int offsetSeconds)
}
}
+void QDateTimePrivate::setDateTime(const QDate &date, const QTime &time)
+{
+ // If the date is valid and the time is not we set time to 00:00:00
+ QTime useTime = time;
+ if (!useTime.isValid() && date.isValid())
+ useTime = QTime::fromMSecsSinceStartOfDay(0);
+
+ // Reset the status
+ m_status = 0;
+
+ // Set date value and status
+ qint64 days = 0;
+ if (date.isValid()) {
+ days = date.toJulianDay() - JULIAN_DAY_FOR_EPOCH;
+ m_status = ValidDate;
+ } else if (date.isNull()) {
+ m_status = NullDate;
+ }
+
+ // Set time value and status
+ int ds = 0;
+ if (useTime.isValid()) {
+ ds = useTime.msecsSinceStartOfDay();
+ m_status = m_status | ValidTime;
+ } else if (time.isNull()) {
+ m_status = m_status | NullTime;
+ }
+
+ // Set msecs serial value
+ m_msecs = (days * MSECS_PER_DAY) + ds;
+}
+
+void QDateTimePrivate::getDateTime(QDate *date, QTime *time) const
+{
+ msecsToTime(m_msecs, date, time);
+
+ if (isNullDate())
+ *date = QDate();
+
+ if (isNullTime())
+ *time = QTime();
+}
+
/*****************************************************************************
QDateTime member functions
*****************************************************************************/
@@ -2238,17 +2625,13 @@ void QDateTimePrivate::setTimeSpec(Qt::TimeSpec spec, int offsetSeconds)
\section2 Range of Valid Dates
- Dates are stored internally as a Julian Day number, an integer count of
- every day in a contiguous range, with 24 November 4714 BCE in the Gregorian
- calendar being Julian Day 0 (1 January 4713 BCE in the Julian calendar).
- As well as being an efficient and accurate way of storing an absolute date,
- it is suitable for converting a Date into other calendar systems such as
- Hebrew, Islamic or Chinese. The Julian Day number can be obtained using
- QDate::toJulianDay() and can be set using QDate::fromJulianDay().
-
- The range of dates able to be stored by QDate as a Julian Day number is
- for technical reasons limited to between -784350574879 and 784354017364,
- which means from before 2 billion BCE to after 2 billion CE.
+ The range of valid values able to be stored in QDateTime is dependent on
+ the internal storage implementation. QDateTime is currently stored in a
+ qint64 as a serial msecs value encoding the date and time. This restricts
+ the date range to about +/- 292 million years, compared to the QDate range
+ of +/- 2 billion years. Care must be taken when creating a QDateTime with
+ extreme values that you do not overflow the storage. The exact range of
+ supported values varies depending on the Qt::TimeSpec and time zone.
\section2
Use of System Timezone
@@ -2361,15 +2744,13 @@ QDateTime::QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec, in
QDateTimePrivate::QDateTimePrivate(const QDate &toDate, const QTime &toTime, Qt::TimeSpec toSpec,
int offsetSeconds)
+ : m_msecs(0),
+ m_spec(Qt::LocalTime),
+ m_offsetFromUtc(0),
+ m_status(0)
{
- date = toDate;
-
- if (!toTime.isValid() && toDate.isValid())
- time = QTime(0, 0, 0);
- else
- time = toTime;
-
setTimeSpec(toSpec, offsetSeconds);
+ setDateTime(toDate, toTime);
}
/*!
@@ -2415,7 +2796,7 @@ QDateTime &QDateTime::operator=(const QDateTime &other)
bool QDateTime::isNull() const
{
- return d->date.isNull() && d->time.isNull();
+ return d->isNullDate() && d->isNullTime();
}
/*!
@@ -2427,7 +2808,7 @@ bool QDateTime::isNull() const
bool QDateTime::isValid() const
{
- return d->date.isValid() && d->time.isValid();
+ return d->isValidDate() && d->isValidTime();
}
/*!
@@ -2438,7 +2819,10 @@ bool QDateTime::isValid() const
QDate QDateTime::date() const
{
- return d->date;
+ QDate dt;
+ QTime tm;
+ d->getDateTime(&dt, &tm);
+ return dt;
}
/*!
@@ -2449,7 +2833,10 @@ QDate QDateTime::date() const
QTime QDateTime::time() const
{
- return d->time;
+ QDate dt;
+ QTime tm;
+ d->getDateTime(&dt, &tm);
+ return tm;
}
/*!
@@ -2486,8 +2873,8 @@ int QDateTime::offsetFromUtc() const
case Qt::UTC:
return 0;
case Qt::LocalTime:
- const QDateTime fakeDate(d->date, d->time, Qt::UTC);
- return (fakeDate.toMSecsSinceEpoch() - toMSecsSinceEpoch()) / 1000;
+ if (isValid())
+ return (d->m_msecs - toMSecsSinceEpoch()) / 1000;
}
return 0;
}
@@ -2520,17 +2907,10 @@ QString QDateTime::timeZoneAbbreviation() const
return QStringLiteral("UTC");
case Qt::OffsetFromUTC:
return QLatin1String("UTC") + toOffsetString(Qt::ISODate, d->m_offsetFromUtc);
- case Qt::LocalTime: {
-#if defined(Q_OS_WINCE)
- // TODO Stub to enable compilation on WinCE
- return QString();
-#else
- QDate dt = adjustDate(d->date);
- QTime tm = d->time;
+ case Qt::LocalTime: {
QString abbrev;
- qt_mktime(&dt, &tm, 0, &abbrev, 0);
+ localMSecsToEpochMSecs(d->m_msecs, 0, 0, 0, &abbrev);
return abbrev;
-#endif // !Q_OS_WINCE
}
}
return QString();
@@ -2546,9 +2926,7 @@ QString QDateTime::timeZoneAbbreviation() const
void QDateTime::setDate(const QDate &date)
{
detach();
- d->date = date;
- if (date.isValid() && !d->time.isValid())
- d->time = QTime(0, 0, 0);
+ d->setDateTime(date, time());
}
/*!
@@ -2560,7 +2938,7 @@ void QDateTime::setDate(const QDate &date)
void QDateTime::setTime(const QTime &time)
{
detach();
- d->time = time;
+ d->setDateTime(date(), time);
}
/*!
@@ -2603,13 +2981,6 @@ void QDateTime::setOffsetFromUtc(int offsetSeconds)
d->setTimeSpec(Qt::OffsetFromUTC, offsetSeconds);
}
-qint64 toMSecsSinceEpoch_helper(qint64 jd, int msecs)
-{
- qint64 days = jd - JULIAN_DAY_FOR_EPOCH;
- qint64 retval = (days * MSECS_PER_DAY) + msecs;
- return retval;
-}
-
/*!
\since 4.7
@@ -2627,11 +2998,15 @@ qint64 toMSecsSinceEpoch_helper(qint64 jd, int msecs)
*/
qint64 QDateTime::toMSecsSinceEpoch() const
{
- QDate utcDate;
- QTime utcTime;
- d->getUTC(utcDate, utcTime);
-
- return toMSecsSinceEpoch_helper(utcDate.toJulianDay(), QTime(0, 0, 0).msecsTo(utcTime));
+ 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;
}
/*!
@@ -2682,21 +3057,24 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs)
{
detach();
- qint64 ddays = msecs / MSECS_PER_DAY;
- msecs %= MSECS_PER_DAY;
- if (msecs < 0) {
- // negative
- --ddays;
- msecs += MSECS_PER_DAY;
+ d->m_status = 0;
+ switch (d->m_spec) {
+ case Qt::UTC:
+ d->m_msecs = msecs;
+ d->m_status = d->m_status | QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime;
+ break;
+ case Qt::OffsetFromUTC:
+ d->m_msecs = msecs + (d->m_offsetFromUtc * 1000);
+ d->m_status = d->m_status | QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime;
+ break;
+ case Qt::LocalTime: {
+ QDate dt;
+ QTime tm;
+ epochMSecsToLocalTime(msecs, &dt, &tm);
+ d->setDateTime(dt, tm);
+ break;
+ }
}
-
- d->date = QDate(1970, 1, 1).addDays(ddays);
- d->time = QTime::fromMSecsSinceStartOfDay(msecs);
-
- if (d->m_spec == Qt::OffsetFromUTC)
- utcToOffset(&d->date, &d->time, d->m_offsetFromUtc);
- else if (d->m_spec != Qt::UTC)
- utcToLocal(d->date, d->time);
}
/*!
@@ -2807,26 +3185,33 @@ QString QDateTime::toString(Qt::DateFormat format) const
}
default:
#ifndef QT_NO_TEXTDATE
- case Qt::TextDate:
+ case Qt::TextDate: {
+ QDate dt;
+ QTime tm;
+ d->getDateTime(&dt, &tm);
//We cant use date.toString(Qt::TextDate) as we need to insert the time before the year
- buf = QString::fromUtf8("%1 %2 %3 %4 %5").arg(d->date.shortDayName(d->date.dayOfWeek()))
- .arg(d->date.shortMonthName(d->date.month()))
- .arg(d->date.day())
- .arg(d->time.toString(Qt::TextDate))
- .arg(d->date.year());
+ buf = QString::fromUtf8("%1 %2 %3 %4 %5").arg(dt.shortDayName(dt.dayOfWeek()))
+ .arg(dt.shortMonthName(dt.month()))
+ .arg(dt.day())
+ .arg(tm.toString(Qt::TextDate))
+ .arg(dt.year());
if (timeSpec() != Qt::LocalTime) {
buf += QStringLiteral(" GMT");
if (d->m_spec == Qt::OffsetFromUTC)
buf += toOffsetString(Qt::TextDate, d->m_offsetFromUtc);
}
return buf;
+ }
#endif
- case Qt::ISODate:
- buf = d->date.toString(Qt::ISODate);
+ case Qt::ISODate: {
+ QDate dt;
+ QTime tm;
+ d->getDateTime(&dt, &tm);
+ buf = dt.toString(Qt::ISODate);
if (buf.isEmpty())
return QString(); // failed to convert
buf += QLatin1Char('T');
- buf += d->time.toString(Qt::ISODate);
+ buf += tm.toString(Qt::ISODate);
switch (d->m_spec) {
case Qt::UTC:
buf += QLatin1Char('Z');
@@ -2839,6 +3224,7 @@ QString QDateTime::toString(Qt::DateFormat format) const
}
return buf;
}
+ }
}
/*!
@@ -2932,7 +3318,10 @@ QDateTime QDateTime::addDays(qint64 ndays) const
{
QDateTime dt(*this);
dt.detach();
- dt.d->date = d->date.addDays(ndays);
+ QDate date;
+ QTime time;
+ d->getDateTime(&date, &time);
+ dt.d->setDateTime(date.addDays(ndays), time);
return dt;
}
@@ -2948,7 +3337,10 @@ QDateTime QDateTime::addMonths(int nmonths) const
{
QDateTime dt(*this);
dt.detach();
- dt.d->date = d->date.addMonths(nmonths);
+ QDate date;
+ QTime time;
+ d->getDateTime(&date, &time);
+ dt.d->setDateTime(date.addMonths(nmonths), time);
return dt;
}
@@ -2964,47 +3356,14 @@ QDateTime QDateTime::addYears(int nyears) const
{
QDateTime dt(*this);
dt.detach();
- dt.d->date = d->date.addYears(nyears);
+ QDate date;
+ QTime time;
+ d->getDateTime(&date, &time);
+ dt.d->setDateTime(date.addYears(nyears), time);
return dt;
}
/*!
- Adds \a msecs to utcDate and \a utcTime as appropriate. It is assumed that
- utcDate and utcTime are adjusted to UTC.
-
- \since 4.5
- \internal
- */
-void QDateTimePrivate::addMSecs(QDate &utcDate, QTime &utcTime, qint64 msecs)
-{
- qint64 dd = utcDate.toJulianDay();
- int tt = QTime(0, 0, 0).msecsTo(utcTime);
- int sign = 1;
- if (msecs < 0) {
- msecs = -msecs;
- sign = -1;
- }
- if (msecs >= int(MSECS_PER_DAY)) {
- dd += sign * (msecs / MSECS_PER_DAY);
- msecs %= MSECS_PER_DAY;
- }
-
- tt += sign * msecs;
- if (tt < 0) {
- tt = MSECS_PER_DAY - tt - 1;
- dd -= tt / MSECS_PER_DAY;
- tt = tt % MSECS_PER_DAY;
- tt = MSECS_PER_DAY - tt - 1;
- } else if (tt >= int(MSECS_PER_DAY)) {
- dd += tt / MSECS_PER_DAY;
- tt = tt % MSECS_PER_DAY;
- }
-
- utcDate = QDate::fromJulianDay(dd);
- utcTime = QTime(0, 0, 0).addMSecs(tt);
-}
-
-/*!
Returns a QDateTime object containing a datetime \a s seconds
later than the datetime of this object (or earlier if \a s is
negative).
@@ -3035,7 +3394,12 @@ QDateTime QDateTime::addMSecs(qint64 msecs) const
QDateTime dt(*this);
dt.detach();
- dt.setMSecsSinceEpoch(toMSecsSinceEpoch() + msecs);
+ if (d->m_spec == Qt::LocalTime)
+ // Convert to real UTC first in case crosses daylight transition
+ dt.setMSecsSinceEpoch(toMSecsSinceEpoch() + msecs);
+ else
+ // No need to convert, just add on
+ dt.d->m_msecs = dt.d->m_msecs + msecs;
return dt;
}
@@ -3151,6 +3515,11 @@ QDateTime QDateTime::toOffsetFromUtc(int offsetSeconds) const
bool QDateTime::operator==(const QDateTime &other) const
{
+ if (d->m_spec == Qt::LocalTime
+ && other.d->m_spec == Qt::LocalTime
+ && d->m_status == other.d->m_status) {
+ return (d->m_msecs == other.d->m_msecs);
+ }
// Convert to UTC and compare
return (toMSecsSinceEpoch() == other.toMSecsSinceEpoch());
}
@@ -3174,6 +3543,11 @@ bool QDateTime::operator==(const QDateTime &other) const
bool QDateTime::operator<(const QDateTime &other) const
{
+ if (d->m_spec == Qt::LocalTime
+ && other.d->m_spec == Qt::LocalTime
+ && d->m_status == other.d->m_status) {
+ return (d->m_msecs < other.d->m_msecs);
+ }
// Convert to UTC and compare
return (toMSecsSinceEpoch() < other.toMSecsSinceEpoch());
}
@@ -3896,19 +4270,20 @@ QDataStream &operator>>(QDataStream &in, QTime &time)
*/
QDataStream &operator<<(QDataStream &out, const QDateTime &dateTime)
{
+ QDate dt;
+ QTime tm;
if (out.version() == QDataStream::Qt_5_0) {
- if (dateTime.isValid()) {
- // This approach is wrong and should not be used again; it breaks
- // the guarantee that a deserialised local datetime is the same time
- // of day, regardless of which timezone it was serialised in.
- QDateTime asUTC = dateTime.toUTC();
- out << asUTC.d->date << asUTC.d->time;
- } else {
- out << dateTime.d->date << dateTime.d->time;
- }
- out << (qint8)dateTime.timeSpec();
+ // This approach is wrong and should not be used again; it breaks
+ // the guarantee that a deserialised local datetime is the same time
+ // of day, regardless of which timezone it was serialised in.
+ if (dateTime.isValid())
+ dateTime.toUTC().d->getDateTime(&dt, &tm);
+ else
+ dateTime.d->getDateTime(&dt, &tm);
+ out << dt << tm << (qint8)dateTime.timeSpec();
} else {
- out << dateTime.d->date << dateTime.d->time;
+ dateTime.d->getDateTime(&dt, &tm);
+ out << dt << tm;
if (out.version() >= QDataStream::Qt_4_0) {
switch (dateTime.d->m_spec) {
case Qt::UTC:
@@ -3942,16 +4317,17 @@ QDataStream &operator>>(QDataStream &in, QDateTime &dateTime)
{
dateTime.detach();
- in >> dateTime.d->date >> dateTime.d->time;
+ QDate dt;
+ QTime tm;
+ in >> dt >> tm;
if (in.version() == QDataStream::Qt_5_0) {
qint8 ts = 0;
in >> ts;
- if (dateTime.isValid()) {
- // We incorrectly stored the datetime as UTC in Qt_5_0.
- dateTime.d->m_spec = Qt::UTC;
- dateTime = dateTime.toTimeSpec(static_cast<Qt::TimeSpec>(ts));
- }
+ // We incorrectly stored the datetime as UTC in Qt_5_0.
+ dateTime.setTimeSpec(Qt::UTC);
+ dateTime.d->setDateTime(dt, tm);
+ dateTime = dateTime.toTimeSpec(static_cast<Qt::TimeSpec>(ts));
} else {
qint8 ts = (qint8)QDateTimePrivate::LocalUnknown;
if (in.version() >= QDataStream::Qt_4_0)
@@ -3973,7 +4349,9 @@ QDataStream &operator>>(QDataStream &in, QDateTime &dateTime)
break;
}
dateTime.d->m_offsetFromUtc = offset;
+ dateTime.d->setDateTime(dt, tm);
}
+
return in;
}
#endif // QT_NO_DATASTREAM
@@ -4046,202 +4424,6 @@ static void rfcDateImpl(const QString &s, QDate *dd, QTime *dt, int *utcOffset)
}
#endif // QT_NO_DATESTRING
-
-#ifdef Q_OS_WIN
-static const int LowerYear = 1980;
-#else
-static const int LowerYear = 1970;
-#endif
-static const int UpperYear = 2037;
-
-static QDate adjustDate(QDate date)
-{
- QDate lowerLimit(LowerYear, 1, 2);
- QDate upperLimit(UpperYear, 12, 30);
-
- if (date > lowerLimit && date < upperLimit)
- return date;
-
- int month = date.month();
- int day = date.day();
-
- // neither 1970 nor 2037 are leap years, so make sure date isn't Feb 29
- if (month == 2 && day == 29)
- --day;
-
- if (date < lowerLimit)
- date.setDate(LowerYear, month, day);
- else
- date.setDate(UpperYear, month, day);
-
- return date;
-}
-
-// Convert passed in UTC datetime into LocalTime and return spec
-static QDateTimePrivate::Spec utcToLocal(QDate &date, QTime &time)
-{
- QDate fakeDate = adjustDate(date);
-
- // won't overflow because of fakeDate
- time_t secsSince1Jan1970UTC = toMSecsSinceEpoch_helper(fakeDate.toJulianDay(), QTime(0, 0, 0).msecsTo(time)) / 1000;
- tm *brokenDown = 0;
-
-#if defined(Q_OS_WINCE)
- tm res;
- FILETIME utcTime = time_tToFt(secsSince1Jan1970UTC);
- FILETIME resultTime;
- FileTimeToLocalFileTime(&utcTime , &resultTime);
- SYSTEMTIME sysTime;
- FileTimeToSystemTime(&resultTime , &sysTime);
-
- res.tm_sec = sysTime.wSecond;
- res.tm_min = sysTime.wMinute;
- res.tm_hour = sysTime.wHour;
- res.tm_mday = sysTime.wDay;
- res.tm_mon = sysTime.wMonth - 1;
- res.tm_year = sysTime.wYear - 1900;
- brokenDown = &res;
-#elif !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
- // use the reentrant version of localtime() where available
- tzset();
- tm res;
- brokenDown = localtime_r(&secsSince1Jan1970UTC, &res);
-#elif defined(_MSC_VER) && _MSC_VER >= 1400
- tm res;
- if (!_localtime64_s(&res, &secsSince1Jan1970UTC))
- brokenDown = &res;
-#else
- brokenDown = localtime(&secsSince1Jan1970UTC);
-#endif
- if (!brokenDown) {
- date = QDate(1970, 1, 1);
- time = QTime();
- return QDateTimePrivate::LocalUnknown;
- } else {
- qint64 deltaDays = fakeDate.daysTo(date);
- date = QDate(brokenDown->tm_year + 1900, brokenDown->tm_mon + 1, brokenDown->tm_mday);
- time = QTime(brokenDown->tm_hour, brokenDown->tm_min, brokenDown->tm_sec, time.msec());
- date = date.addDays(deltaDays);
- if (brokenDown->tm_isdst > 0)
- return QDateTimePrivate::LocalDST;
- else if (brokenDown->tm_isdst < 0)
- return QDateTimePrivate::LocalUnknown;
- else
- return QDateTimePrivate::LocalStandard;
- }
-}
-
-// Convert passed in LocalTime datetime into UTC
-static void localToUtc(QDate &date, QTime &time, int isdst)
-{
- if (!date.isValid())
- return;
-
- QDate fakeDate = adjustDate(date);
-
- tm localTM;
- localTM.tm_sec = time.second();
- localTM.tm_min = time.minute();
- localTM.tm_hour = time.hour();
- localTM.tm_mday = fakeDate.day();
- localTM.tm_mon = fakeDate.month() - 1;
- localTM.tm_year = fakeDate.year() - 1900;
- localTM.tm_isdst = (int)isdst;
-#if defined(Q_OS_WINCE)
- time_t secsSince1Jan1970UTC = (toMSecsSinceEpoch_helper(fakeDate.toJulianDay(), QTime().msecsTo(time)) / 1000);
-#else
-#if defined(Q_OS_WIN)
- _tzset();
-#endif
- time_t secsSince1Jan1970UTC = mktime(&localTM);
-#ifdef Q_OS_QNX
- //mktime sometimes fails on QNX. Following workaround converts the date and time then manually
- if (secsSince1Jan1970UTC == (time_t)-1) {
- QDateTime tempTime = QDateTime(date, time, Qt::UTC);;
- tempTime = tempTime.addMSecs(timezone * 1000);
- date = tempTime.date();
- time = tempTime.time();
- return;
- }
-#endif
-#endif
- tm *brokenDown = 0;
-#if defined(Q_OS_WINCE)
- tm res;
- FILETIME localTime = time_tToFt(secsSince1Jan1970UTC);
- SYSTEMTIME sysTime;
- FileTimeToSystemTime(&localTime, &sysTime);
- FILETIME resultTime;
- LocalFileTimeToFileTime(&localTime , &resultTime);
- FileTimeToSystemTime(&resultTime , &sysTime);
- res.tm_sec = sysTime.wSecond;
- res.tm_min = sysTime.wMinute;
- res.tm_hour = sysTime.wHour;
- res.tm_mday = sysTime.wDay;
- res.tm_mon = sysTime.wMonth - 1;
- res.tm_year = sysTime.wYear - 1900;
- res.tm_isdst = (int)isdst;
- brokenDown = &res;
-#elif !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
- // use the reentrant version of gmtime() where available
- tm res;
- brokenDown = gmtime_r(&secsSince1Jan1970UTC, &res);
-#elif defined(_MSC_VER) && _MSC_VER >= 1400
- tm res;
- if (!_gmtime64_s(&res, &secsSince1Jan1970UTC))
- brokenDown = &res;
-#else
- brokenDown = gmtime(&secsSince1Jan1970UTC);
-#endif // !QT_NO_THREAD && _POSIX_THREAD_SAFE_FUNCTIONS
- if (!brokenDown) {
- date = QDate(1970, 1, 1);
- time = QTime();
- } else {
- qint64 deltaDays = fakeDate.daysTo(date);
- date = QDate(brokenDown->tm_year + 1900, brokenDown->tm_mon + 1, brokenDown->tm_mday);
- time = QTime(brokenDown->tm_hour, brokenDown->tm_min, brokenDown->tm_sec, time.msec());
- date = date.addDays(deltaDays);
- }
-}
-
-// Convert passed in OffsetFromUTC datetime and offset into UTC
-static void offsetToUtc(QDate *outDate, QTime *outTime, qint32 offset)
-{
- QDateTimePrivate::addMSecs(*outDate, *outTime, -(qint64(offset) * 1000));
-}
-
-// Convert passed in UTC datetime and offset into OffsetFromUTC
-static void utcToOffset(QDate *outDate, QTime *outTime, qint32 offset)
-{
- QDateTimePrivate::addMSecs(*outDate, *outTime, (qint64(offset) * 1000));
-}
-
-// Get current date/time in LocalTime and put result in outDate and outTime
-QDateTimePrivate::Spec QDateTimePrivate::getLocal(QDate &outDate, QTime &outTime) const
-{
- outDate = date;
- outTime = time;
- if (m_spec == Qt::UTC)
- return utcToLocal(outDate, outTime);
- if (m_spec == Qt::OffsetFromUTC) {
- offsetToUtc(&outDate, &outTime, m_offsetFromUtc);
- return utcToLocal(outDate, outTime);
- }
- return LocalUnknown;
-}
-
-// Get current date/time in UTC and put result in outDate and outTime
-void QDateTimePrivate::getUTC(QDate &outDate, QTime &outTime) const
-{
- outDate = date;
- outTime = time;
-
- if (m_spec == Qt::OffsetFromUTC)
- offsetToUtc(&outDate, &outTime, m_offsetFromUtc);
- else if (m_spec != Qt::UTC)
- localToUtc(outDate, outTime, -1);
-}
-
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_NO_DATESTRING)
QDebug operator<<(QDebug dbg, const QDate &date)
{
diff --git a/src/corelib/tools/qdatetime_p.h b/src/corelib/tools/qdatetime_p.h
index e6cece141d..124bd98a31 100644
--- a/src/corelib/tools/qdatetime_p.h
+++ b/src/corelib/tools/qdatetime_p.h
@@ -80,28 +80,45 @@ public:
DaylightTime
};
- QDateTimePrivate() : m_spec(Qt::LocalTime), m_offsetFromUtc(0) {}
+ // Status of date/time
+ enum StatusFlag {
+ NullDate = 0x01,
+ NullTime = 0x02,
+ ValidDate = 0x04,
+ ValidTime = 0x08,
+ };
+ Q_DECLARE_FLAGS(StatusFlags, StatusFlag)
+
+ QDateTimePrivate() : m_msecs(0),
+ m_spec(Qt::LocalTime),
+ m_offsetFromUtc(0),
+ m_status(NullDate | NullTime)
+ {}
+
QDateTimePrivate(const QDate &toDate, const QTime &toTime, Qt::TimeSpec toSpec,
int offsetSeconds);
- QDateTimePrivate(const QDateTimePrivate &other)
- : QSharedData(other), date(other.date), time(other.time), m_spec(other.m_spec),
- m_offsetFromUtc(other.m_offsetFromUtc)
+
+ QDateTimePrivate(const QDateTimePrivate &other) : QSharedData(other),
+ m_msecs(other.m_msecs),
+ m_spec(other.m_spec),
+ m_offsetFromUtc(other.m_offsetFromUtc),
+ m_status(other.m_status)
{}
- QDate date;
- QTime time;
+ qint64 m_msecs;
Qt::TimeSpec m_spec;
int m_offsetFromUtc;
-
- // Get current date/time in LocalTime and put result in outDate and outTime
- Spec getLocal(QDate &outDate, QTime &outTime) const;
- // Get current date/time in UTC and put result in outDate and outTime
- void getUTC(QDate &outDate, QTime &outTime) const;
-
- // Add msecs to given datetime and put result in utcDate and utcTime
- static void addMSecs(QDate &utcDate, QTime &utcTime, qint64 msecs);
+ StatusFlags m_status;
void setTimeSpec(Qt::TimeSpec spec, int offsetSeconds);
+ void setDateTime(const QDate &date, const QTime &time);
+ void getDateTime(QDate *date, QTime *time) const;
+
+ // 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; }
static inline qint64 minJd() { return QDate::minJd(); }
static inline qint64 maxJd() { return QDate::maxJd(); }