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.cpp372
1 files changed, 233 insertions, 139 deletions
diff --git a/src/corelib/time/qdatetimeparser.cpp b/src/corelib/time/qdatetimeparser.cpp
index c4e4ddbef7..cce32e7ad2 100644
--- a/src/corelib/time/qdatetimeparser.cpp
+++ b/src/corelib/time/qdatetimeparser.cpp
@@ -3,7 +3,6 @@
#include "qplatformdefs.h"
#include "private/qdatetimeparser_p.h"
-#include "private/qstringiterator_p.h"
#include "qdatastream.h"
#include "qdatetime.h"
@@ -12,6 +11,10 @@
#include "qset.h"
#include "qtimezone.h"
#include "qvarlengtharray.h"
+#include "private/qlocale_p.h"
+
+#include "private/qstringiterator_p.h"
+#include "private/qtenvironmentvariables_p.h"
//#define QDATETIMEPARSER_DEBUG
#if defined (QDATETIMEPARSER_DEBUG) && !defined(QT_NO_DEBUG_STREAM)
@@ -234,9 +237,9 @@ int QDateTimeParser::absoluteMax(int s, const QDateTime &cur) const
case MSecSection:
return 999;
case YearSection2Digits:
- case YearSection:
// sectionMaxSize will prevent people from typing in a larger number in
// count == 2 sections; stepBy() will work on real years anyway.
+ case YearSection:
return 9999;
case MonthSection:
return calendar.maximumMonthsInYear();
@@ -331,7 +334,7 @@ int QDateTimeParser::sectionPos(int sectionIndex) const
return sectionPos(sectionNode(sectionIndex));
}
-int QDateTimeParser::sectionPos(const SectionNode &sn) const
+int QDateTimeParser::sectionPos(SectionNode sn) const
{
switch (sn.type) {
case FirstSection: return 0;
@@ -369,9 +372,9 @@ static qsizetype digitCount(QStringView str)
not escaped and removes the escaping on those that are escaped
*/
-
static QString unquote(QStringView str)
{
+ // ### Align unquoting format strings for both from/toString(), QTBUG-110669
const QLatin1Char quote('\'');
const QLatin1Char slash('\\');
const QLatin1Char zero('0');
@@ -396,14 +399,10 @@ static QString unquote(QStringView str)
static inline int countRepeat(QStringView str, int index, int maxCount)
{
str = str.sliced(index);
- if (maxCount > str.size())
- maxCount = str.size();
-
- const QChar ch(str[0]);
- int count = 1;
- while (count < maxCount && str[count] == ch)
- ++count;
- return count;
+ if (maxCount < str.size())
+ str = str.first(maxCount);
+
+ return qt_repeatCount(str);
}
static inline void appendSeparator(QStringList *list, QStringView string,
@@ -710,6 +709,7 @@ int QDateTimeParser::sectionMaxSize(Section s, int count) const
case DaySectionMask:
qWarning("QDateTimeParser::sectionMaxSize: Invalid section %s",
SectionNode::name(s).toLatin1().constData());
+ break;
case NoSectionIndex:
case FirstSectionIndex:
@@ -728,6 +728,36 @@ int QDateTimeParser::sectionMaxSize(int index) const
return sectionMaxSize(sn.type, sn.count);
}
+// Separator matching
+//
+// QTBUG-114909: user may be oblivious to difference between visibly
+// indistinguishable spacing characters. For now we only treat horizontal
+// spacing characters, excluding tab, as equivalent.
+
+static int matchesSeparator(QStringView text, QStringView separator)
+{
+ const auto isSimpleSpace = [](char32_t ch) {
+ // Distinguish tab, CR and the vertical spaces from the rest:
+ return ch == u' ' || (ch > 127 && QChar::isSpace(ch));
+ };
+ // -1 if not a match, else length of prefix of text that does match.
+ // First check for exact match
+ if (!text.startsWith(separator)) {
+ // Failing that, check for space-identifying match:
+ QStringIterator given(text), sep(separator);
+ while (sep.hasNext()) {
+ if (!given.hasNext())
+ return -1;
+ char32_t s = sep.next(), g = given.next();
+ if (s != g && !(isSimpleSpace(s) && isSimpleSpace(g)))
+ return -1;
+ }
+ // One side may have used a surrogate pair space where the other didn't:
+ return given.index();
+ }
+ return separator.size();
+}
+
/*!
\internal
@@ -769,11 +799,6 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex, i
&& m_text.at(offset) == u'-');
const int negativeYearOffset = negate ? 1 : 0;
- // 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());
-
QStringView sectionTextRef =
QStringView { m_text }.mid(offset + negativeYearOffset, sectionmaxsize);
@@ -809,7 +834,7 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex, i
m_text.replace(offset, used, sectiontext.constData(), used);
break; }
case TimeZoneSection:
- result = findTimeZone(sectionTextRef, defaultValue,
+ result = findTimeZone(sectionTextRef, currentValue,
absoluteMax(sectionIndex),
absoluteMin(sectionIndex), sn.count);
break;
@@ -821,7 +846,7 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex, i
int num = 0, used = 0;
if (sn.type == MonthSection) {
const QDate minDate = getMinimum().date();
- const int year = defaultValue.date().year(calendar);
+ 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, &sectiontext, &used);
} else {
@@ -846,12 +871,26 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex, i
case MinuteSection:
case SecondSection:
case MSecSection: {
+ const auto checkSeparator = [&result, field=QStringView{m_text}.sliced(offset),
+ negativeYearOffset, sectionIndex, this]() {
+ // No-digit field if next separator is here, otherwise invalid.
+ const auto &sep = separators.at(sectionIndex + 1);
+ if (matchesSeparator(field.sliced(negativeYearOffset), sep) != -1)
+ result = ParsedSection(Intermediate, 0, negativeYearOffset);
+ else if (negativeYearOffset && matchesSeparator(field, sep) != -1)
+ result = ParsedSection(Intermediate, 0, 0);
+ else
+ return false;
+ return true;
+ };
int used = negativeYearOffset;
- // We already sliced off the - sign if it was legitimately present.
+ // We already sliced off the - sign if it was acceptable.
+ // QLocale::toUInt() would accept a sign, so we must reject it overtly:
if (sectionTextRef.startsWith(u'-')
|| sectionTextRef.startsWith(u'+')) {
- if (separators.at(sectionIndex + 1).startsWith(sectionTextRef[0]))
- result = ParsedSection(Intermediate, 0, used);
+ // However, a sign here may indicate a field with no digits, if it
+ // starts the next separator:
+ checkSeparator();
break;
}
QStringView digitsStr = sectionTextRef.left(digitCount(sectionTextRef));
@@ -885,13 +924,10 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex, i
}
if (lastVal == -1) {
- const auto &sep = separators.at(sectionIndex + 1);
- if (sep.startsWith(sectionTextRef[0])
- || (negate && sep.startsWith(m_text.at(offset))))
- result = ParsedSection(Intermediate, 0, 0);
- else
+ if (!checkSeparator()) {
QDTPDEBUG << "invalid because" << sectionTextRef << "can't become a uint"
<< lastVal;
+ }
} else {
if (negate)
lastVal = -lastVal;
@@ -915,13 +951,13 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex, i
}
} else if (unfilled && (fi & (FixedWidth | Numeric)) == (FixedWidth | Numeric)) {
- if (skipToNextSection(sectionIndex, defaultValue, digitsStr)) {
+ if (skipToNextSection(sectionIndex, currentValue, digitsStr)) {
const int missingZeroes = sectionmaxsize - digitsStr.size();
result = ParsedSection(Acceptable, lastVal, sectionmaxsize, missingZeroes);
m_text.insert(offset, QString(missingZeroes, u'0'));
++(const_cast<QDateTimeParser*>(this)->sectionNodes[sectionIndex].zeroesAdded);
} else {
- result = ParsedSection(Intermediate, lastVal, used);;
+ result = ParsedSection(Intermediate, lastVal, used);
}
} else {
result = ParsedSection(Acceptable, lastVal, used);
@@ -958,6 +994,17 @@ static int weekDayWithinMonth(QCalendar calendar, int year, int month, int day,
}
/*!
+ \internal
+ Returns whichever of baseYear through baseYear + 99 has its % 100 == y2d.
+*/
+static int yearInCenturyFrom(int y2d, int baseYear)
+{
+ Q_ASSERT(0 <= y2d && y2d < 100);
+ const int year = baseYear - baseYear % 100 + y2d;
+ return year < baseYear ? year + 100 : year;
+}
+
+/*!
\internal
Returns a date consistent with the given data on parts specified by known,
@@ -965,7 +1012,7 @@ static int weekDayWithinMonth(QCalendar calendar, int year, int month, int day,
when on valid date is consistent with the data.
*/
-static QDate actualDate(QDateTimeParser::Sections known, const QCalendar &calendar,
+static QDate actualDate(QDateTimeParser::Sections known, QCalendar calendar, int baseYear,
int year, int year2digits, int month, int day, int dayofweek)
{
QDate actual(year, month, day, calendar);
@@ -979,7 +1026,7 @@ static QDate actualDate(QDateTimeParser::Sections known, const QCalendar &calend
if (year % 100 != year2digits) {
if (known & QDateTimeParser::YearSection2Digits) {
// Over-ride year, even if specified:
- year += year2digits - year % 100;
+ year = yearInCenturyFrom(year2digits, baseYear);
known &= ~QDateTimeParser::YearSection;
} else {
year2digits = year % 100;
@@ -997,7 +1044,7 @@ static QDate actualDate(QDateTimeParser::Sections known, const QCalendar &calend
QDate first(year, month, 1, calendar);
int last = known & QDateTimeParser::MonthSection
- ? (known & QDateTimeParser::YearSection
+ ? (known.testAnyFlag(QDateTimeParser::YearSectionMask)
? calendar.daysInMonth(month, year) : calendar.daysInMonth(month))
: 0;
// We can only fix DOW if we know year as well as month (hence last):
@@ -1074,20 +1121,11 @@ static QDate actualDate(QDateTimeParser::Sections known, const QCalendar &calend
if ((known & QDateTimeParser::YearSection) == 0) {
if (known & QDateTimeParser::YearSection2Digits) {
- /*
- Two-digit year and month are specified; choice of century can only
- fix this if diff is in one of {1, 2, 5} or {2, 4, 6}; but not if
- diff is in the other. It's also only reasonable to consider
- adjacent century, e.g. if year thinks it's 2012 and two-digit year
- is '97, it makes sense to consider 1997. If either adjacent
- century does work, the other won't.
- */
- actual = QDate(year + 100, month, day, calendar);
- if (calendar.dayOfWeek(actual) == dayofweek)
- return actual;
- actual = QDate(year - 100, month, day, calendar);
- if (calendar.dayOfWeek(actual) == dayofweek)
+ actual = calendar.matchCenturyToWeekday({year, month, day}, dayofweek);
+ if (actual.isValid()) {
+ Q_ASSERT(calendar.dayOfWeek(actual) == dayofweek);
return actual;
+ }
} else {
// Offset by 7 is usually enough, but rare cases may need more:
for (int y = 1; y < 12; y++) {
@@ -1140,6 +1178,48 @@ static QTime actualTime(QDateTimeParser::Sections known,
return actual;
}
+/*
+ \internal
+*/
+static int startsWithLocalTimeZone(QStringView name, const QDateTime &when, const QLocale &locale)
+{
+ // Pick longest match that we might get.
+ qsizetype longest = 0;
+ // On MS-Win, at least when system zone is UTC, the tzname[]s may be empty.
+ for (int i = 0; i < 2; ++i) {
+ const QString zone(qTzName(i));
+ if (zone.size() > longest && name.startsWith(zone))
+ longest = zone.size();
+ }
+ // Mimic each candidate QLocale::toString() could have used, to ensure round-trips work:
+ const auto consider = [name, &longest](QStringView zone) {
+ if (name.startsWith(zone)) {
+ // UTC-based zone's displayName() only includes seconds if non-zero:
+ if (9 > longest && zone.size() == 6 && zone.startsWith("UTC"_L1)
+ && name.sliced(6, 3) == ":00"_L1) {
+ longest = 9;
+ } else if (zone.size() > longest) {
+ longest = zone.size();
+ }
+ }
+ };
+#if QT_CONFIG(timezone)
+ /* QLocale::toString would skip this if locale == QLocale::system(), but we
+ might not be using the same system locale as whoever generated the text
+ we're parsing. So consider it anyway. */
+ {
+ const auto localWhen = QDateTime(when.date(), when.time());
+ consider(localWhen.timeRepresentation().displayName(
+ localWhen, QTimeZone::ShortName, locale));
+ }
+#else
+ Q_UNUSED(locale);
+#endif
+ consider(QDateTime(when.date(), when.time()).timeZoneAbbreviation());
+ Q_ASSERT(longest <= INT_MAX); // Timezone names are not that long.
+ return int(longest);
+}
+
/*!
\internal
*/
@@ -1170,25 +1250,25 @@ QDateTimeParser::scanString(const QDateTime &defaultValue, bool fixup) const
for (int index = 0; index < sectionNodesCount; ++index) {
Q_ASSERT(state != Invalid);
const QString &separator = separators.at(index);
- if (QStringView{m_text}.mid(pos, separator.size()) != separator) {
- QDTPDEBUG << "invalid because" << QStringView{m_text}.mid(pos, separator.size())
- << "!=" << separator
+ int step = matchesSeparator(QStringView{m_text}.sliced(pos), separator);
+ if (step == -1) {
+ QDTPDEBUG << "invalid because" << QStringView{m_text}.sliced(pos)
+ << "does not start with" << separator
<< index << pos << currentSectionIndex;
return StateNode();
}
- pos += separator.size();
+ pos += step;
sectionNodes[index].pos = pos;
int *current = nullptr;
int zoneOffset; // Needed to serve as *current when setting zone
const SectionNode sn = sectionNodes.at(index);
- ParsedSection sect;
-
- {
- const QDate date = actualDate(isSet, calendar, year, year2digits,
- month, day, dayofweek);
+ const QDateTime usedDateTime = [&] {
+ const QDate date = actualDate(isSet, calendar, defaultCenturyStart,
+ year, year2digits, month, day, dayofweek);
const QTime time = actualTime(isSet, hour, hour12, ampm, minute, second, msec);
- sect = parseSection(QDateTime(date, time, timeZone), index, pos);
- }
+ return QDateTime(date, time, timeZone);
+ }();
+ ParsedSection sect = parseSection(usedDateTime, index, pos);
QDTPDEBUG << "sectionValue" << sn.name() << m_text
<< "pos" << pos << "used" << sect.used << stateName(sect.state);
@@ -1197,7 +1277,7 @@ QDateTimeParser::scanString(const QDateTime &defaultValue, bool fixup) const
if (fixup && sect.state == Intermediate && sect.used < sn.count) {
const FieldInfo fi = fieldInfo(index);
if ((fi & (Numeric|FixedWidth)) == (Numeric|FixedWidth)) {
- const QString newText = QString("%1"_L1).arg(sect.value, sn.count, 10, '0'_L1);
+ const QString newText = QString::asprintf("%0*d", sn.count, sect.value);
m_text.replace(pos, sect.used, newText);
sect.used = sn.count;
}
@@ -1223,18 +1303,14 @@ QDateTimeParser::scanString(const QDateTime &defaultValue, bool fixup) const
if (isUtc || isUtcOffset) {
timeZone = QTimeZone::fromSecondsAheadOfUtc(sect.value);
- } else {
#if QT_CONFIG(timezone)
+ } else if (startsWithLocalTimeZone(zoneName, usedDateTime, locale()) != sect.used) {
QTimeZone namedZone = QTimeZone(zoneName.toLatin1());
- if (namedZone.isValid()) {
- timeZone = namedZone;
- } else {
- Q_ASSERT(startsWithLocalTimeZone(zoneName));
- timeZone = QTimeZone::LocalTime;
- }
-#else
- timeZone = QTimeZone::LocalTime;
+ Q_ASSERT(namedZone.isValid());
+ timeZone = namedZone;
#endif
+ } else {
+ timeZone = QTimeZone::LocalTime;
}
}
break;
@@ -1275,24 +1351,24 @@ QDateTimeParser::scanString(const QDateTime &defaultValue, bool fixup) const
isSet |= sn.type;
}
- if (QStringView{m_text}.sliced(pos) != separators.last()) {
+ int step = matchesSeparator(QStringView{m_text}.sliced(pos), separators.last());
+ if (step == -1 || step + pos < m_text.size()) {
QDTPDEBUG << "invalid because" << QStringView{m_text}.sliced(pos)
- << "!=" << separators.last() << pos;
+ << "does not match" << separators.last() << pos;
return StateNode();
}
if (parserType != QMetaType::QTime) {
if (year % 100 != year2digits && (isSet & YearSection2Digits)) {
+ const QDate date = actualDate(isSet, calendar, defaultCenturyStart,
+ year, year2digits, month, day, dayofweek);
if (!(isSet & YearSection)) {
- year = (year / 100) * 100;
- year += year2digits;
+ year = date.year();
} else {
conflicts = true;
const SectionNode &sn = sectionNode(currentSectionIndex);
- if (sn.type == YearSection2Digits) {
- year = (year / 100) * 100;
- year += year2digits;
- }
+ if (sn.type == YearSection2Digits)
+ year = date.year();
}
}
@@ -1377,31 +1453,40 @@ QDateTimeParser::scanString(const QDateTime &defaultValue, bool fixup) const
const QTime time(hour, minute, second, msec);
const QDateTime when = QDateTime(date, time, timeZone);
- // 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 (!(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 = QDateTime::fromMSecsSinceEpoch(msecs, timeZone);
- 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);
+ if (when.time() != time || when.date() != date) {
+ // In a spring-forward, if we hit the skipped hour, we may have been
+ // shunted out of it.
+
+ // If hour wasn't specified, so we're using our default, changing it may
+ // fix that.
+ if (!(isSet & HourSectionMask)) {
+ switch (parserType) {
+ case QMetaType::QDateTime: {
+ qint64 msecs = when.toMSecsSinceEpoch();
+ // Fortunately, that gets a useful answer, even though when is invalid ...
+ const QDateTime replace = QDateTime::fromMSecsSinceEpoch(msecs, timeZone);
+ 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(QTimeZone::UTC),
+ state, padding, conflicts);
+ break;
+ case QMetaType::QTime:
+ // Don't care about date or representation, so pick a safe representation:
+ return StateNode(QDateTime(date, time, QTimeZone::UTC),
+ state, padding, conflicts);
+ default:
+ Q_UNREACHABLE_RETURN(StateNode());
}
- } break;
- case QMetaType::QDate:
- // Don't care about time, so just use start of day (and ignore spec):
- return StateNode(date.startOfDay(QTimeZone::UTC), state, padding, conflicts);
- break;
- case QMetaType::QTime:
- // Don't care about date or representation, so pick a safe representation:
- return StateNode(QDateTime(date, time, QTimeZone::UTC), state, padding, conflicts);
- default:
- Q_UNREACHABLE_RETURN(StateNode());
+ } else if (state > Intermediate) {
+ state = Intermediate;
}
}
@@ -1552,12 +1637,8 @@ QDateTimeParser::parse(const QString &input, int position,
}
}
- /*
- 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;
+ // An invalid time should only arise if we set the state to less than acceptable:
+ Q_ASSERT(scan.value.isValid() || scan.state != Acceptable);
return scan;
}
@@ -1573,7 +1654,7 @@ QDateTimeParser::parse(const QString &input, int position,
length of overlap in *used (if \a used is non-NULL) and the first entry that
overlapped this much in *usedText (if \a usedText is non-NULL).
*/
-static int findTextEntry(const QString &text, const ShortVector<QString> &entries, QString *usedText, int *used)
+static int findTextEntry(QStringView text, const ShortVector<QString> &entries, QString *usedText, int *used)
{
if (text.isEmpty())
return -1;
@@ -1610,7 +1691,7 @@ static int findTextEntry(const QString &text, const ShortVector<QString> &entrie
match. Starting from \a index; str should already by lowered
*/
-int QDateTimeParser::findMonth(const QString &str1, int startMonth, int sectionIndex,
+int QDateTimeParser::findMonth(QStringView str, int startMonth, int sectionIndex,
int year, QString *usedMonth, int *used) const
{
const SectionNode &sn = sectionNode(sectionIndex);
@@ -1626,11 +1707,11 @@ int QDateTimeParser::findMonth(const QString &str1, int startMonth, int sectionI
for (int month = startMonth; month <= 12; ++month)
monthNames.append(calendar.monthName(l, month, year, type));
- const int index = findTextEntry(str1, monthNames, usedMonth, used);
+ const int index = findTextEntry(str, monthNames, usedMonth, used);
return index < 0 ? index : index + startMonth;
}
-int QDateTimeParser::findDay(const QString &str1, int startDay, int sectionIndex, QString *usedDay, int *used) const
+int QDateTimeParser::findDay(QStringView str, int startDay, int sectionIndex, QString *usedDay, int *used) const
{
const SectionNode &sn = sectionNode(sectionIndex);
if (!(sn.type & DaySectionMask)) {
@@ -1645,7 +1726,7 @@ int QDateTimeParser::findDay(const QString &str1, int startDay, int sectionIndex
for (int day = startDay; day <= 7; ++day)
daysOfWeek.append(l.dayName(day, type));
- const int index = findTextEntry(str1, daysOfWeek, usedDay, used);
+ const int index = findTextEntry(str, daysOfWeek, usedDay, used);
return index < 0 ? index : index + startDay;
}
@@ -1738,7 +1819,7 @@ QDateTimeParser::ParsedSection QDateTimeParser::findUtcOffset(QStringView str, i
QDateTimeParser::ParsedSection
QDateTimeParser::findTimeZoneName(QStringView str, const QDateTime &when) const
{
- const int systemLength = startsWithLocalTimeZone(str);
+ const int systemLength = startsWithLocalTimeZone(str, when, locale());
#if QT_CONFIG(timezone)
// Collect up plausibly-valid characters; let QTimeZone work out what's
// truly valid.
@@ -2110,22 +2191,22 @@ bool QDateTimeParser::skipToNextSection(int index, const QDateTime &current, QSt
QString QDateTimeParser::SectionNode::name(QDateTimeParser::Section s)
{
switch (s) {
- case QDateTimeParser::AmPmSection: return "AmPmSection"_L1;
- case QDateTimeParser::DaySection: return "DaySection"_L1;
- case QDateTimeParser::DayOfWeekSectionShort: return "DayOfWeekSectionShort"_L1;
- case QDateTimeParser::DayOfWeekSectionLong: return "DayOfWeekSectionLong"_L1;
- case QDateTimeParser::Hour24Section: return "Hour24Section"_L1;
- case QDateTimeParser::Hour12Section: return "Hour12Section"_L1;
- case QDateTimeParser::MSecSection: return "MSecSection"_L1;
- case QDateTimeParser::MinuteSection: return "MinuteSection"_L1;
- case QDateTimeParser::MonthSection: return "MonthSection"_L1;
- case QDateTimeParser::SecondSection: return "SecondSection"_L1;
- case QDateTimeParser::TimeZoneSection: return "TimeZoneSection"_L1;
- case QDateTimeParser::YearSection: return "YearSection"_L1;
- case QDateTimeParser::YearSection2Digits: return "YearSection2Digits"_L1;
- case QDateTimeParser::NoSection: return "NoSection"_L1;
- case QDateTimeParser::FirstSection: return "FirstSection"_L1;
- case QDateTimeParser::LastSection: return "LastSection"_L1;
+ case AmPmSection: return "AmPmSection"_L1;
+ case DaySection: return "DaySection"_L1;
+ case DayOfWeekSectionShort: return "DayOfWeekSectionShort"_L1;
+ case DayOfWeekSectionLong: return "DayOfWeekSectionLong"_L1;
+ case Hour24Section: return "Hour24Section"_L1;
+ case Hour12Section: return "Hour12Section"_L1;
+ case MSecSection: return "MSecSection"_L1;
+ case MinuteSection: return "MinuteSection"_L1;
+ case MonthSection: return "MonthSection"_L1;
+ case SecondSection: return "SecondSection"_L1;
+ case TimeZoneSection: return "TimeZoneSection"_L1;
+ case YearSection: return "YearSection"_L1;
+ case YearSection2Digits: return "YearSection2Digits"_L1;
+ case NoSection: return "NoSection"_L1;
+ case FirstSection: return "FirstSection"_L1;
+ case LastSection: return "LastSection"_L1;
default: return "Unknown section "_L1 + QString::number(int(s));
}
}
@@ -2145,11 +2226,26 @@ QString QDateTimeParser::stateName(State s) const
}
}
+
+/*!
+ \internal
+ Compute a defaultValue to pass to parse().
+*/
+QDateTime QDateTimeParser::baseDate(const QTimeZone &zone) const
+{
+ QDateTime when = QDate(defaultCenturyStart, 1, 1).startOfDay(zone);
+ if (const QDateTime start = getMinimum(); when < start)
+ return start;
+ if (const QDateTime end = getMaximum(); when > end)
+ return end;
+ return when;
+}
+
// Only called when we want only one of date or time; use UTC to avoid bogus DST issues.
-bool QDateTimeParser::fromString(const QString &t, QDate *date, QTime *time) const
+bool QDateTimeParser::fromString(const QString &t, QDate *date, QTime *time, int baseYear) const
{
- QDateTime val(QDate(1900, 1, 1).startOfDay(QTimeZone::UTC));
- const StateNode tmp = parse(t, -1, val, false);
+ defaultCenturyStart = baseYear;
+ const StateNode tmp = parse(t, -1, baseDate(QTimeZone::UTC), false);
if (tmp.state != Acceptable || tmp.conflicts)
return false;
@@ -2172,13 +2268,13 @@ bool QDateTimeParser::fromString(const QString &t, QDate *date, QTime *time) con
}
// Only called when we want both date and time; default to local time.
-bool QDateTimeParser::fromString(const QString &t, QDateTime *datetime) const
+bool QDateTimeParser::fromString(const QString &t, QDateTime *datetime, int baseYear) const
{
- QDateTime val(QDate(1900, 1, 1).startOfDay());
- const StateNode tmp = parse(t, -1, val, false);
+ defaultCenturyStart = baseYear;
+ const StateNode tmp = parse(t, -1, baseDate(QTimeZone::LocalTime), false);
if (datetime)
*datetime = tmp.value;
- return tmp.state == Acceptable && !tmp.conflicts && tmp.value.isValid();
+ return tmp.state >= Intermediate && !tmp.conflicts && tmp.value.isValid();
}
QDateTime QDateTimeParser::getMinimum() const
@@ -2209,8 +2305,8 @@ QString QDateTimeParser::getAmPmText(AmPm ap, Case cs) const
QString raw = ap == AmText ? loc.amText() : loc.pmText();
switch (cs)
{
- case UpperCase: return raw.toUpper();
- case LowerCase: return raw.toLower();
+ case UpperCase: return std::move(raw).toUpper();
+ case LowerCase: return std::move(raw).toLower();
case NativeCase: return raw;
}
Q_UNREACHABLE_RETURN(raw);
@@ -2218,11 +2314,9 @@ QString QDateTimeParser::getAmPmText(AmPm ap, Case cs) const
/*
\internal
-
- I give arg2 preference because arg1 is always a QDateTime.
*/
-bool operator==(const QDateTimeParser::SectionNode &s1, const QDateTimeParser::SectionNode &s2)
+bool operator==(QDateTimeParser::SectionNode s1, QDateTimeParser::SectionNode s2)
{
return (s1.type == s2.type) && (s1.pos == s2.pos) && (s1.count == s2.count);
}
@@ -2231,7 +2325,7 @@ bool operator==(const QDateTimeParser::SectionNode &s1, const QDateTimeParser::S
Sets \a cal as the calendar to use. The default is Gregorian.
*/
-void QDateTimeParser::setCalendar(const QCalendar &cal)
+void QDateTimeParser::setCalendar(QCalendar cal)
{
calendar = cal;
}