diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2022-08-24 16:07:26 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2022-12-09 16:16:48 +0100 |
commit | 5da038ab519622cf18cb22c233f9b4a0111b21be (patch) | |
tree | 847b8c49193369b4d1124faa48307e76c2bd65ed /src/widgets/widgets/qdatetimeedit.cpp | |
parent | 3f282e8896980822b86bc009f85bf047c7d9c189 (diff) |
Port QDateTimeEdit internals to QTimeZone
In the process, centralize the creation of date-time values, where
doing so requires catching invalid results caused by spring-forward
gaps; this saves some repetition and extends the treatment to more
places that did need it. Also, de-inline two overrides of virtuals;
being inline does them no good.
Replace the Qt::TimeSpec member of QDTEPrivate with a QTimeZone so
that creation of values can be streamlined and to make it easier to
add support for (currently unsupported) OffsetFromUTC and TimeZone
timespecs in the public QDTE API.
This greatly simplifies a lot of the code, while preparing it for a
long-needed extension to its functionality.
Task-number: QTBUG-80417
Task-number: QTBUG-108199
Change-Id: I0ac2c78025013bf89899e3ef1a56e89392f67ce5
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src/widgets/widgets/qdatetimeedit.cpp')
-rw-r--r-- | src/widgets/widgets/qdatetimeedit.cpp | 185 |
1 files changed, 100 insertions, 85 deletions
diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp index ac4d4cdc8c..61cf20891b 100644 --- a/src/widgets/widgets/qdatetimeedit.cpp +++ b/src/widgets/widgets/qdatetimeedit.cpp @@ -16,9 +16,6 @@ #include <qset.h> #include <qstyle.h> #include <qstylepainter.h> -#if QT_CONFIG(timezone) -#include <QTimeZone> -#endif #include <algorithm> @@ -190,8 +187,8 @@ QDateTimeEdit::QDateTimeEdit(QTime time, QWidget *parent) \internal */ QDateTimeEdit::QDateTimeEdit(const QVariant &var, QMetaType::Type parserType, QWidget *parent) - : QAbstractSpinBox(*new QDateTimeEditPrivate( - parserType == QMetaType::QDateTime ? Qt::LocalTime : Qt::UTC), + : QAbstractSpinBox(*new QDateTimeEditPrivate(parserType == QMetaType::QDateTime + ? QTimeZone::LocalTime : QTimeZone::UTC), parent) { Q_D(QDateTimeEdit); @@ -210,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 @@ -235,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(); @@ -272,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(); } } @@ -306,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); } } @@ -363,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)); } @@ -405,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); } @@ -439,8 +432,8 @@ 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); } @@ -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() @@ -517,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() @@ -554,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() @@ -593,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() @@ -634,8 +622,8 @@ 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())); } } @@ -673,8 +661,8 @@ 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)); } } @@ -957,7 +945,7 @@ void QDateTimeEdit::setDisplayFormat(const QString &format) } } else if (dateShown && !timeShown) { setTimeRange(QDATETIMEEDIT_TIME_MIN, QDATETIMEEDIT_TIME_MAX); - d->value = d->value.toDate().startOfDay(d->spec); + d->value = d->value.toDate().startOfDay(d->timeZone); } d->updateEdit(); d->_q_editorCursorPositionChanged(-1, 0); @@ -1005,15 +993,27 @@ void QDateTimeEdit::setCalendarPopup(bool enable) 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: + d->timeZone = QTimeZone::UTC; + break; + case Qt::LocalTime: + d->timeZone = QTimeZone::LocalTime; + break; + default: + qWarning() << "Ignoring attempt to set time-spec" << spec + << "which is not yet supported by QDateTimeEdit"; + // TODO: fix that QTBUG-80417. + return; + } + d->updateTimeZone(); } } @@ -1389,7 +1389,7 @@ void QDateTimeEdit::stepBy(int steps) d->updateCache(d->value, d->displayText()); d->setSelected(d->currentSectionIndex); - d->updateTimeSpec(); + d->updateTimeZone(); } /*! @@ -1421,6 +1421,8 @@ QDateTime QDateTimeEdit::dateTimeFromText(const QString &text) const QString copy = text; int pos = d->edit->cursorPosition(); QValidator::State state = QValidator::Acceptable; + // TODO: if the format specifies time-zone, d->timeZone should change as + // determined by the parsed text. return d->validateAndInterpret(copy, pos, state); } @@ -1454,7 +1456,8 @@ void QDateTimeEdit::fixup(QString &input) const CorrectToPreviousValue correction is handled by QAbstractSpinBox. */ if (!value.isValid() && d->correctionMode == QAbstractSpinBox::CorrectToNearestValue) { - value = QDateTime::fromMSecsSinceEpoch(value.toMSecsSinceEpoch(), value.timeSpec()); + value = QDateTime::fromMSecsSinceEpoch(value.toMSecsSinceEpoch(), + value.timeRepresentation()); input = textFromDateTime(value); } } @@ -1703,54 +1706,46 @@ QDateEdit::~QDateEdit() */ -QDateTimeEditPrivate::QDateTimeEditPrivate(Qt::TimeSpec timeSpec) +QDateTimeEditPrivate::QDateTimeEditPrivate(const QTimeZone &zone) : QDateTimeParser(QMetaType::QDateTime, QDateTimeParser::DateTimeEdit, QCalendar()), - spec(timeSpec) + timeZone(zone) { fixday = true; type = QMetaType::QDateTime; currentSectionIndex = FirstSectionIndex; first.pos = 0; - minimum = QDATETIMEEDIT_COMPAT_DATE_MIN.startOfDay(spec); - maximum = QDATETIMEEDIT_DATE_MAX.endOfDay(spec); + minimum = QDATETIMEEDIT_COMPAT_DATE_MIN.startOfDay(timeZone); + maximum = QDATETIMEEDIT_DATE_MAX.endOfDay(timeZone); readLocaleSettings(); } -QDateTime QDateTimeEditPrivate::convertTimeSpec(const QDateTime &datetime) +QDateTime QDateTimeEditPrivate::convertTimeZone(const QDateTime &datetime) { - 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 datetime.toTimeZone(timeZone); } -// FIXME: architecturaly incompatible with OffsetFromUTC or TimeZone as spec (QTBUG-80417). -void QDateTimeEditPrivate::updateTimeSpec() +QDateTime QDateTimeEditPrivate::dateTimeValue(QDate date, QTime time) const { - minimum = minimum.toDateTime().toTimeSpec(spec); - maximum = maximum.toDateTime().toTimeSpec(spec); - value = value.toDateTime().toTimeSpec(spec); + QDateTime when = QDateTime(date, time, timeZone); + if (when.isValid()) + return when; + // Hit a spring-forward gap + return QDateTime::fromMSecsSinceEpoch(when.toMSecsSinceEpoch(), timeZone); +} + +void QDateTimeEditPrivate::updateTimeZone() +{ + 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); } } } @@ -1785,6 +1780,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 @@ -1997,8 +2013,8 @@ QDateTime QDateTimeEditPrivate::validateAndInterpret(QString &input, int &positi StateNode tmp = parse(input, position, value.toDateTime(), fixup); // Take note of any corrections imposed during parsing: input = m_text; - // Impose this widget's spec: - tmp.value = tmp.value.toTimeSpec(spec); + // 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; @@ -2044,7 +2060,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); } @@ -2124,7 +2140,7 @@ QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) c // 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()); + v = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch, v.timeRepresentation()); } // if this sets year or month it will make // sure that days are lowered if needed. @@ -2220,8 +2236,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); } /*! @@ -2439,7 +2454,7 @@ void QDateTimeEditPrivate::interpret(EmitPolicy ep) || currentSectionIndex < 0 || !(fieldInfo(currentSectionIndex) & AllowPartial))) { setValue(value, ep); - updateTimeSpec(); + updateTimeZone(); } else { QAbstractSpinBoxPrivate::interpret(ep); } @@ -2480,22 +2495,22 @@ void QDateTimeEditPrivate::init(const QVariant &var) Q_Q(QDateTimeEdit); switch (var.userType()) { case QMetaType::QDate: - value = var.toDate().startOfDay(spec); - updateTimeSpec(); + value = var.toDate().startOfDay(timeZone); + updateTimeZone(); q->setDisplayFormat(defaultDateFormat); if (sectionNodes.isEmpty()) // ### safeguard for broken locale 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("dd/MM/yyyy hh:mm:ss"_L1); break; case QMetaType::QTime: - value = QDateTime(QDATETIMEEDIT_DATE_INITIAL, var.toTime(), spec); - updateTimeSpec(); + value = dateTimeValue(QDATETIMEEDIT_DATE_INITIAL, var.toTime()); + updateTimeZone(); q->setDisplayFormat(defaultTimeFormat); if (sectionNodes.isEmpty()) // ### safeguard for broken locale q->setDisplayFormat("hh:mm:ss"_L1); @@ -2573,7 +2588,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) |