From 2d1fc405b4048560a6f80add6ee6492fa934c91f Mon Sep 17 00:00:00 2001 From: Matthew Vogt Date: Mon, 4 Jun 2012 15:32:32 +1000 Subject: [PATCH] Allow the global JS UTC offset to be invalidated If the system timezone is changed, it is necessary to inform V8 so that the global offset-from-UTC value can be recalculated. This changes adds the 'timeZoneUpdated' function to the JS 'Date' class exported by QML, which allows QML applications to inform V8 when the timezone has been updated. Change-Id: Ic5898ca7bc640002a4a6fd5a52b8623d87ee8085 Reviewed-by: Martin Jones --- src/qml/doc/src/types/qmldate.qdoc | 26 +++++++++++ src/qml/qml/qqmllocale.cpp | 18 ++++++++ src/qml/qml/qqmllocale_p.h | 1 + tests/auto/qml/qqmllocale/data/timeZoneUpdated.qml | 51 ++++++++++++++++++++++ tests/auto/qml/qqmllocale/tst_qqmllocale.cpp | 41 +++++++++++++++++ 5 files changed, 137 insertions(+) create mode 100644 tests/auto/qml/qqmllocale/data/timeZoneUpdated.qml diff --git a/src/qml/doc/src/types/qmldate.qdoc b/src/qml/doc/src/types/qmldate.qdoc index 802f977..f1cff3d 100644 --- a/src/qml/doc/src/types/qmldate.qdoc +++ b/src/qml/doc/src/types/qmldate.qdoc @@ -198,3 +198,29 @@ \endcode */ +/*! + \qmlmethod string Date::timeZoneUpdated() + + Informs the JS engine that the system's timezone has been changed, which is necessary + for the correct manipulation of date/time data. + + JS stores Date objects in UTC time; all access to and from Date components in local + time involves the application of the current offset from UTC. If the current offset + changes due to the timezone being updated, the JS engine needs to be informed so that + it can recalculate the offset. + + This function should be called after the system's timezone has been updated. + + For example, an application that changes the timezone would call timeZoneUpdated() after + setting the new time zone: + + \code + property string selectedTimeZone + + onSelectedTimeZoneChanged: { + MyFunctions.setSystemTimeZone(selectedTimeZone) + Date.timeZoneUpdated() + } + \endcode +*/ + diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 7d2dafe..9a2ec6f 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -131,6 +131,13 @@ static const char *dateFromLocaleDateStringFunction = " })" "})"; +static const char *dateTimeZoneUpdatedFunction = + "(function(timeZoneUpdatedFunc) { " + " Date.timeZoneUpdated = (function() {" + " return timeZoneUpdatedFunc.apply(null, arguments);" + " })" + "})"; + static void registerFunction(QV8Engine *engine, const char *script, v8::InvocationCallback func) { @@ -150,6 +157,7 @@ void QQmlDateExtension::registerExtension(QV8Engine *engine) registerFunction(engine, dateFromLocaleStringFunction, fromLocaleString); registerFunction(engine, dateFromLocaleTimeStringFunction, fromLocaleTimeString); registerFunction(engine, dateFromLocaleDateStringFunction, fromLocaleDateString); + registerFunction(engine, dateTimeZoneUpdatedFunction, timeZoneUpdated); } v8::Handle QQmlDateExtension::toLocaleString(const v8::Arguments& args) @@ -387,6 +395,16 @@ v8::Handle QQmlDateExtension::fromLocaleDateString(const v8::Argument return QJSConverter::toDateTime(QDateTime(dt)); } +v8::Handle QQmlDateExtension::timeZoneUpdated(const v8::Arguments& args) +{ + if (args.Length() != 0) + V8THROW_ERROR("Locale: Date.timeZoneUpdated(): Invalid arguments"); + + v8::Date::DateTimeConfigurationChangeNotification(); + + return v8::Undefined(); +} + //----------------- // Number extension diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h index c701c1c..7007770 100644 --- a/src/qml/qml/qqmllocale_p.h +++ b/src/qml/qml/qqmllocale_p.h @@ -66,6 +66,7 @@ private: static v8::Handle fromLocaleString(const v8::Arguments& args); static v8::Handle fromLocaleTimeString(const v8::Arguments& args); static v8::Handle fromLocaleDateString(const v8::Arguments& args); + static v8::Handle timeZoneUpdated(const v8::Arguments& args); }; diff --git a/tests/auto/qml/qqmllocale/data/timeZoneUpdated.qml b/tests/auto/qml/qqmllocale/data/timeZoneUpdated.qml new file mode 100644 index 0000000..92d1f5b --- /dev/null +++ b/tests/auto/qml/qqmllocale/data/timeZoneUpdated.qml @@ -0,0 +1,51 @@ +import QtQuick 2.0 + +Item { + property bool success: false + + property date localDate + property date utcDate + + Component.onCompleted: { + // Test date: 2012-06-01T02:15:30+10:00 (AEST timezone) + localDate = new Date(2012, 6-1, 1, 2, 15, 30) + utcDate = new Date(Date.UTC(2012, 6-1, 1, 2, 15, 30)) + + if (localDate.getTimezoneOffset() != -600) return + + if (localDate.toLocaleString() != "Friday, June 1, 2012 2:15:30 AM AEST") return + if (localDate.toISOString() != "2012-05-31T16:15:30.000Z") return + + if (utcDate.toISOString() != "2012-06-01T02:15:30.000Z") return + if (utcDate.toLocaleString() != "Friday, June 1, 2012 12:15:30 PM AEST") return + + success = true + } + + function check() { + success = false + + // We have changed to IST time zone - inform JS: + Date.timeZoneUpdated() + + if (localDate.getTimezoneOffset() != -330) return + + if (localDate.toLocaleString() != "Friday, June 1, 2012 2:15:30 AM IST") return + if (localDate.toISOString() != "2012-05-31T20:45:30.000Z") return + + if (utcDate.toISOString() != "2012-06-01T06:45:30.000Z") return + if (utcDate.toLocaleString() != "Friday, June 1, 2012 12:15:30 PM IST") return + + // Create new dates in this timezone + localDate = new Date(2012, 6-1, 1, 2, 15, 30) + utcDate = new Date(Date.UTC(2012, 6-1, 1, 2, 15, 30)) + + if (localDate.toLocaleString() != "Friday, June 1, 2012 2:15:30 AM IST") return + if (localDate.toISOString() != "2012-05-31T20:45:30.000Z") return + + if (utcDate.toISOString() != "2012-06-01T02:15:30.000Z") return + if (utcDate.toLocaleString() != "Friday, June 1, 2012 7:45:30 AM IST") return + + success = true + } +} diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp index e745702..c394263 100644 --- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp +++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp @@ -47,6 +47,8 @@ #include #include "../../shared/util.h" +#include + class tst_qqmllocale : public QQmlDataTest { Q_OBJECT @@ -80,6 +82,7 @@ private slots: void dateTimeFormat(); void timeFormat_data(); void timeFormat(); + void timeZoneUpdated(); void dateToLocaleString_data(); void dateToLocaleString(); @@ -1214,6 +1217,44 @@ void tst_qqmllocale::stringLocaleCompare() QCOMPARE(obj->property("comparison").toInt(), QString::localeAwareCompare(string1, string2)); } +static void setTimeZone(const QByteArray &tz) +{ + qputenv("TZ", tz); +#if defined(Q_OS_WIN32) + ::_tzset(); +#elif defined(Q_OS_UNIX) + ::tzset(); +#endif +} + +void tst_qqmllocale::timeZoneUpdated() +{ +#if !defined(Q_OS_WIN32) && !defined(Q_OS_UINX) + QSKIP("Timezone manipulation not available for this platform"); +#endif + + QByteArray original(qgetenv("TZ")); + + // Set the timezone to Brisbane time + setTimeZone(QByteArray("AEST-10:00")); + + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("timeZoneUpdated.qml")); + QScopedPointer obj(c.create()); + QVERIFY(obj); + QCOMPARE(obj->property("success").toBool(), true); + + // Change to Indian time + setTimeZone(QByteArray("IST-05:30")); + + QMetaObject::invokeMethod(obj.data(), "check"); + + // Reset to original time + setTimeZone(original); + + QCOMPARE(obj->property("success").toBool(), true); +} + QTEST_MAIN(tst_qqmllocale) #include "tst_qqmllocale.moc" -- 2.7.4