From e7767224a0831565646413a1a0e90b44fbaf3339 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Mon, 5 Dec 2022 12:02:19 +0100 Subject: [PATCH] Fix time zones with QDateTime fromMSecsSinceEpoch/toMSecsSinceEpoch. Overwrite locale display for qml date to avoid taking account of timezone when using only dates. --- .../ConferenceInfoListModel.cpp | 2 +- .../conferenceInfo/ConferenceInfoModel.cpp | 26 +++++++++-------- .../ui/modules/Common/Picker/DatePicker.qml | 28 +++++++++++-------- linphone-app/ui/scripts/Utils/utils.js | 12 +++++++- .../ui/views/App/Dialog/NewConference.qml | 8 +++--- 5 files changed, 47 insertions(+), 29 deletions(-) diff --git a/linphone-app/src/components/conferenceInfo/ConferenceInfoListModel.cpp b/linphone-app/src/components/conferenceInfo/ConferenceInfoListModel.cpp index 1aa28f80c..764780d37 100644 --- a/linphone-app/src/components/conferenceInfo/ConferenceInfoListModel.cpp +++ b/linphone-app/src/components/conferenceInfo/ConferenceInfoListModel.cpp @@ -89,7 +89,7 @@ QVariant ConferenceInfoListModel::data (const QModelIndex &index, int role ) con if (role == Qt::DisplayRole) return QVariant::fromValue(mList[row].get()); else if (role == Qt::DisplayRole +1 ) - return QVariant::fromValue(mList[row].objectCast()->getDateTimeUtc().date()); + return QVariant::fromValue(mList[row].objectCast()->getDateTimeSystem().date()); return QVariant(); } diff --git a/linphone-app/src/components/conferenceInfo/ConferenceInfoModel.cpp b/linphone-app/src/components/conferenceInfo/ConferenceInfoModel.cpp index 0a8adb8b9..941556fcd 100644 --- a/linphone-app/src/components/conferenceInfo/ConferenceInfoModel.cpp +++ b/linphone-app/src/components/conferenceInfo/ConferenceInfoModel.cpp @@ -141,14 +141,15 @@ std::shared_ptr ConferenceInfoModel::findConferenceInf //------------------------------------------------------------------------------------------------ -//Note conferenceInfo->getDateTime uses system timezone. -QDateTime ConferenceInfoModel::getDateTimeUtc() const{ - return QDateTime::fromMSecsSinceEpoch(mConferenceInfo->getDateTime() * 1000).toUTC(); +//Note conferenceInfo->getDateTime uses system timezone and fromMSecsSinceEpoch need a UTC +QDateTime ConferenceInfoModel::getDateTimeSystem() const{ + QDateTime reference(QDateTime::fromMSecsSinceEpoch(mConferenceInfo->getDateTime() * 1000));// Get a reference for timezone offset computing + qint64 utcMs = (mConferenceInfo->getDateTime() - QTimeZone::systemTimeZone().offsetFromUtc(reference)) * 1000;// Remove system timezone offset to get UTC + return QDateTime::fromMSecsSinceEpoch(utcMs, QTimeZone::systemTimeZone()); // Return a System Timezone datetime based } -QDateTime ConferenceInfoModel::getDateTimeSystem() const{ - QDateTime utc = getDateTimeUtc(); - return utc.addSecs(QTimeZone::systemTimeZone().offsetFromUtc(utc)); +QDateTime ConferenceInfoModel::getDateTimeUtc() const{ + return getDateTimeSystem().toUTC(); } int ConferenceInfoModel::getDuration() const{ @@ -230,11 +231,13 @@ LinphoneEnums::ConferenceSchedulerState ConferenceInfoModel::getConferenceSchedu } //------------------------------------------------------------------------------------------------ -// Convert into UTC with TimeZone and pass system timezone to conference info +// Datetime is in Custom (Locale/UTC/System). Convert into system timezone for conference info void ConferenceInfoModel::setDateTime(const QDateTime& dateTime){ - QDateTime utc = dateTime.addSecs( -mTimeZone.offsetFromUtc(dateTime)); - QDateTime system = utc.addSecs(QTimeZone::systemTimeZone().offsetFromUtc(utc)); - mConferenceInfo->setDateTime(system.toMSecsSinceEpoch() / 1000); + QDateTime system = dateTime.toTimeZone(QTimeZone::systemTimeZone());//System + int offset = QTimeZone::systemTimeZone().offsetFromUtc(system);//Get UTC offset in system coordinate + system = system.addSecs( offset - mTimeZone.offsetFromUtc(dateTime));// Delta on offsets + mConferenceInfo->setDateTime(system.toMSecsSinceEpoch() / 1000 + offset);// toMSecsSinceEpoch() is UTC, add system reference. + emit dateTimeChanged(); } @@ -321,12 +324,11 @@ void ConferenceInfoModel::createConference(const int& securityLevel) { CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = false; shared_ptr core = CoreManager::getInstance()->getCore(); static std::shared_ptr conference; - qInfo() << "Conference creation of " << getSubject() << " at " << securityLevel << " security, organized by " << getOrganizer(); + qInfo() << "Conference creation of " << getSubject() << " at " << securityLevel << " security, organized by " << getOrganizer() << " for " << getDateTimeSystem().toString(); qInfo() << "Participants:"; for(auto p : mConferenceInfo->getParticipants()) qInfo() << "\t" << p->asString().c_str(); - mConferenceScheduler = ConferenceScheduler::create(); mConferenceScheduler->mSendInvite = mInviteMode; connect(mConferenceScheduler.get(), &ConferenceScheduler::invitationsSent, this, &ConferenceInfoModel::onInvitationsSent); diff --git a/linphone-app/ui/modules/Common/Picker/DatePicker.qml b/linphone-app/ui/modules/Common/Picker/DatePicker.qml index f740d478b..0d816ac55 100644 --- a/linphone-app/ui/modules/Common/Picker/DatePicker.qml +++ b/linphone-app/ui/modules/Common/Picker/DatePicker.qml @@ -5,6 +5,8 @@ import Common 1.0 import Common.Styles 1.0 import Units 1.0 +import 'qrc:/ui/scripts/Utils/utils.js' as Utils + Item{ id: mainItem property bool allYears : false // if false : years from today @@ -33,9 +35,10 @@ Item{ Layout.fillWidth: true Layout.alignment: Qt.AlignCenter horizontalAlignment: Qt.AlignCenter - text: new Date(monthList.currentYear, monthList.currentMonth, 1).toLocaleString(Qt.locale(), 'MMMM yyyy') + text: new Date(monthList.currentYear, monthList.currentMonth, 15).toLocaleString(Qt.locale(), 'MMMM yyyy')// 15 because of timezones that can change the date for localeString color: DatePickerStyle.title.color font.pointSize: DatePickerStyle.title.pointSize + font.capitalization: Font.Capitalize } ActionButton{ isCustom: true @@ -62,7 +65,7 @@ Item{ } property date selectedDate: new Date() - property int minYear: mainItem.allYears ? new Date(0,0,0).getFullYear() : new Date().getFullYear() + property int minYear: mainItem.allYears ? new Date(0,0,1).getFullYear() : new Date().getFullYear() snapMode: ListView.SnapOneItem orientation: Qt.Horizontal @@ -70,7 +73,7 @@ Item{ // One model per month model: (new Date().getFullYear()- minYear + maxYears) * 12 - + currentIndex: 0 property int currentYear: Math.floor(currentIndex / 12) + minYear property int currentMonth: currentIndex % 12 @@ -87,7 +90,7 @@ Item{ property int year: Math.floor(index / 12) + monthList.minYear property int month: index % 12 property int firstDay: new Date(year, month, 1).getDay() - + GridLayout { // 1 month calender id: grid @@ -105,9 +108,12 @@ Item{ delegate: Item{ id: cellItem - property int day: index - 7 // 0 = top left below Sunday (-7 to 41) + property int day: index - 7 // 0 = top left below Sunday (-7 to 41) property int date: day - firstDay + 1 // 1-31 - property bool selected : text.text != '-' && new Date(year, month, date).toDateString() == monthList.selectedDate.toDateString() && text.text && day >= 0 + property date cellDate: new Date(year, month, date) + property bool selected : text.text != '-' + && Utils.equalDate(cellDate, monthList.selectedDate) + && text.text && day >= 0 width: grid.cellMinSize height: width @@ -132,12 +138,12 @@ Item{ : cellItem.day < 0 ? DatePickerStyle.cell.dayHeaderPointSize : DatePickerStyle.cell.dayPointSize - font.bold: cellItem.day < 0 || cellItem.selected || new Date(year, month, cellItem.date).toDateString() == new Date().toDateString() // today + font.bold: cellItem.day < 0 || cellItem.selected || Utils.equalDate(cellItem.cellDate, new Date()) // today text: { - if(cellItem.day < 0) + if(cellItem.day < 0){ // Magic date to set day names in this order : 'S', 'M', 'T', 'W', 'T', 'F', 'S' in Locale - return new Date(1,3,index).toLocaleString(Qt.locale(), 'ddd')[0] - else if(new Date(year, month, cellItem.date).getMonth() == month && (!hideOldDates || new Date(year, month, cellItem.date+1) >= new Date())) // new Date use time too + return Utils.exactDate(new Date(2000,9,index+1)).toLocaleString(Qt.locale(), 'ddd')[0].toUpperCase() + }else if(cellItem.cellDate.getMonth() == month && (!hideOldDates || new Date(year, month, cellItem.date+1) >= new Date())) // new Date use time too return cellItem.date else return '-' @@ -152,7 +158,7 @@ Item{ enabled: text.text && text.text != '-' && cellItem.day >= 0 onClicked: { - monthList.selectedDate = new Date(year, month, cellItem.date) + monthList.selectedDate = cellItem.cellDate mainItem.clicked(monthList.selectedDate) } } diff --git a/linphone-app/ui/scripts/Utils/utils.js b/linphone-app/ui/scripts/Utils/utils.js index d82ed2452..3b8c80bc8 100644 --- a/linphone-app/ui/scripts/Utils/utils.js +++ b/linphone-app/ui/scripts/Utils/utils.js @@ -563,12 +563,22 @@ function buildDate(date, time){ return dateTime } +function equalDate(date1, date2){ + return date1.getFullYear() == date2.getFullYear() && date1.getMonth() == date2.getMonth() && date1.getDate() == date2.getDate() +} function fromUTC(date){ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds())); } +// return EXACTLY what date has been set (not take account of Locale timezones, eg. Date(2000,0,1) will print january and not december if timezone lead there.) +// Use this function for toLocaleString/toLocaleDateString or other +function exactDate(date) { + var timeOffset = date.getTimezoneOffset() * 60000 + var exactDate = new Date(date.valueOf() - timeOffset) + return exactDate +} // ----------------------------------------------------------------------------- @@ -796,4 +806,4 @@ function printObject(o) { out += p + ': ' + o[p] + '\n'; } return out; -} \ No newline at end of file +} diff --git a/linphone-app/ui/views/App/Dialog/NewConference.qml b/linphone-app/ui/views/App/Dialog/NewConference.qml index 9048fac9d..94c3c652b 100644 --- a/linphone-app/ui/views/App/Dialog/NewConference.qml +++ b/linphone-app/ui/views/App/Dialog/NewConference.qml @@ -20,8 +20,8 @@ DialogPlus { property bool isNew: !conferenceInfoModel || conferenceInfoModel.uri === '' property ConferenceInfoModel conferenceInfoModel: ConferenceInfoModel{} onConferenceInfoModelChanged: { - dateField.setDate(conferenceManager.conferenceInfoModel.dateTimeUtc); - timeField.setTime(conferenceManager.conferenceInfoModel.dateTimeUtc); + dateField.setDate(conferenceManager.conferenceInfoModel.dateTime); + timeField.setTime(conferenceManager.conferenceInfoModel.dateTime); selectedParticipants.setAddresses(conferenceInfoModel) } property bool forceSchedule : false @@ -299,9 +299,9 @@ DialogPlus { } function setDate(date){ currentDate = date - text = date.toLocaleDateString(scheduleForm.locale, Qt.ISODate) + text = Utils.exactDate(date).toLocaleDateString(scheduleForm.locale, Qt.ISODate) } - text: conferenceManager.conferenceInfoModel ? conferenceManager.conferenceInfoModel.dateTime.toLocaleDateString(scheduleForm.locale, Qt.ISODate) : '' + text: conferenceManager.conferenceInfoModel ? Utils.exactDate(conferenceManager.conferenceInfoModel.dateTime).toLocaleDateString(scheduleForm.locale, Qt.ISODate) : '' icon: 'drop_down_custom' MouseArea{ anchors.fill: parent