summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrei Golubev <andrei.golubev@qt.io>2020-03-25 15:48:52 +0100
committerAndrei Golubev <andrei.golubev@qt.io>2020-04-22 14:14:30 +0300
commit76054516047d8efb8529443830bb4d9ddf01010f (patch)
treeb43f21e2828fb16fe5beb8a2298556d6a3da654d /src
parent6c45b1817f1f56207dae8aa028b9cbb922dc8008 (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.cpp9
-rw-r--r--src/corelib/time/qdatetime.cpp7
-rw-r--r--src/corelib/time/qdatetimeparser.cpp74
-rw-r--r--src/corelib/time/qdatetimeparser_p.h4
-rw-r--r--src/widgets/widgets/qdatetimeedit.cpp7
-rw-r--r--src/widgets/widgets/qdatetimeedit_p.h6
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;
};