From 16abff0f85b63e7731cce71d03e521faed305bf4 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 21 Aug 2024 10:09:59 +0200 Subject: [PATCH] Cache UTC versions of QDTP::getM(in,ax)imum() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The static min and max moments of QDateTimeParser are local times. Now that baseDate() needs to check whether the start of the two-year century falls between these, and parsing of simply a date or simply a time (not a date-time) uses UTC, to comparisons involve mapping the local time to UTC, with the usual associated expense. Do that expensive operation once on creation of the min and max local times, so that it'll be cached for use by the comparisons. This shall also help with other comparisons to max and min, so is of use even before the introduction of baseDate(), for all that that change brought the issue to light. Thanks to Jøger Hansegård for analysis of the profiler data that revealed the root cause of a significant slow-down. Pick-to: 6.7 6.5 Fixes: QTBUG-124465 Change-Id: I539c8ae782717635fb722c4573d85bc63073958a Reviewed-by: Jøger Hansegård Reviewed-by: Thiago Macieira (cherry picked from commit b6a11a4ee5319366f14c11c7cf3a4f1dbf33cfd5) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/time/qdatetimeparser.cpp | 44 +++++++++++++++++++-------- src/corelib/time/qdatetimeparser_p.h | 5 +-- src/widgets/widgets/qdatetimeedit.cpp | 20 +++++++----- src/widgets/widgets/qdatetimeedit_p.h | 4 +-- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/corelib/time/qdatetimeparser.cpp b/src/corelib/time/qdatetimeparser.cpp index 72456498554..0d0da82ae0a 100644 --- a/src/corelib/time/qdatetimeparser.cpp +++ b/src/corelib/time/qdatetimeparser.cpp @@ -833,7 +833,7 @@ QDateTimeParser::parseSection(const QDateTime ¤tValue, int sectionIndex, i QString sectiontext = sectionTextRef.toString(); int num = 0, used = 0; if (sn.type == MonthSection) { - const QDate minDate = getMinimum().date(); + const QDate minDate = getMinimum(currentValue.timeRepresentation()).date(); const int year = currentValue.date().year(calendar); const int min = (year == minDate.year(calendar)) ? minDate.month(calendar) : 1; num = findMonth(sectiontext.toLower(), min, sectionIndex, year, §iontext, &used); @@ -1489,8 +1489,8 @@ QDateTimeParser::StateNode QDateTimeParser::parse(const QString &input, int position, const QDateTime &defaultValue, bool fixup) const { - const QDateTime minimum = getMinimum(); - const QDateTime maximum = getMaximum(); + const QDateTime minimum = getMinimum(defaultValue.timeRepresentation()); + const QDateTime maximum = getMaximum(defaultValue.timeRepresentation()); m_text = input; QDTPDEBUG << "parse" << input; @@ -2147,8 +2147,8 @@ bool QDateTimeParser::skipToNextSection(int index, const QDateTime ¤t, QSt int max = absoluteMax(index, current); // Time-zone field is only numeric if given as offset from UTC: if (node.type != TimeZoneSection || current.timeSpec() == Qt::OffsetFromUTC) { - const QDateTime maximum = getMaximum(); - const QDateTime minimum = getMinimum(); + const QDateTime maximum = getMaximum(current.timeRepresentation()); + const QDateTime minimum = getMinimum(current.timeRepresentation()); // Range from minimum to maximum might not contain current if an earlier // field's value was full-width but out of range. In such a case the // parse is already headed for Invalid, so it doesn't matter that we get @@ -2225,9 +2225,9 @@ QString QDateTimeParser::stateName(State s) const QDateTime QDateTimeParser::baseDate(const QTimeZone &zone) const { QDateTime when = QDate(defaultCenturyStart, 1, 1).startOfDay(zone); - if (const QDateTime start = getMinimum(); when < start) + if (const QDateTime start = getMinimum(zone); when < start) return start; - if (const QDateTime end = getMaximum(); when > end) + if (const QDateTime end = getMaximum(zone); when > end) return end; return when; } @@ -2268,18 +2268,28 @@ bool QDateTimeParser::fromString(const QString &t, QDateTime *datetime, int base return tmp.state >= Intermediate && !tmp.conflicts && tmp.value.isValid(); } -QDateTime QDateTimeParser::getMinimum() const +QDateTime QDateTimeParser::getMinimum(const QTimeZone &zone) const { // NB: QDateTimeParser always uses Qt::LocalTime time spec by default. If // any subclass needs a changing time spec, it must override this // method. At the time of writing, this is done by QDateTimeEditPrivate. - // Cache the only case + // Cache the only case (and make sure it knows its UTC offset): static const QDateTime localTimeMin(QDATETIMEEDIT_DATE_MIN.startOfDay()); - return localTimeMin; + static const QDateTime utcTimeMin = localTimeMin.toUTC(); + switch (zone.timeSpec()) { + case Qt::LocalTime: + return localTimeMin; + case Qt::UTC: + return utcTimeMin; + case Qt::OffsetFromUTC: + case Qt::TimeZone: + break; + } + return utcTimeMin.toTimeZone(zone); } -QDateTime QDateTimeParser::getMaximum() const +QDateTime QDateTimeParser::getMaximum(const QTimeZone &zone) const { // NB: QDateTimeParser always uses Qt::LocalTime time spec by default. If // any subclass needs a changing time spec, it must override this @@ -2287,7 +2297,17 @@ QDateTime QDateTimeParser::getMaximum() const // Cache the only case static const QDateTime localTimeMax(QDATETIMEEDIT_DATE_MAX.endOfDay()); - return localTimeMax; + static const QDateTime utcTimeMax = localTimeMax.toUTC(); + switch (zone.timeSpec()) { + case Qt::LocalTime: + return localTimeMax; + case Qt::UTC: + return utcTimeMax; + case Qt::OffsetFromUTC: + case Qt::TimeZone: + break; + } + return utcTimeMax.toTimeZone(zone); } QString QDateTimeParser::getAmPmText(AmPm ap, Case cs) const diff --git a/src/corelib/time/qdatetimeparser_p.h b/src/corelib/time/qdatetimeparser_p.h index dd922bc6276..8539bace69d 100644 --- a/src/corelib/time/qdatetimeparser_p.h +++ b/src/corelib/time/qdatetimeparser_p.h @@ -24,6 +24,7 @@ #include "QtCore/qlist.h" #include "QtCore/qlocale.h" #include "QtCore/qstringlist.h" +#include "QtCore/qtimezone.h" #ifndef QT_BOOTSTRAPPED # include "QtCore/qvariant.h" #endif @@ -229,8 +230,8 @@ protected: // for the benefit of QDateTimeEditPrivate } QString stateName(State s) const; - virtual QDateTime getMinimum() const; - virtual QDateTime getMaximum() const; + virtual QDateTime getMinimum(const QTimeZone &zone) const; + virtual QDateTime getMaximum(const QTimeZone &zone) const; virtual int cursorPosition() const { return -1; } virtual QLocale locale() const { return defaultLocale; } diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp index a9b5babde58..c4094264f14 100644 --- a/src/widgets/widgets/qdatetimeedit.cpp +++ b/src/widgets/widgets/qdatetimeedit.cpp @@ -1820,26 +1820,30 @@ void QDateTimeEditPrivate::updateEdit() } } -QDateTime QDateTimeEditPrivate::getMinimum() const +QDateTime QDateTimeEditPrivate::getMinimum(const QTimeZone &zone) const { if (keyboardTracking) - return minimum.toDateTime(); + return minimum.toDateTime().toTimeZone(zone); + // QDTP's min is the local-time start of QDATETIMEEDIT_DATE_MIN, cached + // (along with its conversion to UTC). if (timeZone.timeSpec() == Qt::LocalTime) - return QDateTimeParser::getMinimum(); + return QDateTimeParser::getMinimum(zone); - return QDATETIMEEDIT_DATE_MIN.startOfDay(timeZone); + return QDATETIMEEDIT_DATE_MIN.startOfDay(timeZone).toTimeZone(zone); } -QDateTime QDateTimeEditPrivate::getMaximum() const +QDateTime QDateTimeEditPrivate::getMaximum(const QTimeZone &zone) const { if (keyboardTracking) - return maximum.toDateTime(); + return maximum.toDateTime().toTimeZone(zone); + // QDTP's max is the local-time end of QDATETIMEEDIT_DATE_MAX, cached + // (along with its conversion to UTC). if (timeZone.timeSpec() == Qt::LocalTime) - return QDateTimeParser::getMaximum(); + return QDateTimeParser::getMaximum(zone); - return QDATETIMEEDIT_DATE_MAX.endOfDay(timeZone); + return QDATETIMEEDIT_DATE_MAX.endOfDay(timeZone).toTimeZone(zone); } /*! diff --git a/src/widgets/widgets/qdatetimeedit_p.h b/src/widgets/widgets/qdatetimeedit_p.h index f93afd1519a..7b9109692d0 100644 --- a/src/widgets/widgets/qdatetimeedit_p.h +++ b/src/widgets/widgets/qdatetimeedit_p.h @@ -61,8 +61,8 @@ public: // Override QDateTimeParser: QString displayText() const override { return edit->text(); } - QDateTime getMinimum() const override; - QDateTime getMaximum() const override; + QDateTime getMinimum(const QTimeZone &zone) const override; + QDateTime getMaximum(const QTimeZone &zone) const override; QLocale locale() const override { return q_func()->locale(); } int cursorPosition() const override { return edit ? edit->cursorPosition() : -1; }