Use UTC when parsing only a date or only a time, not a date-time

This should reduce the amount of fall-out from DST complications.
Also document the assumptions of QDateTimeParser's two fromString()
methods (and fix the punctuation on the QDateTime parameter).
Adjusted some tests to match.

Since only QDateTime-returning methods will show the difference, and
it's at least somewhat odd to be using those on QDateEdit or
QTimeEdit, this should have little impact on API users.

[ChangeLog][QtCore][Behavior Change] QDateEdit and QTimeEdit now
operate in UTC, to avoid spurious complications arising from time-zone
transitions (e.g. DST) causing the implicit other half to combine with
the part being edited to make an invalid result. Returns from their
dateTime() and other methods returning QDateTime (max/min) shall thus
be in UTC where previously they were in local time. QDateTimeEdit
continues using local time. The default can be over-ridden by
setTimeSpec(), as ever.

Change-Id: I44fece004c12342fe536bbe3048217d236fd97b2
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Edward Welbourne 2021-06-01 18:27:48 +02:00
parent 5ad13ec57d
commit c00ee2f310
4 changed files with 22 additions and 14 deletions

View File

@ -2198,21 +2198,25 @@ QString QDateTimeParser::stateName(State s) const
}
}
// Only called when we want only one of date or time; use UTC to avoid bogus DST issues.
bool QDateTimeParser::fromString(const QString &t, QDate *date, QTime *time) const
{
QDateTime datetime;
if (!fromString(t, &datetime))
QDateTime val(QDate(1900, 1, 1).startOfDay(Qt::UTC));
const StateNode tmp = parse(t, -1, val, false);
if (tmp.state != Acceptable || tmp.conflicts)
return false;
if (time) {
const QTime t = datetime.time();
Q_ASSERT(!date);
const QTime t = tmp.value.time();
if (!t.isValid())
return false;
*time = t;
}
if (date) {
const QDate d = datetime.date();
Q_ASSERT(!time);
const QDate d = tmp.value.date();
if (!d.isValid())
return false;
*date = d;
@ -2220,7 +2224,8 @@ bool QDateTimeParser::fromString(const QString &t, QDate *date, QTime *time) con
return true;
}
bool QDateTimeParser::fromString(const QString &t, QDateTime* datetime) const
// Only called when we want both date and time; default to local time.
bool QDateTimeParser::fromString(const QString &t, QDateTime *datetime) const
{
QDateTime val(QDate(1900, 1, 1).startOfDay());
const StateNode tmp = parse(t, -1, val, false);

View File

@ -223,7 +223,9 @@ QDateTimeEdit::QDateTimeEdit(QTime time, QWidget *parent)
\internal
*/
QDateTimeEdit::QDateTimeEdit(const QVariant &var, QMetaType::Type parserType, QWidget *parent)
: QAbstractSpinBox(*new QDateTimeEditPrivate, parent)
: QAbstractSpinBox(*new QDateTimeEditPrivate(
parserType == QMetaType::QDateTime ? Qt::LocalTime : Qt::UTC),
parent)
{
Q_D(QDateTimeEdit);
d->parserType = parserType;
@ -1734,16 +1736,17 @@ QDateEdit::~QDateEdit()
*/
QDateTimeEditPrivate::QDateTimeEditPrivate()
: QDateTimeParser(QMetaType::QDateTime, QDateTimeParser::DateTimeEdit, QCalendar())
QDateTimeEditPrivate::QDateTimeEditPrivate(Qt::TimeSpec timeSpec)
: QDateTimeParser(QMetaType::QDateTime, QDateTimeParser::DateTimeEdit, QCalendar()),
spec(timeSpec)
{
fixday = true;
type = QMetaType::QDateTime;
currentSectionIndex = FirstSectionIndex;
first.pos = 0;
minimum = QDATETIMEEDIT_COMPAT_DATE_MIN.startOfDay();
maximum = QDATETIMEEDIT_DATE_MAX.endOfDay();
minimum = QDATETIMEEDIT_COMPAT_DATE_MIN.startOfDay(spec);
maximum = QDATETIMEEDIT_DATE_MAX.endOfDay(spec);
readLocaleSettings();
}

View File

@ -70,7 +70,7 @@ class Q_AUTOTEST_EXPORT QDateTimeEditPrivate : public QAbstractSpinBoxPrivate, p
{
Q_DECLARE_PUBLIC(QDateTimeEdit)
public:
QDateTimeEditPrivate();
QDateTimeEditPrivate(Qt::TimeSpec timeSpec = Qt::LocalTime);
void init(const QVariant &var);
void readLocaleSettings();
@ -153,7 +153,7 @@ public:
bool focusOnButton = false;
#endif
Qt::TimeSpec spec = Qt::LocalTime;
Qt::TimeSpec spec;
};

View File

@ -1358,7 +1358,7 @@ void tst_QDateTimeEdit::editingRanged_data()
<< QDate(2010, 12, 30) << QTime()
<< QDate(2011, 1, 2) << QTime()
<< QString::fromLatin1("01012011")
<< QDateTime(QDate(2011, 1, 1), QTime());
<< QDateTime(QDate(2011, 1, 1), QTime(), Qt::UTC);
}
void tst_QDateTimeEdit::editingRanged()
@ -3141,9 +3141,9 @@ void tst_QDateTimeEdit::hour12Test()
void tst_QDateTimeEdit::yyTest()
{
testWidget->setDisplayFormat("dd-MMM-yy");
testWidget->setTime(QTime(0, 0, 0));
testWidget->setDateRange(QDate(2005, 1, 1), QDate(2010, 12, 31));
testWidget->setDate(testWidget->minimumDate());
testWidget->setTime(QTime(12, 0, 0)); // Mid-day to avoid DST artefacts.
testWidget->setCurrentSection(QDateTimeEdit::YearSection);
QString jan = QLocale::system().monthName(1, QLocale::ShortFormat);