Fix DatePicker timezones by removing javascript dates and use cpp instead.

Add Timezone debug on start.
Fix date sections on conference and history.
Fix defaut Timezone in conference creation whe system cannot match with available timezones (like default tz).
This commit is contained in:
Julien Wadel 2023-09-01 09:28:26 +02:00
parent 9b66bd1d72
commit 820a34fd50
15 changed files with 256 additions and 41 deletions

View file

@ -13,6 +13,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Chat reactions
## 5.1.3 - Undefined
### Fixed
- Wrong dates from DatePicker.
- Update SDK to 5.2.98
## 5.1.2 - 2023-08-25
### Fixed

View file

@ -293,6 +293,7 @@ set(SOURCES
src/components/other/colors/ColorListModel.cpp
src/components/other/colors/ColorProxyModel.cpp
src/components/other/colors/ImageColorsProxyModel.cpp
src/components/other/date/DateModel.cpp
src/components/other/images/ImageModel.cpp
src/components/other/images/ImageListModel.cpp
src/components/other/images/ImageProxyModel.cpp
@ -439,6 +440,7 @@ set(HEADERS
src/components/other/colors/ColorListModel.hpp
src/components/other/colors/ColorProxyModel.hpp
src/components/other/colors/ImageColorsProxyModel.hpp
src/components/other/date/DateModel.hpp
src/components/other/images/ImageModel.hpp
src/components/other/images/ImageListModel.hpp
src/components/other/images/ImageProxyModel.hpp

View file

@ -52,6 +52,7 @@
#include "utils/Utils.hpp"
#include "utils/Constants.hpp"
#include "components/other/desktop-tools/DesktopTools.hpp"
#include "components/other/date/DateModel.hpp"
#include "components/settings/EmojisSettingsModel.hpp"
#include "components/timeline/TimelineModel.hpp"
@ -284,6 +285,9 @@ App::App (int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::U
qInfo() << QStringLiteral("Starting " APPLICATION_NAME " (bin: " EXECUTABLE_NAME ")");
qInfo() << QStringLiteral("Use locale: %1 with language: %2").arg(mLocale.name()).arg(QLocale::languageToString(mLocale.language()));
qInfo() << QStringLiteral("System timezone: code=%1 / country=%2 / Offset=%3 / ID=%4").arg(QTimeZone::systemTimeZone().country()).arg(Utils::getCountryName(QTimeZone::systemTimeZone().country()))
.arg(QTimeZone::systemTimeZone().standardTimeOffset(QDateTime::currentDateTime()))
.arg(QString(QTimeZone::systemTimeZoneId()));
// Deal with received messages and CLI.
QObject::connect(this, &App::receivedMessage, this, [](int, const QByteArray &byteArray) {
@ -730,6 +734,7 @@ void App::registerTypes () {
registerType<ContactsListProxyModel>("ContactsListProxyModel");
registerType<ContactsImporterListProxyModel>("ContactsImporterListProxyModel");
registerType<ContentProxyModel>("ContentProxyModel");
registerType<DateModel>("DateModel");
registerType<FileDownloader>("FileDownloader");
registerType<FileExtractor>("FileExtractor");
registerType<HistoryProxyModel>("HistoryProxyModel");

View file

@ -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<ConferenceInfoModel>()->getDateTimeSystem().date());
return QVariant::fromValue(Utils::toDateString(mList[row].objectCast<ConferenceInfoModel>()->getDateTimeSystem()));
return QVariant();
}
@ -122,4 +122,4 @@ void ConferenceInfoListModel::onRemoved(bool byUser){
remove(sender());
}

View file

@ -133,8 +133,8 @@ void ConferenceInfoModel::initDateTime(){
setDateTime(QDateTime::fromMSecsSinceEpoch(0));
setDuration(0);
}else{
setDateTime(QDateTime::currentDateTimeUtc());
setDuration(1200);
setDateTime(QDateTime::currentDateTime());
setDuration(60);
}
}
@ -144,7 +144,7 @@ QDateTime ConferenceInfoModel::getDateTimeUtc() const{
}
QDateTime ConferenceInfoModel::getDateTimeSystem() const{
return QDateTime::fromMSecsSinceEpoch(mConferenceInfo->getDateTime() * 1000, QTimeZone::systemTimeZone());
return QDateTime::fromMSecsSinceEpoch(mConferenceInfo->getDateTime() * 1000, Qt::LocalTime);
}
int ConferenceInfoModel::getDuration() const{

View file

@ -111,8 +111,9 @@ QVariant HistoryModel::data (const QModelIndex &index, int role) const {
auto &data = mEntries[row].first;
return QVariant::fromValue(data);
}
case Roles::SectionDate:
return QVariant::fromValue(mEntries[row].first["receivedTimestamp"].toDate());
case Roles::SectionDate:{
return QVariant::fromValue(Utils::toDateString(mEntries[row].first["receivedTimestamp"].toDateTime()));
}
}
return QVariant();

View file

@ -0,0 +1,56 @@
#include "DateModel.hpp"
#include <QDateTime>
#include <QDebug>
#include "utils/Utils.hpp"
DateModel::DateModel(QObject * parent) : QObject(parent){
}
DateModel::DateModel(QDate date, QObject * parent) : QObject(parent), QDate(date) {
}
DateModel::~DateModel(){
}
int DateModel::getYear() const{
return QDate::year();
}
void DateModel::setYear(int year){
QDate::setDate(year, month(), day());
emit yearChanged();
}
int DateModel::getMonth() const{
return month();
}
void DateModel::setMonth(int month){
QDate::setDate(year(), month, day());
emit monthChanged();
}
int DateModel::getDay() const{
return day();
}
void DateModel::setDay(int day) {
QDate::setDate(year(), month(), day);
emit dayChanged();
}
int DateModel::dayOfWeek() const {
return QDate::dayOfWeek();
}
bool DateModel::isEqual(DateModel * model) {
return *dynamic_cast<QDate*>(this) == *dynamic_cast< QDate*>(model);
}
bool DateModel::isGreatherThan(DateModel * model) {
return *dynamic_cast<QDate*>(this) >= *dynamic_cast<QDate*>(model);
}
QString DateModel::toDateString(const QString& format) const {
return Utils::toDateString(*this, format);
}

View file

@ -0,0 +1,39 @@
#ifndef DATE_MODEL_H
#define DATE_MODEL_H
#include <QObject>
#include <QDate>
class DateModel: public QObject, public QDate{
Q_OBJECT
Q_PROPERTY(int year READ getYear WRITE setYear NOTIFY yearChanged)
Q_PROPERTY(int month READ getMonth WRITE setMonth NOTIFY monthChanged)
Q_PROPERTY(int day READ getDay WRITE setDay NOTIFY dayChanged)
public:
DateModel(QObject * parent = 0);
DateModel(QDate date, QObject * parent = 0);
~DateModel();
int getYear() const;
void setYear(int year);
int getMonth() const;
void setMonth(int month);
int getDay() const;
void setDay(int day);
Q_INVOKABLE int dayOfWeek() const;
Q_INVOKABLE bool isEqual(DateModel * model);
Q_INVOKABLE bool isGreatherThan(DateModel * model);
Q_INVOKABLE QString toDateString(const QString& format = "yyyy/MM/dd") const;
signals:
void yearChanged();
void monthChanged();
void dayChanged();
};
Q_DECLARE_METATYPE(DateModel*);
#endif

View file

@ -73,10 +73,19 @@ QVariant TimeZoneListModel::data (const QModelIndex &index, int role) const {
}
int TimeZoneListModel::get (const QTimeZone& timeZone) const {
const auto it = find_if(
auto it = find_if(
mList.cbegin(), mList.cend(), [&timeZone](QSharedPointer<QObject> item) {
return item.objectCast<TimeZoneModel>()->getTimeZone() == timeZone;
}
);
if(it == mList.cend()) {
auto today = QDateTime::currentDateTime();
it = find_if(
mList.cbegin(), mList.cend(), [&timeZone, today](QSharedPointer<QObject> item) {
auto tz = item.objectCast<TimeZoneModel>()->getTimeZone();
return (timeZone.country() == QLocale::AnyCountry || tz.country() == timeZone.country()) && tz.standardTimeOffset(today) == timeZone.standardTimeOffset(today);
}
);
}
return it != mList.cend() ? int(distance(mList.cbegin(), it)) : 0;
}

View file

@ -47,6 +47,7 @@
#include "components/core/CoreManager.hpp"
#include "components/other/colors/ColorListModel.hpp"
#include "components/other/colors/ColorModel.hpp"
#include "components/other/date/DateModel.hpp"
#include "components/contacts/ContactsListModel.hpp"
#include "components/contact/ContactModel.hpp"
#include "components/contact/VcardModel.hpp"
@ -104,6 +105,8 @@ bool Utils::hasCapability(const QString& address, const LinphoneEnums::FriendCap
return defaultCapability;
}
//--------------------------------------------------------------------------------------
QDateTime Utils::addMinutes(QDateTime date, const int& min){
return date.addSecs(min*60);
}
@ -112,7 +115,7 @@ QDateTime Utils::getOffsettedUTC(const QDateTime& date){
QDateTime utc = date.toUTC();// Get a date free of any offsets.
auto timezone = date.timeZone();
utc = utc.addSecs(timezone.offsetFromUtc(date));// add offset from date timezone
utc.setTimeSpec(Qt::UTC);// ensure to have an UTC date
utc.setTimeSpec(Qt::OffsetFromUTC);// ensure to have an UTC date
return utc;
}
@ -133,6 +136,10 @@ QString Utils::toDateString(QDateTime date, const QString& format){
return QLocale().toString(getOffsettedUTC(date), (!format.isEmpty() ? format : QLocale().dateFormat()) );
}
QString Utils::toDateString(QDate date, const QString& format){
return QLocale().toString(date, (!format.isEmpty() ? format : QLocale().dateFormat()) );
}
// Return custom address to be displayed on UI.
// In order to return only the username and to remove all domains from the GUI, you may just change the default mode.
QString Utils::toDisplayString(const QString& str, SipDisplayMode displayMode){
@ -147,6 +154,74 @@ QString Utils::toDisplayString(const QString& str, SipDisplayMode displayMode){
return displayString;
}
QDate Utils::getCurrentDate() {
return QDate::currentDate();
}
DateModel* Utils::getCurrentDateModel() {
return new DateModel(QDate::currentDate());
}
QDate Utils::getMinDate() {
return QDate(1,1,1);
}
DateModel* Utils::getMinDateModel() {
return new DateModel(QDate(1,1,1));
}
QDate Utils::toDate(const QString& str, const QString& format) {
return QDate::fromString(str, format);
}
DateModel* Utils::toDateModel(const QString& str, const QString& format) {
return new DateModel(toDate(str, format));
}
QDate Utils::getDate(int year, int month, int day) {
auto d = QDate(year, month, day);
if(!d.isValid() ){
auto first = QDate(year, month, 1);
if(first.isValid()) {
d = first.addDays(day-1);
}
}
return d;
}
DateModel* Utils::getDateModel(int year, int month, int day) {
auto d = QDate(year, month, day);
if(!d.isValid() ){
auto first = QDate(year, month, 1);
if(first.isValid()) {
d = first.addDays(day-1);
}
}
return new DateModel(d);
}
int Utils::getFullYear(const QDate& date) {
return date.year();
}
int Utils::getMonth(const QDate& date) {
return date.month();
}
int Utils::getDay(const QDate& date) {
return date.day();
}
int Utils::getDayOfWeek(const QDate& date) {
return date.dayOfWeek();
}
bool Utils::equals(const QDate& d1, const QDate& d2){
return d1 == d2;
}
bool Utils::isGreatherThan(const QDate& d1, const QDate& d2) {
return d1 >= d2;
}
//--------------------------------------------------------------------------------------
QString Utils::getDisplayName(const QString& address){
return getDisplayName(interpretUrl(address));
}

View file

@ -34,6 +34,7 @@
class QAction;
class QWidget;
class DateModel;
// =============================================================================
@ -64,12 +65,29 @@ public:
// Qt interfaces
Q_INVOKABLE static bool hasCapability(const QString& address, const LinphoneEnums::FriendCapability& capability, bool defaultCapability = true);
//***** DATE TIME
Q_INVOKABLE static QDateTime addMinutes(QDateTime date, const int& min);
static QDateTime getOffsettedUTC(const QDateTime& date);
Q_INVOKABLE static QString toDateTimeString(QDateTime date);
Q_INVOKABLE static QString toTimeString(QDateTime date, const QString& format = "hh:mm:ss");
Q_INVOKABLE static QString toDateString(QDateTime date, const QString& format = "");
Q_INVOKABLE static QString toDateString(QDate date, const QString& format = "");
Q_INVOKABLE static QString toDisplayString(const QString& str, SipDisplayMode displayMode = SIP_DISPLAY_ALL);
Q_INVOKABLE static QDate getCurrentDate();
Q_INVOKABLE static DateModel* getCurrentDateModel();
Q_INVOKABLE static QDate getMinDate();
Q_INVOKABLE static DateModel* getMinDateModel();
Q_INVOKABLE static QDate toDate(const QString& str, const QString& format = "yyyy/MM/dd");
Q_INVOKABLE static QDate getDate(int year, int month, int day);
Q_INVOKABLE static DateModel* toDateModel(const QString& str, const QString& format = "yyyy/MM/dd");
Q_INVOKABLE static DateModel* getDateModel(int year, int month, int day);
Q_INVOKABLE static int getFullYear(const QDate& date);
Q_INVOKABLE static int getMonth(const QDate& date);
Q_INVOKABLE static int getDay(const QDate& date);
Q_INVOKABLE static int getDayOfWeek(const QDate& date);
Q_INVOKABLE static bool equals(const QDate& d1, const QDate& d2); // Override JS '==' operator
Q_INVOKABLE static bool isGreatherThan(const QDate& d1, const QDate& d2); // Override JS '>=' operator
//*****
static void cleanDisplayNameCache(const QString& address = "");// if "", clean all cache
Q_INVOKABLE static QString getDisplayName(const QString& address);
Q_INVOKABLE static QString getInitials(const QString& username); // Support UTF32

View file

@ -5,6 +5,7 @@ import Common 1.0
import Common.Styles 1.0
import Linphone 1.0
import Units 1.0
import UtilsCpp 1.0
import 'qrc:/ui/scripts/Utils/utils.js' as Utils
@ -13,8 +14,9 @@ Item{
property bool allYears : false // if false : years from today
property alias selectedDate: monthList.selectedDate
property bool hideOldDates : false
signal clicked(date date);
property DateModel currentDate: UtilsCpp.getCurrentDateModel()
Component.onDestruction: {gc()} // Call gc to free all DateModels
signal clicked(var date);
RowLayout {
id: headerRow
@ -36,7 +38,7 @@ Item{
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
horizontalAlignment: Qt.AlignCenter
text: new Date(monthList.currentYear, monthList.currentMonth, 15).toLocaleString(App.locale, 'MMMM yyyy')// 15 because of timezones that can change the date for localeString
text: new Date(monthList.currentYear, monthList.currentMonth-1, 15).toLocaleString(App.locale, 'MMMM yyyy')// 15 because of timezones that can change the date for localeString
color: DatePickerStyle.title.colorModel.color
font.pointSize: DatePickerStyle.title.pointSize
font.capitalization: Font.Capitalize
@ -60,23 +62,25 @@ Item{
property int maxYears: 5 // Max years to be requested.
function set(date) {
selectedDate = new Date(date)
var moveTo = (selectedDate.getFullYear()-minYear) * 12 + selectedDate.getMonth()
currentIndex = moveTo
if(date) {
selectedDate = date
var moveTo = (selectedDate.year-minYear) * 12 + selectedDate.month - 1
currentIndex = moveTo
}
}
property date selectedDate: new Date()
property int minYear: mainItem.allYears ? new Date(0,0,1).getFullYear() : new Date().getFullYear()
property DateModel selectedDate: mainItem.currentDate
property int minYear: (mainItem.allYears ? UtilsCpp.getMinDateModel() : mainItem.currentDate).year
snapMode: ListView.SnapOneItem
orientation: Qt.Horizontal
clip: true
// One model per month
model: (new Date().getFullYear()- minYear + maxYears) * 12
model: (mainItem.currentDate.year - minYear + maxYears) * 12
currentIndex: 0
property int currentYear: Math.floor(currentIndex / 12) + minYear
property int currentMonth: currentIndex % 12
property int currentMonth: currentIndex % 12 + 1
highlightFollowsCurrentItem: true
highlightRangeMode: ListView.StrictlyEnforceRange
@ -89,8 +93,8 @@ Item{
height: monthList.height == 0 ? 100 : monthList.height
property int year: Math.floor(index / 12) + monthList.minYear
property int month: index % 12
property int firstDay: new Date(year, month, 1).getDay()
property int month: index % 12 + 1
property int firstDay: UtilsCpp.getDateModel(year, month, 1).dayOfWeek();
GridLayout { // 1 month calender
id: grid
@ -109,12 +113,12 @@ Item{
delegate: Item{
id: cellItem
property int day: index - 7 // 0 = top left below Sunday (-7 to 41)
property int date: day - firstDay + 1 // 1-31
property date cellDate: new Date(year, month, date)
property int dayIndex: index - 7 // 0 = top left below Sunday (-7 to 41)
property int date: dayIndex - firstDay + 1// 1-31
property DateModel cellDate: UtilsCpp.getDateModel(year, month, date)
property bool selected : text.text != '-'
&& Utils.equalDate(cellDate, monthList.selectedDate)
&& text.text && day >= 0
&& text.text && dayIndex >= 0
&& cellDate.isEqual(monthList.selectedDate) // Do not use == as it will compare JS Date.
width: grid.cellMinSize
height: width
@ -136,15 +140,15 @@ Item{
color: DatePickerStyle.cell.colorModel.color
font.pixelSize: cellItem.selected
? DatePickerStyle.cell.selectedPointSize
: cellItem.day < 0
: cellItem.dayIndex < 0
? DatePickerStyle.cell.dayHeaderPointSize
: DatePickerStyle.cell.dayPointSize
font.bold: cellItem.day < 0 || cellItem.selected || Utils.equalDate(cellItem.cellDate, new Date()) // today
font.bold: cellItem.dayIndex < 0 || cellItem.selected || (cellItem.cellDate && cellItem.cellDate.isEqual(mainItem.currentDate)) // today
text: {
if(cellItem.day < 0){
if(cellItem.dayIndex < 0){
// Magic date to set day names in this order : 'S', 'M', 'T', 'W', 'T', 'F', 'S' in Locale
return App.locale.dayName(index)[0].toUpperCase()
}else if(cellItem.cellDate.getMonth() == month && (!hideOldDates || new Date(year, month, cellItem.date+1) >= new Date())) // new Date use time too
}else if(cellItem.cellDate && cellItem.cellDate.month == month && (!hideOldDates || cellItem.cellDate.isGreatherThan(currentDate)))
return cellItem.date
else
return '-'
@ -156,7 +160,7 @@ Item{
id: mouseArea
anchors.fill: parent
enabled: text.text && text.text != '-' && cellItem.day >= 0
enabled: text.text && text.text != '-' && cellItem.dayIndex >= 0
onClicked: {
monthList.selectedDate = cellItem.cellDate

View file

@ -102,7 +102,7 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: UtilsCpp.toDateString(section)
text: section
}
}
}

View file

@ -20,7 +20,7 @@ DialogPlus {
property bool isNew: !conferenceInfoModel || conferenceInfoModel.uri === ''
property ConferenceInfoModel conferenceInfoModel: ConferenceInfoModel{}
onConferenceInfoModelChanged: {
dateField.setDate(conferenceManager.conferenceInfoModel.dateTime);
dateField.setDateString(UtilsCpp.toDateString(conferenceManager.conferenceInfoModel.dateTime, 'yyyy/MM/dd'));
timeField.setTime(UtilsCpp.toTimeString(conferenceManager.conferenceInfoModel.dateTime , 'hh:mm'));
selectedParticipants.setAddresses(conferenceInfoModel)
}
@ -130,7 +130,7 @@ DialogPlus {
}
conferenceManager.creationState = 1
if( scheduledSwitch.checked){
conferenceInfoModel.setDateTime(dateField.getDate(), timeField.getTime()+':00', timeZoneField.model.getAt(timeZoneField.currentIndex) )
conferenceInfoModel.setDateTime(UtilsCpp.toDate(dateField.getDateString(), 'yyyy/MM/dd'), timeField.getTime()+':00', timeZoneField.model.getAt(timeZoneField.currentIndex) )
conferenceInfoModel.duration = durationField.model[durationField.currentIndex].value
}else{
conferenceInfoModel.isScheduled = false
@ -181,7 +181,7 @@ DialogPlus {
height: window.height - 100
width: window.width - 100
expandHeight: true
Component.onDestruction: gc() // Free DateModels from memory
// ---------------------------------------------------------------------------
RowLayout {
height: parent.height
@ -290,21 +290,21 @@ DialogPlus {
Layout.preferredWidth: parent.cellWidth; wrapMode: Text.WordWrap; color: NewConferenceStyle.titles.textColor.color; font.weight: NewConferenceStyle.titles.weight; font.pointSize: NewConferenceStyle.titles.pointSize }
TextField{id: dateField; Layout.preferredWidth: parent.cellWidth
color: NewConferenceStyle.fields.textColor.color; font.weight: NewConferenceStyle.fields.weight; font.pointSize: NewConferenceStyle.fields.pointSize
function getDate(){
function getDateString(){
return text
}
function setDate(date){
text = UtilsCpp.toDateString(date, 'yyyy/MM/dd')
function setDateString(dateString){
text = dateString
}
text: conferenceManager.conferenceInfoModel ? UtilsCpp.toDateString(conferenceManager.conferenceInfoModel.dateTime, 'yyyy/MM/dd') : ''
icon: 'drop_down_custom'
MouseArea{
anchors.fill: parent
onClicked: {
window.attachVirtualWindow(Utils.buildCommonDialogUri('DateTimeDialog'), {hideOldDates:true, showDatePicker:true, selectedDate: new Date(dateField.getDate())}
window.attachVirtualWindow(Utils.buildCommonDialogUri('DateTimeDialog'), {hideOldDates:true, showDatePicker:true, selectedDate: UtilsCpp.toDateModel(dateField.getDateString(), 'yyyy/MM/dd')}
, function (status) {
if(status){
dateField.setDate(status.selectedDate)
dateField.setDateString(status.selectedDate.toDateString('yyyy/MM/dd'))
}
}
)

View file

@ -141,7 +141,7 @@ Item{
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: UtilsCpp.toDateString(section)
text: section
}
}
}