diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2020-03-25 15:48:52 +0100 |
---|---|---|
committer | Andrei Golubev <andrei.golubev@qt.io> | 2020-04-22 14:14:30 +0300 |
commit | 76054516047d8efb8529443830bb4d9ddf01010f (patch) | |
tree | b43f21e2828fb16fe5beb8a2298556d6a3da654d /src | |
parent | 6c45b1817f1f56207dae8aa028b9cbb922dc8008 (diff) |
Handle specified time-spec in date-time parsing
When a date-time was parsed from a string, the result was equal (as a
date-time) to the correct value, but had (at least in some cases) the
wrong spec, where it should have had a spec reflecting the zone
specifier parsed.
The time-spec imposed for the benefit of QDateTimeEdit is now moved
from QDateTimeParser to QDateTimeEditPrivate, which takes over
responsibility for imposing it. QDateTimeParser assumes Qt::LocalTime
in member functions (where applicable) and uses the time-spec parsed
from the string when constructing the date-time.
QDateTime::fromString() and QLocale::toDateTime() are updated to
use the full QDateTime returned by QDateTimeParser.
Fixes: QTBUG-83075
Done-With: Edward Welbourne <edward.welbourne@qt.io>
Change-Id: I8b79add2c7fc13a200e1252d48dbfa70b36757bf
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/text/qlocale.cpp | 9 | ||||
-rw-r--r-- | src/corelib/time/qdatetime.cpp | 7 | ||||
-rw-r--r-- | src/corelib/time/qdatetimeparser.cpp | 74 | ||||
-rw-r--r-- | src/corelib/time/qdatetimeparser_p.h | 4 | ||||
-rw-r--r-- | src/widgets/widgets/qdatetimeedit.cpp | 7 | ||||
-rw-r--r-- | src/widgets/widgets/qdatetimeedit_p.h | 6 |
6 files changed, 68 insertions, 39 deletions
diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index 6e33e27276..60bfac62ab 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -2559,19 +2559,18 @@ QDateTime QLocale::toDateTime(const QString &string, const QString &format) cons QDateTime QLocale::toDateTime(const QString &string, const QString &format, QCalendar cal) const { #if QT_CONFIG(datetimeparser) - QTime time; - QDate date; + QDateTime datetime; QDateTimeParser dt(QMetaType::QDateTime, QDateTimeParser::FromString, cal); dt.setDefaultLocale(*this); - if (dt.parseFormat(format) && dt.fromString(string, &date, &time)) - return QDateTime(date, time); + if (dt.parseFormat(format) && dt.fromString(string, &datetime)) + return datetime; #else Q_UNUSED(string); Q_UNUSED(format); Q_UNUSED(cal); #endif - return QDateTime(QDate(), QTime(-1, -1, -1)); + return QDateTime(); } #endif // datestring diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 9d95f6eb1c..36942bf68e 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -5537,13 +5537,12 @@ QT_WARNING_POP QDateTime QDateTime::fromString(const QString &string, const QString &format, QCalendar cal) { #if QT_CONFIG(datetimeparser) - QTime time; - QDate date; + QDateTime datetime; QDateTimeParser dt(QMetaType::QDateTime, QDateTimeParser::FromString, cal); // dt.setDefaultLocale(QLocale::c()); ### Qt 6 - if (dt.parseFormat(format) && dt.fromString(string, &date, &time)) - return QDateTime(date, time); + if (dt.parseFormat(format) && dt.fromString(string, &datetime)) + return datetime; #else Q_UNUSED(string); Q_UNUSED(format); diff --git a/src/corelib/time/qdatetimeparser.cpp b/src/corelib/time/qdatetimeparser.cpp index 2a19611493..3374b28b69 100644 --- a/src/corelib/time/qdatetimeparser.cpp +++ b/src/corelib/time/qdatetimeparser.cpp @@ -1207,12 +1207,16 @@ QDateTimeParser::scanString(const QDateTime &defaultValue, Q_ASSERT(!zoneName.isEmpty()); // sect.used > 0 const QByteArray latinZone(zoneName == QLatin1String("Z") ? QByteArray("UTC") : zoneName.toLatin1()); - timeZone = QTimeZone(latinZone); - tspec = timeZone.isValid() - ? (QTimeZone::isTimeZoneIdAvailable(latinZone) - ? Qt::TimeZone - : Qt::OffsetFromUTC) - : (Q_ASSERT(startsWithLocalTimeZone(zoneName)), Qt::LocalTime); + if (latinZone.startsWith("UTC") && + (latinZone.size() == 3 || latinZone.at(3) == '+' || latinZone.at(3) == '-' )) { + timeZone = QTimeZone(sect.value); + tspec = sect.value ? Qt::OffsetFromUTC : Qt::UTC; + } else { + timeZone = QTimeZone(latinZone); + tspec = timeZone.isValid() + ? Qt::TimeZone + : (Q_ASSERT(startsWithLocalTimeZone(zoneName)), Qt::LocalTime); + } #else tspec = Qt::LocalTime; #endif @@ -1537,12 +1541,10 @@ QDateTimeParser::parse(QString input, int position, const QDateTime &defaultValu } } text = scan.input = input; - // Set spec *after* all checking, so validity is a property of the string: - scan.value = scan.value.toTimeSpec(spec); /* - However, even with a valid string we might have ended up with an invalid datetime: - the non-existent hour during dst changes, for instance. + We might have ended up with an invalid datetime: the non-existent hour + during dst changes, for instance. */ if (!scan.value.isValid() && scan.state == Acceptable) scan.state = Intermediate; @@ -2018,13 +2020,12 @@ QString QDateTimeParser::stateName(State s) const #if QT_CONFIG(datestring) bool QDateTimeParser::fromString(const QString &t, QDate *date, QTime *time) const { - QDateTime val(QDate(1900, 1, 1).startOfDay()); - const StateNode tmp = parse(t, -1, val, false); - if (tmp.state != Acceptable || tmp.conflicts) { + QDateTime datetime; + if (!fromString(t, &datetime)) return false; - } + if (time) { - const QTime t = tmp.value.time(); + const QTime t = datetime.time(); if (!t.isValid()) { return false; } @@ -2032,7 +2033,7 @@ bool QDateTimeParser::fromString(const QString &t, QDate *date, QTime *time) con } if (date) { - const QDate d = tmp.value.date(); + const QDate d = datetime.date(); if (!d.isValid()) { return false; } @@ -2040,26 +2041,43 @@ bool QDateTimeParser::fromString(const QString &t, QDate *date, QTime *time) con } return true; } + +bool QDateTimeParser::fromString(const QString &t, QDateTime* datetime) const +{ + QDateTime val(QDate(1900, 1, 1).startOfDay()); + const StateNode tmp = parse(t, -1, val, false); + if (tmp.state != Acceptable || tmp.conflicts) + return false; + if (datetime) { + if (!tmp.value.isValid()) + return false; + *datetime = tmp.value; + } + + return true; +} #endif // datestring QDateTime QDateTimeParser::getMinimum() const { - // Cache the most common case - if (spec == Qt::LocalTime) { - static const QDateTime localTimeMin(QDATETIMEEDIT_DATE_MIN.startOfDay(Qt::LocalTime)); - return localTimeMin; - } - return QDateTime(QDATETIMEEDIT_DATE_MIN.startOfDay(spec)); + // 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 + static const QDateTime localTimeMin(QDATETIMEEDIT_DATE_MIN.startOfDay(Qt::LocalTime)); + return localTimeMin; } QDateTime QDateTimeParser::getMaximum() const { - // Cache the most common case - if (spec == Qt::LocalTime) { - static const QDateTime localTimeMax(QDATETIMEEDIT_DATE_MAX.endOfDay(Qt::LocalTime)); - return localTimeMax; - } - return QDateTime(QDATETIMEEDIT_DATE_MAX.endOfDay(spec)); + // 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 + static const QDateTime localTimeMax(QDATETIMEEDIT_DATE_MAX.endOfDay(Qt::LocalTime)); + return localTimeMax; } 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 5c612ef6a4..f2795c31f0 100644 --- a/src/corelib/time/qdatetimeparser_p.h +++ b/src/corelib/time/qdatetimeparser_p.h @@ -85,7 +85,7 @@ public: }; QDateTimeParser(QMetaType::Type t, Context ctx, const QCalendar &cal = QCalendar()) : currentSectionIndex(-1), cachedDay(-1), parserType(t), - fixday(false), spec(Qt::LocalTime), context(ctx), calendar(cal) + fixday(false), context(ctx), calendar(cal) { defaultLocale = QLocale::system(); first.type = FirstSection; @@ -181,6 +181,7 @@ public: #if QT_CONFIG(datestring) StateNode parse(QString input, int position, const QDateTime &defaultValue, bool fixup) const; bool fromString(const QString &text, QDate *date, QTime *time) const; + bool fromString(const QString &text, QDateTime* datetime) const; #endif bool parseFormat(const QString &format); @@ -297,7 +298,6 @@ protected: // for the benefit of QDateTimeEditPrivate QLocale defaultLocale; QMetaType::Type parserType; bool fixday; - Qt::TimeSpec spec; // spec if used by QDateTimeEdit Context context; QCalendar calendar; }; diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp index d145985c1d..deb4dc36ba 100644 --- a/src/widgets/widgets/qdatetimeedit.cpp +++ b/src/widgets/widgets/qdatetimeedit.cpp @@ -1976,7 +1976,14 @@ QDateTime QDateTimeEditPrivate::validateAndInterpret(QString &input, int &positi return minimum.toDateTime(); } } + StateNode tmp = parse(input, position, value.toDateTime(), fixup); + // Impose this widget's spec: + tmp.value = tmp.value.toTimeSpec(spec); + // ... but that might turn a valid datetime into an invalid one: + if (!tmp.value.isValid() && tmp.state == Acceptable) + tmp.state = Intermediate; + input = tmp.input; position += tmp.padded; state = QValidator::State(int(tmp.state)); diff --git a/src/widgets/widgets/qdatetimeedit_p.h b/src/widgets/widgets/qdatetimeedit_p.h index 323246a87b..7df2b59710 100644 --- a/src/widgets/widgets/qdatetimeedit_p.h +++ b/src/widgets/widgets/qdatetimeedit_p.h @@ -98,12 +98,16 @@ public: { if (keyboardTracking) return minimum.toDateTime(); + if (spec != Qt::LocalTime) + return QDateTime(QDATETIMEEDIT_DATE_MIN.startOfDay(spec)); return QDateTimeParser::getMinimum(); } QDateTime getMaximum() const override { if (keyboardTracking) return maximum.toDateTime(); + if (spec != Qt::LocalTime) + return QDateTime(QDATETIMEEDIT_DATE_MIN.startOfDay(spec)); return QDateTimeParser::getMaximum(); } QLocale locale() const override { return q_func()->locale(); } @@ -148,6 +152,8 @@ public: #ifdef QT_KEYPAD_NAVIGATION bool focusOnButton; #endif + + Qt::TimeSpec spec = Qt::LocalTime; }; |