diff options
Diffstat (limited to 'src/corelib/time/qdatetimeparser.cpp')
-rw-r--r-- | src/corelib/time/qdatetimeparser.cpp | 348 |
1 files changed, 215 insertions, 133 deletions
diff --git a/src/corelib/time/qdatetimeparser.cpp b/src/corelib/time/qdatetimeparser.cpp index aff78eae2f..cce32e7ad2 100644 --- a/src/corelib/time/qdatetimeparser.cpp +++ b/src/corelib/time/qdatetimeparser.cpp @@ -237,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(); @@ -334,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; @@ -709,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: @@ -727,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 @@ -768,11 +799,6 @@ QDateTimeParser::parseSection(const QDateTime ¤tValue, 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); @@ -808,7 +834,7 @@ QDateTimeParser::parseSection(const QDateTime ¤tValue, 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; @@ -820,7 +846,7 @@ QDateTimeParser::parseSection(const QDateTime ¤tValue, 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, §iontext, &used); } else { @@ -845,12 +871,26 @@ QDateTimeParser::parseSection(const QDateTime ¤tValue, 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)); @@ -884,13 +924,10 @@ QDateTimeParser::parseSection(const QDateTime ¤tValue, 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; @@ -914,13 +951,13 @@ QDateTimeParser::parseSection(const QDateTime ¤tValue, 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); @@ -957,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, @@ -964,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); @@ -978,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; @@ -996,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): @@ -1073,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++) { @@ -1142,14 +1181,43 @@ static QTime actualTime(QDateTimeParser::Sections known, /* \internal */ -static int startsWithLocalTimeZone(QStringView name) +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 (name.startsWith(zone)) - return zone.size(); + 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)); } - return 0; +#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); } /*! @@ -1182,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); @@ -1209,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; } @@ -1235,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; @@ -1287,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(); } } @@ -1389,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; } } @@ -1564,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; } @@ -1585,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; @@ -1622,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); @@ -1638,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)) { @@ -1657,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; } @@ -1750,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. @@ -2122,22 +2191,22 @@ bool QDateTimeParser::skipToNextSection(int index, const QDateTime ¤t, 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)); } } @@ -2157,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; @@ -2184,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 @@ -2221,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); @@ -2230,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); } @@ -2243,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; } |