diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2019-05-27 17:47:22 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2019-06-06 15:54:32 +0200 |
commit | 548513a4bd050d3df0a85fed6e2d1a00ce06d2ab (patch) | |
tree | 9e65f2701e013c1d1232082d5635c1b4e7817dd3 /src/corelib/tools/qdatetimeparser.cpp | |
parent | 29e3a4dfeaf5d4924eaa68824fb21998de687809 (diff) |
Separate out the time, zone, date code from corelib/tools/
We'll be adding calendar code here as well, and tools/ was getting
rather crowded, so it looks like time to move out a reasonably
coherent sub-bundle of it all.
Change-Id: I7e8030f38c31aa307f519dd918a43fc44baa6aa1
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/corelib/tools/qdatetimeparser.cpp')
-rw-r--r-- | src/corelib/tools/qdatetimeparser.cpp | 2047 |
1 files changed, 0 insertions, 2047 deletions
diff --git a/src/corelib/tools/qdatetimeparser.cpp b/src/corelib/tools/qdatetimeparser.cpp deleted file mode 100644 index 728b066db1..0000000000 --- a/src/corelib/tools/qdatetimeparser.cpp +++ /dev/null @@ -1,2047 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qplatformdefs.h" -#include "private/qdatetimeparser_p.h" - -#include "qdatastream.h" -#include "qset.h" -#include "qlocale.h" -#include "qdatetime.h" -#if QT_CONFIG(timezone) -#include "qtimezone.h" -#endif -#include "qdebug.h" - -//#define QDATETIMEPARSER_DEBUG -#if defined (QDATETIMEPARSER_DEBUG) && !defined(QT_NO_DEBUG_STREAM) -# define QDTPDEBUG qDebug() -# define QDTPDEBUGN qDebug -#else -# define QDTPDEBUG if (false) qDebug() -# define QDTPDEBUGN if (false) qDebug -#endif - -QT_BEGIN_NAMESPACE - -QDateTimeParser::~QDateTimeParser() -{ -} - -/*! - \internal - Gets the digit from a datetime. E.g. - - QDateTime var(QDate(2004, 02, 02)); - int digit = getDigit(var, Year); - // digit = 2004 -*/ - -int QDateTimeParser::getDigit(const QDateTime &t, int index) const -{ - if (index < 0 || index >= sectionNodes.size()) { -#if QT_CONFIG(datestring) - qWarning("QDateTimeParser::getDigit() Internal error (%ls %d)", - qUtf16Printable(t.toString()), index); -#else - qWarning("QDateTimeParser::getDigit() Internal error (%d)", index); -#endif - return -1; - } - const SectionNode &node = sectionNodes.at(index); - switch (node.type) { - case TimeZoneSection: return t.offsetFromUtc(); - case Hour24Section: case Hour12Section: return t.time().hour(); - case MinuteSection: return t.time().minute(); - case SecondSection: return t.time().second(); - case MSecSection: return t.time().msec(); - case YearSection2Digits: - case YearSection: return t.date().year(); - case MonthSection: return t.date().month(); - case DaySection: return t.date().day(); - case DayOfWeekSectionShort: - case DayOfWeekSectionLong: return t.date().day(); - case AmPmSection: return t.time().hour() > 11 ? 1 : 0; - - default: break; - } - -#if QT_CONFIG(datestring) - qWarning("QDateTimeParser::getDigit() Internal error 2 (%ls %d)", - qUtf16Printable(t.toString()), index); -#else - qWarning("QDateTimeParser::getDigit() Internal error 2 (%d)", index); -#endif - return -1; -} - -/*! - \internal - Sets a digit in a datetime. E.g. - - QDateTime var(QDate(2004, 02, 02)); - int digit = getDigit(var, Year); - // digit = 2004 - setDigit(&var, Year, 2005); - digit = getDigit(var, Year); - // digit = 2005 -*/ - -bool QDateTimeParser::setDigit(QDateTime &v, int index, int newVal) const -{ - if (index < 0 || index >= sectionNodes.size()) { -#if QT_CONFIG(datestring) - qWarning("QDateTimeParser::setDigit() Internal error (%ls %d %d)", - qUtf16Printable(v.toString()), index, newVal); -#else - qWarning("QDateTimeParser::setDigit() Internal error (%d %d)", index, newVal); -#endif - return false; - } - const SectionNode &node = sectionNodes.at(index); - - const QDate date = v.date(); - const QTime time = v.time(); - int year = date.year(); - int month = date.month(); - int day = date.day(); - int hour = time.hour(); - int minute = time.minute(); - int second = time.second(); - int msec = time.msec(); - Qt::TimeSpec tspec = v.timeSpec(); - // Only offset from UTC is amenable to setting an int value: - int offset = tspec == Qt::OffsetFromUTC ? v.offsetFromUtc() : 0; - - switch (node.type) { - case Hour24Section: case Hour12Section: hour = newVal; break; - case MinuteSection: minute = newVal; break; - case SecondSection: second = newVal; break; - case MSecSection: msec = newVal; break; - case YearSection2Digits: - case YearSection: year = newVal; break; - case MonthSection: month = newVal; break; - case DaySection: - case DayOfWeekSectionShort: - case DayOfWeekSectionLong: - if (newVal > 31) { - // have to keep legacy behavior. setting the - // date to 32 should return false. Setting it - // to 31 for february should return true - return false; - } - day = newVal; - break; - case TimeZoneSection: - if (newVal < absoluteMin(index) || newVal > absoluteMax(index)) - return false; - tspec = Qt::OffsetFromUTC; - offset = newVal; - break; - case AmPmSection: hour = (newVal == 0 ? hour % 12 : (hour % 12) + 12); break; - default: - qWarning("QDateTimeParser::setDigit() Internal error (%ls)", - qUtf16Printable(node.name())); - break; - } - - if (!(node.type & DaySectionMask)) { - if (day < cachedDay) - day = cachedDay; - const int max = QDate(year, month, 1).daysInMonth(); - if (day > max) { - day = max; - } - } - - const QDate newDate(year, month, day); - const QTime newTime(hour, minute, second, msec); - if (!newDate.isValid() || !newTime.isValid()) - return false; - - // Preserve zone: - v = -#if QT_CONFIG(timezone) - tspec == Qt::TimeZone ? QDateTime(newDate, newTime, v.timeZone()) : -#endif - QDateTime(newDate, newTime, tspec, offset); - return true; -} - - - -/*! - \internal - - Returns the absolute maximum for a section -*/ - -int QDateTimeParser::absoluteMax(int s, const QDateTime &cur) const -{ - const SectionNode &sn = sectionNode(s); - switch (sn.type) { -#if QT_CONFIG(timezone) - case TimeZoneSection: return QTimeZone::MaxUtcOffsetSecs; -#endif - case Hour24Section: - case Hour12Section: return 23; // this is special-cased in - // parseSection. We want it to be - // 23 for the stepBy case. - case MinuteSection: - case SecondSection: return 59; - case MSecSection: return 999; - case YearSection2Digits: - case YearSection: return 9999; // sectionMaxSize will prevent - // people from typing in a larger - // number in count == 2 sections. - // stepBy() will work on real years anyway - case MonthSection: return 12; - case DaySection: - case DayOfWeekSectionShort: - case DayOfWeekSectionLong: return cur.isValid() ? cur.date().daysInMonth() : 31; - case AmPmSection: return 1; - default: break; - } - qWarning("QDateTimeParser::absoluteMax() Internal error (%ls)", - qUtf16Printable(sn.name())); - return -1; -} - -/*! - \internal - - Returns the absolute minimum for a section -*/ - -int QDateTimeParser::absoluteMin(int s) const -{ - const SectionNode &sn = sectionNode(s); - switch (sn.type) { -#if QT_CONFIG(timezone) - case TimeZoneSection: return QTimeZone::MinUtcOffsetSecs; -#endif - case Hour24Section: - case Hour12Section: - case MinuteSection: - case SecondSection: - case MSecSection: - case YearSection2Digits: - case YearSection: return 0; - case MonthSection: - case DaySection: - case DayOfWeekSectionShort: - case DayOfWeekSectionLong: return 1; - case AmPmSection: return 0; - default: break; - } - qWarning("QDateTimeParser::absoluteMin() Internal error (%ls, %0x)", - qUtf16Printable(sn.name()), sn.type); - return -1; -} - -/*! - \internal - - Returns the sectionNode for the Section \a s. -*/ - -const QDateTimeParser::SectionNode &QDateTimeParser::sectionNode(int sectionIndex) const -{ - if (sectionIndex < 0) { - switch (sectionIndex) { - case FirstSectionIndex: - return first; - case LastSectionIndex: - return last; - case NoSectionIndex: - return none; - } - } else if (sectionIndex < sectionNodes.size()) { - return sectionNodes.at(sectionIndex); - } - - qWarning("QDateTimeParser::sectionNode() Internal error (%d)", - sectionIndex); - return none; -} - -QDateTimeParser::Section QDateTimeParser::sectionType(int sectionIndex) const -{ - return sectionNode(sectionIndex).type; -} - - -/*! - \internal - - Returns the starting position for section \a s. -*/ - -int QDateTimeParser::sectionPos(int sectionIndex) const -{ - return sectionPos(sectionNode(sectionIndex)); -} - -int QDateTimeParser::sectionPos(const SectionNode &sn) const -{ - switch (sn.type) { - case FirstSection: return 0; - case LastSection: return displayText().size() - 1; - default: break; - } - if (sn.pos == -1) { - qWarning("QDateTimeParser::sectionPos Internal error (%ls)", qUtf16Printable(sn.name())); - return -1; - } - return sn.pos; -} - - -/*! - \internal - - helper function for parseFormat. removes quotes that are - not escaped and removes the escaping on those that are escaped - -*/ - -static QString unquote(const QStringRef &str) -{ - const QChar quote(QLatin1Char('\'')); - const QChar slash(QLatin1Char('\\')); - const QChar zero(QLatin1Char('0')); - QString ret; - QChar status(zero); - const int max = str.size(); - for (int i=0; i<max; ++i) { - if (str.at(i) == quote) { - if (status != quote) { - status = quote; - } else if (!ret.isEmpty() && str.at(i - 1) == slash) { - ret[ret.size() - 1] = quote; - } else { - status = zero; - } - } else { - ret += str.at(i); - } - } - return ret; -} -/*! - \internal - - Parses the format \a newFormat. If successful, returns \c true and - sets up the format. Else keeps the old format and returns \c false. - -*/ - -static inline int countRepeat(const QString &str, int index, int maxCount) -{ - int count = 1; - const QChar ch(str.at(index)); - const int max = qMin(index + maxCount, str.size()); - while (index + count < max && str.at(index + count) == ch) { - ++count; - } - return count; -} - -static inline void appendSeparator(QStringList *list, const QString &string, int from, int size, int lastQuote) -{ - const QStringRef separator = string.midRef(from, size); - list->append(lastQuote >= from ? unquote(separator) : separator.toString()); -} - - -bool QDateTimeParser::parseFormat(const QString &newFormat) -{ - const QLatin1Char quote('\''); - const QLatin1Char slash('\\'); - const QLatin1Char zero('0'); - if (newFormat == displayFormat && !newFormat.isEmpty()) { - return true; - } - - QDTPDEBUGN("parseFormat: %s", newFormat.toLatin1().constData()); - - QVector<SectionNode> newSectionNodes; - Sections newDisplay = 0; - QStringList newSeparators; - int i, index = 0; - int add = 0; - QChar status(zero); - const int max = newFormat.size(); - int lastQuote = -1; - for (i = 0; i<max; ++i) { - if (newFormat.at(i) == quote) { - lastQuote = i; - ++add; - if (status != quote) { - status = quote; - } else if (i > 0 && newFormat.at(i - 1) != slash) { - status = zero; - } - } else if (status != quote) { - const char sect = newFormat.at(i).toLatin1(); - switch (sect) { - case 'H': - case 'h': - if (parserType != QVariant::Date) { - const Section hour = (sect == 'h') ? Hour12Section : Hour24Section; - const SectionNode sn = { hour, i - add, countRepeat(newFormat, i, 2), 0 }; - newSectionNodes.append(sn); - appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); - i += sn.count - 1; - index = i + 1; - newDisplay |= hour; - } - break; - case 'm': - if (parserType != QVariant::Date) { - const SectionNode sn = { MinuteSection, i - add, countRepeat(newFormat, i, 2), 0 }; - newSectionNodes.append(sn); - appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); - i += sn.count - 1; - index = i + 1; - newDisplay |= MinuteSection; - } - break; - case 's': - if (parserType != QVariant::Date) { - const SectionNode sn = { SecondSection, i - add, countRepeat(newFormat, i, 2), 0 }; - newSectionNodes.append(sn); - appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); - i += sn.count - 1; - index = i + 1; - newDisplay |= SecondSection; - } - break; - - case 'z': - if (parserType != QVariant::Date) { - const SectionNode sn = { MSecSection, i - add, countRepeat(newFormat, i, 3) < 3 ? 1 : 3, 0 }; - newSectionNodes.append(sn); - appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); - i += sn.count - 1; - index = i + 1; - newDisplay |= MSecSection; - } - break; - case 'A': - case 'a': - if (parserType != QVariant::Date) { - const bool cap = (sect == 'A'); - const SectionNode sn = { AmPmSection, i - add, (cap ? 1 : 0), 0 }; - newSectionNodes.append(sn); - appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); - newDisplay |= AmPmSection; - if (i + 1 < newFormat.size() - && newFormat.at(i+1) == (cap ? QLatin1Char('P') : QLatin1Char('p'))) { - ++i; - } - index = i + 1; - } - break; - case 'y': - if (parserType != QVariant::Time) { - const int repeat = countRepeat(newFormat, i, 4); - if (repeat >= 2) { - const SectionNode sn = { repeat == 4 ? YearSection : YearSection2Digits, - i - add, repeat == 4 ? 4 : 2, 0 }; - newSectionNodes.append(sn); - appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); - i += sn.count - 1; - index = i + 1; - newDisplay |= sn.type; - } - } - break; - case 'M': - if (parserType != QVariant::Time) { - const SectionNode sn = { MonthSection, i - add, countRepeat(newFormat, i, 4), 0 }; - newSectionNodes.append(sn); - newSeparators.append(unquote(newFormat.midRef(index, i - index))); - i += sn.count - 1; - index = i + 1; - newDisplay |= MonthSection; - } - break; - case 'd': - if (parserType != QVariant::Time) { - const int repeat = countRepeat(newFormat, i, 4); - const Section sectionType = (repeat == 4 ? DayOfWeekSectionLong - : (repeat == 3 ? DayOfWeekSectionShort : DaySection)); - const SectionNode sn = { sectionType, i - add, repeat, 0 }; - newSectionNodes.append(sn); - appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); - i += sn.count - 1; - index = i + 1; - newDisplay |= sn.type; - } - break; - case 't': - if (parserType != QVariant::Time) { - const SectionNode sn = { TimeZoneSection, i - add, countRepeat(newFormat, i, 4), 0 }; - newSectionNodes.append(sn); - appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); - i += sn.count - 1; - index = i + 1; - newDisplay |= TimeZoneSection; - } - break; - default: - break; - } - } - } - if (newSectionNodes.isEmpty() && context == DateTimeEdit) { - return false; - } - - if ((newDisplay & (AmPmSection|Hour12Section)) == Hour12Section) { - const int count = newSectionNodes.size(); - for (int i = 0; i < count; ++i) { - SectionNode &node = newSectionNodes[i]; - if (node.type == Hour12Section) - node.type = Hour24Section; - } - } - - if (index < max) { - appendSeparator(&newSeparators, newFormat, index, index - max, lastQuote); - } else { - newSeparators.append(QString()); - } - - displayFormat = newFormat; - separators = newSeparators; - sectionNodes = newSectionNodes; - display = newDisplay; - last.pos = -1; - -// for (int i=0; i<sectionNodes.size(); ++i) { -// QDTPDEBUG << sectionNodes.at(i).name() << sectionNodes.at(i).count; -// } - - QDTPDEBUG << newFormat << displayFormat; - QDTPDEBUGN("separators:\n'%s'", separators.join(QLatin1String("\n")).toLatin1().constData()); - - return true; -} - -/*! - \internal - - Returns the size of section \a s. -*/ - -int QDateTimeParser::sectionSize(int sectionIndex) const -{ - if (sectionIndex < 0) - return 0; - - if (sectionIndex >= sectionNodes.size()) { - qWarning("QDateTimeParser::sectionSize Internal error (%d)", sectionIndex); - return -1; - } - - if (sectionIndex == sectionNodes.size() - 1) { - // In some cases there is a difference between displayText() and text. - // e.g. when text is 2000/01/31 and displayText() is "2000/2/31" - text - // is the previous value and displayText() is the new value. - // The size difference is always due to leading zeroes. - int sizeAdjustment = 0; - const int displayTextSize = displayText().size(); - if (displayTextSize != text.size()) { - // Any zeroes added before this section will affect our size. - int preceedingZeroesAdded = 0; - if (sectionNodes.size() > 1 && context == DateTimeEdit) { - const auto begin = sectionNodes.cbegin(); - const auto end = begin + sectionIndex; - for (auto sectionIt = begin; sectionIt != end; ++sectionIt) - preceedingZeroesAdded += sectionIt->zeroesAdded; - } - sizeAdjustment = preceedingZeroesAdded; - } - - return displayTextSize + sizeAdjustment - sectionPos(sectionIndex) - separators.last().size(); - } else { - return sectionPos(sectionIndex + 1) - sectionPos(sectionIndex) - - separators.at(sectionIndex + 1).size(); - } -} - - -int QDateTimeParser::sectionMaxSize(Section s, int count) const -{ -#if QT_CONFIG(textdate) - int mcount = 12; -#endif - - switch (s) { - case FirstSection: - case NoSection: - case LastSection: return 0; - - case AmPmSection: { - const int lowerMax = qMin(getAmPmText(AmText, LowerCase).size(), - getAmPmText(PmText, LowerCase).size()); - const int upperMax = qMin(getAmPmText(AmText, UpperCase).size(), - getAmPmText(PmText, UpperCase).size()); - return qMin(4, qMin(lowerMax, upperMax)); - } - - case Hour24Section: - case Hour12Section: - case MinuteSection: - case SecondSection: - case DaySection: return 2; - case DayOfWeekSectionShort: - case DayOfWeekSectionLong: -#if !QT_CONFIG(textdate) - return 2; -#else - mcount = 7; - Q_FALLTHROUGH(); -#endif - case MonthSection: -#if !QT_CONFIG(textdate) - return 2; -#else - if (count <= 2) - return 2; - - { - int ret = 0; - const QLocale l = locale(); - const QLocale::FormatType format = count == 4 ? QLocale::LongFormat : QLocale::ShortFormat; - for (int i=1; i<=mcount; ++i) { - const QString str = (s == MonthSection - ? l.monthName(i, format) - : l.dayName(i, format)); - ret = qMax(str.size(), ret); - } - return ret; - } -#endif - case MSecSection: return 3; - case YearSection: return 4; - case YearSection2Digits: return 2; - // Arbitrarily many tokens (each up to 14 bytes) joined with / separators: - case TimeZoneSection: return std::numeric_limits<int>::max(); - - case CalendarPopupSection: - case Internal: - case TimeSectionMask: - case DateSectionMask: - case HourSectionMask: - case YearSectionMask: - case DayOfWeekSectionMask: - case DaySectionMask: - qWarning("QDateTimeParser::sectionMaxSize: Invalid section %s", - SectionNode::name(s).toLatin1().constData()); - - case NoSectionIndex: - case FirstSectionIndex: - case LastSectionIndex: - case CalendarPopupIndex: - // these cases can't happen - break; - } - return -1; -} - - -int QDateTimeParser::sectionMaxSize(int index) const -{ - const SectionNode &sn = sectionNode(index); - return sectionMaxSize(sn.type, sn.count); -} - -/*! - \internal - - Returns the text of section \a s. This function operates on the - arg text rather than edit->text(). -*/ - - -QString QDateTimeParser::sectionText(const QString &text, int sectionIndex, int index) const -{ - const SectionNode &sn = sectionNode(sectionIndex); - switch (sn.type) { - case NoSectionIndex: - case FirstSectionIndex: - case LastSectionIndex: - return QString(); - default: break; - } - - return text.mid(index, sectionSize(sectionIndex)); -} - -QString QDateTimeParser::sectionText(int sectionIndex) const -{ - const SectionNode &sn = sectionNode(sectionIndex); - return sectionText(displayText(), sectionIndex, sn.pos); -} - - -#if QT_CONFIG(datestring) - -QDateTimeParser::ParsedSection -QDateTimeParser::parseSection(const QDateTime ¤tValue, int sectionIndex, - int offset, QString *text) const -{ - ParsedSection result; // initially Invalid - const SectionNode &sn = sectionNode(sectionIndex); - if (sn.type & Internal) { - qWarning("QDateTimeParser::parseSection Internal error (%ls %d)", - qUtf16Printable(sn.name()), sectionIndex); - return result; - } - - const int sectionmaxsize = sectionMaxSize(sectionIndex); - QStringRef sectionTextRef = text->midRef(offset, sectionmaxsize); - - QDTPDEBUG << "sectionValue for" << sn.name() - << "with text" << *text << "and (at" << offset - << ") st:" << sectionTextRef; - - switch (sn.type) { - case AmPmSection: { - QString sectiontext = sectionTextRef.toString(); - int used; - const int ampm = findAmPm(sectiontext, sectionIndex, &used); - switch (ampm) { - case AM: // sectiontext == AM - case PM: // sectiontext == PM - result = ParsedSection(Acceptable, ampm, used); - break; - case PossibleAM: // sectiontext => AM - case PossiblePM: // sectiontext => PM - result = ParsedSection(Intermediate, ampm - 2, used); - break; - case PossibleBoth: // sectiontext => AM|PM - result = ParsedSection(Intermediate, 0, used); - break; - case Neither: - QDTPDEBUG << "invalid because findAmPm(" << sectiontext << ") returned -1"; - break; - default: - QDTPDEBUGN("This should never happen (findAmPm returned %d)", ampm); - break; - } - if (result.state != Invalid) - text->replace(offset, used, sectiontext.constData(), used); - break; } - case TimeZoneSection: -#if QT_CONFIG(timezone) - result = findTimeZone(sectionTextRef, currentValue, - absoluteMax(sectionIndex), - absoluteMin(sectionIndex)); -#endif - break; - case MonthSection: - case DayOfWeekSectionShort: - case DayOfWeekSectionLong: - if (sn.count >= 3) { - QString sectiontext = sectionTextRef.toString(); - int num = 0, used = 0; - if (sn.type == MonthSection) { - const QDate minDate = getMinimum().date(); - const int min = (currentValue.date().year() == minDate.year()) - ? minDate.month() : 1; - num = findMonth(sectiontext.toLower(), min, sectionIndex, §iontext, &used); - } else { - num = findDay(sectiontext.toLower(), 1, sectionIndex, §iontext, &used); - } - - result = ParsedSection(Intermediate, num, used); - if (num != -1) { - text->replace(offset, used, sectiontext.constData(), used); - if (used == sectiontext.size()) - result = ParsedSection(Acceptable, num, used); - } - break; - } - Q_FALLTHROUGH(); - // All numeric: - case DaySection: - case YearSection: - case YearSection2Digits: - case Hour12Section: - case Hour24Section: - case MinuteSection: - case SecondSection: - case MSecSection: { - int sectiontextSize = sectionTextRef.size(); - if (sectiontextSize == 0) { - result = ParsedSection(Intermediate); - } else { - for (int i = 0; i < sectiontextSize; ++i) { - if (sectionTextRef.at(i).isSpace()) - sectiontextSize = i; // which exits the loop - } - - const int absMax = absoluteMax(sectionIndex); - QLocale loc; - bool ok = true; - int last = -1, used = -1; - - Q_ASSERT(sectiontextSize <= sectionmaxsize); - QStringRef digitsStr = sectionTextRef.left(sectiontextSize); - for (int digits = sectiontextSize; digits >= 1; --digits) { - digitsStr.truncate(digits); - int tmp = (int)loc.toUInt(digitsStr, &ok); - if (ok && sn.type == Hour12Section) { - if (tmp > 12) { - tmp = -1; - ok = false; - } else if (tmp == 12) { - tmp = 0; - } - } - if (ok && tmp <= absMax) { - QDTPDEBUG << sectionTextRef.left(digits) << tmp << digits; - last = tmp; - used = digits; - break; - } - } - - if (last == -1) { - QChar first(sectionTextRef.at(0)); - if (separators.at(sectionIndex + 1).startsWith(first)) - result = ParsedSection(Intermediate, 0, used); - else - QDTPDEBUG << "invalid because" << sectionTextRef << "can't become a uint" << last << ok; - } else { - const FieldInfo fi = fieldInfo(sectionIndex); - const bool unfilled = used < sectionmaxsize; - if (unfilled && fi & Fraction) { // typing 2 in a zzz field should be .200, not .002 - for (int i = used; i < sectionmaxsize; ++i) - last *= 10; - } - // Even those *= 10s can't take last above absMax: - Q_ASSERT(last <= absMax); - const int absMin = absoluteMin(sectionIndex); - if (last < absMin) { - if (unfilled) - result = ParsedSection(Intermediate, last, used); - else - QDTPDEBUG << "invalid because" << last << "is less than absoluteMin" << absMin; - } else if (unfilled && (fi & (FixedWidth|Numeric)) == (FixedWidth|Numeric)) { - if (skipToNextSection(sectionIndex, currentValue, digitsStr)) { - const int missingZeroes = sectionmaxsize - digitsStr.size(); - result = ParsedSection(Acceptable, last, sectionmaxsize, missingZeroes); - text->insert(offset, QString(missingZeroes, QLatin1Char('0'))); - ++(const_cast<QDateTimeParser*>(this)->sectionNodes[sectionIndex].zeroesAdded); - } else { - result = ParsedSection(Intermediate, last, used);; - } - } else { - result = ParsedSection(Acceptable, last, used); - } - } - } - break; } - default: - qWarning("QDateTimeParser::parseSection Internal error (%ls %d)", - qUtf16Printable(sn.name()), sectionIndex); - return result; - } - Q_ASSERT(result.state != Invalid || result.value == -1); - - return result; -} - -/*! - \internal - - Returns a date consistent with the given data on parts specified by known, - while staying as close to the given data as it can. Returns an invalid date - when on valid date is consistent with the data. -*/ - -static QDate actualDate(QDateTimeParser::Sections known, int year, int year2digits, - int month, int day, int dayofweek) -{ - QDate actual(year, month, day); - if (actual.isValid() && year % 100 == year2digits && actual.dayOfWeek() == dayofweek) - return actual; // The obvious candidate is fine :-) - - if (dayofweek < 1 || dayofweek > 7) // Invalid: ignore - known &= ~QDateTimeParser::DayOfWeekSectionMask; - - // Assuming year > 0 ... - if (year % 100 != year2digits) { - if (known & QDateTimeParser::YearSection2Digits) { - // Over-ride year, even if specified: - year += year2digits - year % 100; - known &= ~QDateTimeParser::YearSection; - } else { - year2digits = year % 100; - } - } - Q_ASSERT(year % 100 == year2digits); - - if (month < 1) { // If invalid, clip to nearest valid and ignore in known. - month = 1; - known &= ~QDateTimeParser::MonthSection; - } else if (month > 12) { - month = 12; - known &= ~QDateTimeParser::MonthSection; - } - - QDate first(year, month, 1); - int last = known & QDateTimeParser::YearSection && known & QDateTimeParser::MonthSection - ? first.daysInMonth() : 0; - // If we also know day-of-week, tweak last to the last in the month that matches it: - if (last && known & QDateTimeParser::DayOfWeekSectionMask) { - int diff = (dayofweek - first.dayOfWeek() - last) % 7; - Q_ASSERT(diff <= 0); // C++11 specifies (-ve) % (+ve) to be <= 0. - last += diff; - } - if (day < 1) { - if (known & QDateTimeParser::DayOfWeekSectionMask && last) { - day = 1 + dayofweek - first.dayOfWeek(); - if (day < 1) - day += 7; - } else { - day = 1; - } - known &= ~QDateTimeParser::DaySection; - } else if (day > 31) { - day = last; - known &= ~QDateTimeParser::DaySection; - } else if (last && day > last && (known & QDateTimeParser::DaySection) == 0) { - day = last; - } - - actual = QDate(year, month, day); - if (!actual.isValid() // We can't do better than we have, in this case - || (known & QDateTimeParser::DaySection - && known & QDateTimeParser::MonthSection - && known & QDateTimeParser::YearSection) // ditto - || actual.dayOfWeek() == dayofweek // Good enough, use it. - || (known & QDateTimeParser::DayOfWeekSectionMask) == 0) { // No contradiction, use it. - return actual; - } - - /* - Now it gets trickier. - - We have some inconsistency in our data; we've been told day of week, but - it doesn't fit with our year, month and day. At least one of these is - unknown, though: so we can fix day of week by tweaking it. - */ - - if ((known & QDateTimeParser::DaySection) == 0) { - // Relatively easy to fix. - day += dayofweek - actual.dayOfWeek(); - if (day < 1) - day += 7; - else if (day > actual.daysInMonth()) - day -= 7; - actual = QDate(year, month, day); - return actual; - } - - if ((known & QDateTimeParser::MonthSection) == 0) { - /* - Try possible month-offsets, m, preferring small; at least one (present - month doesn't work) and at most 11 (max month, 12, minus min, 1); try - in both directions, ignoring any offset that takes us out of range. - */ - for (int m = 1; m < 12; m++) { - if (m < month) { - actual = QDate(year, month - m, day); - if (actual.dayOfWeek() == dayofweek) - return actual; - } - if (m + month <= 12) { - actual = QDate(year, month + m, day); - if (actual.dayOfWeek() == dayofweek) - return actual; - } - } - // Should only get here in corner cases; e.g. day == 31 - actual = QDate(year, month, day); // Restore from trial values. - } - - 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); - if (actual.dayOfWeek() == dayofweek) - return actual; - actual = QDate(year - 100, month, day); - if (actual.dayOfWeek() == dayofweek) - return actual; - } else { - // Offset by 7 is usually enough, but rare cases may need more: - for (int y = 1; y < 12; y++) { - actual = QDate(year - y, month, day); - if (actual.dayOfWeek() == dayofweek) - return actual; - actual = QDate(year + y, month, day); - if (actual.dayOfWeek() == dayofweek) - return actual; - } - } - actual = QDate(year, month, day); // Restore from trial values. - } - - return actual; // It'll just have to do :-( -} - -/*! - \internal -*/ - -static QTime actualTime(QDateTimeParser::Sections known, - int hour, int hour12, int ampm, - int minute, int second, int msec) -{ - // If we have no conflict, or don't know enough to diagonose one, use this: - QTime actual(hour, minute, second, msec); - if (hour12 < 0 || hour12 > 12) { // ignore bogus value - known &= ~QDateTimeParser::Hour12Section; - hour12 = hour % 12; - } - - if (ampm == -1 || (known & QDateTimeParser::AmPmSection) == 0) { - if ((known & QDateTimeParser::Hour12Section) == 0 || hour % 12 == hour12) - return actual; - - if ((known & QDateTimeParser::Hour24Section) == 0) - hour = hour12 + (hour > 12 ? 12 : 0); - } else { - Q_ASSERT(ampm == 0 || ampm == 1); - if (hour - hour12 == ampm * 12) - return actual; - - if ((known & QDateTimeParser::Hour24Section) == 0 - && known & QDateTimeParser::Hour12Section) { - hour = hour12 + ampm * 12; - } - } - actual = QTime(hour, minute, second, msec); - return actual; -} - -/*! - \internal -*/ -QDateTimeParser::StateNode -QDateTimeParser::scanString(const QDateTime &defaultValue, - bool fixup, QString *input) const -{ - State state = Acceptable; - bool conflicts = false; - const int sectionNodesCount = sectionNodes.size(); - int padding = 0; - int pos = 0; - int year, month, day; - const QDate defaultDate = defaultValue.date(); - const QTime defaultTime = defaultValue.time(); - defaultDate.getDate(&year, &month, &day); - int year2digits = year % 100; - int hour = defaultTime.hour(); - int hour12 = -1; - int minute = defaultTime.minute(); - int second = defaultTime.second(); - int msec = defaultTime.msec(); - int dayofweek = defaultDate.dayOfWeek(); - Qt::TimeSpec tspec = defaultValue.timeSpec(); - int zoneOffset = 0; // In seconds; local - UTC -#if QT_CONFIG(timezone) - QTimeZone timeZone; -#endif - switch (tspec) { - case Qt::OffsetFromUTC: // timeZone is ignored - zoneOffset = defaultValue.offsetFromUtc(); - break; -#if QT_CONFIG(timezone) - case Qt::TimeZone: - timeZone = defaultValue.timeZone(); - if (timeZone.isValid()) - zoneOffset = timeZone.offsetFromUtc(defaultValue); - // else: is there anything we can do about this ? - break; -#endif - default: // zoneOffset and timeZone are ignored - break; - } - - int ampm = -1; - Sections isSet = NoSection; - - for (int index = 0; index < sectionNodesCount; ++index) { - Q_ASSERT(state != Invalid); - const QString &separator = separators.at(index); - if (input->midRef(pos, separator.size()) != separator) { - QDTPDEBUG << "invalid because" << input->midRef(pos, separator.size()) - << "!=" << separator - << index << pos << currentSectionIndex; - return StateNode(); - } - pos += separator.size(); - sectionNodes[index].pos = pos; - int *current = 0; - const SectionNode sn = sectionNodes.at(index); - ParsedSection sect; - - { - const QDate date = actualDate(isSet, year, year2digits, month, day, dayofweek); - const QTime time = actualTime(isSet, hour, hour12, ampm, minute, second, msec); - sect = parseSection( -#if QT_CONFIG(timezone) - tspec == Qt::TimeZone ? QDateTime(date, time, timeZone) : -#endif - QDateTime(date, time, tspec, zoneOffset), - index, pos, input); - } - - QDTPDEBUG << "sectionValue" << sn.name() << *input - << "pos" << pos << "used" << sect.used << stateName(sect.state); - - padding += sect.zeroes; - if (fixup && sect.state == Intermediate && sect.used < sn.count) { - const FieldInfo fi = fieldInfo(index); - if ((fi & (Numeric|FixedWidth)) == (Numeric|FixedWidth)) { - const QString newText = QString::fromLatin1("%1").arg(sect.value, sn.count, 10, QLatin1Char('0')); - input->replace(pos, sect.used, newText); - sect.used = sn.count; - } - } - - state = qMin<State>(state, sect.state); - // QDateTimeEdit can fix Intermediate and zeroes, but input needing that didn't match format: - if (state == Invalid || (context == FromString && (state == Intermediate || sect.zeroes))) - return StateNode(); - - switch (sn.type) { - case TimeZoneSection: - current = &zoneOffset; - if (sect.used > 0) { -#if QT_CONFIG(timezone) // Synchronize with what findTimeZone() found: - QStringRef zoneName = input->midRef(pos, sect.used); - 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); -#else - tspec = Qt::LocalTime; -#endif - } - break; - case Hour24Section: current = &hour; break; - case Hour12Section: current = &hour12; break; - case MinuteSection: current = &minute; break; - case SecondSection: current = &second; break; - case MSecSection: current = &msec; break; - case YearSection: current = &year; break; - case YearSection2Digits: current = &year2digits; break; - case MonthSection: current = &month; break; - case DayOfWeekSectionShort: - case DayOfWeekSectionLong: current = &dayofweek; break; - case DaySection: current = &day; sect.value = qMax<int>(1, sect.value); break; - case AmPmSection: current = &m; break; - default: - qWarning("QDateTimeParser::parse Internal error (%ls)", - qUtf16Printable(sn.name())); - break; - } - - if (sect.used > 0) - pos += sect.used; - QDTPDEBUG << index << sn.name() << "is set to" - << pos << "state is" << stateName(state); - - if (!current) { - qWarning("QDateTimeParser::parse Internal error 2"); - return StateNode(); - } - if (isSet & sn.type && *current != sect.value) { - QDTPDEBUG << "CONFLICT " << sn.name() << *current << sect.value; - conflicts = true; - if (index != currentSectionIndex || sect.state == Invalid) { - continue; - } - } - if (sect.state != Invalid) - *current = sect.value; - - // Record the present section: - isSet |= sn.type; - } - - if (input->midRef(pos) != separators.last()) { - QDTPDEBUG << "invalid because" << input->midRef(pos) - << "!=" << separators.last() << pos; - return StateNode(); - } - - if (parserType != QVariant::Time) { - if (year % 100 != year2digits && (isSet & YearSection2Digits)) { - if (!(isSet & YearSection)) { - year = (year / 100) * 100; - year += year2digits; - } else { - conflicts = true; - const SectionNode &sn = sectionNode(currentSectionIndex); - if (sn.type == YearSection2Digits) { - year = (year / 100) * 100; - year += year2digits; - } - } - } - - const QDate date(year, month, day); - const int diff = dayofweek - date.dayOfWeek(); - if (diff != 0 && state == Acceptable && isSet & DayOfWeekSectionMask) { - if (isSet & DaySection) - conflicts = true; - const SectionNode &sn = sectionNode(currentSectionIndex); - if (sn.type & DayOfWeekSectionMask || currentSectionIndex == -1) { - // dayofweek should be preferred - day += diff; - if (day <= 0) { - day += 7; - } else if (day > date.daysInMonth()) { - day -= 7; - } - QDTPDEBUG << year << month << day << dayofweek - << diff << QDate(year, month, day).dayOfWeek(); - } - } - - bool needfixday = false; - if (sectionType(currentSectionIndex) & DaySectionMask) { - cachedDay = day; - } else if (cachedDay > day) { - day = cachedDay; - needfixday = true; - } - - if (!QDate::isValid(year, month, day)) { - if (day < 32) { - cachedDay = day; - } - if (day > 28 && QDate::isValid(year, month, 1)) { - needfixday = true; - } - } - if (needfixday) { - if (context == FromString) { - return StateNode(); - } - if (state == Acceptable && fixday) { - day = qMin<int>(day, QDate(year, month, 1).daysInMonth()); - - const QLocale loc = locale(); - for (int i=0; i<sectionNodesCount; ++i) { - const SectionNode sn = sectionNode(i); - if (sn.type & DaySection) { - input->replace(sectionPos(sn), sectionSize(i), loc.toString(day)); - } else if (sn.type & DayOfWeekSectionMask) { - const int dayOfWeek = QDate(year, month, day).dayOfWeek(); - const QLocale::FormatType dayFormat = - (sn.type == DayOfWeekSectionShort - ? QLocale::ShortFormat : QLocale::LongFormat); - const QString dayName(loc.dayName(dayOfWeek, dayFormat)); - input->replace(sectionPos(sn), sectionSize(i), dayName); - } - } - } else if (state > Intermediate) { - state = Intermediate; - } - } - } - - if (parserType != QVariant::Date) { - if (isSet & Hour12Section) { - const bool hasHour = isSet & Hour24Section; - if (ampm == -1) { - if (hasHour) { - ampm = (hour < 12 ? 0 : 1); - } else { - ampm = 0; // no way to tell if this is am or pm so I assume am - } - } - hour12 = (ampm == 0 ? hour12 % 12 : (hour12 % 12) + 12); - if (!hasHour) { - hour = hour12; - } else if (hour != hour12) { - conflicts = true; - } - } else if (ampm != -1) { - if (!(isSet & (Hour24Section))) { - hour = (12 * ampm); // special case. Only ap section - } else if ((ampm == 0) != (hour < 12)) { - conflicts = true; - } - } - - } - - QDTPDEBUG << year << month << day << hour << minute << second << msec; - Q_ASSERT(state != Invalid); - - const QDate date(year, month, day); - const QTime time(hour, minute, second, msec); - const QDateTime when = -#if QT_CONFIG(timezone) - tspec == Qt::TimeZone ? QDateTime(date, time, timeZone) : -#endif - QDateTime(date, time, tspec, zoneOffset); - - // 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 == QVariant::DateTime && !(isSet & HourSectionMask) && !when.isValid()) { - qint64 msecs = when.toMSecsSinceEpoch(); - // Fortunately, that gets a useful answer ... - const QDateTime replace = -#if QT_CONFIG(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); - } - } - - return StateNode(when, state, padding, conflicts); -} - -/*! - \internal -*/ - -QDateTimeParser::StateNode -QDateTimeParser::parse(QString input, int position, const QDateTime &defaultValue, bool fixup) const -{ - const QDateTime minimum = getMinimum(); - const QDateTime maximum = getMaximum(); - - QDTPDEBUG << "parse" << input; - StateNode scan = scanString(defaultValue, fixup, &input); - QDTPDEBUGN("'%s' => '%s'(%s)", input.toLatin1().constData(), - scan.value.toString(QLatin1String("yyyy/MM/dd hh:mm:ss.zzz")).toLatin1().constData(), - stateName(scan.state).toLatin1().constData()); - - if (scan.value.isValid() && scan.state != Invalid) { - if (context != FromString && scan.value < minimum) { - const QLatin1Char space(' '); - if (scan.value >= minimum) - qWarning("QDateTimeParser::parse Internal error 3 (%ls %ls)", - qUtf16Printable(scan.value.toString()), qUtf16Printable(minimum.toString())); - - bool done = false; - scan.state = Invalid; - const int sectionNodesCount = sectionNodes.size(); - for (int i=0; i<sectionNodesCount && !done; ++i) { - const SectionNode &sn = sectionNodes.at(i); - QString t = sectionText(input, i, sn.pos).toLower(); - if ((t.size() < sectionMaxSize(i) - && (((int)fieldInfo(i) & (FixedWidth|Numeric)) != Numeric)) - || t.contains(space)) { - switch (sn.type) { - case AmPmSection: - switch (findAmPm(t, i)) { - case AM: - case PM: - scan.state = Acceptable; - done = true; - break; - case Neither: - scan.state = Invalid; - done = true; - break; - case PossibleAM: - case PossiblePM: - case PossibleBoth: { - const QDateTime copy(scan.value.addSecs(12 * 60 * 60)); - if (copy >= minimum && copy <= maximum) { - scan.state = Intermediate; - done = true; - } - break; } - } - Q_FALLTHROUGH(); - case MonthSection: - if (sn.count >= 3) { - const int finalMonth = scan.value.date().month(); - int tmp = finalMonth; - // I know the first possible month makes the date too early - while ((tmp = findMonth(t, tmp + 1, i)) != -1) { - const QDateTime copy(scan.value.addMonths(tmp - finalMonth)); - if (copy >= minimum && copy <= maximum) - break; // break out of while - } - if (tmp != -1) { - scan.state = Intermediate; - done = true; - } - break; - } - Q_FALLTHROUGH(); - default: { - int toMin; - int toMax; - - if (sn.type & TimeSectionMask) { - if (scan.value.daysTo(minimum) != 0) { - break; - } - const QTime time = scan.value.time(); - toMin = time.msecsTo(minimum.time()); - if (scan.value.daysTo(maximum) > 0) - toMax = -1; // can't get to max - else - toMax = time.msecsTo(maximum.time()); - } else { - toMin = scan.value.daysTo(minimum); - toMax = scan.value.daysTo(maximum); - } - const int maxChange = sn.maxChange(); - if (toMin > maxChange) { - QDTPDEBUG << "invalid because toMin > maxChange" << toMin - << maxChange << t << scan.value << minimum; - scan.state = Invalid; - done = true; - break; - } else if (toMax > maxChange) { - toMax = -1; // can't get to max - } - - const int min = getDigit(minimum, i); - if (min == -1) { - qWarning("QDateTimeParser::parse Internal error 4 (%ls)", - qUtf16Printable(sn.name())); - scan.state = Invalid; - done = true; - break; - } - - int max = toMax != -1 ? getDigit(maximum, i) : absoluteMax(i, scan.value); - int pos = position + scan.padded - sn.pos; - if (pos < 0 || pos >= t.size()) - pos = -1; - if (!potentialValue(t.simplified(), min, max, i, scan.value, pos)) { - QDTPDEBUG << "invalid because potentialValue(" << t.simplified() << min << max - << sn.name() << "returned" << toMax << toMin << pos; - scan.state = Invalid; - done = true; - break; - } - scan.state = Intermediate; - done = true; - break; } - } - } - } - } else { - if (context == FromString) { - // optimization - Q_ASSERT(maximum.date().toJulianDay() == 5373484); - if (scan.value.date().toJulianDay() > 5373484) - scan.state = Invalid; - } else { - if (scan.value > maximum) - scan.state = Invalid; - } - - QDTPDEBUG << "not checking intermediate because scanned value is" << scan.value << minimum << maximum; - } - } - text = scan.input = input; - // Set spec *after* all checking, so validity is a property of the string: - scan.value = scan.value.toTimeSpec(spec); - return scan; -} - -/* - \internal - \brief Returns the index in \a entries with the best prefix match to \a text - - Scans \a entries looking for an entry overlapping \a text as much as possible - (an exact match beats any prefix match; a match of the full entry as prefix of - text beats any entry but one matching a longer prefix; otherwise, the match of - longest prefix wins, earlier entries beating later on a draw). Records the - 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 QVector<QString> &entries, QString *usedText, int *used) -{ - if (text.isEmpty()) - return -1; - - int bestMatch = -1; - int bestCount = 0; - for (int n = 0; n < entries.size(); ++n) - { - const QString &name = entries.at(n); - - const int limit = qMin(text.size(), name.size()); - int i = 0; - while (i < limit && text.at(i) == name.at(i).toLower()) - ++i; - // Full match beats an equal prefix match: - if (i > bestCount || (i == bestCount && i == name.size())) { - bestCount = i; - bestMatch = n; - if (i == name.size() && i == text.size()) - break; // Exact match, name == text, wins. - } - } - if (usedText && bestMatch != -1) - *usedText = entries.at(bestMatch); - if (used) - *used = bestCount; - - return bestMatch; -} - -/*! - \internal - finds the first possible monthname that \a str1 can - match. Starting from \a index; str should already by lowered -*/ - -int QDateTimeParser::findMonth(const QString &str1, int startMonth, int sectionIndex, - QString *usedMonth, int *used) const -{ - const SectionNode &sn = sectionNode(sectionIndex); - if (sn.type != MonthSection) { - qWarning("QDateTimeParser::findMonth Internal error"); - return -1; - } - - QLocale::FormatType type = sn.count == 3 ? QLocale::ShortFormat : QLocale::LongFormat; - QLocale l = locale(); - QVector<QString> monthNames; - monthNames.reserve(13 - startMonth); - for (int month = startMonth; month <= 12; ++month) - monthNames.append(l.monthName(month, type)); - - const int index = findTextEntry(str1, monthNames, usedMonth, used); - return index < 0 ? index : index + startMonth; -} - -int QDateTimeParser::findDay(const QString &str1, int startDay, int sectionIndex, QString *usedDay, int *used) const -{ - const SectionNode &sn = sectionNode(sectionIndex); - if (!(sn.type & DaySectionMask)) { - qWarning("QDateTimeParser::findDay Internal error"); - return -1; - } - - QLocale::FormatType type = sn.count == 4 ? QLocale::LongFormat : QLocale::ShortFormat; - QLocale l = locale(); - QVector<QString> daysOfWeek; - daysOfWeek.reserve(8 - startDay); - for (int day = startDay; day <= 7; ++day) - daysOfWeek.append(l.dayName(day, type)); - - const int index = findTextEntry(str1, daysOfWeek, usedDay, used); - return index < 0 ? index : index + startDay; -} - -/*! - \internal - - Return's .value is zone's offset, zone time - UTC time, in seconds. - See QTimeZonePrivate::isValidId() for the format of zone names. - */ -QDateTimeParser::ParsedSection -QDateTimeParser::findTimeZone(QStringRef str, const QDateTime &when, - int maxVal, int minVal) const -{ -#if QT_CONFIG(timezone) - int index = startsWithLocalTimeZone(str); - int offset; - - if (index > 0) { - // We won't actually use this, but we need a valid return: - offset = QDateTime(when.date(), when.time(), Qt::LocalTime).offsetFromUtc(); - } else { - int size = str.length(); - offset = std::numeric_limits<int>::max(); // deliberately out of range - Q_ASSERT(offset > QTimeZone::MaxUtcOffsetSecs); // cf. absoluteMax() - - // Collect up plausibly-valid characters; let QTimeZone work out what's truly valid. - while (index < size) { - QChar here = str[index]; - if (here < 127 - && (here.isLetterOrNumber() - || here == '/' || here == '-' - || here == '_' || here == '.' - || here == '+' || here == ':')) - index++; - else - break; - } - - while (index > 0) { - str.truncate(index); - if (str == QLatin1String("Z")) { - offset = 0; // "Zulu" time - a.k.a. UTC - break; - } - QTimeZone zone(str.toLatin1()); - if (zone.isValid()) { - offset = zone.offsetFromUtc(when); - break; - } - index--; // maybe we collected too much ... - } - } - - if (index > 0 && maxVal >= offset && offset >= minVal) - return ParsedSection(Acceptable, offset, index); - -#endif // timezone - return ParsedSection(); -} - -/*! - \internal - - Returns - AM if str == tr("AM") - PM if str == tr("PM") - PossibleAM if str can become tr("AM") - PossiblePM if str can become tr("PM") - PossibleBoth if str can become tr("PM") and can become tr("AM") - Neither if str can't become anything sensible -*/ -QDateTimeParser::AmPmFinder QDateTimeParser::findAmPm(QString &str, int sectionIndex, int *used) const -{ - const SectionNode &s = sectionNode(sectionIndex); - if (s.type != AmPmSection) { - qWarning("QDateTimeParser::findAmPm Internal error"); - return Neither; - } - if (used) - *used = str.size(); - if (QStringRef(&str).trimmed().isEmpty()) { - return PossibleBoth; - } - const QLatin1Char space(' '); - int size = sectionMaxSize(sectionIndex); - - enum { - amindex = 0, - pmindex = 1 - }; - QString ampm[2]; - ampm[amindex] = getAmPmText(AmText, s.count == 1 ? UpperCase : LowerCase); - ampm[pmindex] = getAmPmText(PmText, s.count == 1 ? UpperCase : LowerCase); - for (int i=0; i<2; ++i) - ampm[i].truncate(size); - - QDTPDEBUG << "findAmPm" << str << ampm[0] << ampm[1]; - - if (str.indexOf(ampm[amindex], 0, Qt::CaseInsensitive) == 0) { - str = ampm[amindex]; - return AM; - } else if (str.indexOf(ampm[pmindex], 0, Qt::CaseInsensitive) == 0) { - str = ampm[pmindex]; - return PM; - } else if (context == FromString || (str.count(space) == 0 && str.size() >= size)) { - return Neither; - } - size = qMin(size, str.size()); - - bool broken[2] = {false, false}; - for (int i=0; i<size; ++i) { - if (str.at(i) != space) { - for (int j=0; j<2; ++j) { - if (!broken[j]) { - int index = ampm[j].indexOf(str.at(i)); - QDTPDEBUG << "looking for" << str.at(i) - << "in" << ampm[j] << "and got" << index; - if (index == -1) { - if (str.at(i).category() == QChar::Letter_Uppercase) { - index = ampm[j].indexOf(str.at(i).toLower()); - QDTPDEBUG << "trying with" << str.at(i).toLower() - << "in" << ampm[j] << "and got" << index; - } else if (str.at(i).category() == QChar::Letter_Lowercase) { - index = ampm[j].indexOf(str.at(i).toUpper()); - QDTPDEBUG << "trying with" << str.at(i).toUpper() - << "in" << ampm[j] << "and got" << index; - } - if (index == -1) { - broken[j] = true; - if (broken[amindex] && broken[pmindex]) { - QDTPDEBUG << str << "didn't make it"; - return Neither; - } - continue; - } else { - str[i] = ampm[j].at(index); // fix case - } - } - ampm[j].remove(index, 1); - } - } - } - } - if (!broken[pmindex] && !broken[amindex]) - return PossibleBoth; - return (!broken[amindex] ? PossibleAM : PossiblePM); -} -#endif // datestring - -/*! - \internal - Max number of units that can be changed by this section. -*/ - -int QDateTimeParser::SectionNode::maxChange() const -{ - switch (type) { - // Time. unit is msec - case MSecSection: return 999; - case SecondSection: return 59 * 1000; - case MinuteSection: return 59 * 60 * 1000; - case Hour24Section: case Hour12Section: return 59 * 60 * 60 * 1000; - - // Date. unit is day - case DayOfWeekSectionShort: - case DayOfWeekSectionLong: return 7; - case DaySection: return 30; - case MonthSection: return 365 - 31; - case YearSection: return 9999 * 365; - case YearSection2Digits: return 100 * 365; - default: - qWarning("QDateTimeParser::maxChange() Internal error (%ls)", - qUtf16Printable(name())); - } - - return -1; -} - -QDateTimeParser::FieldInfo QDateTimeParser::fieldInfo(int index) const -{ - FieldInfo ret = 0; - const SectionNode &sn = sectionNode(index); - switch (sn.type) { - case MSecSection: - ret |= Fraction; - Q_FALLTHROUGH(); - case SecondSection: - case MinuteSection: - case Hour24Section: - case Hour12Section: - case YearSection2Digits: - ret |= AllowPartial; - Q_FALLTHROUGH(); - case YearSection: - ret |= Numeric; - if (sn.count != 1) - ret |= FixedWidth; - break; - case MonthSection: - case DaySection: - switch (sn.count) { - case 2: - ret |= FixedWidth; - Q_FALLTHROUGH(); - case 1: - ret |= (Numeric|AllowPartial); - break; - } - break; - case DayOfWeekSectionShort: - case DayOfWeekSectionLong: - if (sn.count == 3) - ret |= FixedWidth; - break; - case AmPmSection: - ret |= FixedWidth; - break; - case TimeZoneSection: - break; - default: - qWarning("QDateTimeParser::fieldInfo Internal error 2 (%d %ls %d)", - index, qUtf16Printable(sn.name()), sn.count); - break; - } - return ret; -} - -QString QDateTimeParser::SectionNode::format() const -{ - QChar fillChar; - switch (type) { - case AmPmSection: return count == 1 ? QLatin1String("AP") : QLatin1String("ap"); - case MSecSection: fillChar = QLatin1Char('z'); break; - case SecondSection: fillChar = QLatin1Char('s'); break; - case MinuteSection: fillChar = QLatin1Char('m'); break; - case Hour24Section: fillChar = QLatin1Char('H'); break; - case Hour12Section: fillChar = QLatin1Char('h'); break; - case DayOfWeekSectionShort: - case DayOfWeekSectionLong: - case DaySection: fillChar = QLatin1Char('d'); break; - case MonthSection: fillChar = QLatin1Char('M'); break; - case YearSection2Digits: - case YearSection: fillChar = QLatin1Char('y'); break; - default: - qWarning("QDateTimeParser::sectionFormat Internal error (%ls)", - qUtf16Printable(name(type))); - return QString(); - } - if (fillChar.isNull()) { - qWarning("QDateTimeParser::sectionFormat Internal error 2"); - return QString(); - } - return QString(count, fillChar); -} - - -/*! - \internal - - Returns \c true if str can be modified to represent a - number that is within min and max. -*/ - -bool QDateTimeParser::potentialValue(const QStringRef &str, int min, int max, int index, - const QDateTime ¤tValue, int insert) const -{ - if (str.isEmpty()) { - return true; - } - const int size = sectionMaxSize(index); - int val = (int)locale().toUInt(str); - const SectionNode &sn = sectionNode(index); - if (sn.type == YearSection2Digits) { - const int year = currentValue.date().year(); - val += year - (year % 100); - } - if (val >= min && val <= max && str.size() == size) { - return true; - } else if (val > max) { - return false; - } else if (str.size() == size && val < min) { - return false; - } - - const int len = size - str.size(); - for (int i=0; i<len; ++i) { - for (int j=0; j<10; ++j) { - if (potentialValue(str + QLatin1Char('0' + j), min, max, index, currentValue, insert)) { - return true; - } else if (insert >= 0) { - const QString tmp = str.left(insert) + QLatin1Char('0' + j) + str.mid(insert); - if (potentialValue(tmp, min, max, index, currentValue, insert)) - return true; - } - } - } - - return false; -} - -/*! - \internal -*/ -bool QDateTimeParser::skipToNextSection(int index, const QDateTime ¤t, const QStringRef &text) const -{ - Q_ASSERT(text.size() < sectionMaxSize(index)); - const SectionNode &node = sectionNode(index); - int min = absoluteMin(index); - int max = absoluteMax(index, current); - // Time-zone field is only numeric if given as offset from UTC: - if (node.type != TimeZoneSection || current.timeSpec() == Qt::OffsetFromUTC) { - const QDateTime maximum = getMaximum(); - const QDateTime minimum = getMinimum(); - Q_ASSERT(current >= minimum && current <= maximum); - - QDateTime tmp = current; - if (!setDigit(tmp, index, min) || tmp < minimum) - min = getDigit(minimum, index); - - if (!setDigit(tmp, index, max) || tmp > maximum) - max = getDigit(maximum, index); - } - int pos = cursorPosition() - node.pos; - if (pos < 0 || pos >= text.size()) - pos = -1; - - /* - If the value potentially can become another valid entry we don't want to - skip to the next. E.g. In a M field (month without leading 0) if you type - 1 we don't want to autoskip (there might be [012] following) but if you - type 3 we do. - */ - return !potentialValue(text, min, max, index, current, pos); -} - -/*! - \internal - For debugging. Returns the name of the section \a s. -*/ - -QString QDateTimeParser::SectionNode::name(QDateTimeParser::Section s) -{ - switch (s) { - case QDateTimeParser::AmPmSection: return QLatin1String("AmPmSection"); - case QDateTimeParser::DaySection: return QLatin1String("DaySection"); - case QDateTimeParser::DayOfWeekSectionShort: return QLatin1String("DayOfWeekSectionShort"); - case QDateTimeParser::DayOfWeekSectionLong: return QLatin1String("DayOfWeekSectionLong"); - case QDateTimeParser::Hour24Section: return QLatin1String("Hour24Section"); - case QDateTimeParser::Hour12Section: return QLatin1String("Hour12Section"); - case QDateTimeParser::MSecSection: return QLatin1String("MSecSection"); - case QDateTimeParser::MinuteSection: return QLatin1String("MinuteSection"); - case QDateTimeParser::MonthSection: return QLatin1String("MonthSection"); - case QDateTimeParser::SecondSection: return QLatin1String("SecondSection"); - case QDateTimeParser::TimeZoneSection: return QLatin1String("TimeZoneSection"); - case QDateTimeParser::YearSection: return QLatin1String("YearSection"); - case QDateTimeParser::YearSection2Digits: return QLatin1String("YearSection2Digits"); - case QDateTimeParser::NoSection: return QLatin1String("NoSection"); - case QDateTimeParser::FirstSection: return QLatin1String("FirstSection"); - case QDateTimeParser::LastSection: return QLatin1String("LastSection"); - default: return QLatin1String("Unknown section ") + QString::number(int(s)); - } -} - -/*! - \internal - For debugging. Returns the name of the state \a s. -*/ - -QString QDateTimeParser::stateName(State s) const -{ - switch (s) { - case Invalid: return QLatin1String("Invalid"); - case Intermediate: return QLatin1String("Intermediate"); - case Acceptable: return QLatin1String("Acceptable"); - default: return QLatin1String("Unknown state ") + QString::number(s); - } -} - -#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) { - return false; - } - if (time) { - const QTime t = tmp.value.time(); - if (!t.isValid()) { - return false; - } - *time = t; - } - - if (date) { - const QDate d = tmp.value.date(); - if (!d.isValid()) { - return false; - } - *date = d; - } - 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)); -} - -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)); -} - -QString QDateTimeParser::getAmPmText(AmPm ap, Case cs) const -{ - const QLocale loc = locale(); - QString raw = ap == AmText ? loc.amText() : loc.pmText(); - return cs == UpperCase ? raw.toUpper() : raw.toLower(); -} - -/* - \internal - - I give arg2 preference because arg1 is always a QDateTime. -*/ - -bool operator==(const QDateTimeParser::SectionNode &s1, const QDateTimeParser::SectionNode &s2) -{ - return (s1.type == s2.type) && (s1.pos == s2.pos) && (s1.count == s2.count); -} - -QT_END_NAMESPACE |