summaryrefslogtreecommitdiffstats
path: root/src/widgets/widgets/qdatetimeedit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/widgets/qdatetimeedit.cpp')
-rw-r--r--src/widgets/widgets/qdatetimeedit.cpp455
1 files changed, 260 insertions, 195 deletions
diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp
index d825676035..01e52b2fa6 100644
--- a/src/widgets/widgets/qdatetimeedit.cpp
+++ b/src/widgets/widgets/qdatetimeedit.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWidgets module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <private/qapplication_p.h>
#include <private/qdatetimeedit_p.h>
@@ -51,9 +15,7 @@
#include <qlayout.h>
#include <qset.h>
#include <qstyle.h>
-#if QT_CONFIG(timezone)
-#include <QTimeZone>
-#endif
+#include <qstylepainter.h>
#include <algorithm>
@@ -68,6 +30,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
// --- QDateTimeEdit ---
/*!
@@ -104,6 +68,23 @@ QT_BEGIN_NAMESPACE
calendar pop-up by calling the setCalendarWidget() function. The existing
calendar widget can be retrieved with calendarWidget().
+ \section1 Keyboard Tracking
+
+ When \l{QAbstractSpinBox::keyboardTracking}{keyboard tracking} is enabled
+ (the default), every keystroke of editing a field triggers signals for value
+ changes.
+
+ When the allowed \l{QDateTimeEdit::setDateTimeRange}{range} is narrower than
+ some time interval whose end it straddles, keyboard tracking prevents the
+ user editing the date or time to access the later part of the interval. For
+ example, for a range from 29.04.2020 to 02.05.2020 and an initial date of
+ 30.04.2020, the user can change neither the month (May 30th is outside the
+ range) nor the day (April 2nd is outside the range).
+
+ When keyboard tracking is disabled, changes are only signalled when focus
+ leaves the text field after edits have modified the content. This allows the
+ user to edit via an invalid date-time to reach a valid one.
+
\sa QDateEdit, QTimeEdit, QDate, QTime
*/
@@ -128,6 +109,8 @@ QT_BEGIN_NAMESPACE
This signal is emitted whenever the date or time is changed. The
new date and time is passed in \a datetime.
+
+ \sa {Keyboard Tracking}
*/
/*!
@@ -135,6 +118,8 @@ QT_BEGIN_NAMESPACE
This signal is emitted whenever the time is changed. The new time
is passed in \a time.
+
+ \sa {Keyboard Tracking}
*/
/*!
@@ -142,6 +127,8 @@ QT_BEGIN_NAMESPACE
This signal is emitted whenever the date is changed. The new date
is passed in \a date.
+
+ \sa {Keyboard Tracking}
*/
@@ -200,7 +187,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
+ ? QTimeZone::LocalTime : QTimeZone::UTC),
+ parent)
{
Q_D(QDateTimeEdit);
d->parserType = parserType;
@@ -218,8 +207,8 @@ QDateTimeEdit::~QDateTimeEdit()
\property QDateTimeEdit::dateTime
\brief The QDateTime that is set in the QDateTimeEdit.
- When setting this property, the new QDateTime is converted to the timespec of
- the QDateTimeEdit, which thus remains unchanged.
+ When setting this property, the new QDateTime is converted to the time system
+ of the QDateTimeEdit, which thus remains unchanged.
By default, this property is set to the start of 2000 CE. It can only be set
to a valid QDateTime value. If any operation causes this property to have an
@@ -230,7 +219,7 @@ QDateTimeEdit::~QDateTimeEdit()
widget's date-range to start and end on the date of the new value of this
property.
- \sa date, time, minimumDateTime, maximumDateTime
+ \sa date, time, minimumDateTime, maximumDateTime, timeZone
*/
QDateTime QDateTimeEdit::dateTime() const
@@ -243,8 +232,8 @@ void QDateTimeEdit::setDateTime(const QDateTime &datetime)
{
Q_D(QDateTimeEdit);
if (datetime.isValid()) {
- QDateTime when = d->convertTimeSpec(datetime);
- Q_ASSERT(when.timeSpec() == d->spec);
+ QDateTime when = d->convertTimeZone(datetime);
+ Q_ASSERT(when.timeRepresentation() == d->timeZone);
d->clearCache();
const QDate date = when.date();
@@ -280,14 +269,10 @@ void QDateTimeEdit::setDate(QDate date)
setDateRange(date, date);
d->clearCache();
- QDateTime when(date, d->value.toTime(), d->spec);
- // The specified time might not exist on the specified day,
- // i.e. the time is in the gap a spring-forward jumps over.
- if (!when.isValid())
- when = QDateTime::fromMSecsSinceEpoch(when.toMSecsSinceEpoch(), d->spec);
+ QDateTime when = d->dateTimeValue(date, d->value.toTime());
Q_ASSERT(when.isValid());
d->setValue(when, EmitIfChanged);
- d->updateTimeSpec();
+ d->updateTimeZone();
}
}
@@ -314,7 +299,7 @@ void QDateTimeEdit::setTime(QTime time)
Q_D(QDateTimeEdit);
if (time.isValid()) {
d->clearCache();
- d->setValue(QDateTime(d->value.toDate(), time, d->spec), EmitIfChanged);
+ d->setValue(d->dateTimeValue(d->value.toDate(), time), EmitIfChanged);
}
}
@@ -345,14 +330,15 @@ void QDateTimeEdit::setCalendar(QCalendar calendar)
minimumTime properties to the date and time parts of this property,
respectively. When setting this property, the \l maximumDateTime is adjusted,
if necessary, to ensure that the range remains valid. Otherwise, changing this
- property preserves the \l minimumDateTime property.
+ property preserves the \l maximumDateTime property.
This property can only be set to a valid QDateTime value. The earliest
date-time that setMinimumDateTime() accepts is the start of 100 CE. The
property's default is the start of September 14, 1752 CE. This default can be
restored with clearMinimumDateTime().
- \sa maximumDateTime, minimumTime, minimumDate, setDateTimeRange(), QDateTime::isValid()
+ \sa maximumDateTime, minimumTime, minimumDate, setDateTimeRange(),
+ QDateTime::isValid(), {Keyboard Tracking}
*/
QDateTime QDateTimeEdit::minimumDateTime() const
@@ -370,7 +356,7 @@ void QDateTimeEdit::setMinimumDateTime(const QDateTime &dt)
{
Q_D(QDateTimeEdit);
if (dt.isValid() && dt.date() >= QDATETIMEEDIT_DATE_MIN) {
- const QDateTime m = dt.toTimeSpec(d->spec);
+ const QDateTime m = dt.toTimeZone(d->timeZone);
const QDateTime max = d->maximum.toDateTime();
d->setRange(m, (max > m ? max : m));
}
@@ -393,7 +379,8 @@ void QDateTimeEdit::setMinimumDateTime(const QDateTime &dt)
default for this property. This default can be restored with
clearMaximumDateTime().
- \sa minimumDateTime, maximumTime, maximumDate(), setDateTimeRange(), QDateTime::isValid()
+ \sa minimumDateTime, maximumTime, maximumDate(), setDateTimeRange(),
+ QDateTime::isValid(), {Keyboard Tracking}
*/
QDateTime QDateTimeEdit::maximumDateTime() const
@@ -411,7 +398,7 @@ void QDateTimeEdit::setMaximumDateTime(const QDateTime &dt)
{
Q_D(QDateTimeEdit);
if (dt.isValid() && dt.date() <= QDATETIMEEDIT_DATE_MAX) {
- const QDateTime m = dt.toTimeSpec(d->spec);
+ const QDateTime m = dt.toTimeZone(d->timeZone);
const QDateTime min = d->minimum.toDateTime();
d->setRange((min < m ? min : m), m);
}
@@ -433,15 +420,20 @@ void QDateTimeEdit::setMaximumDateTime(const QDateTime &dt)
If either \a min or \a max is invalid, this function does nothing. If \a max
is less than \a min, \a min is used also as \a max.
- \sa minimumDateTime, maximumDateTime, setDateRange(), setTimeRange(), QDateTime::isValid()
+ If the range is narrower then a time interval whose end it spans, for example
+ a week that spans the end of a month, users can only edit the date-time to one
+ in the later part of the range if keyboard-tracking is disabled.
+
+ \sa minimumDateTime, maximumDateTime, setDateRange(), setTimeRange(),
+ QDateTime::isValid(), {Keyboard Tracking}
*/
void QDateTimeEdit::setDateTimeRange(const QDateTime &min, const QDateTime &max)
{
Q_D(QDateTimeEdit);
// FIXME: does none of the range checks applied to setMin/setMax methods !
- const QDateTime minimum = min.toTimeSpec(d->spec);
- const QDateTime maximum = (min > max ? minimum : max.toTimeSpec(d->spec));
+ const QDateTime minimum = min.toTimeZone(d->timeZone);
+ const QDateTime maximum = (min > max ? minimum : max.toTimeZone(d->timeZone));
d->setRange(minimum, maximum);
}
@@ -463,7 +455,8 @@ void QDateTimeEdit::setDateTimeRange(const QDateTime &min, const QDateTime &max)
default for this property is September 14, 1752 CE. This default can be
restored with clearMinimumDateTime().
- \sa maximumDate, minimumTime, minimumDateTime, setDateRange(), QDate::isValid()
+ \sa maximumDate, minimumTime, minimumDateTime, setDateRange(),
+ QDate::isValid(), {Keyboard Tracking}
*/
QDate QDateTimeEdit::minimumDate() const
@@ -475,9 +468,8 @@ QDate QDateTimeEdit::minimumDate() const
void QDateTimeEdit::setMinimumDate(QDate min)
{
Q_D(QDateTimeEdit);
- if (min.isValid() && min >= QDATETIMEEDIT_DATE_MIN) {
- setMinimumDateTime(QDateTime(min, d->minimum.toTime(), d->spec));
- }
+ if (min.isValid() && min >= QDATETIMEEDIT_DATE_MIN)
+ setMinimumDateTime(d->dateTimeValue(min, d->minimum.toTime()));
}
void QDateTimeEdit::clearMinimumDate()
@@ -503,7 +495,8 @@ void QDateTimeEdit::clearMinimumDate()
default for this property. This default can be restored with
clearMaximumDateTime().
- \sa minimumDate, maximumTime, maximumDateTime, setDateRange(), QDate::isValid()
+ \sa minimumDate, maximumTime, maximumDateTime, setDateRange(),
+ QDate::isValid(), {Keyboard Tracking}
*/
QDate QDateTimeEdit::maximumDate() const
@@ -516,7 +509,7 @@ void QDateTimeEdit::setMaximumDate(QDate max)
{
Q_D(QDateTimeEdit);
if (max.isValid())
- setMaximumDateTime(QDateTime(max, d->maximum.toTime(), d->spec));
+ setMaximumDateTime(d->dateTimeValue(max, d->maximum.toTime()));
}
void QDateTimeEdit::clearMaximumDate()
@@ -540,7 +533,8 @@ void QDateTimeEdit::clearMaximumDate()
contains a time of 00:00:00 and 0 milliseconds. This default can be restored
with clearMinimumTime().
- \sa maximumTime, minimumDate, minimumDateTime, setTimeRange(), QTime::isValid()
+ \sa maximumTime, minimumDate, minimumDateTime, setTimeRange(),
+ QTime::isValid(), {Keyboard Tracking}
*/
QTime QDateTimeEdit::minimumTime() const
@@ -552,10 +546,8 @@ QTime QDateTimeEdit::minimumTime() const
void QDateTimeEdit::setMinimumTime(QTime min)
{
Q_D(QDateTimeEdit);
- if (min.isValid()) {
- const QDateTime m(d->minimum.toDate(), min, d->spec);
- setMinimumDateTime(m);
- }
+ if (min.isValid())
+ setMinimumDateTime(d->dateTimeValue(d->minimum.toDate(), min));
}
void QDateTimeEdit::clearMinimumTime()
@@ -579,7 +571,8 @@ void QDateTimeEdit::clearMinimumTime()
contains a time of 23:59:59 and 999 milliseconds. This default can be restored
with clearMaximumTime().
- \sa minimumTime, maximumDate, maximumDateTime, setTimeRange(), QTime::isValid()
+ \sa minimumTime, maximumDate, maximumDateTime, setTimeRange(),
+ QTime::isValid(), {Keyboard Tracking}
*/
QTime QDateTimeEdit::maximumTime() const
{
@@ -590,10 +583,8 @@ QTime QDateTimeEdit::maximumTime() const
void QDateTimeEdit::setMaximumTime(QTime max)
{
Q_D(QDateTimeEdit);
- if (max.isValid()) {
- const QDateTime m(d->maximum.toDate(), max, d->spec);
- setMaximumDateTime(m);
- }
+ if (max.isValid())
+ setMaximumDateTime(d->dateTimeValue(d->maximum.toDate(), max));
}
void QDateTimeEdit::clearMaximumTime()
@@ -620,15 +611,19 @@ void QDateTimeEdit::clearMaximumTime()
minimumTime property, the \l maximumTime property is set to the \l minimumTime
property. Otherwise, this preserves the \l maximumTime property.
- \sa minimumDate, maximumDate, setDateTimeRange(), QDate::isValid()
+ If the range is narrower then a time interval whose end it spans, for example
+ a week that spans the end of a month, users can only edit the date to one in
+ the later part of the range if keyboard-tracking is disabled.
+
+ \sa minimumDate, maximumDate, setDateTimeRange(), QDate::isValid(), {Keyboard Tracking}
*/
void QDateTimeEdit::setDateRange(QDate min, QDate max)
{
Q_D(QDateTimeEdit);
if (min.isValid() && max.isValid()) {
- setDateTimeRange(QDateTime(min, d->minimum.toTime(), d->spec),
- QDateTime(max, d->maximum.toTime(), d->spec));
+ setDateTimeRange(d->dateTimeValue(min, d->minimum.toTime()),
+ d->dateTimeValue(max, d->maximum.toTime()));
}
}
@@ -654,15 +649,20 @@ void QDateTimeEdit::setDateRange(QDate min, QDate max)
function preserves the \l minimumDate and \l maximumDate properties. If those
properties coincide and \a max is less than \a min, \a min is used as \a max.
- \sa minimumTime, maximumTime, setDateTimeRange(), QTime::isValid()
+ If the range is narrower then a time interval whose end it spans, for example
+ the interval from ten to an hour to ten past the same hour, users can only
+ edit the time to one in the later part of the range if keyboard-tracking is
+ disabled.
+
+ \sa minimumTime, maximumTime, setDateTimeRange(), QTime::isValid(), {Keyboard Tracking}
*/
void QDateTimeEdit::setTimeRange(QTime min, QTime max)
{
Q_D(QDateTimeEdit);
if (min.isValid() && max.isValid()) {
- setDateTimeRange(QDateTime(d->minimum.toDate(), min, d->spec),
- QDateTime(d->maximum.toDate(), max, d->spec));
+ setDateTimeRange(d->dateTimeValue(d->minimum.toDate(), min),
+ d->dateTimeValue(d->maximum.toDate(), max));
}
}
@@ -945,10 +945,10 @@ void QDateTimeEdit::setDisplayFormat(const QString &format)
}
} else if (dateShown && !timeShown) {
setTimeRange(QDATETIMEEDIT_TIME_MIN, QDATETIMEEDIT_TIME_MAX);
- d->value = QDateTime(d->value.toDate(), QTime(), d->spec);
+ d->value = d->value.toDate().startOfDay(d->timeZone);
}
d->updateEdit();
- d->_q_editorCursorPositionChanged(-1, 0);
+ d->editorCursorPositionChanged(-1, 0);
}
}
@@ -984,24 +984,70 @@ void QDateTimeEdit::setCalendarPopup(bool enable)
update();
}
+#if QT_DEPRECATED_SINCE(6, 10)
/*!
\property QDateTimeEdit::timeSpec
- \brief The current timespec used by the date time edit.
\since 4.4
+ \deprecated[6.10] Use QDateTimeEdit::timeZone instead.
+ \brief The current timespec used by the date time edit.
+
+ Since Qt 6.7 this is an indirect accessor for the timeZone property.
+
+ \sa QDateTimeEdit::timeZone
*/
Qt::TimeSpec QDateTimeEdit::timeSpec() const
{
Q_D(const QDateTimeEdit);
- return d->spec;
+ return d->timeZone.timeSpec();
}
void QDateTimeEdit::setTimeSpec(Qt::TimeSpec spec)
{
Q_D(QDateTimeEdit);
- if (spec != d->spec) {
- d->spec = spec;
- d->updateTimeSpec();
+ if (spec != d->timeZone.timeSpec()) {
+ switch (spec) {
+ case Qt::UTC:
+ setTimeZone(QTimeZone::UTC);
+ break;
+ case Qt::LocalTime:
+ setTimeZone(QTimeZone::LocalTime);
+ break;
+ default:
+ qWarning() << "Ignoring attempt to set time-spec" << spec
+ << "which needs ancillary data: see setTimeZone()";
+ return;
+ }
+ }
+}
+#endif // 6.10 deprecation
+
+// TODO: enable user input to control timeZone, when the format includes it.
+/*!
+ \property QDateTimeEdit::timeZone
+ \since 6.7
+ \brief The current timezone used by the datetime editing widget
+
+ If the datetime format in use includes a timezone indicator - that is, a
+ \c{t}, \c{tt}, \c{ttt} or \c{tttt} format specifier - the user's input is
+ re-expressed in this timezone whenever it is parsed, overriding any timezone
+ the user may have specified.
+
+ \sa QDateTimeEdit::displayFormat
+*/
+
+QTimeZone QDateTimeEdit::timeZone() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->timeZone;
+}
+
+void QDateTimeEdit::setTimeZone(const QTimeZone &zone)
+{
+ Q_D(QDateTimeEdit);
+ if (zone != d->timeZone) {
+ d->timeZone = zone;
+ d->updateTimeZone();
}
}
@@ -1019,9 +1065,9 @@ QSize QDateTimeEdit::sizeHint() const
int h = d->edit->sizeHint().height();
int w = 0;
QString s;
- s = d->textFromValue(d->minimum) + QLatin1Char(' ');
+ s = d->textFromValue(d->minimum) + u' ';
w = qMax<int>(w, fm.horizontalAdvance(s));
- s = d->textFromValue(d->maximum) + QLatin1Char(' ');
+ s = d->textFromValue(d->maximum) + u' ';
w = qMax<int>(w, fm.horizontalAdvance(s));
if (d->specialValueText.size()) {
s = d->specialValueText;
@@ -1252,9 +1298,9 @@ void QDateTimeEdit::focusInEvent(QFocusEvent *event)
{
Q_D(QDateTimeEdit);
QAbstractSpinBox::focusInEvent(event);
- QString *frm = nullptr;
const int oldPos = d->edit->cursorPosition();
if (!d->formatExplicitlySet) {
+ QString *frm = nullptr;
if (d->displayFormat == d->defaultTimeFormat) {
frm = &d->defaultTimeFormat;
} else if (d->displayFormat == d->defaultDateFormat) {
@@ -1285,6 +1331,7 @@ void QDateTimeEdit::focusInEvent(QFocusEvent *event)
case Qt::ActiveWindowFocusReason:
if (oldHasHadFocus)
return;
+ break;
case Qt::ShortcutFocusReason:
case Qt::TabFocusReason:
default:
@@ -1377,7 +1424,7 @@ void QDateTimeEdit::stepBy(int steps)
d->updateCache(d->value, d->displayText());
d->setSelected(d->currentSectionIndex);
- d->updateTimeSpec();
+ d->updateTimeZone();
}
/*!
@@ -1436,15 +1483,11 @@ void QDateTimeEdit::fixup(QString &input) const
int copy = d->edit->cursorPosition();
QDateTime value = d->validateAndInterpret(input, copy, state, true);
- /*
- String was valid, but the datetime still is not; use the time that
- has the same distance from epoch.
- CorrectToPreviousValue correction is handled by QAbstractSpinBox.
- */
- if (!value.isValid() && d->correctionMode == QAbstractSpinBox::CorrectToNearestValue) {
- value = QDateTime::fromMSecsSinceEpoch(value.toMSecsSinceEpoch(), value.timeSpec());
+ // CorrectToPreviousValue correction is handled by QAbstractSpinBox.
+ // The value might not match the input if the input represents a date-time
+ // skipped over by its time representation, such as a spring-forward.
+ if (d->correctionMode == QAbstractSpinBox::CorrectToNearestValue)
input = textFromDateTime(value);
- }
}
@@ -1468,7 +1511,7 @@ QDateTimeEdit::StepEnabled QDateTimeEdit::stepEnabled() const
if (d->wrapping)
return StepEnabled(StepUpEnabled | StepDownEnabled);
// 3 cases. date, time, datetime. each case look
- // at just the relavant component.
+ // at just the relevant component.
QVariant max, min, val;
if (!(d->sections & DateSections_Mask)) {
// time only, no date
@@ -1585,7 +1628,7 @@ QTimeEdit::QTimeEdit(QWidget *parent)
*/
QTimeEdit::QTimeEdit(QTime time, QWidget *parent)
- : QDateTimeEdit(time, QMetaType::QTime, parent)
+ : QDateTimeEdit(time.isValid() ? time : QDATETIMEEDIT_TIME_MIN, QMetaType::QTime, parent)
{
connect(this, &QTimeEdit::timeChanged, this, &QTimeEdit::userTimeChanged);
}
@@ -1655,7 +1698,7 @@ QDateEdit::QDateEdit(QWidget *parent)
*/
QDateEdit::QDateEdit(QDate date, QWidget *parent)
- : QDateTimeEdit(date, QMetaType::QDate, parent)
+ : QDateTimeEdit(date.isValid() ? date : QDATETIMEEDIT_DATE_INITIAL, QMetaType::QDate, parent)
{
connect(this, &QDateEdit::dateChanged, this, &QDateEdit::userDateChanged);
}
@@ -1691,65 +1734,42 @@ QDateEdit::~QDateEdit()
*/
-QDateTimeEditPrivate::QDateTimeEditPrivate()
- : QDateTimeParser(QMetaType::QDateTime, QDateTimeParser::DateTimeEdit, QCalendar())
+QDateTimeEditPrivate::QDateTimeEditPrivate(const QTimeZone &zone)
+ : QDateTimeParser(QMetaType::QDateTime, QDateTimeParser::DateTimeEdit, QCalendar()),
+ timeZone(zone)
{
- hasHadFocus = false;
- formatExplicitlySet = false;
- cacheGuard = false;
fixday = true;
type = QMetaType::QDateTime;
- sections = { };
- cachedDay = -1;
currentSectionIndex = FirstSectionIndex;
first.pos = 0;
- calendarPopup = false;
- minimum = QDATETIMEEDIT_COMPAT_DATE_MIN.startOfDay();
- maximum = QDATETIMEEDIT_DATE_MAX.endOfDay();
- arrowState = QStyle::State_None;
- monthCalendar = nullptr;
+ minimum = QDATETIMEEDIT_COMPAT_DATE_MIN.startOfDay(timeZone);
+ maximum = QDATETIMEEDIT_DATE_MAX.endOfDay(timeZone);
readLocaleSettings();
+}
-#ifdef QT_KEYPAD_NAVIGATION
- focusOnButton = false;
-#endif
+QDateTime QDateTimeEditPrivate::convertTimeZone(const QDateTime &datetime)
+{
+ return datetime.toTimeZone(timeZone);
}
-QDateTime QDateTimeEditPrivate::convertTimeSpec(const QDateTime &datetime)
+QDateTime QDateTimeEditPrivate::dateTimeValue(QDate date, QTime time) const
{
- Q_ASSERT(value.toDateTime().timeSpec() == spec);
- switch (spec) {
- case Qt::UTC:
- return datetime.toUTC();
- case Qt::LocalTime:
- return datetime.toLocalTime();
- case Qt::OffsetFromUTC:
- return datetime.toOffsetFromUtc(value.toDateTime().offsetFromUtc());
- case Qt::TimeZone:
-#if QT_CONFIG(timezone)
- return datetime.toTimeZone(value.toDateTime().timeZone());
-#else
- qWarning("QDateTimeEdit: Internal: enable timezone feature to support Qt::TimeZone");
- return datetime;
-#endif
- }
- Q_UNREACHABLE();
+ return QDateTime(date, time, timeZone);
}
-// FIXME: architecturaly incompatible with OffsetFromUTC or TimeZone as spec (QTBUG-80417).
-void QDateTimeEditPrivate::updateTimeSpec()
+void QDateTimeEditPrivate::updateTimeZone()
{
- minimum = minimum.toDateTime().toTimeSpec(spec);
- maximum = maximum.toDateTime().toTimeSpec(spec);
- value = value.toDateTime().toTimeSpec(spec);
+ minimum = minimum.toDateTime().toTimeZone(timeZone);
+ maximum = maximum.toDateTime().toTimeZone(timeZone);
+ value = value.toDateTime().toTimeZone(timeZone);
// time zone changes can lead to 00:00:00 becomes 01:00:00 and 23:59:59 becomes 00:59:59 (invalid range)
const bool dateShown = (sections & QDateTimeEdit::DateSections_Mask);
if (!dateShown) {
if (minimum.toTime() >= maximum.toTime()){
- minimum = value.toDate().startOfDay(spec);
- maximum = value.toDate().endOfDay(spec);
+ minimum = value.toDate().startOfDay(timeZone);
+ maximum = value.toDate().endOfDay(timeZone);
}
}
}
@@ -1784,6 +1804,27 @@ void QDateTimeEditPrivate::updateEdit()
}
}
+QDateTime QDateTimeEditPrivate::getMinimum() const
+{
+ if (keyboardTracking)
+ return minimum.toDateTime();
+
+ if (timeZone.timeSpec() == Qt::LocalTime)
+ return QDateTimeParser::getMinimum();
+
+ return QDATETIMEEDIT_DATE_MIN.startOfDay(timeZone);
+}
+
+QDateTime QDateTimeEditPrivate::getMaximum() const
+{
+ if (keyboardTracking)
+ return maximum.toDateTime();
+
+ if (timeZone.timeSpec() == Qt::LocalTime)
+ return QDateTimeParser::getMaximum();
+
+ return QDATETIMEEDIT_DATE_MAX.endOfDay(timeZone);
+}
/*!
\internal
@@ -1829,7 +1870,7 @@ int QDateTimeEditPrivate::sectionAt(int pos) const
const int textSize = text.size();
if (textSize - pos < separators.last().size() + 1) {
if (separators.last().size() == 0) {
- return sectionNodes.count() - 1;
+ return sectionNodes.size() - 1;
}
return (pos == textSize ? LastSectionIndex : NoSectionIndex);
}
@@ -1915,7 +1956,7 @@ int QDateTimeEditPrivate::nextPrevSection(int current, bool forward) const
void QDateTimeEditPrivate::clearSection(int index)
{
- const QLatin1Char space(' ');
+ const auto space = u' ';
int cursorPos = edit->cursorPosition();
const QSignalBlocker blocker(edit);
QString t = edit->text();
@@ -1994,13 +2035,16 @@ QDateTime QDateTimeEditPrivate::validateAndInterpret(QString &input, int &positi
}
StateNode tmp = parse(input, position, value.toDateTime(), fixup);
- // Impose this widget's spec:
- tmp.value = tmp.value.toTimeSpec(spec);
+ // Take note of any corrections imposed during parsing:
+ input = m_text;
+ // TODO: if the format specifies time-zone, update timeZone to match the
+ // parsed text; but we're in const context, so can't - QTBUG-118393.
+ // Impose this widget's time system:
+ tmp.value = tmp.value.toTimeZone(timeZone);
// ... but that might turn a valid datetime into an invalid one:
if (!tmp.value.isValid() && tmp.state == Acceptable)
tmp.state = Intermediate;
- input = tmp.input;
position += tmp.padded;
state = QValidator::State(int(tmp.state));
if (state == QValidator::Acceptable) {
@@ -2042,7 +2086,7 @@ QString QDateTimeEditPrivate::textFromValue(const QVariant &f) const
QVariant QDateTimeEditPrivate::valueFromText(const QString &f) const
{
Q_Q(const QDateTimeEdit);
- return q->dateTimeFromText(f).toTimeSpec(spec);
+ return q->dateTimeFromText(f).toTimeZone(timeZone);
}
@@ -2064,43 +2108,64 @@ QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) c
int pos = edit->cursorPosition();
const SectionNode sn = sectionNode(sectionIndex);
- int val;
// to make sure it behaves reasonably when typing something and then stepping in non-tracking mode
- if (!test && pendingEmit) {
- if (q->validate(str, pos) != QValidator::Acceptable) {
- v = value.toDateTime();
- } else {
- v = q->dateTimeFromText(str);
- }
- val = getDigit(v, sectionIndex);
- } else {
- val = getDigit(v, sectionIndex);
- }
-
- val += steps;
+ if (!test && pendingEmit && q->validate(str, pos) == QValidator::Acceptable)
+ v = q->dateTimeFromText(str);
+ int val = getDigit(v, sectionIndex);
const int min = absoluteMin(sectionIndex);
const int max = absoluteMax(sectionIndex, value.toDateTime());
- if (val < min) {
- val = (wrapping ? max - (min - val) + 1 : min);
- } else if (val > max) {
- val = (wrapping ? min + val - max - 1 : max);
+ if (sn.type & DayOfWeekSectionMask) {
+ // Must take locale's first day of week into account when *not*
+ // wrapping; min and max don't help us.
+#ifndef QT_ALWAYS_WRAP_WEEKDAY // (documentation, not an actual define)
+ if (!wrapping) {
+ /* It's not clear this is ever really a desirable behavior.
+
+ It refuses to step backwards from the first day of the week or
+ forwards from the day before, only allowing day-of-week stepping
+ from start to end of one week. That's strictly what non-wrapping
+ behavior must surely mean, when put in locale-neutral terms.
+
+ It is, however, likely that users would prefer the "more natural"
+ behavior of cycling through the week.
+ */
+ const int first = int(locale().firstDayOfWeek()); // Mon = 1 through 7 = Sun
+ val = qBound(val < first ? first - 7 : first,
+ val + steps,
+ val < first ? first - 1 : first + 6);
+ } else
+#endif
+ {
+ val += steps;
+ }
+
+ // Restore to range from 1 through 7:
+ val = val % 7;
+ if (val <= 0)
+ val += 7;
+ } else {
+ val += steps;
+ const int span = max - min + 1;
+ if (val < min)
+ val = wrapping ? val + span : min;
+ else if (val > max)
+ val = wrapping ? val - span : max;
}
const int oldDay = v.date().day(calendar);
- setDigit(v, sectionIndex, val);
/*
- Stepping into a daylight saving time that doesn't exist,
- so use the time that has the same distance from epoch.
+ Stepping into a daylight saving time that doesn't exist (setDigit() is
+ true when date and time are valid, even if the date-time returned
+ isn't), so use the time that has the same distance from epoch.
*/
- if (!v.isValid()) {
- auto msecsSinceEpoch = v.toMSecsSinceEpoch();
+ if (setDigit(v, sectionIndex, val) && getDigit(v, sectionIndex) != val
+ && sn.type & HourSectionMask && steps < 0) {
// decreasing from e.g 3am to 2am would get us back to 3am, but we want 1am
- if (steps < 0 && sn.type & HourSectionMask)
- msecsSinceEpoch -= 3600 * 1000;
- v = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch, v.timeSpec());
+ auto msecsSinceEpoch = v.toMSecsSinceEpoch() - 3600 * 1000;
+ v = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch, v.timeRepresentation());
}
// if this sets year or month it will make
// sure that days are lowered if needed.
@@ -2108,7 +2173,8 @@ QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) c
const QDateTime minimumDateTime = minimum.toDateTime();
const QDateTime maximumDateTime = maximum.toDateTime();
// changing one section should only modify that section, if possible
- if (sn.type != AmPmSection && (v < minimumDateTime || v > maximumDateTime)) {
+ if (sn.type != AmPmSection && !(sn.type & DayOfWeekSectionMask)
+ && (v < minimumDateTime || v > maximumDateTime)) {
const int localmin = getDigit(minimumDateTime, sectionIndex);
const int localmax = getDigit(maximumDateTime, sectionIndex);
@@ -2195,8 +2261,7 @@ QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) c
}
}
- const QDateTime ret = bound(v, value, steps).toDateTime().toTimeSpec(spec);
- return ret;
+ return bound(v, value, steps).toDateTime().toTimeZone(timeZone);
}
/*!
@@ -2232,7 +2297,7 @@ void QDateTimeEditPrivate::emitSignals(EmitPolicy ep, const QVariant &old)
\internal
*/
-void QDateTimeEditPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos)
+void QDateTimeEditPrivate::editorCursorPositionChanged(int oldpos, int newpos)
{
if (ignoreCursorPositionChanged || specialValue())
return;
@@ -2290,7 +2355,7 @@ void QDateTimeEditPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos
currentSectionIndex = s;
Q_ASSERT_X(currentSectionIndex < sectionNodes.size(),
- "QDateTimeEditPrivate::_q_editorCursorPositionChanged()",
+ "QDateTimeEditPrivate::editorCursorPositionChanged()",
qPrintable(QString::fromLatin1("Internal error (%1 %2)").
arg(currentSectionIndex).
arg(sectionNodes.size())));
@@ -2383,8 +2448,8 @@ void QDateTimeEdit::paintEvent(QPaintEvent *event)
optCombo.state &= ~QStyle::State_Enabled;
}
- QPainter p(this);
- style()->drawComplexControl(QStyle::CC_ComboBox, &optCombo, &p, this);
+ QStylePainter p(this);
+ p.drawComplexControl(QStyle::CC_ComboBox, optCombo);
}
int QDateTimeEditPrivate::absoluteIndex(QDateTimeEdit::Section s, int index) const
@@ -2414,7 +2479,7 @@ void QDateTimeEditPrivate::interpret(EmitPolicy ep)
|| currentSectionIndex < 0
|| !(fieldInfo(currentSectionIndex) & AllowPartial))) {
setValue(value, ep);
- updateTimeSpec();
+ updateTimeZone();
} else {
QAbstractSpinBoxPrivate::interpret(ep);
}
@@ -2455,25 +2520,25 @@ void QDateTimeEditPrivate::init(const QVariant &var)
Q_Q(QDateTimeEdit);
switch (var.userType()) {
case QMetaType::QDate:
- value = var.toDate().startOfDay();
- updateTimeSpec();
+ value = var.toDate().startOfDay(timeZone);
+ updateTimeZone();
q->setDisplayFormat(defaultDateFormat);
if (sectionNodes.isEmpty()) // ### safeguard for broken locale
- q->setDisplayFormat(QLatin1String("dd/MM/yyyy"));
+ q->setDisplayFormat("dd/MM/yyyy"_L1);
break;
case QMetaType::QDateTime:
value = var;
- updateTimeSpec();
+ updateTimeZone();
q->setDisplayFormat(defaultDateTimeFormat);
if (sectionNodes.isEmpty()) // ### safeguard for broken locale
- q->setDisplayFormat(QLatin1String("dd/MM/yyyy hh:mm:ss"));
+ q->setDisplayFormat("dd/MM/yyyy hh:mm:ss"_L1);
break;
case QMetaType::QTime:
- value = QDateTime(QDATETIMEEDIT_DATE_INITIAL, var.toTime());
- updateTimeSpec();
+ value = dateTimeValue(QDATETIMEEDIT_DATE_INITIAL, var.toTime());
+ updateTimeZone();
q->setDisplayFormat(defaultTimeFormat);
if (sectionNodes.isEmpty()) // ### safeguard for broken locale
- q->setDisplayFormat(QLatin1String("hh:mm:ss"));
+ q->setDisplayFormat("hh:mm:ss"_L1);
break;
default:
Q_ASSERT_X(0, "QDateTimeEditPrivate::init", "Internal error");
@@ -2548,7 +2613,7 @@ void QDateTimeEditPrivate::updateEditFieldGeometry()
QVariant QDateTimeEditPrivate::getZeroVariant() const
{
Q_ASSERT(type == QMetaType::QDateTime);
- return QDateTime(QDATETIMEEDIT_DATE_INITIAL, QTime(), spec);
+ return QDATETIMEEDIT_DATE_INITIAL.startOfDay(timeZone);
}
void QDateTimeEditPrivate::setRange(const QVariant &min, const QVariant &max)
@@ -2577,7 +2642,7 @@ void QDateTimeEditPrivate::initCalendarPopup(QCalendarWidget *cw)
Q_Q(QDateTimeEdit);
if (!monthCalendar) {
monthCalendar = new QCalendarPopup(q, cw, calendar);
- monthCalendar->setObjectName(QLatin1String("qt_datetimedit_calendar"));
+ monthCalendar->setObjectName("qt_datetimedit_calendar"_L1);
QObject::connect(monthCalendar, SIGNAL(newDateSelected(QDate)), q, SLOT(setDate(QDate)));
QObject::connect(monthCalendar, SIGNAL(hidingCalendar(QDate)), q, SLOT(setDate(QDate)));
QObject::connect(monthCalendar, SIGNAL(activated(QDate)), q, SLOT(setDate(QDate)));