diff options
-rw-r--r-- | src/qml/doc/src/cppintegration/data.qdoc | 12 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4dateobject.cpp | 22 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4dateobject_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 9 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 1 | ||||
-rw-r--r-- | tests/auto/qml/qqmlqt/data/timeRoundtrip.qml | 8 | ||||
-rw-r--r-- | tests/auto/qml/qqmlqt/tst_qqmlqt.cpp | 112 |
7 files changed, 163 insertions, 3 deletions
diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index 7bb4d701e2..cc2fe90483 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -249,6 +249,18 @@ parameter, the value can be created as a JavaScript \c Date object in QML, and is automatically converted to a QDateTime value when it is passed to C++. +\section2 QTime to JavaScript Date + +The QML engine provides automatic type conversion from QTime values to +JavaScript \c Date objects. The date component of the resulting Date +object should not be relied upon, as it is operating system dependent. +Specifically, the year (and month and day) are set to zero. Conversion +from a JavaScript \c Date object to QTime is done by converting to a +QDateTime, and then relying on QVariant to convert it to a QTime. The end +effect is that the date part of the \c Date object is ignored, but the +local timezone will be used ignoring any DST complications it may have. + + \section2 Sequence Type to JavaScript Array Certain C++ sequence types are supported transparently in QML as JavaScript diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index df648ba9ee..04358fe3b5 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -639,6 +639,28 @@ Heap::DateObject::DateObject(const QDateTime &date) this->date = date.isValid() ? date.toMSecsSinceEpoch() : qt_qnan(); } +Heap::DateObject::DateObject(const QTime &time) +{ + if (!time.isValid()) { + date = qt_qnan(); + return; + } + + /* All programmers know that stuff starts at 0. Whatever that may mean in this context (and + * local timezone), it's before the epoch, so there is defenitely no DST problem. Specifically: + * you can't start with a date before the epoch, add some[*] hours, and end up with a date + * after. That's a problem for timezones where new year happens during DST, like + * Australia/Hobart, because we have to ignore DST before the epoch (but honor it after the + * epoch). + * + * [*] Well, when "some" is in the range 0-24. If you add something like 1M then this might + * still happen. + */ + static const double d = MakeDay(0, 0, 0); + double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec()); + date = TimeClip(UTC(MakeDate(d, t))); +} + QDateTime DateObject::toQDateTime() const { return ToDateTime(date(), Qt::LocalTime); diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 13e9e04040..c67acdcfa2 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -74,6 +74,8 @@ struct DateObject : Object { } DateObject(const QDateTime &date); double date; + + DateObject(const QTime &time); }; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 20294700f6..27397fe3d8 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -621,6 +621,13 @@ Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt) return object->d(); } +Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t) +{ + Scope scope(this); + Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(t)); + return object->d(); +} + Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { bool global = (flags & IR::RegExp::RegExp_Global); @@ -1291,7 +1298,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) case QMetaType::QDate: return QV4::Encode(newDateObject(QDateTime(*reinterpret_cast<const QDate *>(ptr)))); case QMetaType::QTime: - return QV4::Encode(newDateObject(QDateTime(QDate(1970,1,1), *reinterpret_cast<const QTime *>(ptr)))); + return QV4::Encode(newDateObjectFromTime(*reinterpret_cast<const QTime *>(ptr))); case QMetaType::QRegExp: return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr))); case QMetaType::QObjectStar: diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index f42f727295..843a6f4d94 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -419,6 +419,7 @@ public: Heap::DateObject *newDateObject(const Value &value); Heap::DateObject *newDateObject(const QDateTime &dt); + Heap::DateObject *newDateObjectFromTime(const QTime &t); Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags); Heap::RegExpObject *newRegExpObject(RegExp *re, bool global); diff --git a/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml new file mode 100644 index 0000000000..9d73640c87 --- /dev/null +++ b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +QtObject { + Component.onCompleted: { + var t = tp.time; + tp.time = t; + } +} diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp index 69791085c5..0576650d01 100644 --- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp +++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp @@ -46,6 +46,15 @@ #include <QFont> #include "../../shared/util.h" +// Copied from tst_qdatetime.cpp +#ifdef Q_OS_WIN +# include <qt_windows.h> +# include <time.h> +# if defined(Q_OS_WINRT) +# define tzset() +# endif +#endif + class tst_qqmlqt : public QQmlDataTest { Q_OBJECT @@ -93,6 +102,9 @@ private slots: void later(); void qtObjectContents(); + void timeRoundtrip_data(); + void timeRoundtrip(); + private: QQmlEngine engine; }; @@ -873,8 +885,6 @@ void tst_qqmlqt::dateTimeFormattingVariants_data() QTime time(11, 16, 39, 755); temporary = QDateTime(QDate(1970,1,1), time); - QTest::newRow("formatDate, qtime") << "formatDate" << QVariant::fromValue(time) << (QStringList() << temporary.date().toString(Qt::DefaultLocaleShortDate) << temporary.date().toString(Qt::DefaultLocaleLongDate) << temporary.date().toString("ddd MMMM d yy")); - QTest::newRow("formatDateTime, qtime") << "formatDateTime" << QVariant::fromValue(time) << (QStringList() << temporary.toString(Qt::DefaultLocaleShortDate) << temporary.toString(Qt::DefaultLocaleLongDate) << temporary.toString("M/d/yy H:m:s a")); QTest::newRow("formatTime, qtime") << "formatTime" << QVariant::fromValue(time) << (QStringList() << temporary.time().toString(Qt::DefaultLocaleShortDate) << temporary.time().toString(Qt::DefaultLocaleLongDate) << temporary.time().toString("H:m:s a") << temporary.time().toString("hh:mm:ss.zzz")); QDate date(2011,5,31); @@ -1154,6 +1164,104 @@ void tst_qqmlqt::qtObjectContents() delete object; } +class TimeProvider: public QObject +{ + Q_OBJECT + Q_PROPERTY(QTime time READ time WRITE setTime NOTIFY timeChanged) + +public: + TimeProvider(const QTime &t) + : m_getTime(t) + {} + + QTime time() const { return m_getTime; } + void setTime(const QTime &t) { m_putTime = t; emit timeChanged(); } + +signals: + void timeChanged(); + +public: + QTime m_getTime, m_putTime; +}; + +class TimeZoneSwitch +{ +public: + TimeZoneSwitch(const char *newZone) + : doChangeZone(qstrcmp(newZone, "localtime") == 0) + { + if (!doChangeZone) + return; + + hadOldZone = qEnvironmentVariableIsSet("TZ"); + if (hadOldZone) { + oldZone = qgetenv("TZ"); + } + qputenv("TZ", newZone); + tzset(); + } + + ~TimeZoneSwitch() + { + if (!doChangeZone) + return; + + if (hadOldZone) + qputenv("TZ", oldZone); + else + qunsetenv("TZ"); + tzset(); + } + +private: + bool doChangeZone; + bool hadOldZone; + QByteArray oldZone; +}; + +void tst_qqmlqt::timeRoundtrip_data() +{ + QTest::addColumn<QTime>("time"); + + // Local timezone: + QTest::newRow("localtime") << QTime(0, 0, 0); + + // No DST: + QTest::newRow("UTC") << QTime(0, 0, 0); + QTest::newRow("Europe/Amsterdam") << QTime(1, 0, 0); + QTest::newRow("Asia/Jakarta") << QTime(7, 0, 0); + + // DST: + QTest::newRow("Namibia/Windhoek") << QTime(1, 0, 0); + QTest::newRow("Australia/Adelaide") << QTime(10, 0, 0); + QTest::newRow("Australia/Hobart") << QTime(10, 0, 0); + QTest::newRow("Pacific/Auckland") << QTime(12, 0, 0); + QTest::newRow("Pacific/Samoa") << QTime(13, 0, 0); +} + +void tst_qqmlqt::timeRoundtrip() +{ +#ifdef Q_OS_WIN + QSKIP("On Windows, the DateObject doesn't handle DST transitions correctly when the timezone is not localtime."); // I.e.: for this test. +#endif + + TimeZoneSwitch tzs(QTest::currentDataTag()); + QFETCH(QTime, time); + + TimeProvider tp(time); + + QQmlEngine eng; + eng.rootContext()->setContextProperty(QLatin1String("tp"), &tp); + QQmlComponent component(&eng, testFileUrl("timeRoundtrip.qml")); + QObject *obj = component.create(); + QVERIFY(obj != 0); + + // QML reads m_getTime and saves the result as m_putTime; this should come out the same, without + // any perturbation (e.g. by DST effects) from converting from QTime to V4's Date and back + // again. + QCOMPARE(tp.m_getTime, tp.m_putTime); +} + QTEST_MAIN(tst_qqmlqt) #include "tst_qqmlqt.moc" |