diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2019-08-08 19:40:32 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2019-08-30 10:52:42 +0200 |
commit | 20f69beae48d2efb1fdb5754218a8cdc990ba13e (patch) | |
tree | 0f79f023ee153875ee4028f609ec6f077ef71234 /src/corelib/time | |
parent | 108382e236dcdc09a822b89db35cc8bc4509273f (diff) |
Optimize QDate by shortcutting some gregorian calendar methods
Add some static methods to QGregorianCalendar, some of which serve to
implement its methods, that QDate can use to bypass vtables and
exploit fixed truths of the Gregorian calendar in its default
handling.
Change-Id: Iec191cdf4d52945dbd5679e609180cb4fe59b5fd
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src/corelib/time')
-rw-r--r-- | src/corelib/time/qdatetime.cpp | 107 | ||||
-rw-r--r-- | src/corelib/time/qgregoriancalendar.cpp | 39 | ||||
-rw-r--r-- | src/corelib/time/qgregoriancalendar_p.h | 8 |
3 files changed, 139 insertions, 15 deletions
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 020eac6dec..a9fc47e053 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -74,6 +74,7 @@ #endif #include "qcalendar.h" +#include "qgregoriancalendar_p.h" QT_BEGIN_NAMESPACE @@ -105,6 +106,17 @@ static inline QDate fixedDate(QCalendar::YearMonthDay &&parts, QCalendar cal) return cal.dateFromParts(parts); } +static inline QDate fixedDate(QCalendar::YearMonthDay &&parts) +{ + if (parts.year) { + parts.day = qMin(parts.day, QGregorianCalendar::monthLength(parts.month, parts.year)); + qint64 jd; + if (QGregorianCalendar::julianFromParts(parts.year, parts.month, parts.day, &jd)) + return QDate::fromJulianDay(jd); + } + return QDate(); +} + /***************************************************************************** Date/Time formatting helper functions *****************************************************************************/ @@ -340,7 +352,8 @@ static int fromOffsetString(const QStringRef &offsetString, bool *valid) noexcep QDate::QDate(int y, int m, int d) { - *this = QCalendar().dateFromParts(y, m, d); + if (!QGregorianCalendar::julianFromParts(y, m, d, &jd)) + jd = nullJd(); } QDate::QDate(int y, int m, int d, QCalendar cal) @@ -405,7 +418,12 @@ int QDate::year(QCalendar cal) const int QDate::year() const { - return year(QCalendar()); + if (isValid()) { + const auto parts = QGregorianCalendar::partsFromJulian(jd); + if (parts.isValid()) + return parts.year; + } + return 0; } /*! @@ -452,7 +470,12 @@ int QDate::month(QCalendar cal) const int QDate::month() const { - return month(QCalendar()); + if (isValid()) { + const auto parts = QGregorianCalendar::partsFromJulian(jd); + if (parts.isValid()) + return parts.month; + } + return 0; } /*! @@ -480,7 +503,12 @@ int QDate::day(QCalendar cal) const int QDate::day() const { - return day(QCalendar()); + if (isValid()) { + const auto parts = QGregorianCalendar::partsFromJulian(jd); + if (parts.isValid()) + return parts.day; + } + return 0; } /*! @@ -507,7 +535,7 @@ int QDate::dayOfWeek(QCalendar cal) const int QDate::dayOfWeek() const { - return dayOfWeek(QCalendar()); + return isValid() ? QGregorianCalendar::weekDayOfJulian(jd) : 0; } /*! @@ -535,7 +563,12 @@ int QDate::dayOfYear(QCalendar cal) const int QDate::dayOfYear() const { - return dayOfYear(QCalendar()); + if (isValid()) { + qint64 first; + if (QGregorianCalendar::julianFromParts(year(), 1, 1, &first)) + return jd - first + 1; + } + return 0; } /*! @@ -563,7 +596,12 @@ int QDate::daysInMonth(QCalendar cal) const int QDate::daysInMonth() const { - return daysInMonth(QCalendar()); + if (isValid()) { + const auto parts = QGregorianCalendar::partsFromJulian(jd); + if (parts.isValid()) + return QGregorianCalendar::monthLength(parts.month, parts.year); + } + return 0; } /*! @@ -589,7 +627,7 @@ int QDate::daysInYear(QCalendar cal) const int QDate::daysInYear() const { - return daysInYear(QCalendar()); + return isValid() ? QGregorianCalendar::leapTest(year()) ? 366 : 365 : 0; } /*! @@ -1303,7 +1341,11 @@ QString QDate::toString(const QString &format, QCalendar cal) const */ bool QDate::setDate(int year, int month, int day) { - return setDate(year, month, day, QCalendar()); + if (QGregorianCalendar::julianFromParts(year, month, day, &jd)) + return true; + + jd = nullJd(); + return false; } /*! @@ -1339,7 +1381,7 @@ void QDate::getDate(int *year, int *month, int *day) const { QCalendar::YearMonthDay parts; // invalid by default if (isValid()) - parts = QCalendar().partsFromDate(*this); + parts = QGregorianCalendar::partsFromJulian(jd); const bool ok = parts.isValid(); if (year) @@ -1428,7 +1470,30 @@ QDate QDate::addMonths(int nmonths, QCalendar cal) const QDate QDate::addMonths(int nmonths) const { - return addMonths(nmonths, QCalendar()); + if (isNull()) + return QDate(); + + if (nmonths == 0) + return *this; + + auto parts = QGregorianCalendar::partsFromJulian(jd); + + if (!parts.isValid()) + return QDate(); + Q_ASSERT(parts.year); + + parts.month += nmonths; + while (parts.month <= 0) { + if (--parts.year) // skip over year 0 + parts.month += 12; + } + while (parts.month > 12) { + parts.month -= 12; + if (!++parts.year) // skip over year 0 + ++parts.year; + } + + return fixedDate(std::move(parts)); } /*! @@ -1470,7 +1535,21 @@ QDate QDate::addYears(int nyears, QCalendar cal) const QDate QDate::addYears(int nyears) const { - return addYears(nyears, QCalendar()); + if (isNull()) + return QDate(); + + auto parts = QGregorianCalendar::partsFromJulian(jd); + if (!parts.isValid()) + return QDate(); + + int old_y = parts.year; + parts.year += nyears; + + // If we just crossed (or hit) a missing year zero, adjust year by +/- 1: + if ((old_y > 0) != (parts.year > 0) || !parts.year) + parts.year += nyears > 0 ? +1 : -1; + + return fixedDate(std::move(parts)); } /*! @@ -1719,7 +1798,7 @@ QDate QDate::fromString(const QString &string, const QString &format) bool QDate::isValid(int year, int month, int day) { - return QCalendar().isDateValid(year, month, day); + return QGregorianCalendar::validParts(year, month, day); } /*! @@ -1733,7 +1812,7 @@ bool QDate::isValid(int year, int month, int day) bool QDate::isLeapYear(int y) { - return QCalendar().isLeapYear(y); + return QGregorianCalendar::leapTest(y); } /*! \fn static QDate QDate::fromJulianDay(qint64 jd) diff --git a/src/corelib/time/qgregoriancalendar.cpp b/src/corelib/time/qgregoriancalendar.cpp index d3db572aa7..447185d124 100644 --- a/src/corelib/time/qgregoriancalendar.cpp +++ b/src/corelib/time/qgregoriancalendar.cpp @@ -78,6 +78,11 @@ QCalendar::System QGregorianCalendar::calendarSystem() const bool QGregorianCalendar::isLeapYear(int year) const { + return leapTest(year); +} + +bool QGregorianCalendar::leapTest(int year) +{ if (year == QCalendar::Unspecified) return false; @@ -88,10 +93,37 @@ bool QGregorianCalendar::isLeapYear(int year) const return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); } +// Duplicating code from QRomanCalendar, but inlining isLeapYear() as leapTest(): +int QGregorianCalendar::monthLength(int month, int year) +{ + if (month < 1 || month > 12) + return 0; + + if (month == 2) + return leapTest(year) ? 29 : 28; + + return 30 | ((month & 1) ^ (month >> 3)); +} + +bool QGregorianCalendar::validParts(int year, int month, int day) +{ + return year && 0 < day && day <= monthLength(month, year); +} + +int QGregorianCalendar::weekDayOfJulian(qint64 jd) +{ + return qMod(jd, 7) + 1; +} + bool QGregorianCalendar::dateToJulianDay(int year, int month, int day, qint64 *jd) const { + return julianFromParts(year, month, day, jd); +} + +bool QGregorianCalendar::julianFromParts(int year, int month, int day, qint64 *jd) +{ Q_ASSERT(jd); - if (!isDateValid(year, month, day)) + if (!validParts(year, month, day)) return false; if (year < 0) @@ -112,6 +144,11 @@ bool QGregorianCalendar::dateToJulianDay(int year, int month, int day, qint64 *j QCalendar::YearMonthDay QGregorianCalendar::julianDayToDate(qint64 jd) const { + return partsFromJulian(jd); +} + +QCalendar::YearMonthDay QGregorianCalendar::partsFromJulian(qint64 jd) +{ /* * Math from The Calendar FAQ at http://www.tondering.dk/claus/cal/julperiod.php * This formula is correct for all julian days, when using mathematical integer diff --git a/src/corelib/time/qgregoriancalendar_p.h b/src/corelib/time/qgregoriancalendar_p.h index 4e6c42ef76..191f9c127b 100644 --- a/src/corelib/time/qgregoriancalendar_p.h +++ b/src/corelib/time/qgregoriancalendar_p.h @@ -75,6 +75,14 @@ public: QLocale::FormatType format) const override; QString standaloneMonthName(const QLocale &locale, int month, int year, QLocale::FormatType format) const override; + + // Static optimized versions for the benefit of QDate: + static int weekDayOfJulian(qint64 jd); + static bool leapTest(int year); + static int monthLength(int month, int year); + static bool validParts(int year, int month, int day); + static QCalendar::YearMonthDay partsFromJulian(qint64 jd); + static bool julianFromParts(int year, int month, int day, qint64 *jd); }; QT_END_NAMESPACE |