summaryrefslogtreecommitdiffstats
path: root/src/corelib/time
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2019-08-08 19:40:32 +0200
committerEdward Welbourne <edward.welbourne@qt.io>2019-08-30 10:52:42 +0200
commit20f69beae48d2efb1fdb5754218a8cdc990ba13e (patch)
tree0f79f023ee153875ee4028f609ec6f077ef71234 /src/corelib/time
parent108382e236dcdc09a822b89db35cc8bc4509273f (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.cpp107
-rw-r--r--src/corelib/time/qgregoriancalendar.cpp39
-rw-r--r--src/corelib/time/qgregoriancalendar_p.h8
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