File indexing completed on 2024-05-12 16:14:34

0001 /****************************************************************************
0002 **
0003 ** SPDX-FileCopyrightText: 2016 The Qt Company Ltd.
0004 ** Contact: https://www.qt.io/licensing/
0005 **
0006 ** This file is part of the QtCore module of the Qt Toolkit.
0007 **
0008 ** SPDX-License-Identifier: LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KFQF-Accepted-GPL OR LicenseRef-Qt-Commercial
0009 **
0010 ****************************************************************************/
0011 
0012 #include "qplatformdefs.h"
0013 #include "qdatetimeparser_p.h"
0014 
0015 #include <QBasicMutex>
0016 #include <QDataStream>
0017 #include <QDateTime>
0018 #include <QDebug>
0019 #include <QSet>
0020 #include <QTimeZone>
0021 
0022 #include <mutex>
0023 
0024 //#define QDATETIMEPARSER_DEBUG
0025 #if defined (QDATETIMEPARSER_DEBUG) && !defined(QT_NO_DEBUG_STREAM)
0026 #  define QDTPDEBUG qDebug()
0027 #  define QDTPDEBUGN qDebug
0028 #else
0029 #  define QDTPDEBUG if (false) qDebug()
0030 #  define QDTPDEBUGN if (false) qDebug
0031 #endif
0032 
0033 static QBasicMutex environmentMutex;
0034 
0035 QT_BEGIN_NAMESPACE
0036 
0037 QDateTimeParser::~QDateTimeParser()
0038 {
0039 }
0040 
0041 /*!
0042   \internal
0043   Gets the digit from a datetime. E.g.
0044 
0045   QDateTime var(QDate(2004, 02, 02));
0046   int digit = getDigit(var, Year);
0047   // digit = 2004
0048 */
0049 
0050 int QDateTimeParser::getDigit(const QDateTime &t, int index) const
0051 {
0052     if (index < 0 || index >= sectionNodes.size()) {
0053 #if QT_CONFIG(datestring)
0054         qWarning("QDateTimeParser::getDigit() Internal error (%s %d)",
0055                  qPrintable(t.toString()), index);
0056 #else
0057         qWarning("QDateTimeParser::getDigit() Internal error (%d)", index);
0058 #endif
0059         return -1;
0060     }
0061     const SectionNode &node = sectionNodes.at(index);
0062     switch (node.type) {
0063     case TimeZoneSection: return t.offsetFromUtc();
0064     case Hour24Section: case Hour12Section: return t.time().hour();
0065     case MinuteSection: return t.time().minute();
0066     case SecondSection: return t.time().second();
0067     case MSecSection: return t.time().msec();
0068     case YearSection2Digits:
0069     case YearSection: return t.date().year();
0070     case MonthSection: return t.date().month();
0071     case DaySection: return t.date().day();
0072     case DayOfWeekSectionShort:
0073     case DayOfWeekSectionLong: return t.date().day();
0074     case AmPmSection: return t.time().hour() > 11 ? 1 : 0;
0075 
0076     default: break;
0077     }
0078 
0079 #if QT_CONFIG(datestring)
0080     qWarning("QDateTimeParser::getDigit() Internal error 2 (%s %d)",
0081              qPrintable(t.toString()), index);
0082 #else
0083     qWarning("QDateTimeParser::getDigit() Internal error 2 (%d)", index);
0084 #endif
0085     return -1;
0086 }
0087 
0088 /*!
0089   \internal
0090   Sets a digit in a datetime. E.g.
0091 
0092   QDateTime var(QDate(2004, 02, 02));
0093   int digit = getDigit(var, Year);
0094   // digit = 2004
0095   setDigit(&var, Year, 2005);
0096   digit = getDigit(var, Year);
0097   // digit = 2005
0098 */
0099 
0100 bool QDateTimeParser::setDigit(QDateTime &v, int index, int newVal) const
0101 {
0102     if (index < 0 || index >= sectionNodes.size()) {
0103 #if QT_CONFIG(datestring)
0104         qWarning("QDateTimeParser::setDigit() Internal error (%s %d %d)",
0105                  qPrintable(v.toString()), index, newVal);
0106 #else
0107         qWarning("QDateTimeParser::setDigit() Internal error (%d %d)", index, newVal);
0108 #endif
0109         return false;
0110     }
0111     const SectionNode &node = sectionNodes.at(index);
0112 
0113     const QDate date = v.date();
0114     const QTime time = v.time();
0115     int year = date.year();
0116     int month = date.month();
0117     int day = date.day();
0118     int hour = time.hour();
0119     int minute = time.minute();
0120     int second = time.second();
0121     int msec = time.msec();
0122     Qt::TimeSpec tspec = v.timeSpec();
0123     // Only offset from UTC is amenable to setting an int value:
0124     int offset = tspec == Qt::OffsetFromUTC ? v.offsetFromUtc() : 0;
0125 
0126     switch (node.type) {
0127     case Hour24Section: case Hour12Section: hour = newVal; break;
0128     case MinuteSection: minute = newVal; break;
0129     case SecondSection: second = newVal; break;
0130     case MSecSection: msec = newVal; break;
0131     case YearSection2Digits:
0132     case YearSection: year = newVal; break;
0133     case MonthSection: month = newVal; break;
0134     case DaySection:
0135     case DayOfWeekSectionShort:
0136     case DayOfWeekSectionLong:
0137         if (newVal > 31) {
0138             // have to keep legacy behavior. setting the
0139             // date to 32 should return false. Setting it
0140             // to 31 for february should return true
0141             return false;
0142         }
0143         day = newVal;
0144         break;
0145     case TimeZoneSection:
0146         if (newVal < absoluteMin(index) || newVal > absoluteMax(index))
0147             return false;
0148         tspec = Qt::OffsetFromUTC;
0149         offset = newVal;
0150         break;
0151     case AmPmSection: hour = (newVal == 0 ? hour % 12 : (hour % 12) + 12); break;
0152     default:
0153         qWarning("QDateTimeParser::setDigit() Internal error (%s)",
0154                  qPrintable(node.name()));
0155         break;
0156     }
0157 
0158     if (!(node.type & DaySectionMask)) {
0159         if (day < cachedDay)
0160             day = cachedDay;
0161         const int max = QDate(year, month, 1).daysInMonth();
0162         if (day > max) {
0163             day = max;
0164         }
0165     }
0166 
0167     const QDate newDate(year, month, day);
0168     const QTime newTime(hour, minute, second, msec);
0169     if (!newDate.isValid() || !newTime.isValid())
0170         return false;
0171 
0172     // Preserve zone:
0173     v =
0174 #if QT_CONFIG(timezone)
0175          tspec == Qt::TimeZone ? QDateTime(newDate, newTime, v.timeZone()) :
0176 #endif
0177          QDateTime(newDate, newTime, tspec, offset);
0178     return true;
0179 }
0180 
0181 
0182 
0183 /*!
0184   \internal
0185 
0186   Returns the absolute maximum for a section
0187 */
0188 
0189 int QDateTimeParser::absoluteMax(int s, const QDateTime &cur) const
0190 {
0191     const SectionNode &sn = sectionNode(s);
0192     switch (sn.type) {
0193 #if QT_CONFIG(timezone)
0194     case TimeZoneSection: return QTimeZone::MaxUtcOffsetSecs;
0195 #endif
0196     case Hour24Section:
0197     case Hour12Section: return 23; // this is special-cased in
0198                                    // parseSection. We want it to be
0199                                    // 23 for the stepBy case.
0200     case MinuteSection:
0201     case SecondSection: return 59;
0202     case MSecSection: return 999;
0203     case YearSection2Digits:
0204     case YearSection: return 9999; // sectionMaxSize will prevent
0205                                    // people from typing in a larger
0206                                    // number in count == 2 sections.
0207                                    // stepBy() will work on real years anyway
0208     case MonthSection: return 12;
0209     case DaySection:
0210     case DayOfWeekSectionShort:
0211     case DayOfWeekSectionLong: return cur.isValid() ? cur.date().daysInMonth() : 31;
0212     case AmPmSection: return 1;
0213     default: break;
0214     }
0215     qWarning("QDateTimeParser::absoluteMax() Internal error (%s)",
0216              qPrintable(sn.name()));
0217     return -1;
0218 }
0219 
0220 /*!
0221   \internal
0222 
0223   Returns the absolute minimum for a section
0224 */
0225 
0226 int QDateTimeParser::absoluteMin(int s) const
0227 {
0228     const SectionNode &sn = sectionNode(s);
0229     switch (sn.type) {
0230 #if QT_CONFIG(timezone)
0231     case TimeZoneSection: return QTimeZone::MinUtcOffsetSecs;
0232 #endif
0233     case Hour24Section:
0234     case Hour12Section:
0235     case MinuteSection:
0236     case SecondSection:
0237     case MSecSection:
0238     case YearSection2Digits:
0239     case YearSection: return 0;
0240     case MonthSection:
0241     case DaySection:
0242     case DayOfWeekSectionShort:
0243     case DayOfWeekSectionLong: return 1;
0244     case AmPmSection: return 0;
0245     default: break;
0246     }
0247     qWarning("QDateTimeParser::absoluteMin() Internal error (%s, %0x)",
0248              qPrintable(sn.name()), sn.type);
0249     return -1;
0250 }
0251 
0252 /*!
0253   \internal
0254 
0255   Returns the sectionNode for the Section \a s.
0256 */
0257 
0258 const QDateTimeParser::SectionNode &QDateTimeParser::sectionNode(int sectionIndex) const
0259 {
0260     if (sectionIndex < 0) {
0261         switch (sectionIndex) {
0262         case FirstSectionIndex:
0263             return first;
0264         case LastSectionIndex:
0265             return last;
0266         case NoSectionIndex:
0267             return none;
0268         }
0269     } else if (sectionIndex < sectionNodes.size()) {
0270         return sectionNodes.at(sectionIndex);
0271     }
0272 
0273     qWarning("QDateTimeParser::sectionNode() Internal error (%d)",
0274              sectionIndex);
0275     return none;
0276 }
0277 
0278 QDateTimeParser::Section QDateTimeParser::sectionType(int sectionIndex) const
0279 {
0280     return sectionNode(sectionIndex).type;
0281 }
0282 
0283 
0284 /*!
0285   \internal
0286 
0287   Returns the starting position for section \a s.
0288 */
0289 
0290 int QDateTimeParser::sectionPos(int sectionIndex) const
0291 {
0292     return sectionPos(sectionNode(sectionIndex));
0293 }
0294 
0295 int QDateTimeParser::sectionPos(const SectionNode &sn) const
0296 {
0297     switch (sn.type) {
0298     case FirstSection: return 0;
0299     case LastSection: return displayText().size() - 1;
0300     default: break;
0301     }
0302     if (sn.pos == -1) {
0303         qWarning("QDateTimeParser::sectionPos Internal error (%s)", qPrintable(sn.name()));
0304         return -1;
0305     }
0306     return sn.pos;
0307 }
0308 
0309 
0310 /*!
0311   \internal
0312 
0313   helper function for parseFormat. removes quotes that are
0314   not escaped and removes the escaping on those that are escaped
0315 
0316 */
0317 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0318 static QString unquote(const QStringRef &str)
0319 #else
0320 static QString unquote(const QStringView &str)
0321 #endif
0322 {
0323     const QChar quote(QLatin1Char('\''));
0324     const QChar slash(QLatin1Char('\\'));
0325     const QChar zero(QLatin1Char('0'));
0326     QString ret;
0327     QChar status(zero);
0328     const int max = str.size();
0329     for (int i=0; i<max; ++i) {
0330         if (str.at(i) == quote) {
0331             if (status != quote) {
0332                 status = quote;
0333             } else if (!ret.isEmpty() && str.at(i - 1) == slash) {
0334                 ret[ret.size() - 1] = quote;
0335             } else {
0336                 status = zero;
0337             }
0338         } else {
0339             ret += str.at(i);
0340         }
0341     }
0342     return ret;
0343 }
0344 /*!
0345   \internal
0346 
0347   Parses the format \a newFormat. If successful, returns \c true and
0348   sets up the format. Else keeps the old format and returns \c false.
0349 
0350 */
0351 
0352 static inline int countRepeat(const QString &str, int index, int maxCount)
0353 {
0354     int count = 1;
0355     const QChar ch(str.at(index));
0356     const int max = qMin(index + maxCount, str.size());
0357     while (index + count < max && str.at(index + count) == ch) {
0358         ++count;
0359     }
0360     return count;
0361 }
0362 
0363 static inline void appendSeparator(QStringList *list, const QString &string, int from, int size, int lastQuote)
0364 {
0365 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0366     const QStringRef separator = string.midRef(from, size);
0367 #else
0368     const auto separator = QStringView(string).mid(from, size);
0369 #endif
0370     list->append(lastQuote >= from ? unquote(separator) : separator.toString());
0371 }
0372 
0373 
0374 bool QDateTimeParser::parseFormat(const QString &newFormat)
0375 {
0376     const QLatin1Char quote('\'');
0377     const QLatin1Char slash('\\');
0378     const QLatin1Char zero('0');
0379     if (newFormat == displayFormat && !newFormat.isEmpty()) {
0380         return true;
0381     }
0382 
0383     QDTPDEBUGN("parseFormat: %s", newFormat.toLatin1().constData());
0384 
0385     QVector<SectionNode> newSectionNodes;
0386     Sections newDisplay = NoSection;
0387     QStringList newSeparators;
0388     int index = 0;
0389     int add = 0;
0390     QChar status(zero);
0391     const int max = newFormat.size();
0392     int lastQuote = -1;
0393     for (int i = 0; i<max; ++i) {
0394         if (newFormat.at(i) == quote) {
0395             lastQuote = i;
0396             ++add;
0397             if (status != quote) {
0398                 status = quote;
0399             } else if (i > 0 && newFormat.at(i - 1) != slash) {
0400                 status = zero;
0401             }
0402         } else if (status != quote) {
0403             const char sect = newFormat.at(i).toLatin1();
0404             switch (sect) {
0405             case 'H':
0406             case 'h':
0407                 if (parserType != QMetaType::QDate) {
0408                     const Section hour = (sect == 'h') ? Hour12Section : Hour24Section;
0409                     const SectionNode sn = { hour, i - add, countRepeat(newFormat, i, 2), 0 };
0410                     newSectionNodes.append(sn);
0411                     appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote);
0412                     i += sn.count - 1;
0413                     index = i + 1;
0414                     newDisplay |= hour;
0415                 }
0416                 break;
0417             case 'm':
0418                 if (parserType != QMetaType::QDate) {
0419                     const SectionNode sn = { MinuteSection, i - add, countRepeat(newFormat, i, 2), 0 };
0420                     newSectionNodes.append(sn);
0421                     appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote);
0422                     i += sn.count - 1;
0423                     index = i + 1;
0424                     newDisplay |= MinuteSection;
0425                 }
0426                 break;
0427             case 's':
0428                 if (parserType != QMetaType::QDate) {
0429                     const SectionNode sn = { SecondSection, i - add, countRepeat(newFormat, i, 2), 0 };
0430                     newSectionNodes.append(sn);
0431                     appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote);
0432                     i += sn.count - 1;
0433                     index = i + 1;
0434                     newDisplay |= SecondSection;
0435                 }
0436                 break;
0437 
0438             case 'z':
0439                 if (parserType != QMetaType::QDate) {
0440                     const SectionNode sn = { MSecSection, i - add, countRepeat(newFormat, i, 3) < 3 ? 1 : 3, 0 };
0441                     newSectionNodes.append(sn);
0442                     appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote);
0443                     i += sn.count - 1;
0444                     index = i + 1;
0445                     newDisplay |= MSecSection;
0446                 }
0447                 break;
0448             case 'A':
0449             case 'a':
0450                 if (parserType != QMetaType::QDate) {
0451                     const bool cap = (sect == 'A');
0452                     const SectionNode sn = { AmPmSection, i - add, (cap ? 1 : 0), 0 };
0453                     newSectionNodes.append(sn);
0454                     appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote);
0455                     newDisplay |= AmPmSection;
0456                     if (i + 1 < newFormat.size()
0457                         && newFormat.at(i+1) == (cap ? QLatin1Char('P') : QLatin1Char('p'))) {
0458                         ++i;
0459                     }
0460                     index = i + 1;
0461                 }
0462                 break;
0463             case 'y':
0464                 if (parserType != QMetaType::QTime) {
0465                     const int repeat = countRepeat(newFormat, i, 4);
0466                     if (repeat >= 2) {
0467                         const SectionNode sn = { repeat == 4 ? YearSection : YearSection2Digits,
0468                                                  i - add, repeat == 4 ? 4 : 2, 0 };
0469                         newSectionNodes.append(sn);
0470                         appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote);
0471                         i += sn.count - 1;
0472                         index = i + 1;
0473                         newDisplay |= sn.type;
0474                     }
0475                 }
0476                 break;
0477             case 'M':
0478                 if (parserType != QMetaType::QTime) {
0479                     const SectionNode sn = { MonthSection, i - add, countRepeat(newFormat, i, 4), 0 };
0480                     newSectionNodes.append(sn);
0481 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0482                     newSeparators.append(unquote(newFormat.midRef(index, i - index)));
0483 #else
0484                     newSeparators.append(unquote(QStringView(newFormat).mid(index, i - index)));
0485 #endif
0486                     i += sn.count - 1;
0487                     index = i + 1;
0488                     newDisplay |= MonthSection;
0489                 }
0490                 break;
0491             case 'd':
0492                 if (parserType != QMetaType::QTime) {
0493                     const int repeat = countRepeat(newFormat, i, 4);
0494                     const Section sectionType = (repeat == 4 ? DayOfWeekSectionLong
0495                         : (repeat == 3 ? DayOfWeekSectionShort : DaySection));
0496                     const SectionNode sn = { sectionType, i - add, repeat, 0 };
0497                     newSectionNodes.append(sn);
0498                     appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote);
0499                     i += sn.count - 1;
0500                     index = i + 1;
0501                     newDisplay |= sn.type;
0502                 }
0503                 break;
0504             case 't':
0505                 if (parserType != QMetaType::QTime) {
0506                     const SectionNode sn = { TimeZoneSection, i - add, countRepeat(newFormat, i, 4), 0 };
0507                     newSectionNodes.append(sn);
0508                     appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote);
0509                     i += sn.count - 1;
0510                     index = i + 1;
0511                     newDisplay |= TimeZoneSection;
0512                 }
0513                 break;
0514             default:
0515                 break;
0516             }
0517         }
0518     }
0519     if (newSectionNodes.isEmpty() && context == DateTimeEdit) {
0520         return false;
0521     }
0522 
0523     if ((newDisplay & (AmPmSection|Hour12Section)) == Hour12Section) {
0524         const int count = newSectionNodes.size();
0525         for (int i = 0; i < count; ++i) {
0526             SectionNode &node = newSectionNodes[i];
0527             if (node.type == Hour12Section)
0528                 node.type = Hour24Section;
0529         }
0530     }
0531 
0532     if (index < max) {
0533         appendSeparator(&newSeparators, newFormat, index, index - max, lastQuote);
0534     } else {
0535         newSeparators.append(QString());
0536     }
0537 
0538     displayFormat = newFormat;
0539     separators = newSeparators;
0540     sectionNodes = newSectionNodes;
0541     display = newDisplay;
0542     last.pos = -1;
0543 
0544 //     for (int i=0; i<sectionNodes.size(); ++i) {
0545 //         QDTPDEBUG << sectionNodes.at(i).name() << sectionNodes.at(i).count;
0546 //     }
0547 
0548     QDTPDEBUG << newFormat << displayFormat;
0549     QDTPDEBUGN("separators:\n'%s'", separators.join(QLatin1String("\n")).toLatin1().constData());
0550 
0551     return true;
0552 }
0553 
0554 /*!
0555   \internal
0556 
0557   Returns the size of section \a s.
0558 */
0559 
0560 int QDateTimeParser::sectionSize(int sectionIndex) const
0561 {
0562     if (sectionIndex < 0)
0563         return 0;
0564 
0565     if (sectionIndex >= sectionNodes.size()) {
0566         qWarning("QDateTimeParser::sectionSize Internal error (%d)", sectionIndex);
0567         return -1;
0568     }
0569 
0570     if (sectionIndex == sectionNodes.size() - 1) {
0571         // In some cases there is a difference between displayText() and text.
0572         // e.g. when text is 2000/01/31 and displayText() is "2000/2/31" - text
0573         // is the previous value and displayText() is the new value.
0574         // The size difference is always due to leading zeroes.
0575         int sizeAdjustment = 0;
0576         const int displayTextSize = displayText().size();
0577         if (displayTextSize != text.size()) {
0578             // Any zeroes added before this section will affect our size.
0579             int preceedingZeroesAdded = 0;
0580             if (sectionNodes.size() > 1 && context == DateTimeEdit) {
0581                 const auto begin = sectionNodes.cbegin();
0582                 const auto end = begin + sectionIndex;
0583                 for (auto sectionIt = begin; sectionIt != end; ++sectionIt)
0584                     preceedingZeroesAdded += sectionIt->zeroesAdded;
0585             }
0586             sizeAdjustment = preceedingZeroesAdded;
0587         }
0588 
0589         return displayTextSize + sizeAdjustment - sectionPos(sectionIndex) - separators.last().size();
0590     } else {
0591         return sectionPos(sectionIndex + 1) - sectionPos(sectionIndex)
0592             - separators.at(sectionIndex + 1).size();
0593     }
0594 }
0595 
0596 
0597 int QDateTimeParser::sectionMaxSize(Section s, int count) const
0598 {
0599 #if QT_CONFIG(textdate)
0600     int mcount = 12;
0601 #endif
0602 
0603     switch (s) {
0604     case FirstSection:
0605     case NoSection:
0606     case LastSection: return 0;
0607 
0608     case AmPmSection: {
0609         const int lowerMax = qMin(getAmPmText(AmText, LowerCase).size(),
0610                                   getAmPmText(PmText, LowerCase).size());
0611         const int upperMax = qMin(getAmPmText(AmText, UpperCase).size(),
0612                                   getAmPmText(PmText, UpperCase).size());
0613         return qMin(4, qMin(lowerMax, upperMax));
0614     }
0615 
0616     case Hour24Section:
0617     case Hour12Section:
0618     case MinuteSection:
0619     case SecondSection:
0620     case DaySection: return 2;
0621     case DayOfWeekSectionShort:
0622     case DayOfWeekSectionLong:
0623 #if !QT_CONFIG(textdate)
0624         return 2;
0625 #else
0626         mcount = 7;
0627         Q_FALLTHROUGH();
0628 #endif
0629     case MonthSection:
0630 #if !QT_CONFIG(textdate)
0631         return 2;
0632 #else
0633         if (count <= 2)
0634             return 2;
0635 
0636         {
0637             int ret = 0;
0638             const QLocale l = locale();
0639             const QLocale::FormatType format = count == 4 ? QLocale::LongFormat : QLocale::ShortFormat;
0640             for (int i=1; i<=mcount; ++i) {
0641                 const QString str = (s == MonthSection
0642                                      ? l.monthName(i, format)
0643                                      : l.dayName(i, format));
0644                 ret = qMax(str.size(), ret);
0645             }
0646             return ret;
0647         }
0648 #endif
0649     case MSecSection: return 3;
0650     case YearSection: return 4;
0651     case YearSection2Digits: return 2;
0652         // Arbitrarily many tokens (each up to 14 bytes) joined with / separators:
0653     case TimeZoneSection: return std::numeric_limits<int>::max();
0654 
0655     case CalendarPopupSection:
0656     case Internal:
0657     case TimeSectionMask:
0658     case DateSectionMask:
0659     case HourSectionMask:
0660     case YearSectionMask:
0661     case DayOfWeekSectionMask:
0662     case DaySectionMask:
0663         qWarning("QDateTimeParser::sectionMaxSize: Invalid section %s",
0664                  SectionNode::name(s).toLatin1().constData());
0665 
0666     case NoSectionIndex:
0667     case FirstSectionIndex:
0668     case LastSectionIndex:
0669     case CalendarPopupIndex:
0670         // these cases can't happen
0671         break;
0672     }
0673     return -1;
0674 }
0675 
0676 
0677 int QDateTimeParser::sectionMaxSize(int index) const
0678 {
0679     const SectionNode &sn = sectionNode(index);
0680     return sectionMaxSize(sn.type, sn.count);
0681 }
0682 
0683 /*!
0684   \internal
0685 
0686   Returns the text of section \a s. This function operates on the
0687   arg text rather than edit->text().
0688 */
0689 
0690 
0691 QString QDateTimeParser::sectionText(const QString &text, int sectionIndex, int index) const
0692 {
0693     const SectionNode &sn = sectionNode(sectionIndex);
0694     switch (sn.type) {
0695     case NoSectionIndex:
0696     case FirstSectionIndex:
0697     case LastSectionIndex:
0698         return QString();
0699     default: break;
0700     }
0701 
0702     return text.mid(index, sectionSize(sectionIndex));
0703 }
0704 
0705 QString QDateTimeParser::sectionText(int sectionIndex) const
0706 {
0707     const SectionNode &sn = sectionNode(sectionIndex);
0708     return sectionText(displayText(), sectionIndex, sn.pos);
0709 }
0710 
0711 
0712 #if QT_CONFIG(datestring)
0713 
0714 QDateTimeParser::ParsedSection
0715 QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex,
0716                               int offset, QString *text) const
0717 {
0718     ParsedSection result; // initially Invalid
0719     const SectionNode &sn = sectionNode(sectionIndex);
0720     if (sn.type & Internal) {
0721         qWarning("QDateTimeParser::parseSection Internal error (%s %d)",
0722                  qPrintable(sn.name()), sectionIndex);
0723         return result;
0724     }
0725 
0726     const int sectionmaxsize = sectionMaxSize(sectionIndex);
0727 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0728     QStringRef sectionTextRef = text->midRef(offset, sectionmaxsize);
0729 #else
0730     QStringView sectionTextRef = QStringView(*text).mid(offset, sectionmaxsize);
0731 #endif
0732 
0733     QDTPDEBUG << "sectionValue for" << sn.name()
0734               << "with text" << *text << "and (at" << offset
0735               << ") st:" << sectionTextRef;
0736 
0737     switch (sn.type) {
0738     case AmPmSection: {
0739         QString sectiontext = sectionTextRef.toString();
0740         int used;
0741         const int ampm = findAmPm(sectiontext, sectionIndex, &used);
0742         switch (ampm) {
0743         case AM: // sectiontext == AM
0744         case PM: // sectiontext == PM
0745             result = ParsedSection(Acceptable, ampm, used);
0746             break;
0747         case PossibleAM: // sectiontext => AM
0748         case PossiblePM: // sectiontext => PM
0749             result = ParsedSection(Intermediate, ampm - 2, used);
0750             break;
0751         case PossibleBoth: // sectiontext => AM|PM
0752             result = ParsedSection(Intermediate, 0, used);
0753             break;
0754         case Neither:
0755             QDTPDEBUG << "invalid because findAmPm(" << sectiontext << ") returned -1";
0756             break;
0757         default:
0758             QDTPDEBUGN("This should never happen (findAmPm returned %d)", ampm);
0759             break;
0760         }
0761         if (result.state != Invalid)
0762             text->replace(offset, used, sectiontext.constData(), used);
0763         break; }
0764     case TimeZoneSection:
0765 #if QT_CONFIG(timezone)
0766         result = findTimeZone(sectionTextRef, currentValue,
0767                               absoluteMax(sectionIndex),
0768                               absoluteMin(sectionIndex));
0769 #endif
0770         break;
0771     case MonthSection:
0772     case DayOfWeekSectionShort:
0773     case DayOfWeekSectionLong:
0774         if (sn.count >= 3) {
0775             QString sectiontext = sectionTextRef.toString();
0776             int num = 0, used = 0;
0777             if (sn.type == MonthSection) {
0778                 const QDate minDate = getMinimum().date();
0779                 const int min = (currentValue.date().year() == minDate.year())
0780                     ? minDate.month() : 1;
0781                 num = findMonth(sectiontext.toLower(), min, sectionIndex, &sectiontext, &used);
0782             } else {
0783                 num = findDay(sectiontext.toLower(), 1, sectionIndex, &sectiontext, &used);
0784             }
0785 
0786             result = ParsedSection(Intermediate, num, used);
0787             if (num != -1) {
0788                 text->replace(offset, used, sectiontext.constData(), used);
0789                 if (used == sectiontext.size())
0790                     result = ParsedSection(Acceptable, num, used);
0791             }
0792             break;
0793         }
0794         Q_FALLTHROUGH();
0795         // All numeric:
0796     case DaySection:
0797     case YearSection:
0798     case YearSection2Digits:
0799     case Hour12Section:
0800     case Hour24Section:
0801     case MinuteSection:
0802     case SecondSection:
0803     case MSecSection: {
0804         int sectiontextSize = sectionTextRef.size();
0805         if (sectiontextSize == 0) {
0806             result = ParsedSection(Intermediate);
0807         } else {
0808             for (int i = 0; i < sectiontextSize; ++i) {
0809                 if (sectionTextRef.at(i).isSpace())
0810                     sectiontextSize = i; // which exits the loop
0811             }
0812 
0813             const int absMax = absoluteMax(sectionIndex);
0814             QLocale loc;
0815             bool ok = true;
0816             int last = -1, used = -1;
0817 
0818             Q_ASSERT(sectiontextSize <= sectionmaxsize);
0819 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0820             QStringRef digitsStr = sectionTextRef.left(sectiontextSize);
0821 #else
0822             QStringView digitsStr = sectionTextRef.left(sectiontextSize);
0823 #endif
0824             for (int digits = sectiontextSize; digits >= 1; --digits) {
0825                 digitsStr.truncate(digits);
0826                 int tmp = (int)loc.toUInt(digitsStr, &ok);
0827                 if (ok && sn.type == Hour12Section) {
0828                     if (tmp > 12) {
0829                         tmp = -1;
0830                         ok = false;
0831                     } else if (tmp == 12) {
0832                         tmp = 0;
0833                     }
0834                 }
0835                 if (ok && tmp <= absMax) {
0836                     QDTPDEBUG << sectionTextRef.left(digits) << tmp << digits;
0837                     last = tmp;
0838                     used = digits;
0839                     break;
0840                 }
0841             }
0842 
0843             if (last == -1) {
0844                 QChar first(sectionTextRef.at(0));
0845                 if (separators.at(sectionIndex + 1).startsWith(first))
0846                     result = ParsedSection(Intermediate, 0, used);
0847                 else
0848                     QDTPDEBUG << "invalid because" << sectionTextRef << "can't become a uint" << last << ok;
0849             } else {
0850                 const FieldInfo fi = fieldInfo(sectionIndex);
0851                 const bool unfilled = used < sectionmaxsize;
0852                 if (unfilled && fi & Fraction) { // typing 2 in a zzz field should be .200, not .002
0853                     for (int i = used; i < sectionmaxsize; ++i)
0854                         last *= 10;
0855                 }
0856                 // Even those *= 10s can't take last above absMax:
0857                 Q_ASSERT(last <= absMax);
0858                 const int absMin = absoluteMin(sectionIndex);
0859                 if (last < absMin) {
0860                     if (unfilled)
0861                         result = ParsedSection(Intermediate, last, used);
0862                     else
0863                         QDTPDEBUG << "invalid because" << last << "is less than absoluteMin" << absMin;
0864                 } else if (unfilled && (fi & (FixedWidth|Numeric)) == (FixedWidth|Numeric)) {
0865                     if (skipToNextSection(sectionIndex, currentValue, digitsStr)) {
0866                         const int missingZeroes = sectionmaxsize - digitsStr.size();
0867                         result = ParsedSection(Acceptable, last, sectionmaxsize, missingZeroes);
0868                         text->insert(offset, QString(missingZeroes, QLatin1Char('0')));
0869                         ++(const_cast<QDateTimeParser*>(this)->sectionNodes[sectionIndex].zeroesAdded);
0870                     } else {
0871                         result = ParsedSection(Intermediate, last, used);;
0872                     }
0873                 } else {
0874                     result = ParsedSection(Acceptable, last, used);
0875                 }
0876             }
0877         }
0878         break; }
0879     default:
0880         qWarning("QDateTimeParser::parseSection Internal error (%s %d)",
0881                  qPrintable(sn.name()), sectionIndex);
0882         return result;
0883     }
0884     Q_ASSERT(result.state != Invalid || result.value == -1);
0885 
0886     return result;
0887 }
0888 
0889 /*!
0890   \internal
0891 
0892   Returns a date consistent with the given data on parts specified by known,
0893   while staying as close to the given data as it can.  Returns an invalid date
0894   when on valid date is consistent with the data.
0895 */
0896 
0897 static QDate actualDate(QDateTimeParser::Sections known, int year, int year2digits,
0898                         int month, int day, int dayofweek)
0899 {
0900     QDate actual(year, month, day);
0901     if (actual.isValid() && year % 100 == year2digits && actual.dayOfWeek() == dayofweek)
0902         return actual; // The obvious candidate is fine :-)
0903 
0904     if (dayofweek < 1 || dayofweek > 7) // Invalid: ignore
0905         known &= ~QDateTimeParser::DayOfWeekSectionMask;
0906 
0907     // Assuming year > 0 ...
0908     if (year % 100 != year2digits) {
0909         if (known & QDateTimeParser::YearSection2Digits) {
0910             // Over-ride year, even if specified:
0911             year += year2digits - year % 100;
0912             known &= ~QDateTimeParser::YearSection;
0913         } else {
0914             year2digits = year % 100;
0915         }
0916     }
0917     Q_ASSERT(year % 100 == year2digits);
0918 
0919     if (month < 1) { // If invalid, clip to nearest valid and ignore in known.
0920         month = 1;
0921         known &= ~QDateTimeParser::MonthSection;
0922     } else if (month > 12) {
0923         month = 12;
0924         known &= ~QDateTimeParser::MonthSection;
0925     }
0926 
0927     QDate first(year, month, 1);
0928     int last = known & QDateTimeParser::YearSection && known & QDateTimeParser::MonthSection
0929         ? first.daysInMonth() : 0;
0930     // If we also know day-of-week, tweak last to the last in the month that matches it:
0931     if (last && known & QDateTimeParser::DayOfWeekSectionMask) {
0932         int diff = (dayofweek - first.dayOfWeek() - last) % 7;
0933         Q_ASSERT(diff <= 0); // C++11 specifies (-ve) % (+ve) to be <= 0.
0934         last += diff;
0935     }
0936     if (day < 1) {
0937         if (known & QDateTimeParser::DayOfWeekSectionMask && last) {
0938             day = 1 + dayofweek - first.dayOfWeek();
0939             if (day < 1)
0940                 day += 7;
0941         } else {
0942             day = 1;
0943         }
0944         known &= ~QDateTimeParser::DaySection;
0945     } else if (day > 31) {
0946         day = last;
0947         known &= ~QDateTimeParser::DaySection;
0948     } else if (last && day > last && (known & QDateTimeParser::DaySection) == 0) {
0949         day = last;
0950     }
0951 
0952     actual = QDate(year, month, day);
0953     if (!actual.isValid() // We can't do better than we have, in this case
0954         || (known & QDateTimeParser::DaySection
0955             && known & QDateTimeParser::MonthSection
0956             && known & QDateTimeParser::YearSection) // ditto
0957         || actual.dayOfWeek() == dayofweek // Good enough, use it.
0958         || (known & QDateTimeParser::DayOfWeekSectionMask) == 0) { // No contradiction, use it.
0959         return actual;
0960     }
0961 
0962     /*
0963       Now it gets trickier.
0964 
0965       We have some inconsistency in our data; we've been told day of week, but
0966       it doesn't fit with our year, month and day.  At least one of these is
0967       unknown, though: so we can fix day of week by tweaking it.
0968     */
0969 
0970     if ((known & QDateTimeParser::DaySection) == 0) {
0971         // Relatively easy to fix.
0972         day += dayofweek - actual.dayOfWeek();
0973         if (day < 1)
0974             day += 7;
0975         else if (day > actual.daysInMonth())
0976             day -= 7;
0977         actual = QDate(year, month, day);
0978         return actual;
0979     }
0980 
0981     if ((known & QDateTimeParser::MonthSection) == 0) {
0982         /*
0983           Try possible month-offsets, m, preferring small; at least one (present
0984           month doesn't work) and at most 11 (max month, 12, minus min, 1); try
0985           in both directions, ignoring any offset that takes us out of range.
0986         */
0987         for (int m = 1; m < 12; m++) {
0988             if (m < month) {
0989                 actual = QDate(year, month - m, day);
0990                 if (actual.dayOfWeek() == dayofweek)
0991                     return actual;
0992             }
0993             if (m + month <= 12) {
0994                 actual = QDate(year, month + m, day);
0995                 if (actual.dayOfWeek() == dayofweek)
0996                     return actual;
0997             }
0998         }
0999         // Should only get here in corner cases; e.g. day == 31
1000         actual = QDate(year, month, day); // Restore from trial values.
1001     }
1002 
1003     if ((known & QDateTimeParser::YearSection) == 0) {
1004         if (known & QDateTimeParser::YearSection2Digits) {
1005             /*
1006               Two-digit year and month are specified; choice of century can only
1007               fix this if diff is in one of {1, 2, 5} or {2, 4, 6}; but not if
1008               diff is in the other.  It's also only reasonable to consider
1009               adjacent century, e.g. if year thinks it's 2012 and two-digit year
1010               is '97, it makes sense to consider 1997.  If either adjacent
1011               century does work, the other won't.
1012             */
1013             actual = QDate(year + 100, month, day);
1014             if (actual.dayOfWeek() == dayofweek)
1015                 return actual;
1016             actual = QDate(year - 100, month, day);
1017             if (actual.dayOfWeek() == dayofweek)
1018                 return actual;
1019         } else {
1020             // Offset by 7 is usually enough, but rare cases may need more:
1021             for (int y = 1; y < 12; y++) {
1022                 actual = QDate(year - y, month, day);
1023                 if (actual.dayOfWeek() == dayofweek)
1024                     return actual;
1025                 actual = QDate(year + y, month, day);
1026                 if (actual.dayOfWeek() == dayofweek)
1027                     return actual;
1028             }
1029         }
1030         actual = QDate(year, month, day); // Restore from trial values.
1031     }
1032 
1033     return actual; // It'll just have to do :-(
1034 }
1035 
1036 /*!
1037   \internal
1038 */
1039 
1040 static QTime actualTime(QDateTimeParser::Sections known,
1041                         int hour, int hour12, int ampm,
1042                         int minute, int second, int msec)
1043 {
1044     // If we have no conflict, or don't know enough to diagonose one, use this:
1045     QTime actual(hour, minute, second, msec);
1046     if (hour12 < 0 || hour12 > 12) { // ignore bogus value
1047         known &= ~QDateTimeParser::Hour12Section;
1048         hour12 = hour % 12;
1049     }
1050 
1051     if (ampm == -1 || (known & QDateTimeParser::AmPmSection) == 0) {
1052         if ((known & QDateTimeParser::Hour12Section) == 0 || hour % 12 == hour12)
1053             return actual;
1054 
1055         if ((known & QDateTimeParser::Hour24Section) == 0)
1056             hour = hour12 + (hour > 12 ? 12 : 0);
1057     } else {
1058         Q_ASSERT(ampm == 0 || ampm == 1);
1059         if (hour - hour12 == ampm * 12)
1060             return actual;
1061 
1062         if ((known & QDateTimeParser::Hour24Section) == 0
1063             && known & QDateTimeParser::Hour12Section) {
1064             hour = hour12 + ampm * 12;
1065         }
1066     }
1067     actual = QTime(hour, minute, second, msec);
1068     return actual;
1069 }
1070 
1071 /*!
1072   \internal
1073 */
1074 QDateTimeParser::StateNode
1075 QDateTimeParser::scanString(const QDateTime &defaultValue,
1076                             bool fixup, QString *input) const
1077 {
1078     State state = Acceptable;
1079     bool conflicts = false;
1080     const int sectionNodesCount = sectionNodes.size();
1081     int padding = 0;
1082     int pos = 0;
1083     int year, month, day;
1084     const QDate defaultDate = defaultValue.date();
1085     const QTime defaultTime = defaultValue.time();
1086     defaultDate.getDate(&year, &month, &day);
1087     int year2digits = year % 100;
1088     int hour = defaultTime.hour();
1089     int hour12 = -1;
1090     int minute = defaultTime.minute();
1091     int second = defaultTime.second();
1092     int msec = defaultTime.msec();
1093     int dayofweek = defaultDate.dayOfWeek();
1094     Qt::TimeSpec tspec = defaultValue.timeSpec();
1095     int zoneOffset = 0; // In seconds; local - UTC
1096 #if QT_CONFIG(timezone)
1097     QTimeZone timeZone;
1098 #endif
1099     switch (tspec) {
1100     case Qt::OffsetFromUTC: // timeZone is ignored
1101         zoneOffset = defaultValue.offsetFromUtc();
1102         break;
1103 #if QT_CONFIG(timezone)
1104     case Qt::TimeZone:
1105         timeZone = defaultValue.timeZone();
1106         if (timeZone.isValid())
1107             zoneOffset = timeZone.offsetFromUtc(defaultValue);
1108         // else: is there anything we can do about this ?
1109         break;
1110 #endif
1111     default: // zoneOffset and timeZone are ignored
1112         break;
1113     }
1114 
1115     int ampm = -1;
1116     Sections isSet = NoSection;
1117 
1118     for (int index = 0; index < sectionNodesCount; ++index) {
1119         Q_ASSERT(state != Invalid);
1120         const QString &separator = separators.at(index);
1121 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1122         if (input->midRef(pos, separator.size()) != separator) {
1123             QDTPDEBUG << "invalid because" << input->midRef(pos, separator.size())
1124                       << "!=" << separator
1125                       << index << pos << currentSectionIndex;
1126             return StateNode();
1127         }
1128 #else
1129         if (QStringView(*input).mid(pos, separator.size()) != separator) {
1130             QDTPDEBUG << "invalid because" << QStringView(*input).mid(pos, separator.size())
1131                       << "!=" << separator
1132                       << index << pos << currentSectionIndex;
1133             return StateNode();
1134         }
1135 #endif
1136         pos += separator.size();
1137         sectionNodes[index].pos = pos;
1138         int *current = nullptr;
1139         const SectionNode sn = sectionNodes.at(index);
1140         ParsedSection sect;
1141 
1142         {
1143             const QDate date = actualDate(isSet, year, year2digits, month, day, dayofweek);
1144             const QTime time = actualTime(isSet, hour, hour12, ampm, minute, second, msec);
1145             sect = parseSection(
1146 #if QT_CONFIG(timezone)
1147                                 tspec == Qt::TimeZone ? QDateTime(date, time, timeZone) :
1148 #endif
1149                                 QDateTime(date, time, tspec, zoneOffset),
1150                                 index, pos, input);
1151         }
1152 
1153         QDTPDEBUG << "sectionValue" << sn.name() << *input
1154                   << "pos" << pos << "used" << sect.used << stateName(sect.state);
1155 
1156         padding += sect.zeroes;
1157         if (fixup && sect.state == Intermediate && sect.used < sn.count) {
1158             const FieldInfo fi = fieldInfo(index);
1159             if ((fi & (Numeric|FixedWidth)) == (Numeric|FixedWidth)) {
1160                 const QString newText = QString::fromLatin1("%1").arg(sect.value, sn.count, 10, QLatin1Char('0'));
1161                 input->replace(pos, sect.used, newText);
1162                 sect.used = sn.count;
1163             }
1164         }
1165 
1166         state = qMin<State>(state, sect.state);
1167         // QDateTimeEdit can fix Intermediate and zeroes, but input needing that didn't match format:
1168         if (state == Invalid || (context == FromString && (state == Intermediate || sect.zeroes)))
1169             return StateNode();
1170 
1171         switch (sn.type) {
1172         case TimeZoneSection:
1173             current = &zoneOffset;
1174             if (sect.used > 0) {
1175 #if QT_CONFIG(timezone) // Synchronize with what findTimeZone() found:
1176 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1177                 QStringRef zoneName = input->midRef(pos, sect.used);
1178 #else
1179                 QStringView zoneName = QStringView(*input).mid(pos, sect.used);
1180 #endif
1181                 Q_ASSERT(!zoneName.isEmpty()); // sect.used > 0
1182                 const QByteArray latinZone(zoneName == QLatin1String("Z")
1183                                            ? QByteArray("UTC") : zoneName.toLatin1());
1184                 timeZone = QTimeZone(latinZone);
1185                 tspec = timeZone.isValid() ? (QTimeZone::isTimeZoneIdAvailable(latinZone) ? Qt::TimeZone : Qt::OffsetFromUTC) : Qt::LocalTime;
1186 #else
1187                 tspec = Qt::LocalTime;
1188 #endif
1189             }
1190             break;
1191         case Hour24Section: current = &hour; break;
1192         case Hour12Section: current = &hour12; break;
1193         case MinuteSection: current = &minute; break;
1194         case SecondSection: current = &second; break;
1195         case MSecSection: current = &msec; break;
1196         case YearSection: current = &year; break;
1197         case YearSection2Digits: current = &year2digits; break;
1198         case MonthSection: current = &month; break;
1199         case DayOfWeekSectionShort:
1200         case DayOfWeekSectionLong: current = &dayofweek; break;
1201         case DaySection: current = &day; sect.value = qMax<int>(1, sect.value); break;
1202         case AmPmSection: current = &ampm; break;
1203         default:
1204             qWarning("QDateTimeParser::parse Internal error (%s)",
1205                      qPrintable(sn.name()));
1206             break;
1207         }
1208 
1209         if (sect.used > 0)
1210             pos += sect.used;
1211         QDTPDEBUG << index << sn.name() << "is set to"
1212                   << pos << "state is" << stateName(state);
1213 
1214         if (!current) {
1215             qWarning("QDateTimeParser::parse Internal error 2");
1216             return StateNode();
1217         }
1218         if (isSet & sn.type && *current != sect.value) {
1219             QDTPDEBUG << "CONFLICT " << sn.name() << *current << sect.value;
1220             conflicts = true;
1221             if (index != currentSectionIndex || sect.state == Invalid) {
1222                 continue;
1223             }
1224         }
1225         if (sect.state != Invalid)
1226             *current = sect.value;
1227 
1228         // Record the present section:
1229         isSet |= sn.type;
1230     }
1231 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1232     if (input->midRef(pos) != separators.last()) {
1233         QDTPDEBUG << "invalid because" << input->midRef(pos)
1234                   << "!=" << separators.last() << pos;
1235         return StateNode();
1236     }
1237 #else
1238     if (QStringView(*input).mid(pos) != separators.last()) {
1239         QDTPDEBUG << "invalid because" << QStringView(*input).mid(pos)
1240                   << "!=" << separators.last() << pos;
1241         return StateNode();
1242     }
1243 #endif
1244     if (parserType != QMetaType::QTime) {
1245         if (year % 100 != year2digits && (isSet & YearSection2Digits)) {
1246             if (!(isSet & YearSection)) {
1247                 year = (year / 100) * 100;
1248                 year += year2digits;
1249             } else {
1250                 conflicts = true;
1251                 const SectionNode &sn = sectionNode(currentSectionIndex);
1252                 if (sn.type == YearSection2Digits) {
1253                     year = (year / 100) * 100;
1254                     year += year2digits;
1255                 }
1256             }
1257         }
1258 
1259         const QDate date(year, month, day);
1260         const int diff = dayofweek - date.dayOfWeek();
1261         if (diff != 0 && state == Acceptable && isSet & DayOfWeekSectionMask) {
1262             if (isSet & DaySection)
1263                 conflicts = true;
1264             const SectionNode &sn = sectionNode(currentSectionIndex);
1265             if (sn.type & DayOfWeekSectionMask || currentSectionIndex == -1) {
1266                 // dayofweek should be preferred
1267                 day += diff;
1268                 if (day <= 0) {
1269                     day += 7;
1270                 } else if (day > date.daysInMonth()) {
1271                     day -= 7;
1272                 }
1273                 QDTPDEBUG << year << month << day << dayofweek
1274                           << diff << QDate(year, month, day).dayOfWeek();
1275             }
1276         }
1277 
1278         bool needfixday = false;
1279         if (sectionType(currentSectionIndex) & DaySectionMask) {
1280             cachedDay = day;
1281         } else if (cachedDay > day) {
1282             day = cachedDay;
1283             needfixday = true;
1284         }
1285 
1286         if (!QDate::isValid(year, month, day)) {
1287             if (day < 32) {
1288                 cachedDay = day;
1289             }
1290             if (day > 28 && QDate::isValid(year, month, 1)) {
1291                 needfixday = true;
1292             }
1293         }
1294         if (needfixday) {
1295             if (context == FromString) {
1296                 return StateNode();
1297             }
1298             if (state == Acceptable && fixday) {
1299                 day = qMin<int>(day, QDate(year, month, 1).daysInMonth());
1300 
1301                 const QLocale loc = locale();
1302                 for (int i=0; i<sectionNodesCount; ++i) {
1303                     const SectionNode sn = sectionNode(i);
1304                     if (sn.type & DaySection) {
1305                         input->replace(sectionPos(sn), sectionSize(i), loc.toString(day));
1306                     } else if (sn.type & DayOfWeekSectionMask) {
1307                         const int dayOfWeek = QDate(year, month, day).dayOfWeek();
1308                         const QLocale::FormatType dayFormat =
1309                             (sn.type == DayOfWeekSectionShort
1310                              ? QLocale::ShortFormat : QLocale::LongFormat);
1311                         const QString dayName(loc.dayName(dayOfWeek, dayFormat));
1312                         input->replace(sectionPos(sn), sectionSize(i), dayName);
1313                     }
1314                 }
1315             } else if (state > Intermediate) {
1316                 state = Intermediate;
1317             }
1318         }
1319     }
1320 
1321     if (parserType != QMetaType::QDate) {
1322         if (isSet & Hour12Section) {
1323             const bool hasHour = isSet & Hour24Section;
1324             if (ampm == -1) {
1325                 if (hasHour) {
1326                     ampm = (hour < 12 ? 0 : 1);
1327                 } else {
1328                     ampm = 0; // no way to tell if this is am or pm so I assume am
1329                 }
1330             }
1331             hour12 = (ampm == 0 ? hour12 % 12 : (hour12 % 12) + 12);
1332             if (!hasHour) {
1333                 hour = hour12;
1334             } else if (hour != hour12) {
1335                 conflicts = true;
1336             }
1337         } else if (ampm != -1) {
1338             if (!(isSet & (Hour24Section))) {
1339                 hour = (12 * ampm); // special case. Only ap section
1340             } else if ((ampm == 0) != (hour < 12)) {
1341                 conflicts = true;
1342             }
1343         }
1344 
1345     }
1346 
1347     QDTPDEBUG << year << month << day << hour << minute << second << msec;
1348     Q_ASSERT(state != Invalid);
1349 
1350     const QDate date(year, month, day);
1351     const QTime time(hour, minute, second, msec);
1352     const QDateTime when =
1353 #if QT_CONFIG(timezone)
1354             tspec == Qt::TimeZone ? QDateTime(date, time, timeZone) :
1355 #endif
1356             QDateTime(date, time, tspec, zoneOffset);
1357 
1358     // If hour wasn't specified, check the default we're using exists on the
1359     // given date (which might be a spring-forward, skipping an hour).
1360     if (parserType == QMetaType::QDateTime && !(isSet & HourSectionMask) && !when.isValid()) {
1361         qint64 msecs = when.toMSecsSinceEpoch();
1362         // Fortunately, that gets a useful answer ...
1363         const QDateTime replace =
1364 #if QT_CONFIG(timezone)
1365             tspec == Qt::TimeZone
1366             ? QDateTime::fromMSecsSinceEpoch(msecs, timeZone) :
1367 #endif
1368             QDateTime::fromMSecsSinceEpoch(msecs, tspec, zoneOffset);
1369         const QTime tick = replace.time();
1370         if (replace.date() == date
1371             && (!(isSet & MinuteSection) || tick.minute() == minute)
1372             && (!(isSet & SecondSection) || tick.second() == second)
1373             && (!(isSet & MSecSection)   || tick.msec() == msec)) {
1374             return StateNode(replace, state, padding, conflicts);
1375         }
1376     }
1377 
1378     return StateNode(when, state, padding, conflicts);
1379 }
1380 
1381 /*!
1382   \internal
1383 */
1384 
1385 QDateTimeParser::StateNode
1386 QDateTimeParser::parse(QString input, int position, const QDateTime &defaultValue, bool fixup) const
1387 {
1388     const QDateTime minimum = getMinimum();
1389     const QDateTime maximum = getMaximum();
1390 
1391     QDTPDEBUG << "parse" << input;
1392     StateNode scan = scanString(defaultValue, fixup, &input);
1393     QDTPDEBUGN("'%s' => '%s'(%s)", input.toLatin1().constData(),
1394                scan.value.toString(QLatin1String("yyyy/MM/dd hh:mm:ss.zzz")).toLatin1().constData(),
1395                stateName(scan.state).toLatin1().constData());
1396 
1397     if (scan.value.isValid() && scan.state != Invalid) {
1398         if (context != FromString && scan.value < minimum) {
1399             const QLatin1Char space(' ');
1400             if (scan.value >= minimum)
1401                 qWarning("QDateTimeParser::parse Internal error 3 (%s %s)",
1402                          qPrintable(scan.value.toString()), qPrintable(minimum.toString()));
1403 
1404             bool done = false;
1405             scan.state = Invalid;
1406             const int sectionNodesCount = sectionNodes.size();
1407             for (int i=0; i<sectionNodesCount && !done; ++i) {
1408                 const SectionNode &sn = sectionNodes.at(i);
1409                 QString t = sectionText(input, i, sn.pos).toLower();
1410                 if ((t.size() < sectionMaxSize(i)
1411                      && (((int)fieldInfo(i) & (FixedWidth|Numeric)) != Numeric))
1412                     || t.contains(space)) {
1413                     switch (sn.type) {
1414                     case AmPmSection:
1415                         switch (findAmPm(t, i)) {
1416                         case AM:
1417                         case PM:
1418                             scan.state = Acceptable;
1419                             done = true;
1420                             break;
1421                         case Neither:
1422                             scan.state = Invalid;
1423                             done = true;
1424                             break;
1425                         case PossibleAM:
1426                         case PossiblePM:
1427                         case PossibleBoth: {
1428                             const QDateTime copy(scan.value.addSecs(12 * 60 * 60));
1429                             if (copy >= minimum && copy <= maximum) {
1430                                 scan.state = Intermediate;
1431                                 done = true;
1432                             }
1433                             break; }
1434                         }
1435                         Q_FALLTHROUGH();
1436                     case MonthSection:
1437                         if (sn.count >= 3) {
1438                             const int finalMonth = scan.value.date().month();
1439                             int tmp = finalMonth;
1440                             // I know the first possible month makes the date too early
1441                             while ((tmp = findMonth(t, tmp + 1, i)) != -1) {
1442                                 const QDateTime copy(scan.value.addMonths(tmp - finalMonth));
1443                                 if (copy >= minimum && copy <= maximum)
1444                                     break; // break out of while
1445                             }
1446                             if (tmp != -1) {
1447                                 scan.state = Intermediate;
1448                                 done = true;
1449                             }
1450                             break;
1451                         }
1452                         Q_FALLTHROUGH();
1453                     default: {
1454                         int toMin;
1455                         int toMax;
1456 
1457                         if (sn.type & TimeSectionMask) {
1458                             if (scan.value.daysTo(minimum) != 0) {
1459                                 break;
1460                             }
1461                             const QTime time = scan.value.time();
1462                             toMin = time.msecsTo(minimum.time());
1463                             if (scan.value.daysTo(maximum) > 0)
1464                                 toMax = -1; // can't get to max
1465                             else
1466                                 toMax = time.msecsTo(maximum.time());
1467                         } else {
1468                             toMin = scan.value.daysTo(minimum);
1469                             toMax = scan.value.daysTo(maximum);
1470                         }
1471                         const int maxChange = sn.maxChange();
1472                         if (toMin > maxChange) {
1473                             QDTPDEBUG << "invalid because toMin > maxChange" << toMin
1474                                       << maxChange << t << scan.value << minimum;
1475                             scan.state = Invalid;
1476                             done = true;
1477                             break;
1478                         } else if (toMax > maxChange) {
1479                             toMax = -1; // can't get to max
1480                         }
1481 
1482                         const int min = getDigit(minimum, i);
1483                         if (min == -1) {
1484                             qWarning("QDateTimeParser::parse Internal error 4 (%s)",
1485                                      qPrintable(sn.name()));
1486                             scan.state = Invalid;
1487                             done = true;
1488                             break;
1489                         }
1490 
1491                         int max = toMax != -1 ? getDigit(maximum, i) : absoluteMax(i, scan.value);
1492                         int pos = position + scan.padded - sn.pos;
1493                         if (pos < 0 || pos >= t.size())
1494                             pos = -1;
1495                         if (!potentialValue(t.simplified(), min, max, i, scan.value, pos)) {
1496                             QDTPDEBUG << "invalid because potentialValue(" << t.simplified() << min << max
1497                                       << sn.name() << "returned" << toMax << toMin << pos;
1498                             scan.state = Invalid;
1499                             done = true;
1500                             break;
1501                         }
1502                         scan.state = Intermediate;
1503                         done = true;
1504                         break; }
1505                     }
1506                 }
1507             }
1508         } else {
1509             if (context == FromString) {
1510                 // optimization
1511                 Q_ASSERT(maximum.date().toJulianDay() == 5373484);
1512                 if (scan.value.date().toJulianDay() > 5373484)
1513                     scan.state = Invalid;
1514             } else {
1515                 if (scan.value > maximum)
1516                     scan.state = Invalid;
1517             }
1518 
1519             QDTPDEBUG << "not checking intermediate because scanned value is" << scan.value << minimum << maximum;
1520         }
1521     }
1522     text = scan.input = input;
1523     // Set spec *after* all checking, so validity is a property of the string:
1524     scan.value = scan.value.toTimeSpec(spec);
1525     return scan;
1526 }
1527 
1528 /*
1529   \internal
1530   \brief Returns the index in \a entries with the best prefix match to \a text
1531 
1532   Scans \a entries looking for an entry overlapping \a text as much as possible
1533   (an exact match beats any prefix match; a match of the full entry as prefix of
1534   text beats any entry but one matching a longer prefix; otherwise, the match of
1535   longest prefix wins, earlier entries beating later on a draw).  Records the
1536   length of overlap in *used (if \a used is non-NULL) and the first entry that
1537   overlapped this much in *usedText (if \a usedText is non-NULL).
1538  */
1539 static int findTextEntry(const QString &text, const QVector<QString> &entries, QString *usedText, int *used)
1540 {
1541     if (text.isEmpty())
1542         return -1;
1543 
1544     int bestMatch = -1;
1545     int bestCount = 0;
1546     for (int n = 0; n < entries.size(); ++n)
1547     {
1548         const QString &name = entries.at(n);
1549 
1550         const int limit = qMin(text.size(), name.size());
1551         int i = 0;
1552         while (i < limit && text.at(i) == name.at(i).toLower())
1553             ++i;
1554         // Full match beats an equal prefix match:
1555         if (i > bestCount || (i == bestCount && i == name.size())) {
1556             bestCount = i;
1557             bestMatch = n;
1558             if (i == name.size() && i == text.size())
1559                 break; // Exact match, name == text, wins.
1560         }
1561     }
1562     if (usedText && bestMatch != -1)
1563         *usedText = entries.at(bestMatch);
1564     if (used)
1565         *used = bestCount;
1566 
1567     return bestMatch;
1568 }
1569 
1570 /*!
1571   \internal
1572   finds the first possible monthname that \a str1 can
1573   match. Starting from \a index; str should already by lowered
1574 */
1575 
1576 int QDateTimeParser::findMonth(const QString &str1, int startMonth, int sectionIndex,
1577                                QString *usedMonth, int *used) const
1578 {
1579     const SectionNode &sn = sectionNode(sectionIndex);
1580     if (sn.type != MonthSection) {
1581         qWarning("QDateTimeParser::findMonth Internal error");
1582         return -1;
1583     }
1584 
1585     QLocale::FormatType type = sn.count == 3 ? QLocale::ShortFormat : QLocale::LongFormat;
1586     QLocale l = locale();
1587     QVector<QString> monthNames;
1588     monthNames.reserve(13 - startMonth);
1589     for (int month = startMonth; month <= 12; ++month)
1590         monthNames.append(l.monthName(month, type));
1591 
1592     const int index = findTextEntry(str1, monthNames, usedMonth, used);
1593     return index < 0 ? index : index + startMonth;
1594 }
1595 
1596 int QDateTimeParser::findDay(const QString &str1, int startDay, int sectionIndex, QString *usedDay, int *used) const
1597 {
1598     const SectionNode &sn = sectionNode(sectionIndex);
1599     if (!(sn.type & DaySectionMask)) {
1600         qWarning("QDateTimeParser::findDay Internal error");
1601         return -1;
1602     }
1603 
1604     QLocale::FormatType type = sn.count == 4 ? QLocale::LongFormat : QLocale::ShortFormat;
1605     QLocale l = locale();
1606     QVector<QString> daysOfWeek;
1607     daysOfWeek.reserve(8 - startDay);
1608     for (int day = startDay; day <= 7; ++day)
1609         daysOfWeek.append(l.dayName(day, type));
1610 
1611     const int index = findTextEntry(str1, daysOfWeek, usedDay, used);
1612     return index < 0 ? index : index + startDay;
1613 }
1614 
1615 /*!
1616   \internal
1617 
1618   Return's .value is zone's offset, zone time - UTC time, in seconds.
1619   See QTimeZonePrivate::isValidId() for the format of zone names.
1620  */
1621 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1622 QDateTimeParser::ParsedSection
1623 QDateTimeParser::findTimeZone(QStringRef str, const QDateTime &when,
1624                               int maxVal, int minVal) const
1625 #else
1626 QDateTimeParser::ParsedSection
1627 QDateTimeParser::findTimeZone(QStringView str, const QDateTime &when,
1628                               int maxVal, int minVal) const
1629 #endif
1630 {
1631 #if QT_CONFIG(timezone)
1632 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1633     int index = startsWithLocalTimeZone(str);
1634 #else
1635     int index = startsWithLocalTimeZone(str, when);
1636 #endif
1637     int offset;
1638 
1639     if (index > 0) {
1640         // We won't actually use this, but we need a valid return:
1641         offset = QDateTime(when.date(), when.time(), Qt::LocalTime).offsetFromUtc();
1642     } else {
1643         int size = str.length();
1644         offset = std::numeric_limits<int>::max(); // deliberately out of range
1645         Q_ASSERT(offset > QTimeZone::MaxUtcOffsetSecs); // cf. absoluteMax()
1646 
1647         // Collect up plausibly-valid characters; let QTimeZone work out what's truly valid.
1648         while (index < size) {
1649             QChar here = str[index];
1650             if (here < QChar(127)
1651                 && (here.isLetterOrNumber()
1652                     || here == QLatin1Char('/') || here == QLatin1Char('-')
1653                     || here == QLatin1Char('_') || here == QLatin1Char('.')
1654                     || here == QLatin1Char('+') || here == QLatin1Char(':')))
1655                 index++;
1656             else
1657                 break;
1658         }
1659 
1660         while (index > 0) {
1661             str.truncate(index);
1662             if (str == QLatin1String("Z")) {
1663                 offset = 0; // "Zulu" time - a.k.a. UTC
1664                 break;
1665             }
1666             QTimeZone zone(str.toLatin1());
1667             if (zone.isValid()) {
1668                 offset = zone.offsetFromUtc(when);
1669                 break;
1670             }
1671             index--; // maybe we collected too much ...
1672         }
1673     }
1674 
1675     if (index > 0 && maxVal >= offset && offset >= minVal)
1676         return ParsedSection(Acceptable, offset, index);
1677 
1678 #endif // timezone
1679     return ParsedSection();
1680 }
1681 
1682 /*!
1683   \internal
1684 
1685   Returns
1686   AM if str == tr("AM")
1687   PM if str == tr("PM")
1688   PossibleAM if str can become tr("AM")
1689   PossiblePM if str can become tr("PM")
1690   PossibleBoth if str can become tr("PM") and can become tr("AM")
1691   Neither if str can't become anything sensible
1692 */
1693 QDateTimeParser::AmPmFinder QDateTimeParser::findAmPm(QString &str, int sectionIndex, int *used) const
1694 {
1695     const SectionNode &s = sectionNode(sectionIndex);
1696     if (s.type != AmPmSection) {
1697         qWarning("QDateTimeParser::findAmPm Internal error");
1698         return Neither;
1699     }
1700     if (used)
1701         *used = str.size();
1702 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1703     if (QStringRef(&str).trimmed().isEmpty()) {
1704         return PossibleBoth;
1705     }
1706 #else
1707     if (QStringView(str).trimmed().isEmpty()) {
1708         return PossibleBoth;
1709     }
1710 #endif
1711     const QLatin1Char space(' ');
1712     int size = sectionMaxSize(sectionIndex);
1713 
1714     enum {
1715         amindex = 0,
1716         pmindex = 1
1717     };
1718     QString ampm[2];
1719     ampm[amindex] = getAmPmText(AmText, s.count == 1 ? UpperCase : LowerCase);
1720     ampm[pmindex] = getAmPmText(PmText, s.count == 1 ? UpperCase : LowerCase);
1721     for (int i=0; i<2; ++i)
1722         ampm[i].truncate(size);
1723 
1724     QDTPDEBUG << "findAmPm" << str << ampm[0] << ampm[1];
1725 
1726     if (str.indexOf(ampm[amindex], 0, Qt::CaseInsensitive) == 0) {
1727         str = ampm[amindex];
1728         return AM;
1729     } else if (str.indexOf(ampm[pmindex], 0, Qt::CaseInsensitive) == 0) {
1730         str = ampm[pmindex];
1731         return PM;
1732     } else if (context == FromString || (str.count(space) == 0 && str.size() >= size)) {
1733         return Neither;
1734     }
1735     size = qMin(size, str.size());
1736 
1737     bool broken[2] = {false, false};
1738     for (int i=0; i<size; ++i) {
1739         if (str.at(i) != space) {
1740             for (int j=0; j<2; ++j) {
1741                 if (!broken[j]) {
1742                     int index = ampm[j].indexOf(str.at(i));
1743                     QDTPDEBUG << "looking for" << str.at(i)
1744                               << "in" << ampm[j] << "and got" << index;
1745                     if (index == -1) {
1746                         if (str.at(i).category() == QChar::Letter_Uppercase) {
1747                             index = ampm[j].indexOf(str.at(i).toLower());
1748                             QDTPDEBUG << "trying with" << str.at(i).toLower()
1749                                       << "in" << ampm[j] << "and got" << index;
1750                         } else if (str.at(i).category() == QChar::Letter_Lowercase) {
1751                             index = ampm[j].indexOf(str.at(i).toUpper());
1752                             QDTPDEBUG << "trying with" << str.at(i).toUpper()
1753                                       << "in" << ampm[j] << "and got" << index;
1754                         }
1755                         if (index == -1) {
1756                             broken[j] = true;
1757                             if (broken[amindex] && broken[pmindex]) {
1758                                 QDTPDEBUG << str << "didn't make it";
1759                                 return Neither;
1760                             }
1761                             continue;
1762                         } else {
1763                             str[i] = ampm[j].at(index); // fix case
1764                         }
1765                     }
1766                     ampm[j].remove(index, 1);
1767                 }
1768             }
1769         }
1770     }
1771     if (!broken[pmindex] && !broken[amindex])
1772         return PossibleBoth;
1773     return (!broken[amindex] ? PossibleAM : PossiblePM);
1774 }
1775 #endif // datestring
1776 
1777 /*!
1778   \internal
1779   Max number of units that can be changed by this section.
1780 */
1781 
1782 int QDateTimeParser::SectionNode::maxChange() const
1783 {
1784     switch (type) {
1785         // Time. unit is msec
1786     case MSecSection: return 999;
1787     case SecondSection: return 59 * 1000;
1788     case MinuteSection: return 59 * 60 * 1000;
1789     case Hour24Section: case Hour12Section: return 59 * 60 * 60 * 1000;
1790 
1791         // Date. unit is day
1792     case DayOfWeekSectionShort:
1793     case DayOfWeekSectionLong: return 7;
1794     case DaySection: return 30;
1795     case MonthSection: return 365 - 31;
1796     case YearSection: return 9999 * 365;
1797     case YearSection2Digits: return 100 * 365;
1798     default:
1799         qWarning("QDateTimeParser::maxChange() Internal error (%s)",
1800                  qPrintable(name()));
1801     }
1802 
1803     return -1;
1804 }
1805 
1806 QDateTimeParser::FieldInfo QDateTimeParser::fieldInfo(int index) const
1807 {
1808     FieldInfo ret = {};
1809     const SectionNode &sn = sectionNode(index);
1810     switch (sn.type) {
1811     case MSecSection:
1812         ret |= Fraction;
1813         Q_FALLTHROUGH();
1814     case SecondSection:
1815     case MinuteSection:
1816     case Hour24Section:
1817     case Hour12Section:
1818     case YearSection2Digits:
1819         ret |= AllowPartial;
1820         Q_FALLTHROUGH();
1821     case YearSection:
1822         ret |= Numeric;
1823         if (sn.count != 1)
1824             ret |= FixedWidth;
1825         break;
1826     case MonthSection:
1827     case DaySection:
1828         switch (sn.count) {
1829         case 2:
1830             ret |= FixedWidth;
1831             Q_FALLTHROUGH();
1832         case 1:
1833             ret |= (Numeric|AllowPartial);
1834             break;
1835         }
1836         break;
1837     case DayOfWeekSectionShort:
1838     case DayOfWeekSectionLong:
1839         if (sn.count == 3)
1840             ret |= FixedWidth;
1841         break;
1842     case AmPmSection:
1843         ret |= FixedWidth;
1844         break;
1845     case TimeZoneSection:
1846         break;
1847     default:
1848         qWarning("QDateTimeParser::fieldInfo Internal error 2 (%d %s %d)",
1849                  index, qPrintable(sn.name()), sn.count);
1850         break;
1851     }
1852     return ret;
1853 }
1854 
1855 QString QDateTimeParser::SectionNode::format() const
1856 {
1857     QChar fillChar;
1858     switch (type) {
1859     case AmPmSection: return count == 1 ? QLatin1String("AP") : QLatin1String("ap");
1860     case MSecSection: fillChar = QLatin1Char('z'); break;
1861     case SecondSection: fillChar = QLatin1Char('s'); break;
1862     case MinuteSection: fillChar = QLatin1Char('m'); break;
1863     case Hour24Section: fillChar = QLatin1Char('H'); break;
1864     case Hour12Section: fillChar = QLatin1Char('h'); break;
1865     case DayOfWeekSectionShort:
1866     case DayOfWeekSectionLong:
1867     case DaySection: fillChar = QLatin1Char('d'); break;
1868     case MonthSection: fillChar = QLatin1Char('M'); break;
1869     case YearSection2Digits:
1870     case YearSection: fillChar = QLatin1Char('y'); break;
1871     default:
1872         qWarning("QDateTimeParser::sectionFormat Internal error (%s)",
1873                  qPrintable(name(type)));
1874         return QString();
1875     }
1876     if (fillChar.isNull()) {
1877         qWarning("QDateTimeParser::sectionFormat Internal error 2");
1878         return QString();
1879     }
1880     return QString(count, fillChar);
1881 }
1882 
1883 
1884 /*!
1885   \internal
1886 
1887   Returns \c true if str can be modified to represent a
1888   number that is within min and max.
1889 */
1890 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1891 bool QDateTimeParser::potentialValue(const QStringRef &str, int min, int max, int index,
1892                                      const QDateTime &currentValue, int insert) const
1893 #else
1894 bool QDateTimeParser::potentialValue(const QStringView &str, int min, int max, int index,
1895                                      const QDateTime &currentValue, int insert) const
1896 #endif
1897 {
1898     if (str.isEmpty()) {
1899         return true;
1900     }
1901     const int size = sectionMaxSize(index);
1902     int val = (int)locale().toUInt(str);
1903     const SectionNode &sn = sectionNode(index);
1904     if (sn.type == YearSection2Digits) {
1905         const int year = currentValue.date().year();
1906         val += year - (year % 100);
1907     }
1908     if (val >= min && val <= max && str.size() == size) {
1909         return true;
1910     } else if (val > max) {
1911         return false;
1912     } else if (str.size() == size && val < min) {
1913         return false;
1914     }
1915 
1916     const int len = size - str.size();
1917     for (int i=0; i<len; ++i) {
1918         for (int j=0; j<10; ++j) {
1919             if (potentialValue(str + QLatin1Char('0' + j), min, max, index, currentValue, insert)) {
1920                 return true;
1921             } else if (insert >= 0) {
1922                 const QString tmp = str.left(insert) + QLatin1Char('0' + j) + str.mid(insert);
1923                 if (potentialValue(tmp, min, max, index, currentValue, insert))
1924                     return true;
1925             }
1926         }
1927     }
1928 
1929     return false;
1930 }
1931 
1932 /*!
1933   \internal
1934 */
1935 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1936 bool QDateTimeParser::skipToNextSection(int index, const QDateTime &current, const QStringRef &text) const
1937 #else
1938 bool QDateTimeParser::skipToNextSection(int index, const QDateTime &current, const QStringView &text) const
1939 #endif
1940 {
1941     Q_ASSERT(text.size() < sectionMaxSize(index));
1942     const SectionNode &node = sectionNode(index);
1943     int min = absoluteMin(index);
1944     int max = absoluteMax(index, current);
1945     // Time-zone field is only numeric if given as offset from UTC:
1946     if (node.type != TimeZoneSection || current.timeSpec() == Qt::OffsetFromUTC) {
1947         const QDateTime maximum = getMaximum();
1948         const QDateTime minimum = getMinimum();
1949         Q_ASSERT(current >= minimum && current <= maximum);
1950 
1951         QDateTime tmp = current;
1952         if (!setDigit(tmp, index, min) || tmp < minimum)
1953             min = getDigit(minimum, index);
1954 
1955         if (!setDigit(tmp, index, max) || tmp > maximum)
1956             max = getDigit(maximum, index);
1957     }
1958     int pos = cursorPosition() - node.pos;
1959     if (pos < 0 || pos >= text.size())
1960         pos = -1;
1961 
1962     /*
1963       If the value potentially can become another valid entry we don't want to
1964       skip to the next. E.g. In a M field (month without leading 0) if you type
1965       1 we don't want to autoskip (there might be [012] following) but if you
1966       type 3 we do.
1967     */
1968     return !potentialValue(text, min, max, index, current, pos);
1969 }
1970 
1971 /*!
1972   \internal
1973   For debugging. Returns the name of the section \a s.
1974 */
1975 
1976 QString QDateTimeParser::SectionNode::name(QDateTimeParser::Section s)
1977 {
1978     switch (s) {
1979     case QDateTimeParser::AmPmSection: return QLatin1String("AmPmSection");
1980     case QDateTimeParser::DaySection: return QLatin1String("DaySection");
1981     case QDateTimeParser::DayOfWeekSectionShort: return QLatin1String("DayOfWeekSectionShort");
1982     case QDateTimeParser::DayOfWeekSectionLong: return QLatin1String("DayOfWeekSectionLong");
1983     case QDateTimeParser::Hour24Section: return QLatin1String("Hour24Section");
1984     case QDateTimeParser::Hour12Section: return QLatin1String("Hour12Section");
1985     case QDateTimeParser::MSecSection: return QLatin1String("MSecSection");
1986     case QDateTimeParser::MinuteSection: return QLatin1String("MinuteSection");
1987     case QDateTimeParser::MonthSection: return QLatin1String("MonthSection");
1988     case QDateTimeParser::SecondSection: return QLatin1String("SecondSection");
1989     case QDateTimeParser::TimeZoneSection: return QLatin1String("TimeZoneSection");
1990     case QDateTimeParser::YearSection: return QLatin1String("YearSection");
1991     case QDateTimeParser::YearSection2Digits: return QLatin1String("YearSection2Digits");
1992     case QDateTimeParser::NoSection: return QLatin1String("NoSection");
1993     case QDateTimeParser::FirstSection: return QLatin1String("FirstSection");
1994     case QDateTimeParser::LastSection: return QLatin1String("LastSection");
1995     default: return QLatin1String("Unknown section ") + QString::number(int(s));
1996     }
1997 }
1998 
1999 /*!
2000   \internal
2001   For debugging. Returns the name of the state \a s.
2002 */
2003 
2004 QString QDateTimeParser::stateName(State s) const
2005 {
2006     switch (s) {
2007     case Invalid: return QLatin1String("Invalid");
2008     case Intermediate: return QLatin1String("Intermediate");
2009     case Acceptable: return QLatin1String("Acceptable");
2010     default: return QLatin1String("Unknown state ") + QString::number(s);
2011     }
2012 }
2013 
2014 #if QT_CONFIG(datestring)
2015 bool QDateTimeParser::fromString(const QString &t, QDate *date, QTime *time) const
2016 {
2017     QDateTime val(QDate(1900, 1, 1), QDATETIMEEDIT_TIME_MIN);
2018     const StateNode tmp = parse(t, -1, val, false);
2019     if (tmp.state != Acceptable || tmp.conflicts) {
2020         return false;
2021     }
2022     if (time) {
2023         const QTime t = tmp.value.time();
2024         if (!t.isValid()) {
2025             return false;
2026         }
2027         *time = t;
2028     }
2029 
2030     if (date) {
2031         const QDate d = tmp.value.date();
2032         if (!d.isValid()) {
2033             return false;
2034         }
2035         *date = d;
2036     }
2037     return true;
2038 }
2039 #endif // datestring
2040 
2041 QDateTime QDateTimeParser::getMinimum() const
2042 {
2043     // Cache the most common case
2044     if (spec == Qt::LocalTime) {
2045         static const QDateTime localTimeMin(QDATETIMEEDIT_DATE_MIN, QDATETIMEEDIT_TIME_MIN, Qt::LocalTime);
2046         return localTimeMin;
2047     }
2048     return QDateTime(QDATETIMEEDIT_DATE_MIN, QDATETIMEEDIT_TIME_MIN, spec);
2049 }
2050 
2051 QDateTime QDateTimeParser::getMaximum() const
2052 {
2053     // Cache the most common case
2054     if (spec == Qt::LocalTime) {
2055         static const QDateTime localTimeMax(QDATETIMEEDIT_DATE_MAX, QDATETIMEEDIT_TIME_MAX, Qt::LocalTime);
2056         return localTimeMax;
2057     }
2058     return QDateTime(QDATETIMEEDIT_DATE_MAX, QDATETIMEEDIT_TIME_MAX, spec);
2059 }
2060 
2061 QString QDateTimeParser::getAmPmText(AmPm ap, Case cs) const
2062 {
2063     const QLocale loc = locale();
2064     QString raw = ap == AmText ? loc.amText() : loc.pmText();
2065     return cs == UpperCase ? raw.toUpper() : raw.toLower();
2066 }
2067 
2068 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
2069 QString qTzName(int dstIndex)
2070 {
2071     char name[512];
2072     bool ok;
2073 #if defined(Q_CC_MSVC)
2074     size_t s = 0;
2075     {
2076         const auto locker = std::scoped_lock(environmentMutex);
2077         ok = _get_tzname(&s, name, 512, dstIndex) != 0;
2078     }
2079 #else
2080     {
2081         const auto locker = std::scoped_lock(environmentMutex);
2082         const char *const src = tzname[dstIndex];
2083         ok = src != nullptr;
2084         if (ok)
2085             memcpy(name, src, std::min(sizeof(name), strlen(src) + 1));
2086     }
2087 #endif // Q_OS_WIN
2088     return ok ? QString::fromLocal8Bit(name) : QString();
2089 }
2090 
2091 int QDateTimeParser::startsWithLocalTimeZone(QStringView name, const QDateTime &when)
2092 {
2093     // On MS-Win, at least when system zone is UTC, the tzname[]s may be empty.
2094     for (int i = 0; i < 2; ++i) {
2095         const QString zone(qTzName(i));
2096         if (!zone.isEmpty() && name.startsWith(zone))
2097             return zone.size();
2098     }
2099     // Mimic what QLocale::toString() would have used, to ensure round-trips
2100     // work:
2101     const QString local = QDateTime(when.date(), when.time()).timeZoneAbbreviation();
2102     if (name.startsWith(local))
2103         return local.size();
2104     return 0;
2105 }
2106 #endif
2107 
2108 /*
2109   \internal
2110 
2111   I give arg2 preference because arg1 is always a QDateTime.
2112 */
2113 
2114 bool operator==(const QDateTimeParser::SectionNode &s1, const QDateTimeParser::SectionNode &s2)
2115 {
2116     return (s1.type == s2.type) && (s1.pos == s2.pos) && (s1.count == s2.count);
2117 }
2118 
2119 QT_END_NAMESPACE