summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/global/qnamespace.qdoc23
-rw-r--r--src/corelib/time/qdatetime.cpp36
-rw-r--r--tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp20
-rw-r--r--tests/auto/corelib/time/qtime/tst_qtime.cpp14
4 files changed, 69 insertions, 24 deletions
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index a877b17a4f..3cddb711c5 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -709,18 +709,17 @@
fractional part suffix on the preceding field, which may be separated from
that field either by a comma \c{','} or the dot \c{'.'} shown. Precision
beyond milliseconds is accepted but discarded, rounding to the nearest
- millisecond or, when rounding fractional seconds up would change the second
- field, rounded down. The presence of a literal \c T character is used to
- separate the date and time when both are specified. For the \c TextDate and
- \c RFC2822Date formats, \c{ddd} stands for the first three letters of the
- name of the day of the week and \c{MMM} stands for the first three letters
- of the month name. The names of days and months are always in English (C
- locale) regardless of user preferences or system settings. The other format
- characters have the same meaning as for the ISODate format, except that 24
- is not accepted as an hour. Parts of a format enclosed in square brackets
- \c{[...]} are optional; the square brackets do not form part of the
- format. The plus-or-minus character \c{'±'} here stands for either sign
- character, \c{'-'} for minus or \c{'+'} for plus.
+ representable millisecond. The presence of a literal \c T character is used
+ to separate the date and time when both are specified. For the \c TextDate
+ and \c RFC2822Date formats, \c{ddd} stands for the first three letters of
+ the name of the day of the week and \c{MMM} stands for the first three
+ letters of the month name. The names of days and months are always in
+ English (C locale) regardless of user preferences or system settings. The
+ other format characters have the same meaning as for the ISODate format,
+ except that 24 is not accepted as an hour. Parts of a format enclosed in
+ square brackets \c{[...]} are optional; the square brackets do not form part
+ of the format. The plus-or-minus character \c{'±'} here stands for either
+ sign character, \c{'-'} for minus or \c{'+'} for plus.
\sa QDate::toString(), QTime::toString(), QDateTime::toString(),
QDate::fromString(), QTime::fromString(), QDateTime::fromString()
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp
index da3fee5b81..96932ff129 100644
--- a/src/corelib/time/qdatetime.cpp
+++ b/src/corelib/time/qdatetime.cpp
@@ -2113,6 +2113,7 @@ int QTime::msecsTo(QTime t) const
static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *isMidnight24)
{
+ Q_ASSERT(format == Qt::TextDate || format == Qt::ISODate || format == Qt::ISODateWithMs);
if (isMidnight24)
*isMidnight24 = false;
// Match /\d\d(:\d\d(:\d\d)?)?([,.]\d+)?/ as "HH[:mm[:ss]][.zzz]"
@@ -2145,14 +2146,14 @@ static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *
return QTime();
ParsedInt hour = readInt(string.first(2));
- if (!hour.ok)
+ if (!hour.ok || hour.value > (format == Qt::TextDate ? 23 : 24))
return QTime();
ParsedInt minute;
if (string.size() > 2) {
if (string[2] == u':' && string.size() > 4)
minute = readInt(string.sliced(3, 2));
- if (!minute.ok)
+ if (!minute.ok || minute.value >= 60)
return QTime();
} else if (format == Qt::TextDate) { // Requires minutes
return QTime();
@@ -2167,7 +2168,7 @@ static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *
if (string.size() > 5) {
if (string[5] == u':' && string.size() == 8)
second = readInt(string.sliced(6, 2));
- if (!second.ok)
+ if (!second.ok || second.value >= 60)
return QTime();
} else if (frac.ok) {
if (format == Qt::TextDate) // Doesn't allow fraction of minutes
@@ -2179,13 +2180,32 @@ static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *
}
Q_ASSERT(!(fraction < 0.0) && fraction < 1.0);
- // Round millis to nearest (unlike minutes and seconds, rounded down),
- // but clip to 999 (historical behavior):
- const int msec = frac.ok ? qMin(qRound(1000 * fraction), 999) : 0;
+ // Round millis to nearest (unlike minutes and seconds, rounded down):
+ int msec = frac.ok ? qRound(1000 * fraction) : 0;
+ // But handle overflow gracefully:
+ if (msec == 1000) {
+ // If we can (when data were otherwise valid) validly propagate overflow
+ // into other fields, do so:
+ if (isMidnight24 || hour.value < 23 || minute.value < 59 || second.value < 59) {
+ msec = 0;
+ if (++second.value == 60) {
+ second.value = 0;
+ if (++minute.value == 60) {
+ minute.value = 0;
+ ++hour.value;
+ // May need to propagate further via isMidnight24, see below
+ }
+ }
+ } else {
+ // QTime::fromString() or Qt::TextDate: rounding up would cause
+ // 23:59:59.999... to become invalid; clip to 999 ms instead:
+ msec = 999;
+ }
+ }
// For ISO date format, 24:0:0 means 0:0:0 on the next day:
- if ((format == Qt::ISODate || format == Qt::ISODateWithMs)
- && hour.value == 24 && minute.value == 0 && second.value == 0 && msec == 0) {
+ if (hour.value == 24 && minute.value == 0 && second.value == 0 && msec == 0) {
+ Q_ASSERT(format != Qt::TextDate); // It clipped hour at 23, above.
if (isMidnight24)
*isMidnight24 = true;
hour.value = 0;
diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp
index 18b7a9d74f..2e2b5fbf24 100644
--- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp
+++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp
@@ -2202,6 +2202,15 @@ void tst_QDateTime::fromStringDateFormat_data()
<< Qt::TextDate << QDateTime();
QTest::newRow("text second fraction") << QString::fromLatin1("Mon 6. May 2013 01:02:03.456")
<< Qt::TextDate << QDateTime(QDate(2013, 5, 6), QTime(1, 2, 3, 456));
+ QTest::newRow("text max milli")
+ << QString::fromLatin1("Mon 6. May 2013 01:02:03.999499999")
+ << Qt::TextDate << QDateTime(QDate(2013, 5, 6), QTime(1, 2, 3, 999));
+ QTest::newRow("text milli wrap")
+ << QString::fromLatin1("Mon 6. May 2013 01:02:03.9995")
+ << Qt::TextDate << QDateTime(QDate(2013, 5, 6), QTime(1, 2, 4));
+ QTest::newRow("text last milli") // Special case, don't round up to invalid:
+ << QString::fromLatin1("Mon 6. May 2013 23:59:59.9999999999")
+ << Qt::TextDate << QDateTime(QDate(2013, 5, 6), QTime(23, 59, 59, 999));
const QDateTime ref(QDate(1974, 12, 1), QTime(13, 2));
QTest::newRow("day:,:month")
@@ -2331,6 +2340,17 @@ void tst_QDateTime::fromStringDateFormat_data()
<< Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 333), Qt::LocalTime);
QTest::newRow("ISO .00009 of a second (period)") << QString::fromLatin1("2012-01-01T08:00:00.00009")
<< Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 0), Qt::LocalTime);
+ QTest::newRow("ISO second fraction") << QString::fromLatin1("2013-05-06T01:02:03.456")
+ << Qt::ISODate << QDateTime(QDate(2013, 5, 6), QTime(1, 2, 3, 456));
+ QTest::newRow("ISO max milli")
+ << QString::fromLatin1("2013-05-06T01:02:03.999499999")
+ << Qt::ISODate << QDateTime(QDate(2013, 5, 6), QTime(1, 2, 3, 999));
+ QTest::newRow("ISO milli wrap")
+ << QString::fromLatin1("2013-05-06T01:02:03.9995")
+ << Qt::ISODate << QDateTime(QDate(2013, 5, 6), QTime(1, 2, 4));
+ QTest::newRow("ISO last milli") // Does round up and overflow into new day:
+ << QString::fromLatin1("2013-05-06T23:59:59.9999999999")
+ << Qt::ISODate << QDate(2013, 5, 7).startOfDay();
QTest::newRow("ISO no fraction specified")
<< QString::fromLatin1("2012-01-01T08:00:00.") << Qt::ISODate << QDateTime();
// Test invalid characters (should ignore invalid characters at end of string).
diff --git a/tests/auto/corelib/time/qtime/tst_qtime.cpp b/tests/auto/corelib/time/qtime/tst_qtime.cpp
index b79cb609e7..ce8a24515b 100644
--- a/tests/auto/corelib/time/qtime/tst_qtime.cpp
+++ b/tests/auto/corelib/time/qtime/tst_qtime.cpp
@@ -588,10 +588,12 @@ void tst_QTime::fromStringDateFormat_data()
<< QString("10:12:34") << Qt::TextDate << QTime(10, 12, 34);
QTest::newRow("TextDate - milli-max")
<< QString("19:03:54.998601") << Qt::TextDate << QTime(19, 3, 54, 999);
- QTest::newRow("TextDate - milli-no-overflow")
- << QString("19:03:54.999601") << Qt::TextDate << QTime(19, 3, 54, 999);
+ QTest::newRow("TextDate - milli-wrap")
+ << QString("19:03:54.999601") << Qt::TextDate << QTime(19, 3, 55);
QTest::newRow("TextDate - no-secs")
<< QString("10:12") << Qt::TextDate << QTime(10, 12);
+ QTest::newRow("TextDate - midnight-nowrap")
+ << QString("23:59:59.9999") << Qt::TextDate << QTime(23, 59, 59, 999);
QTest::newRow("TextDate - invalid, minutes") << QString::fromLatin1("23:XX:00") << Qt::TextDate << invalidTime();
QTest::newRow("TextDate - invalid, minute fraction") << QString::fromLatin1("23:00.123456") << Qt::TextDate << invalidTime();
QTest::newRow("TextDate - invalid, seconds") << QString::fromLatin1("23:00:XX") << Qt::TextDate << invalidTime();
@@ -601,6 +603,8 @@ void tst_QTime::fromStringDateFormat_data()
QTest::newRow("IsoDate - valid, start of day, omit seconds") << QString::fromLatin1("00:00") << Qt::ISODate << QTime(0, 0, 0);
QTest::newRow("IsoDate - valid, omit seconds") << QString::fromLatin1("22:21") << Qt::ISODate << QTime(22, 21, 0);
+ QTest::newRow("IsoDate - minute fraction") // 60 * 0.816666 = 48.99996 should round up:
+ << QString::fromLatin1("22:21.816666") << Qt::ISODate << QTime(22, 21, 49);
QTest::newRow("IsoDate - valid, omit seconds (2)") << QString::fromLatin1("23:59") << Qt::ISODate << QTime(23, 59, 0);
QTest::newRow("IsoDate - valid, end of day") << QString::fromLatin1("23:59:59") << Qt::ISODate << QTime(23, 59, 59);
@@ -619,8 +623,10 @@ void tst_QTime::fromStringDateFormat_data()
QTest::newRow("IsoDate - ordinary") << QString("10:12:34") << Qt::ISODate << QTime(10, 12, 34);
QTest::newRow("IsoDate - milli-max")
<< QString("19:03:54.998601") << Qt::ISODate << QTime(19, 3, 54, 999);
- QTest::newRow("IsoDate - milli-no-overflow")
- << QString("19:03:54.999601") << Qt::ISODate << QTime(19, 3, 54, 999);
+ QTest::newRow("IsoDate - milli-wrap")
+ << QString("19:03:54.999601") << Qt::ISODate << QTime(19, 3, 55);
+ QTest::newRow("IsoDate - midnight-nowrap")
+ << QString("23:59:59.9999") << Qt::ISODate << QTime(23, 59, 59, 999);
QTest::newRow("IsoDate - midnight 24")
<< QString("24:00:00") << Qt::ISODate << QTime(0, 0);
QTest::newRow("IsoDate - minute fraction midnight")