From 0e62fc73c905d1f476f3c1493db1bde8096ac609 Mon Sep 17 00:00:00 2001 From: John Layt Date: Sun, 17 Jul 2011 19:30:12 +0100 Subject: QDateTime: Add validity checks Check the validity of date/time before attempting to perform maths. Change-Id: Ia6a2caf07c6c36f7d7fac713a77bc4eb456c6ed6 Reviewed-by: Thiago Macieira --- src/corelib/tools/qdatetime.cpp | 176 ++++++++++++++++++++++++++++++++-------- 1 file changed, 144 insertions(+), 32 deletions(-) (limited to 'src/corelib/tools/qdatetime.cpp') diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index 6430e2c8bc..97bcd58c40 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -330,11 +330,16 @@ bool QDate::isValid() const Returns the year of this date. Negative numbers indicate years before 1 A.D. = 1 C.E., such that year -44 is 44 B.C. + Returns 0 if the date is invalid. + \sa month(), day() */ int QDate::year() const { + if (isNull()) + return 0; + int y; getDateFromJulianDay(jd, &y, 0, 0); return y; @@ -359,11 +364,16 @@ int QDate::year() const \i 12 = "December" \endlist + Returns 0 if the date is invalid. + \sa year(), day() */ int QDate::month() const { + if (isNull()) + return 0; + int m; getDateFromJulianDay(jd, 0, &m, 0); return m; @@ -372,11 +382,16 @@ int QDate::month() const /*! Returns the day of the month (1 to 31) of this date. + Returns 0 if the date is invalid. + \sa year(), month(), dayOfWeek() */ int QDate::day() const { + if (isNull()) + return 0; + int d; getDateFromJulianDay(jd, 0, 0, &d); return d; @@ -385,11 +400,16 @@ int QDate::day() const /*! Returns the weekday (1 to 7) for this date. + Returns 0 if the date is invalid. + \sa day(), dayOfYear(), Qt::DayOfWeek */ int QDate::dayOfWeek() const { + if (isNull()) + return 0; + return (jd % 7) + 1; } @@ -397,26 +417,38 @@ int QDate::dayOfWeek() const Returns the day of the year (1 to 365 or 366 on leap years) for this date. + Returns 0 if the date is invalid. + \sa day(), dayOfWeek() */ int QDate::dayOfYear() const { + if (isNull()) + return 0; + return jd - julianDayFromDate(year(), 1, 1) + 1; } /*! Returns the number of days in the month (28 to 31) for this date. + Returns 0 if the date is invalid. + \sa day(), daysInYear() */ int QDate::daysInMonth() const { - int y, m, d; - getDateFromJulianDay(jd, &y, &m, &d); + if (isNull()) + return 0; + + int y, m; + getDateFromJulianDay(jd, &y, &m, 0); if (m == 2 && isLeapYear(y)) return 29; + else if (m < 1 || m > 12) + return 0; else return monthDays[m]; } @@ -424,13 +456,18 @@ int QDate::daysInMonth() const /*! Returns the number of days in the year (365 or 366) for this date. + Returns 0 if the date is invalid. + \sa day(), daysInMonth() */ int QDate::daysInYear() const { - int y, m, d; - getDateFromJulianDay(jd, &y, &m, &d); + if (isNull()) + return 0; + + int y; + getDateFromJulianDay(jd, &y, 0, 0); return isLeapYear(y) ? 366 : 365; } @@ -542,14 +579,16 @@ int QDate::weekNumber(int *yearNumber) const The month names will be localized according to the system's locale settings. + Returns an empty string if the date is invalid. + \sa toString(), longMonthName(), shortDayName(), longDayName() */ QString QDate::shortMonthName(int month, QDate::MonthNameType type) { - if (month < 1 || month > 12) { - month = 1; - } + if (month < 1 || month > 12) + return QString(); + switch (type) { case QDate::DateFormat: return QLocale::system().monthName(month, QLocale::ShortFormat); @@ -587,14 +626,16 @@ QString QDate::shortMonthName(int month, QDate::MonthNameType type) The month names will be localized according to the system's locale settings. + Returns an empty string if the date is invalid. + \sa toString(), shortMonthName(), shortDayName(), longDayName() */ QString QDate::longMonthName(int month, MonthNameType type) { - if (month < 1 || month > 12) { - month = 1; - } + if (month < 1 || month > 12) + return QString(); + switch (type) { case QDate::DateFormat: return QLocale::system().monthName(month, QLocale::LongFormat); @@ -627,14 +668,16 @@ QString QDate::longMonthName(int month, MonthNameType type) The day names will be localized according to the system's locale settings. + Returns an empty string if the date is invalid. + \sa toString(), shortMonthName(), longMonthName(), longDayName() */ QString QDate::shortDayName(int weekday, MonthNameType type) { - if (weekday < 1 || weekday > 7) { - weekday = 1; - } + if (weekday < 1 || weekday > 7) + return QString(); + switch (type) { case QDate::DateFormat: return QLocale::system().dayName(weekday, QLocale::ShortFormat); @@ -667,14 +710,16 @@ QString QDate::shortDayName(int weekday, MonthNameType type) The day names will be localized according to the system's locale settings. + Returns an empty string if the date is invalid. + \sa toString(), shortDayName(), shortMonthName(), longMonthName() */ QString QDate::longDayName(int weekday, MonthNameType type) { - if (weekday < 1 || weekday > 7) { - weekday = 1; - } + if (weekday < 1 || weekday > 7) + return QString(); + switch (type) { case QDate::DateFormat: return QLocale::system().dayName(weekday, QLocale::LongFormat); @@ -872,22 +917,39 @@ bool QDate::setDate(int year, int month, int day) Extracts the date's year, month, and day, and assigns them to *\a year, *\a month, and *\a day. The pointers may be null. + Returns 0 if the date is invalid. + \sa year(), month(), day(), isValid() */ void QDate::getDate(int *year, int *month, int *day) { - getDateFromJulianDay(jd, year, month, day); + if (isValid()) { + getDateFromJulianDay(jd, year, month, day); + } else { + if (year) + *year = 0; + if (month) + *month = 0; + if (day) + *day = 0; + } } /*! Returns a QDate object containing a date \a ndays later than the date of this object (or earlier if \a ndays is negative). + Returns a null date if the current date is invalid or the new date is + out-of-range. + \sa addMonths() addYears() daysTo() */ QDate QDate::addDays(int ndays) const { + if (isNull()) + return QDate(); + QDate d; // this is basically "d.jd = jd + ndays" with checks for integer overflow if (ndays >= 0) @@ -1002,6 +1064,8 @@ QDate QDate::addYears(int nyears) const Returns the number of days from this date to \a d (which is negative if \a d is earlier than this date). + Returns 0 if either date is invalid. + Example: \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 0 @@ -1010,6 +1074,9 @@ QDate QDate::addYears(int nyears) const int QDate::daysTo(const QDate &d) const { + if (isNull() || d.isNull()) + return 0; + return d.jd - jd; } @@ -1394,44 +1461,64 @@ bool QTime::isValid() const /*! Returns the hour part (0 to 23) of the time. + Returns -1 if the time is invalid. + \sa minute(), second(), msec() */ int QTime::hour() const { + if (!isValid()) + return -1; + return ds() / MSECS_PER_HOUR; } /*! Returns the minute part (0 to 59) of the time. + Returns -1 if the time is invalid. + \sa hour(), second(), msec() */ int QTime::minute() const { + if (!isValid()) + return -1; + return (ds() % MSECS_PER_HOUR) / MSECS_PER_MIN; } /*! Returns the second part (0 to 59) of the time. + Returns -1 if the time is invalid. + \sa hour(), minute(), msec() */ int QTime::second() const { + if (!isValid()) + return -1; + return (ds() / 1000)%SECS_PER_MIN; } /*! Returns the millisecond part (0 to 999) of the time. + Returns -1 if the time is invalid. + \sa hour(), minute(), second() */ int QTime::msec() const { + if (!isValid()) + return -1; + return ds() % 1000; } @@ -1579,6 +1666,8 @@ bool QTime::setHMS(int h, int m, int s, int ms) Note that the time will wrap if it passes midnight. + Returns a null time if this time is invalid. + Example: \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 5 @@ -1601,11 +1690,16 @@ QTime QTime::addSecs(int s) const secsTo() does not take into account any milliseconds. + Returns 0 if either time is invalid. + \sa addSecs(), QDateTime::secsTo() */ int QTime::secsTo(const QTime &t) const { + if (!isValid() || !t.isValid()) + return 0; + return (t.ds() - ds()) / 1000; } @@ -1616,18 +1710,22 @@ int QTime::secsTo(const QTime &t) const Note that the time will wrap if it passes midnight. See addSecs() for an example. + Returns a null time if this time is invalid. + \sa addSecs(), msecsTo(), QDateTime::addMSecs() */ QTime QTime::addMSecs(int ms) const { QTime t; - if (ms < 0) { - // % not well-defined for -ve, but / is. - int negdays = (MSECS_PER_DAY - ms) / MSECS_PER_DAY; - t.mds = (ds() + ms + negdays * MSECS_PER_DAY) % MSECS_PER_DAY; - } else { - t.mds = (ds() + ms) % MSECS_PER_DAY; + if (isValid()) { + if (ms < 0) { + // % not well-defined for -ve, but / is. + int negdays = (MSECS_PER_DAY - ms) / MSECS_PER_DAY; + t.mds = (ds() + ms + negdays * MSECS_PER_DAY) % MSECS_PER_DAY; + } else { + t.mds = (ds() + ms) % MSECS_PER_DAY; + } } #if defined(Q_OS_WINCE) if (startTick > NullTime) @@ -1645,11 +1743,15 @@ QTime QTime::addMSecs(int ms) const seconds in a day, the result is always between -86400000 and 86400000 ms. + Returns 0 if either time is invalid. + \sa secsTo(), addMSecs(), QDateTime::msecsTo() */ int QTime::msecsTo(const QTime &t) const { + if (!isValid() || !t.isValid()) + return 0; #if defined(Q_OS_WINCE) // GetLocalTime() for Windows CE has no milliseconds resolution if (t.startTick > NullTime && startTick > NullTime) @@ -2260,7 +2362,7 @@ qint64 QDateTime::toMSecsSinceEpoch() const QTime utcTime; d->getUTC(utcDate, utcTime); - return toMSecsSinceEpoch_helper(utcDate.jd, utcTime.ds()); + return toMSecsSinceEpoch_helper(utcDate.toJulianDay(), QTime(0, 0, 0).msecsTo(utcTime)); } /*! @@ -2322,7 +2424,7 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs) } d->date = QDate(1970, 1, 1).addDays(ddays); - d->time = QTime().addMSecs(msecs); + d->time = QTime(0, 0, 0).addMSecs(msecs); d->spec = QDateTimePrivate::UTC; if (oldSpec != QDateTimePrivate::UTC) @@ -2347,7 +2449,7 @@ void QDateTime::setTime_t(uint secsSince1Jan1970UTC) QDateTimePrivate::Spec oldSpec = d->spec; d->date = QDate(1970, 1, 1).addDays(secsSince1Jan1970UTC / SECS_PER_DAY); - d->time = QTime().addSecs(secsSince1Jan1970UTC % SECS_PER_DAY); + d->time = QTime(0, 0, 0).addSecs(secsSince1Jan1970UTC % SECS_PER_DAY); d->spec = QDateTimePrivate::UTC; if (oldSpec != QDateTimePrivate::UTC) @@ -2605,8 +2707,8 @@ QDateTime QDateTimePrivate::addMSecs(const QDateTime &dt, qint64 msecs) */ void QDateTimePrivate::addMSecs(QDate &utcDate, QTime &utcTime, qint64 msecs) { - uint dd = utcDate.jd; - int tt = utcTime.ds(); + int dd = utcDate.toJulianDay(); + int tt = QTime(0, 0, 0).msecsTo(utcTime); int sign = 1; if (msecs < 0) { msecs = -msecs; @@ -2628,8 +2730,8 @@ void QDateTimePrivate::addMSecs(QDate &utcDate, QTime &utcTime, qint64 msecs) tt = tt % MSECS_PER_DAY; } - utcDate.jd = dd; - utcTime.mds = tt; + utcDate = QDate::fromJulianDay(dd); + utcTime = QTime(0, 0, 0).addMSecs(tt); } /*! @@ -2679,6 +2781,8 @@ int QDateTime::daysTo(const QDateTime &other) const to Qt::UTC to ensure that the result is correct if one of the two datetimes has daylight saving time (DST) and the other doesn't. + Returns 0 if either time is invalid. + Example: \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 11 @@ -2687,6 +2791,9 @@ int QDateTime::daysTo(const QDateTime &other) const int QDateTime::secsTo(const QDateTime &other) const { + if (!isValid() || !other.isValid()) + return 0; + QDate date1, date2; QTime time1, time2; @@ -2705,11 +2812,16 @@ int QDateTime::secsTo(const QDateTime &other) const to Qt::UTC to ensure that the result is correct if one of the two datetimes has daylight saving time (DST) and the other doesn't. + Returns 0 if either time is null. + \sa addMSecs(), daysTo(), QTime::msecsTo() */ qint64 QDateTime::msecsTo(const QDateTime &other) const { + if (!isValid() || !other.isValid()) + return 0; + QDate selfDate; QDate otherDate; QTime selfTime; @@ -2873,7 +2985,7 @@ QTime QTime::currentTime() SYSTEMTIME st; memset(&st, 0, sizeof(SYSTEMTIME)); GetLocalTime(&st); - ct.mds = msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); + ct.setHMS(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); #if defined(Q_OS_WINCE) ct.startTick = GetTickCount() % MSECS_PER_DAY; #endif @@ -3780,7 +3892,7 @@ 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().msecsTo(time)) / 1000; + time_t secsSince1Jan1970UTC = toMSecsSinceEpoch_helper(fakeDate.toJulianDay(), QTime(0, 0, 0).msecsTo(time)) / 1000; tm *brokenDown = 0; #if defined(Q_OS_WINCE) -- cgit v1.2.3