summaryrefslogtreecommitdiffstats
path: root/src/corelib/time/qdatetimeparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/time/qdatetimeparser.cpp')
-rw-r--r--src/corelib/time/qdatetimeparser.cpp70
1 files changed, 53 insertions, 17 deletions
diff --git a/src/corelib/time/qdatetimeparser.cpp b/src/corelib/time/qdatetimeparser.cpp
index 68ec73f471..ce28c6d034 100644
--- a/src/corelib/time/qdatetimeparser.cpp
+++ b/src/corelib/time/qdatetimeparser.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2019 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -761,6 +761,11 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex,
const int sectionmaxsize = sectionMaxSize(sectionIndex);
QStringRef sectionTextRef = text->midRef(offset, sectionmaxsize);
+ // If the fields we've read thus far imply a time in a spring-forward,
+ // coerce to a nearby valid time:
+ const QDateTime defaultValue = currentValue.isValid() ? currentValue
+ : QDateTime::fromMSecsSinceEpoch(currentValue.toMSecsSinceEpoch());
+
QDTPDEBUG << "sectionValue for" << sn.name()
<< "with text" << *text << "and (at" << offset
<< ") st:" << sectionTextRef;
@@ -793,7 +798,7 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex,
text->replace(offset, used, sectiontext.constData(), used);
break; }
case TimeZoneSection:
- result = findTimeZone(sectionTextRef, currentValue,
+ result = findTimeZone(sectionTextRef, defaultValue,
absoluteMax(sectionIndex),
absoluteMin(sectionIndex));
break;
@@ -805,7 +810,7 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex,
int num = 0, used = 0;
if (sn.type == MonthSection) {
const QDate minDate = getMinimum().date();
- const int year = currentValue.date().year(calendar);
+ const int year = defaultValue.date().year(calendar);
const int min = (year == minDate.year(calendar)) ? minDate.month(calendar) : 1;
num = findMonth(sectiontext.toLower(), min, sectionIndex, year, &sectiontext, &used);
} else {
@@ -887,7 +892,7 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex,
else
QDTPDEBUG << "invalid because" << last << "is less than absoluteMin" << absMin;
} else if (unfilled && (fi & (FixedWidth|Numeric)) == (FixedWidth|Numeric)) {
- if (skipToNextSection(sectionIndex, currentValue, digitsStr)) {
+ if (skipToNextSection(sectionIndex, defaultValue, digitsStr)) {
const int missingZeroes = sectionmaxsize - digitsStr.size();
result = ParsedSection(Acceptable, last, sectionmaxsize, missingZeroes);
text->insert(offset, QString(missingZeroes, QLatin1Char('0')));
@@ -1385,21 +1390,34 @@ QDateTimeParser::scanString(const QDateTime &defaultValue,
// If hour wasn't specified, check the default we're using exists on the
// given date (which might be a spring-forward, skipping an hour).
- if (parserType == QMetaType::QDateTime && !(isSet & HourSectionMask) && !when.isValid()) {
- qint64 msecs = when.toMSecsSinceEpoch();
- // Fortunately, that gets a useful answer, even though when is invalid ...
- const QDateTime replace =
+ if (!(isSet & HourSectionMask) && !when.isValid()) {
+ switch (parserType) {
+ case QMetaType::QDateTime: {
+ qint64 msecs = when.toMSecsSinceEpoch();
+ // Fortunately, that gets a useful answer, even though when is invalid ...
+ const QDateTime replace =
#if QT_CONFIG(timezone)
- tspec == Qt::TimeZone
- ? QDateTime::fromMSecsSinceEpoch(msecs, timeZone) :
+ tspec == Qt::TimeZone ? QDateTime::fromMSecsSinceEpoch(msecs, timeZone) :
#endif
- QDateTime::fromMSecsSinceEpoch(msecs, tspec, zoneOffset);
- const QTime tick = replace.time();
- if (replace.date() == date
- && (!(isSet & MinuteSection) || tick.minute() == minute)
- && (!(isSet & SecondSection) || tick.second() == second)
- && (!(isSet & MSecSection) || tick.msec() == msec)) {
- return StateNode(replace, state, padding, conflicts);
+ QDateTime::fromMSecsSinceEpoch(msecs, tspec, zoneOffset);
+ const QTime tick = replace.time();
+ if (replace.date() == date
+ && (!(isSet & MinuteSection) || tick.minute() == minute)
+ && (!(isSet & SecondSection) || tick.second() == second)
+ && (!(isSet & MSecSection) || tick.msec() == msec)) {
+ return StateNode(replace, state, padding, conflicts);
+ }
+ } break;
+ case QMetaType::QDate:
+ // Don't care about time, so just use start of day (and ignore spec):
+ return StateNode(date.startOfDay(Qt::UTC), state, padding, conflicts);
+ break;
+ case QMetaType::QTime:
+ // Don't care about date or spec, so pick a safe spec:
+ return StateNode(QDateTime(date, time, Qt::UTC), state, padding, conflicts);
+ default:
+ Q_UNREACHABLE();
+ return StateNode();
}
}
@@ -1737,6 +1755,24 @@ QDateTimeParser::findTimeZoneName(QStringRef str, const QDateTime &when) const
int index = std::distance(str.cbegin(),
std::find_if(str.cbegin(), str.cend(), invalidZoneNameCharacter));
+ // Limit name fragments (between slashes) to 20 characters.
+ // (Valid time-zone IDs are allowed up to 14 and Android has quirks up to 17.)
+ // Limit number of fragments to six; no known zone name has more than four.
+ int lastSlash = -1;
+ int count = 0;
+ Q_ASSERT(index <= str.size());
+ while (lastSlash < index) {
+ int slash = str.indexOf(QLatin1Char('/'), lastSlash + 1);
+ if (slash < 0)
+ slash = index; // i.e. the end of the candidate text
+ else if (++count > 5)
+ index = slash; // Truncate
+ if (slash - lastSlash > 20)
+ index = lastSlash + 20; // Truncate
+ // If any of those conditions was met, index <= slash, so this exits the loop:
+ lastSlash = slash;
+ }
+
for (; index > systemLength; --index) { // Find longest match
str.truncate(index);
QTimeZone zone(str.toLatin1());