diff options
-rw-r--r-- | src/qml/jsruntime/qv4dateobject.cpp | 5 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4dateobject_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlbuiltinfunctions.cpp | 146 | ||||
-rw-r--r-- | src/qml/qml/qqmlbuiltinfunctions_p.h | 16 | ||||
-rw-r--r-- | tests/auto/qml/qqmlqt/tst_qqmlqt.cpp | 60 |
5 files changed, 212 insertions, 16 deletions
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 98d930eece..ed64493d9a 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -719,6 +719,11 @@ QString DateObject::dateTimeToString(const QDateTime &dateTime, ExecutionEngine return ToString(TimeClip(dateTime.toMSecsSinceEpoch()), engine->localTZA); } +QDateTime DateObject::stringToDateTime(const QString &string, ExecutionEngine *engine) +{ + return ToDateTime(ParseString(string, engine->localTZA), QTimeZone::LocalTime); +} + QDate DateObject::dateTimeToDate(const QDateTime &dateTime) { // If the Date object was parse()d from a string with no time part diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 147638744d..ad5b51f063 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -207,6 +207,7 @@ struct DateObject: ReferenceObject { static QString dateTimeToString(const QDateTime &dateTime, ExecutionEngine *engine); static QDate dateTimeToDate(const QDateTime &dateTime); + static QDateTime stringToDateTime(const QString &string, ExecutionEngine *engine); }; template<> diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp index 2ae58f2178..3807db7456 100644 --- a/src/qml/qml/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/qqmlbuiltinfunctions.cpp @@ -757,6 +757,11 @@ QString formatDateTimeObjectUsingDateFormat(T formatThis, Qt::DateFormat format) } } +static QTime dateTimeToTime(const QDateTime &dateTime) +{ + return dateTime.toLocalTime().time(); +} + /*! \qmlmethod string Qt::formatDate(datetime date, variant format, variant localeFormatOption) @@ -773,6 +778,36 @@ default locale. \sa Locale */ +static std::optional<QDate> dateFromString(const QString &string, QV4::ExecutionEngine *engine) +{ + { + const QDate date = QDate::fromString(string, Qt::ISODate); + if (date.isValid()) + return date; + } + + { + // For historical reasons, the string argument is parsed as datetime, not as only date + const QDateTime dateTime = QDateTime::fromString(string, Qt::ISODate); + if (dateTime.isValid()) { + qCWarning(lcRootProperties()) + << string << "is a date/time string being passed to formatDate()." + << "You should only pass date strings to formatDate()."; + return dateTime.date(); + } + } + + { + // Since we can coerce QDate to QString, allow the resulting string format here. + const QDateTime dateTime = DateObject::stringToDateTime(string, engine); + if (dateTime.isValid()) + return DateObject::dateTimeToDate(dateTime); + } + + engine->throwError(QStringLiteral("Invalid argument passed to formatDate(): %1").arg(string)); + return std::nullopt; +} + QString QtObject::formatDate(const QDate &date, const QString &format) const { return date.toString(format); @@ -783,12 +818,53 @@ QString QtObject::formatDate(const QDate &date, Qt::DateFormat format) const return formatDateTimeObjectUsingDateFormat(date, format); } +QString QtObject::formatDate(const QDateTime &dateTime, const QString &format) const +{ + return DateObject::dateTimeToDate(dateTime).toString(format); +} + +QString QtObject::formatDate(const QString &string, const QString &format) const +{ + if (const auto qDate = dateFromString(string, v4Engine())) + return formatDate(qDate.value(), format); + + return QString(); +} + +QString QtObject::formatDate(const QDateTime &dateTime, Qt::DateFormat format) const +{ + return formatDateTimeObjectUsingDateFormat(DateObject::dateTimeToDate(dateTime), format); +} + +QString QtObject::formatDate(const QString &string, Qt::DateFormat format) const +{ + if (const auto qDate = dateFromString(string, v4Engine())) + return formatDate(qDate.value(), format); + + return QString(); +} + #if QT_CONFIG(qml_locale) QString QtObject::formatDate(const QDate &date, const QLocale &locale, QLocale::FormatType formatType) const { return locale.toString(date, formatType); } + +QString QtObject::formatDate(const QDateTime &dateTime, const QLocale &locale, + QLocale::FormatType formatType) const +{ + return locale.toString(DateObject::dateTimeToDate(dateTime), formatType); +} + +QString QtObject::formatDate(const QString &string, const QLocale &locale, + QLocale::FormatType formatType) const +{ + if (const auto qDate = dateFromString(string, v4Engine())) + return locale.toString(qDate.value(), formatType); + + return QString(); +} #endif /*! @@ -826,6 +902,13 @@ static std::optional<QTime> timeFromString(const QString &string, QV4::Execution } } + { + // Since we can coerce QTime to QString, allow the resulting string format here. + const QDateTime dateTime = DateObject::stringToDateTime(string, engine); + if (dateTime.isValid()) + return dateTimeToTime(dateTime); + } + engine->throwError(QStringLiteral("Invalid argument passed to formatTime(): %1").arg(string)); return std::nullopt; } @@ -835,6 +918,11 @@ QString QtObject::formatTime(const QTime &time, const QString &format) const return time.toString(format); } +QString QtObject::formatTime(const QDateTime &dateTime, const QString &format) const +{ + return dateTimeToTime(dateTime).toString(format); +} + QString QtObject::formatTime(const QString &time, const QString &format) const { @@ -849,6 +937,11 @@ QString QtObject::formatTime(const QTime &time, Qt::DateFormat format) const return formatDateTimeObjectUsingDateFormat(time, format); } +QString QtObject::formatTime(const QDateTime &dateTime, Qt::DateFormat format) const +{ + return formatDateTimeObjectUsingDateFormat(dateTimeToTime(dateTime), format); +} + QString QtObject::formatTime(const QString &time, Qt::DateFormat format) const { if (auto qTime = timeFromString(time, v4Engine())) @@ -864,6 +957,12 @@ QString QtObject::formatTime(const QTime &time, const QLocale &locale, return locale.toString(time, formatType); } +QString QtObject::formatTime(const QDateTime &dateTime, const QLocale &locale, + QLocale::FormatType formatType) const +{ + return locale.toString(dateTimeToTime(dateTime), formatType); +} + QString QtObject::formatTime(const QString &time, const QLocale &locale, QLocale::FormatType formatType) const { @@ -972,22 +1071,69 @@ with the \a format values below to produce the following results: \sa Locale */ +static std::optional<QDateTime> dateTimeFromString(const QString &string, QV4::ExecutionEngine *engine) +{ + { + const QDateTime dateTime = QDateTime::fromString(string, Qt::ISODate); + if (dateTime.isValid()) + return dateTime; + } + + { + // Since we can coerce QDateTime to QString, allow the resulting string format here. + const QDateTime dateTime = DateObject::stringToDateTime(string, engine); + if (dateTime.isValid()) + return dateTime; + } + + engine->throwError(QStringLiteral("Invalid argument passed to formatDateTime(): %1").arg(string)); + return std::nullopt; +} + QString QtObject::formatDateTime(const QDateTime &dateTime, const QString &format) const { return dateTime.toString(format); } +QString QtObject::formatDateTime(const QString &string, const QString &format) const +{ + + if (const auto qDateTime = dateTimeFromString(string, v4Engine())) + return formatDateTime(qDateTime.value(), format); + + return QString(); +} + QString QtObject::formatDateTime(const QDateTime &dateTime, Qt::DateFormat format) const { return formatDateTimeObjectUsingDateFormat(dateTime, format); } +QString QtObject::formatDateTime(const QString &string, Qt::DateFormat format) const +{ + + if (const auto qDateTime = dateTimeFromString(string, v4Engine())) + return formatDateTime(qDateTime.value(), format); + + return QString(); +} + #if QT_CONFIG(qml_locale) QString QtObject::formatDateTime(const QDateTime &dateTime, const QLocale &locale, QLocale::FormatType formatType) const { return locale.toString(dateTime, formatType); } + +QString QtObject::formatDateTime(const QString &string, const QLocale &locale, + QLocale::FormatType formatType) const +{ + + if (const auto qDateTime = dateTimeFromString(string, v4Engine())) + return formatDateTime(qDateTime.value(), locale, formatType); + + return QString(); +} #endif /*! diff --git a/src/qml/qml/qqmlbuiltinfunctions_p.h b/src/qml/qml/qqmlbuiltinfunctions_p.h index 2739524516..46c4f415e3 100644 --- a/src/qml/qml/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/qqmlbuiltinfunctions_p.h @@ -86,25 +86,41 @@ public: Q_INVOKABLE QVariant tint(const QJSValue &baseColor, const QJSValue &tintColor) const; Q_INVOKABLE QString formatDate(const QDate &date, const QString &format) const; + Q_INVOKABLE QString formatDate(const QDateTime &dateTime, const QString &format) const; + Q_INVOKABLE QString formatDate(const QString &string, const QString &format) const; Q_INVOKABLE QString formatDate(const QDate &date, Qt::DateFormat format) const; + Q_INVOKABLE QString formatDate(const QDateTime &dateTime, Qt::DateFormat format) const; + Q_INVOKABLE QString formatDate(const QString &string, Qt::DateFormat format) const; Q_INVOKABLE QString formatTime(const QTime &time, const QString &format) const; + Q_INVOKABLE QString formatTime(const QDateTime &dateTime, const QString &format) const; Q_INVOKABLE QString formatTime(const QString &time, const QString &format) const; Q_INVOKABLE QString formatTime(const QTime &time, Qt::DateFormat format) const; + Q_INVOKABLE QString formatTime(const QDateTime &dateTime, Qt::DateFormat format) const; Q_INVOKABLE QString formatTime(const QString &time, Qt::DateFormat format) const; Q_INVOKABLE QString formatDateTime(const QDateTime &date, const QString &format) const; + Q_INVOKABLE QString formatDateTime(const QString &string, const QString &format) const; Q_INVOKABLE QString formatDateTime(const QDateTime &date, Qt::DateFormat format) const; + Q_INVOKABLE QString formatDateTime(const QString &string, Qt::DateFormat format) const; #if QT_CONFIG(qml_locale) Q_INVOKABLE QString formatDate(const QDate &date, const QLocale &locale = QLocale(), QLocale::FormatType formatType = QLocale::ShortFormat) const; + Q_INVOKABLE QString formatDate(const QDateTime &dateTime, const QLocale &locale = QLocale(), + QLocale::FormatType formatType = QLocale::ShortFormat) const; + Q_INVOKABLE QString formatDate(const QString &string, const QLocale &locale = QLocale(), + QLocale::FormatType formatType = QLocale::ShortFormat) const; Q_INVOKABLE QString formatTime(const QTime &time, const QLocale &locale = QLocale(), QLocale::FormatType formatType = QLocale::ShortFormat) const; + Q_INVOKABLE QString formatTime(const QDateTime &dateTime, const QLocale &locale = QLocale(), + QLocale::FormatType formatType = QLocale::ShortFormat) const; Q_INVOKABLE QString formatTime(const QString &time, const QLocale &locale = QLocale(), QLocale::FormatType formatType = QLocale::ShortFormat) const; Q_INVOKABLE QString formatDateTime(const QDateTime &date, const QLocale &locale = QLocale(), QLocale::FormatType formatType = QLocale::ShortFormat) const; + Q_INVOKABLE QString formatDateTime(const QString &string, const QLocale &locale = QLocale(), + QLocale::FormatType formatType = QLocale::ShortFormat) const; Q_INVOKABLE QLocale locale() const; Q_INVOKABLE QLocale locale(const QString &name) const; #endif diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp index 40f7a28d3e..73f8a0c81c 100644 --- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp +++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp @@ -909,7 +909,10 @@ void tst_qqmlqt::dateTimeFormattingVariants() QTest::ignoreMessage(QtWarningMsg, QRegularExpression(warning)); warnings.clear(); - if (method == QStringLiteral("formatTime") && variant.typeId() == QMetaType::QString) { + + if (method == QStringLiteral("formatTime") + && variant.typeId() == QMetaType::QString + && QByteArrayView(QTest::currentDataTag()).endsWith("ISO")) { for (int i = 0; i < 4; ++i) { QTest::ignoreMessage(QtWarningMsg, "\"2011/05/31 11:16:39.755\" is a " @@ -925,13 +928,18 @@ void tst_qqmlqt::dateTimeFormattingVariants() QtWarningMsg, QRegularExpression("formatting.qml:19: Error: Invalid argument passed to " "formatTime")); - } else { - QTest::ignoreMessage(QtWarningMsg, - QRegularExpression("Could not convert argument 0 at")); - QTest::ignoreMessage(QtWarningMsg, QRegularExpression(method + "@")); - QTest::ignoreMessage(QtWarningMsg, QRegularExpression( - "TypeError: Passing incompatible arguments to " - "C.. functions from JavaScript is not allowed.")); + } else if (method == "formatDate") { + // formatDate has special error handling as it parses the strings itself. + QTest::ignoreMessage( + QtWarningMsg, + QRegularExpression("formatting.qml:10: Error: Invalid argument passed to " + "formatDate")); + } else if (method == "formatDateTime") { + // formatDateTime has special error handling as it parses the strings itself. + QTest::ignoreMessage( + QtWarningMsg, + QRegularExpression("formatting.qml:29: Error: Invalid argument passed to " + "formatDateTime")); } } @@ -1010,27 +1018,47 @@ void tst_qqmlqt::dateTimeFormattingVariants_data() << temporary.time().toString("H:m:s a") << temporary.time().toString("hh:mm:ss.zzz")); - QString string(QLatin1String("2011/05/31 11:16:39.755")); - temporary = QDateTime::fromString(string, "yyyy/MM/dd HH:mm:ss.zzz"); - QTest::newRow("formatDate, qstring") - << "formatDate" << QVariant::fromValue(string) + const QString isoString(QLatin1String("2011/05/31 11:16:39.755")); + temporary = QDateTime::fromString(isoString, "yyyy/MM/dd HH:mm:ss.zzz"); + const QString jsString = engine.coerceValue<QDateTime, QString>(temporary); + QTest::newRow("formatDate, qstring, ISO") + << "formatDate" << QVariant::fromValue(isoString) << (QStringList() << QLocale().toString(temporary.date(), QLocale::ShortFormat) << QLocale().toString(temporary.date(), QLocale::LongFormat) << temporary.date().toString("ddd MMMM d yy")); - QTest::newRow("formatDateTime, qstring") - << "formatDateTime" << QVariant::fromValue(string) + QTest::newRow("formatDate, qstring, JS") + << "formatDate" << QVariant::fromValue(jsString) + << (QStringList() + << QLocale().toString(temporary.date(), QLocale::ShortFormat) + << QLocale().toString(temporary.date(), QLocale::LongFormat) + << temporary.date().toString("ddd MMMM d yy")); + QTest::newRow("formatDateTime, qstring, ISO") + << "formatDateTime" << QVariant::fromValue(isoString) + << (QStringList() + << QLocale().toString(temporary, QLocale::ShortFormat) + << QLocale().toString(temporary, QLocale::LongFormat) + << temporary.toString("M/d/yy H:m:s a")); + QTest::newRow("formatDateTime, qstring, JS") + << "formatDateTime" << QVariant::fromValue(jsString) << (QStringList() << QLocale().toString(temporary, QLocale::ShortFormat) << QLocale().toString(temporary, QLocale::LongFormat) << temporary.toString("M/d/yy H:m:s a")); - QTest::newRow("formatTime, qstring") - << "formatTime" << QVariant::fromValue(string) + QTest::newRow("formatTime, qstring, ISO") + << "formatTime" << QVariant::fromValue(isoString) << (QStringList() << QLocale().toString(temporary.time(), QLocale::ShortFormat) << QLocale().toString(temporary.time(), QLocale::LongFormat) << temporary.time().toString("H:m:s a") << temporary.time().toString("hh:mm:ss.zzz")); + QTest::newRow("formatTime, qstring, JS") + << "formatTime" << QVariant::fromValue(jsString) + << (QStringList() + << QLocale().toString(temporary.time(), QLocale::ShortFormat) + << QLocale().toString(temporary.time(), QLocale::LongFormat) + << temporary.time().toString("H:m:s a") + << temporary.time().toString("hh:mm:ss.000")); // JS Date to string coercion drops milliseconds QColor color(Qt::red); temporary = QVariant::fromValue(color).toDateTime(); |