summaryrefslogtreecommitdiffstats
path: root/src/corelib/time/qdatetime.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/time/qdatetime.cpp')
-rw-r--r--src/corelib/time/qdatetime.cpp157
1 files changed, 92 insertions, 65 deletions
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp
index 756264a00e..f881d47284 100644
--- a/src/corelib/time/qdatetime.cpp
+++ b/src/corelib/time/qdatetime.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -75,6 +75,7 @@
#include "qcalendar.h"
#include "qgregoriancalendar_p.h"
+#include "private/qnumeric_p.h"
QT_BEGIN_NAMESPACE
@@ -240,7 +241,7 @@ static int fromOffsetString(QStringView offsetString, bool *valid) noexcept
const QStringView hhRef = time.left(qMin(hhLen, time.size()));
bool ok = false;
const int hour = C.toInt(hhRef, &ok);
- if (!ok)
+ if (!ok || hour > 23) // More generous than QTimeZone::MaxUtcOffsetSecs
return 0;
const QStringView mmRef = time.mid(qMin(mmIndex, time.size()));
@@ -1429,9 +1430,11 @@ QDate QDate::addDays(qint64 ndays) const
if (isNull())
return QDate();
- // Due to limits on minJd() and maxJd() we know that any overflow
- // will be invalid and caught by fromJulianDay().
- return fromJulianDay(jd + ndays);
+ qint64 r;
+ if (Q_UNLIKELY(add_overflow(jd, ndays, &r)))
+ return QDate();
+ else
+ return fromJulianDay(r);
}
/*!
@@ -1701,22 +1704,25 @@ QT_WARNING_POP
return rfcDateImpl(string).date;
default:
case Qt::TextDate: {
+ // Accept only "ddd MMM d yyyy" form (in contrast with QDateTime), e.g. "Sun Dec 1 1974"
QVector<QStringRef> parts = string.splitRef(QLatin1Char(' '), Qt::SkipEmptyParts);
-
- if (parts.count() != 4)
+ const int count = parts.count();
+ bool ok = count > 3;
+ int year = ok ? parts.at(count - 1).toInt(&ok) : 0;
+ int day = ok ? parts.at(count - 2).toInt(&ok) : 0;
+ if (!ok || !day || !year)
return QDate();
- bool ok = false;
- int year = parts.at(3).toInt(&ok);
- int day = ok ? parts.at(2).toInt(&ok) : 0;
- if (!ok || !day)
- return QDate();
-
- const int month = fromShortMonthName(parts.at(1), year);
- if (month == -1) // Month name matches no English or localised name.
- return QDate();
-
- return QDate(year, month, day);
+ // Some locales have multi-word month names:
+ int i = count - 3;
+ QString monthName = parts.at(i).toString();
+ while (i > 0) {
+ const int month = fromShortMonthName(monthName, year);
+ if (month > 0) // Month name matches an English or localised name.
+ return QDate(year, month, day);
+ monthName = parts.at(--i) + QLatin1Char(' ') + monthName;
+ }
+ return QDate();
}
case Qt::ISODate:
// Semi-strict parsing, must be long enough and have punctuators as separators
@@ -2612,6 +2618,8 @@ bool QTime::isValid(int h, int m, int s, int ms)
#if QT_DEPRECATED_SINCE(5, 14) // ### Qt 6: remove
/*!
+ \deprecated
+
Sets this time to the current time. This is practical for timing:
\snippet code/src_corelib_tools_qdatetime.cpp 10
@@ -2655,6 +2663,8 @@ int QTime::restart()
}
/*!
+ \deprecated
+
Returns the number of milliseconds that have elapsed since the
last time start() or restart() was called.
@@ -4541,9 +4551,9 @@ static inline void massageAdjustedDateTime(const QDateTimeData &d, QDate *date,
QDateTimePrivate::DaylightStatus status = QDateTimePrivate::UnknownDaylightTime;
localMSecsToEpochMSecs(timeToMSecs(*date, *time), &status, date, time);
#if QT_CONFIG(timezone)
- } else if (spec == Qt::TimeZone && d->m_timeZone.isValid()) {
+ } else if (spec == Qt::TimeZone && d.d->m_timeZone.isValid()) {
QDateTimePrivate::zoneMSecsToEpochMSecs(timeToMSecs(*date, *time),
- d->m_timeZone,
+ d.d->m_timeZone,
QDateTimePrivate::UnknownDaylightTime,
date, time);
#endif // timezone
@@ -4644,7 +4654,7 @@ QDateTime QDateTime::addSecs(qint64 s) const
}
/*!
- Returns a QDateTime object containing a datetime \a msecs miliseconds
+ Returns a QDateTime object containing a datetime \a msecs milliseconds
later than the datetime of this object (or earlier if \a msecs is
negative).
@@ -5374,49 +5384,66 @@ QT_WARNING_POP
case Qt::TextDate: {
QVector<QStringRef> parts = string.splitRef(QLatin1Char(' '), Qt::SkipEmptyParts);
- if ((parts.count() < 5) || (parts.count() > 6))
+ const int count = parts.count();
+ if (count < 5)
return QDateTime();
- // Accept "Sun Dec 1 13:02:00 1974" and "Sun 1. Dec 13:02:00 1974"
+ // Accept "Sun Dec 1 13:02:00 1974" and "Sun 1. Dec 13:02:00 1974" with
+ // optional GMT-based zone-suffix, with time and year in either order;
+ // and some locales have spaces even in short names of months and days.
+ int tail = count - 1, zonePart = 0;
+ if (parts.at(tail).startsWith(QLatin1String("GMT"), Qt::CaseInsensitive))
+ zonePart = tail--;
// Year and time can be in either order.
// Guess which by looking for ':' in the time
- int yearPart = 3;
- int timePart = 3;
- if (parts.at(3).contains(QLatin1Char(':')))
- yearPart = 4;
- else if (parts.at(4).contains(QLatin1Char(':')))
- timePart = 4;
+ int yearPart = tail;
+ int timePart = tail;
+ if (parts.at(timePart).contains(QLatin1Char(':')))
+ yearPart--;
+ else if (parts.at(timePart - 1).contains(QLatin1Char(':')))
+ timePart--;
else
return QDateTime();
- int month = 0;
- int day = 0;
- bool ok = false;
+ int dayPart = tail - 2; // but may be earlier, with a comma after:
+ for (int i = 1; i < dayPart; i++) {
+ if (parts.at(i).endsWith(QLatin1Char('.')))
+ dayPart = i; // exits the loop
+ }
+ bool ok = false;
int year = parts.at(yearPart).toInt(&ok);
- if (!ok || year == 0)
+ int day = 0;
+
+ if (ok && year) {
+ QStringRef dayStr = parts.at(dayPart);
+ if (dayStr.endsWith(QLatin1Char('.')))
+ dayStr.chop(1);
+ day = dayStr.toInt(&ok);
+ }
+ if (!ok || !year || !day)
return QDateTime();
- // Next try month then day
- month = fromShortMonthName(parts.at(1), year);
- if (month)
- day = parts.at(2).toInt(&ok);
-
- // If failed, try day then month
- if (!ok || !month || !day) {
- month = fromShortMonthName(parts.at(2), year);
- if (month) {
- QStringRef dayStr = parts.at(1);
- if (dayStr.endsWith(QLatin1Char('.'))) {
- dayStr = dayStr.left(dayStr.size() - 1);
- day = dayStr.toInt(&ok);
- }
+ int month = 0;
+ if (dayPart < tail - 2) {
+ // Easy case, month is the parts from dayPart + 1 to count - 3
+ int i = dayPart + 1;
+ QString monthName = parts.at(i).toString();
+ while (++i < tail - 1)
+ monthName += QLatin1Char(' ') + parts.at(i);
+ month = fromShortMonthName(monthName, year);
+ } else {
+ int i = dayPart - 1;
+ QString monthName = parts.at(i).toString();
+ while (i > 0) {
+ month = fromShortMonthName(monthName, year);
+ if (month > 0)
+ break;
+ monthName = parts.at(--i) + QLatin1Char(' ') + monthName;
}
}
-
- // If both failed, give up
- if (!ok || !month || !day)
+ if (month <= 0)
return QDateTime();
QDate date(year, month, day);
@@ -5460,21 +5487,15 @@ QT_WARNING_POP
if (!time.isValid())
return QDateTime();
- if (parts.count() == 5)
+ if (!zonePart)
return QDateTime(date, time, Qt::LocalTime);
- QStringView tz = parts.at(5);
- if (!tz.startsWith(QLatin1String("GMT"), Qt::CaseInsensitive))
- return QDateTime();
- tz = tz.mid(3);
- if (!tz.isEmpty()) {
- int offset = fromOffsetString(tz, &ok);
- if (!ok)
- return QDateTime();
- return QDateTime(date, time, Qt::OffsetFromUTC, offset);
- } else {
+ const QStringView tz = parts.at(zonePart).mid(3);
+ if (tz.isEmpty())
return QDateTime(date, time, Qt::UTC);
- }
+
+ int offset = fromOffsetString(tz, &ok);
+ return ok ? QDateTime(date, time, Qt::OffsetFromUTC, offset) : QDateTime();
}
}
@@ -5502,7 +5523,13 @@ QT_WARNING_POP
\snippet code/src_corelib_tools_qdatetime.cpp 12
- If the format is not satisfied, an invalid QDateTime is returned.
+ If the format is not satisfied, an invalid QDateTime is returned. If the
+ format is satisfied but \a string represents an invalid date-time (e.g. in a
+ gap skipped by a time-zone transition), an invalid QDateTime is returned,
+ whose toMSecsSinceEpoch() represents a near-by date-time that is
+ valid. Passing that to fromMSecsSinceEpoch() will produce a valid date-time
+ that isn't faithfully represented by the string parsed.
+
The expressions that don't have leading zeroes (d, M, h, m, s, z) will be
greedy. This means that they will use two digits even if this will
put them outside the range and/or leave too few digits for other
@@ -5557,7 +5584,7 @@ QDateTime QDateTime::fromString(const QString &string, const QString &format, QC
QDateTimeParser dt(QMetaType::QDateTime, QDateTimeParser::FromString, cal);
// dt.setDefaultLocale(QLocale::c()); ### Qt 6
- if (dt.parseFormat(format) && dt.fromString(string, &datetime))
+ if (dt.parseFormat(format) && (dt.fromString(string, &datetime) || !datetime.isValid()))
return datetime;
#else
Q_UNUSED(string);
@@ -5567,7 +5594,7 @@ QDateTime QDateTime::fromString(const QString &string, const QString &format, QC
return QDateTime();
}
-/*
+/*!
\overload
*/