aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/doc/src/cppintegration/data.qdoc12
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp22
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4engine.cpp9
-rw-r--r--src/qml/jsruntime/qv4engine_p.h1
-rw-r--r--tests/auto/qml/qqmlqt/data/timeRoundtrip.qml8
-rw-r--r--tests/auto/qml/qqmlqt/tst_qqmlqt.cpp112
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"