📜 ⬆️ ⬇️

Development for Sailfish OS: Features of working with dates and time zones

Hello! This article is a continuation of a series of articles devoted to the development of applications for the Sailfish OS mobile platform. This time we will discuss the features of working with dates and time zones in QML. We will begin the article with a description of the problem itself, and then we will go over how to solve it.

Description of the problem


When developing Sailfish OS applications, quite often in one form or another you will have to work with dates and time (as, indeed, when developing for any other platform). Sailfish OS uses components such as DatePickerDialog and TimePickerDialog to specify the date and time. Internally, they use the Date QML object inherited from the standard JavaScript Date object, which does not support the ability to create a date and time with a time zone other than UTC or local, to control the date and time. The Date object simply has no constructor and methods for this.

new Date(); new Date(value); new Date(dateString); new Date(year, month[, day[, hour[, minute[, second[, millisecond]]]]]); 

It would seem that a third constructor from the list should help here, if you pass it a string with the date, time and offset relative to UTC, but no. The time zone of the object will still be local, and not the one specified in the offset.

 new Date('Jan 30 2017 10:00:00 GMT+0700') // Jan 30 2017 06:00:00 GMT+0300 

You may ask: “Why use time zones at all? Why it’s impossible to manage with time in UTC? ”And I will answer you: yes, sometimes time zones do not make sense. Just use the date and time. For example, if your working day starts at 9:00, then you hardly expect your colleague from Kamchatka to start working at 18:00. However, in the case of regular events occurring at the same time in different time zones, the time zone is still needed. For example, the daily discussion of the current work on the project starts at 10:00 for you and at 19:00 for your colleagues in Kamchatka.
')
One of the solutions to the problem of creating a date and time with the installation of a time zone was the use of one third-party libraries: timezone-js and moment.js . But they turned out to be unsuitable, because DatePickerDialog and TimePickerDialog do not know anything about these libraries, and inside they actively use the standard Date , which is incompatible with objects created using timezone-js and moment.js . As a result, two other solutions were developed.

Decision number 1


The first solution that occurred to us is to create our own JavaScript object to manage the date and time. Such an object should allow storing the date, time and information about the time zone, and most importantly, changing the date and time using the Sailfish OS components DatePickerDialog and TimePickerDialog , without affecting the time zone.

To create your own JavaScript object, you need to define a constructor function in a separate JavaScript file.

 // CustomDateTime.js function CustomDateTime(dateTimeString) { this.dateTime = Date.fromLocaleString(Qt.locale(), dateTimeString.substring(0, dateTimeString.length - 6), "yyyy-MM-ddTHH:mm:ss"); this.utcOffset = dateTimeString.substring(dateTimeString.length - 6); } 

The constructor function accepts a string of the form “yyyy-MM-ddTHH: mm: ssZ”, where Z is an offset relative to UTC of the form “[+ -] HH: mm”, ISO 8601 standard. A Date object is created from part of the string and assigned to the dateTime property. This property will contain information about the date and time, excluding the time zone. The rest of the string, containing the offset relative to UTC, is stored in a separate property utcOffset . Now we can create an object that will contain information about the date, time and time zone.

 var myDateTime = new CustomDateTime("2016-12-22T13:40:00+05:00"); print(myDateTime.dateTime); // Dec 22 2016 13:40:00 GMT+03:00 print(myDateTime.utcOffset); // "+05:00" myDateTime.dateTime = new Date(2016, 11, 23, 13, 00, 00); print(myDateTime.dateTime); // Dec 23 2016 13:00:00 GMT+03:00 print(myDateTime.utcOffset); // "+05:00" 

Add a method to our object that returns the date and time in the same format “yyyy-MM-ddTHH: mm: ssZ”.

 // CustomDateTime.js CustomDateTime.prototype.toISO8601String = function() { return this.dateTime.toLocaleString(Qt.locale(), "yyyy-MM-ddTHH:mm:ss").concat(this.utcOffset); } 

Often, applications that work with date and time need to display the corresponding values. We, as developers, must ensure that all users will display the date and time correctly in accordance with the current locale. To do this, we add methods to our JavaScript object that return strings with a language-dependent representation of the date and time.

 // CustomDateTime.js CustomDateTime.prototype.toLocaleDateString = function() { return Qt.formatDate(this.dateTime, Qt.SystemLocaleShortDate); } CustomDateTime.prototype.toLocaleTimeString = function() { return Qt.formatTime(this.dateTime, "HH:mm"); } CustomDateTime.prototype.toLocaleDateTimeString = function() { return this.toLocaleDateString() + " " + this.toLocaleTimeString(); } 

Thus, we have an object that stores and allows editing date, time, and time zone information, is created using a string in a specific format, can return a string in the same format, as well as formatted strings in the current locale. Such an object will easily allow us to operate with date and time in the required time zone.

Consider an example of using the CustomDateTime object.

 //... import "../model/CustomDateTime.js" as CustomDateTime Page { property var ustomDateTime: new CustomDateTime.CustomDateTime("2017-01-15T13:45:00+05:00") SilicaFlickable { anchors.fill: parent contentHeight: column.height Column { id: column //... ValueButton { label: qsTr("Date").concat(":") value: ustomDateTime.toLocaleDateString() //... } ValueButton { width: parent.width label: qsTr("Time").concat(":") value: ustomDateTime.toLocaleTimeString() onClicked: { var dialog = pageStack.push("Sailfish.Silica.TimePickerDialog", { hour: ustomDateTime.dateTime.getHours(), minute: ustomDateTime.dateTime.getMinutes()}); dialog.accepted.connect(function() { ustomDateTime.dateTime = new Date(ustomDateTime.dateTime.getFullYear(), ustomDateTime.dateTime.getMonth(), ustomDateTime t.dateTime.getDate(), dialog.hour, dialog.minute); }); } } } } } 

The example contains the ValueButton components for editing the date and time. Clicking on one component opens DatePickerDialog , on clicking on the second component - TimePickerDialog . The ValueButton component for time editing is described in more detail. The CustomDateTime object is created as a property of the Page component and is used to display the date and time in the ValueButton using the value property and also to pass the values ​​to the DatePickerDialog and TimePickerDialog , as described in the onClicked event handler . It also describes how to get data from DatePickerDialog and TimePickerDialog and update the dateTime property of the CustomDateTime object.

So, a JavaScript object was created CustomDateTime, which allows you to store information about the date, time and time zone, as well as allowing you to edit the date and time using DatePickerDialog and TimePickerDialog .

The disadvantage of this solution is that the JavaScript object does not support binding properties. In the example, after changing the date or time (changing the dateTime property of the CustomDateTime object), the value property of the ValueButton object will not update , i.e. visually there will be no changes on the screen, despite the fact that the CustomDateTime object has actually changed. This is because the DateTime property of the CustomDateTime object cannot be associated with the value property of the ValueButton object.

In those cases where the binding of properties does not matter, you can use the solution described above, but in other cases you need to refer to decision # 2.

Decision number 2


The second solution is to create your own QML component, in particular a component of type QtObject . QtObject is the most "lightweight" standard QML type, has no visual component and can be useful when creating a model object. And most importantly, QML components support binding properties. Rewrite the JavaScript object defined above into a QML component.

 // CustomDateTime.qml import QtQuick 2.0 QtObject { property string dateTimeStringToSet property date dateTime: Date.fromLocaleString(Qt.locale(), dateTimeStringToSet.substring(0, dateTimeStringToSet.length - 6), "yyyy-MM-ddTHH:mm:ss") property string utcOffset: dateTimeStringToSet.substring(dateTimeStringToSet.length - 6) property string localeDateString: Qt.formatDate(dateTime, Qt.SystemLocaleShortDate) property string localeTimeString: Qt.formatTime(dateTime, "HH:mm") property string localeDateTimeString: localeDateString.concat(" ").concat(localeTimeString) property string iso8601String: dateTime.toLocaleString(Qt.locale(), "yyyy-MM-ddTHH:mm:ss") .concat(utcOffset) } 

The code has become more concise, the constructor function and JavaScript methods of the object have been replaced with the properties inside QtObject . Now, to create a new object, we need to use the standard QML syntax and define only one dateTimeStringToSet property, all other properties will be calculated automatically, because the binding properties will work.

 CustomDateTime { dateTimeStringToSet: "2017-01-15T13:45:00+05:00" } 

We will rewrite the example above, using the CustomDateTime QML object.

 //... Page { CustomDateTime { id: customDateTime dateTimeStringToSet: "2017-01-15T13:45:00+05:00" } SilicaFlickable { anchors.fill: parent contentHeight: column.height Column { id: column //... ValueButton { label: qsTr("Date").concat(":") value: customDateTime.localeDateString //... } ValueButton { width: parent.width label: qsTr("Time").concat(":") value: customDateTime.localeTimeString onClicked: { var dialog = pageStack.push("Sailfish.Silica.TimePickerDialog", { hour: customDateTime.dateTime.getHours(), minute: customDateTime.dateTime.getMinutes()}); dialog.accepted.connect(function() { customDateTime.dateTime = new Date(customDateTime.dateTime.getFullYear(), customDateTime.dateTime.getMonth(), customDateTime.dateTime.getDate(), dialog.hour, dialog.minute); }); } } } } } 

It is easy to see that the changes are not much. The property declaration has been replaced by the declaration of the CustomDateTime QML component, and the localeDateString and localeTimeString properties are used instead of the toLocaleDateString () and toLocaleTimeString () functions . In all other respects, the code has not changed at all, but property binding is now working. Changing the dateTime property of the CustomDateTime object will update all the properties of the object and the localeTimeString property in particular, which will update the appearance of the ValueButton object.

Conclusion


As a result, a solution for managing the date, time, and time zone information was developed, supported by components for editing the date and time in Sailfish OS. The solution is to create your own QML component and use it as a model. Such an object allows you to store the date, time, and time zone, and also supports a mechanism for linking properties and can be used inside Sailfish OS of the DatePickerDialog and TimePickerDailog components for editing. The source code for this example is available on GitHub .

The author: Ivan Shchitov

Source: https://habr.com/ru/post/324812/


All Articles