summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
authorAndreas Buhr <andreas.buhr@qt.io>2020-09-04 15:54:19 +0200
committerAndreas Buhr <andreas.buhr@qt.io>2020-10-30 17:19:27 +0100
commit91140eb7268bc36efceef0459e7b124ce333829e (patch)
treea9c69248e57d2f02e1636e380ef8e22eeb1015e4 /src/corelib
parent2da301fe46450eedabfde7860338a2c0066c550b (diff)
Adapt QDate::fromString() to accept negative year numbers
The documentation states that QDate::fromString() accepts negative year numbers, but it did not. This patch adds support for negative year numbers to QDate::fromString() and corresponding unit tests. Furthermore, tests are added for positive signs (+) in date strings. Fixes: QTBUG-84334 Task-number: QTBUG-84349 Change-Id: I575291e7b8317055d4bb530011d7b10c9cd37ae1 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/time/qdatetimeparser.cpp124
1 files changed, 80 insertions, 44 deletions
diff --git a/src/corelib/time/qdatetimeparser.cpp b/src/corelib/time/qdatetimeparser.cpp
index df07e68999..a36eded2e5 100644
--- a/src/corelib/time/qdatetimeparser.cpp
+++ b/src/corelib/time/qdatetimeparser.cpp
@@ -39,6 +39,7 @@
#include "qplatformdefs.h"
#include "private/qdatetimeparser_p.h"
+#include "private/qstringiterator_p.h"
#include "qdatastream.h"
#include "qset.h"
@@ -265,7 +266,9 @@ int QDateTimeParser::absoluteMin(int s) const
case SecondSection:
case MSecSection:
case YearSection2Digits:
- case YearSection: return 0;
+ return 0;
+ case YearSection:
+ return -9999;
case MonthSection:
case DaySection:
case DayOfWeekSectionShort:
@@ -335,6 +338,22 @@ int QDateTimeParser::sectionPos(const SectionNode &sn) const
return sn.pos;
}
+/*!
+ \internal
+
+ Helper function for parseSection.
+*/
+
+static qsizetype digitCount(QStringView str)
+{
+ qsizetype digits = 0;
+ for (QStringIterator it(str); it.hasNext();) {
+ if (!QChar::isDigit(it.next()))
+ break;
+ digits++;
+ }
+ return digits;
+}
/*!
\internal
@@ -739,7 +758,12 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex, i
"QDateTimeParser::parseSection", "Internal error");
const int sectionmaxsize = sectionMaxSize(sectionIndex);
- QStringView sectionTextRef = QStringView{m_text}.mid(offset, sectionmaxsize);
+ const bool negate = (sn.type == YearSection && m_text.size() > offset
+ && m_text.at(offset) == QLatin1Char('-'));
+ const int negativeYearOffset = negate ? 1 : 0;
+
+ QStringView sectionTextRef =
+ QStringView { m_text }.mid(offset + negativeYearOffset, sectionmaxsize);
QDTPDEBUG << "sectionValue for" << sn.name()
<< "with text" << m_text << "and (at" << offset
@@ -810,63 +834,75 @@ QDateTimeParser::parseSection(const QDateTime &currentValue, int sectionIndex, i
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).isDigit())
- sectiontextSize = i; // which exits the loop
- }
+ int used = negativeYearOffset;
+ // We already sliced off the - sign if it was legitimately present.
+ if (sectionTextRef.startsWith(QLatin1Char('-'))
+ || sectionTextRef.startsWith(QLatin1Char('+'))) {
+ if (separators.at(sectionIndex + 1).startsWith(sectionTextRef[0]))
+ result = ParsedSection(Intermediate, 0, used);
+ break;
+ }
+ QStringView digitsStr = sectionTextRef.left(digitCount(sectionTextRef));
- const int absMax = absoluteMax(sectionIndex);
+ if (digitsStr.isEmpty()) {
+ result = ParsedSection(Intermediate, 0, used);
+ } else {
const QLocale loc = locale();
- bool ok = true;
- int last = -1, used = -1;
-
- Q_ASSERT(sectiontextSize <= sectionmaxsize);
- QStringView digitsStr = sectionTextRef.first(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.first(digits) << tmp << digits;
- last = tmp;
- used = digits;
- break;
+ const int absMax = absoluteMax(sectionIndex);
+ const int absMin = absoluteMin(sectionIndex);
+
+ int last = -1;
+
+ for (; digitsStr.size(); digitsStr.chop(1)) {
+ bool ok = false;
+ int value = int(loc.toUInt(digitsStr, &ok));
+ if (!ok || (negate ? -value < absMin : value > absMax))
+ continue;
+
+ if (sn.type == Hour12Section) {
+ if (value > 12)
+ continue;
+ if (value == 12)
+ value = 0;
}
+
+ QDTPDEBUG << digitsStr << value << digitsStr.size();
+ last = value;
+ used += digitsStr.size();
+ break;
}
if (last == -1) {
- QChar first(sectionTextRef.at(0));
- if (separators.at(sectionIndex + 1).startsWith(first))
- result = ParsedSection(Intermediate, 0, used);
+ const auto &sep = separators.at(sectionIndex + 1);
+ if (sep.startsWith(sectionTextRef[0])
+ || (negate && sep.startsWith(m_text.at(offset))))
+ result = ParsedSection(Intermediate, 0, 0);
else
- QDTPDEBUG << "invalid because" << sectionTextRef << "can't become a uint" << last << ok;
+ QDTPDEBUG << "invalid because" << sectionTextRef << "can't become a uint"
+ << last;
} else {
+ if (negate)
+ last = -last;
const FieldInfo fi = fieldInfo(sectionIndex);
- const bool unfilled = used < sectionmaxsize;
+ const bool unfilled = used - negativeYearOffset < 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)
+ Q_ASSERT(negate ? last >= absMin : last <= absMax);
+ if (negate ? last > absMax : 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)) {
+ } else if (negate) {
+ QDTPDEBUG << "invalid because" << last << "is greater than absoluteMax"
+ << absMax;
+ } 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);