mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
Unify handling of QDateTime time zone information
Subsurface uses "local time" which in particular means we never display time zone information to the user. The user (and our file format) only sees times like 5pm or 17:00. A better name than local time (which could mean "local at the dive spot) would be "watch time", the time displayed by the diver's watch when she entered the water. Internally, we store times as time_t, seconds since Jan 1 1970 0:00 UTC. Our convention for conversion between 5pm and time_t as always been to treat 5pm as if it were UTC. Then confusion arose since Qt's QDateTime (which is tied to UI elements like QTimeEdit and similar) is time zone aware and by default assumes the system time zone. So when we set a QDateTime to 5pm and then later convert it to time_t we have to take care about the difference between UTC and the system time zone. This patch unifies our solution to this problem: With it, we set all QDateTime's time zone to UTC. This means we don't have to correct for a time zone anymore when converting to time_t (note, however, the signedness issue: Qt's idea of time_t is broken since it assumes it to be unsigned thus not allowing for dates before 1970. Better use the millisecont variants). We only need to be careful about time zones when using the current time. With this convention, when assigning the current time to a QDateTime, we need to shift for the time zone since its value in UTC should actually be the watch time of the user who is most likely used to the system time zone. Signed-off-by: Robert C. Helling <helling@atdotde.de> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
bd40ef7f42
commit
2c715542fd
4 changed files with 16 additions and 15 deletions
|
@ -475,9 +475,8 @@ void GpsLocation::deleteFixesFromServer()
|
||||||
QList<qint64> keys = m_trackers.keys();
|
QList<qint64> keys = m_trackers.keys();
|
||||||
while (!m_deletedTrackers.isEmpty()) {
|
while (!m_deletedTrackers.isEmpty()) {
|
||||||
gpsTracker gt = m_deletedTrackers.takeFirst();
|
gpsTracker gt = m_deletedTrackers.takeFirst();
|
||||||
QDateTime dt;
|
QDateTime dt = QDateTime::fromTime_t(gt.when, Qt::UTC);
|
||||||
QUrlQuery data;
|
QUrlQuery data;
|
||||||
dt.setTime_t(gt.when - gettimezoneoffset(gt.when));
|
|
||||||
data.addQueryItem("login", prefs.userid);
|
data.addQueryItem("login", prefs.userid);
|
||||||
data.addQueryItem("dive_date", dt.toString("yyyy-MM-dd"));
|
data.addQueryItem("dive_date", dt.toString("yyyy-MM-dd"));
|
||||||
data.addQueryItem("dive_time", dt.toString("hh:mm"));
|
data.addQueryItem("dive_time", dt.toString("hh:mm"));
|
||||||
|
@ -521,9 +520,8 @@ void GpsLocation::uploadToServer()
|
||||||
QUrl url(GPS_FIX_ADD_URL);
|
QUrl url(GPS_FIX_ADD_URL);
|
||||||
Q_FOREACH(qint64 key, m_trackers.keys()) {
|
Q_FOREACH(qint64 key, m_trackers.keys()) {
|
||||||
struct gpsTracker gt = m_trackers.value(key);
|
struct gpsTracker gt = m_trackers.value(key);
|
||||||
QDateTime dt;
|
QDateTime dt = QDateTime::fromTime_t(gt.when, Qt::UTC);
|
||||||
QUrlQuery data;
|
QUrlQuery data;
|
||||||
dt.setTime_t(gt.when - gettimezoneoffset(gt.when));
|
|
||||||
data.addQueryItem("login", prefs.userid);
|
data.addQueryItem("login", prefs.userid);
|
||||||
data.addQueryItem("dive_date", dt.toString("yyyy-MM-dd"));
|
data.addQueryItem("dive_date", dt.toString("yyyy-MM-dd"));
|
||||||
data.addQueryItem("dive_time", dt.toString("hh:mm"));
|
data.addQueryItem("dive_time", dt.toString("hh:mm"));
|
||||||
|
@ -602,15 +600,18 @@ void GpsLocation::downloadFromServer()
|
||||||
qDebug() << downloadedFixes.count() << "GPS fixes downloaded";
|
qDebug() << downloadedFixes.count() << "GPS fixes downloaded";
|
||||||
for (int i = 0; i < downloadedFixes.count(); i++) {
|
for (int i = 0; i < downloadedFixes.count(); i++) {
|
||||||
QJsonObject fix = downloadedFixes[i].toObject();
|
QJsonObject fix = downloadedFixes[i].toObject();
|
||||||
QString date = fix.value("date").toString();
|
QDate date = QDate::fromString(fix.value("date").toString(), "yyy-M-d");
|
||||||
QString time = fix.value("time").toString();
|
QTime time = QTime::fromString(fix.value("time").toString(), "hh:m:s");
|
||||||
QString name = fix.value("name").toString();
|
QString name = fix.value("name").toString();
|
||||||
QString latitude = fix.value("latitude").toString();
|
QString latitude = fix.value("latitude").toString();
|
||||||
QString longitude = fix.value("longitude").toString();
|
QString longitude = fix.value("longitude").toString();
|
||||||
QDateTime timestamp = QDateTime::fromString(date + " " + time, "yyyy-M-d hh:m:s");
|
QDateTime timestamp;
|
||||||
|
timestamp.setTimeSpec(Qt::UTC);
|
||||||
|
timestamp.setDate(date);
|
||||||
|
timestamp.setTime(time);
|
||||||
|
|
||||||
struct gpsTracker gt;
|
struct gpsTracker gt;
|
||||||
gt.when = timestamp.toMSecsSinceEpoch() / 1000 + gettimezoneoffset(timestamp.toMSecsSinceEpoch() / 1000);
|
gt.when = timestamp.toMSecsSinceEpoch() / 1000;
|
||||||
gt.latitude.udeg = latitude.toDouble() * 1000000;
|
gt.latitude.udeg = latitude.toDouble() * 1000000;
|
||||||
gt.longitude.udeg = longitude.toDouble() * 1000000;
|
gt.longitude.udeg = longitude.toDouble() * 1000000;
|
||||||
gt.name = name;
|
gt.name = name;
|
||||||
|
|
|
@ -292,7 +292,6 @@ void ShiftImageTimesDialog::buttonClicked(QAbstractButton *button)
|
||||||
void ShiftImageTimesDialog::syncCameraClicked()
|
void ShiftImageTimesDialog::syncCameraClicked()
|
||||||
{
|
{
|
||||||
QPixmap picture;
|
QPixmap picture;
|
||||||
QDateTime dcDateTime = QDateTime();
|
|
||||||
QStringList fileNames = QFileDialog::getOpenFileNames(this,
|
QStringList fileNames = QFileDialog::getOpenFileNames(this,
|
||||||
tr("Open image file"),
|
tr("Open image file"),
|
||||||
DiveListView::lastUsedImageDir(),
|
DiveListView::lastUsedImageDir(),
|
||||||
|
@ -308,7 +307,7 @@ void ShiftImageTimesDialog::syncCameraClicked()
|
||||||
ui.DCImage->setScene(scene);
|
ui.DCImage->setScene(scene);
|
||||||
|
|
||||||
dcImageEpoch = picture_get_timestamp(fileNames.at(0).toUtf8().data());
|
dcImageEpoch = picture_get_timestamp(fileNames.at(0).toUtf8().data());
|
||||||
dcDateTime.setTime_t(dcImageEpoch - gettimezoneoffset(displayed_dive.when));
|
QDateTime dcDateTime = QDateTime::fromTime_t(dcImageEpoch, Qt::UTC);
|
||||||
ui.dcTime->setDateTime(dcDateTime);
|
ui.dcTime->setDateTime(dcDateTime);
|
||||||
connect(ui.dcTime, SIGNAL(dateTimeChanged(const QDateTime &)), this, SLOT(dcDateTimeChanged(const QDateTime &)));
|
connect(ui.dcTime, SIGNAL(dateTimeChanged(const QDateTime &)), this, SLOT(dcDateTimeChanged(const QDateTime &)));
|
||||||
}
|
}
|
||||||
|
@ -365,11 +364,10 @@ void ShiftImageTimesDialog::setOffset(time_t offset)
|
||||||
void ShiftImageTimesDialog::updateInvalid()
|
void ShiftImageTimesDialog::updateInvalid()
|
||||||
{
|
{
|
||||||
timestamp_t timestamp;
|
timestamp_t timestamp;
|
||||||
QDateTime time;
|
|
||||||
bool allValid = true;
|
bool allValid = true;
|
||||||
ui.warningLabel->hide();
|
ui.warningLabel->hide();
|
||||||
ui.invalidLabel->hide();
|
ui.invalidLabel->hide();
|
||||||
time.setTime_t(displayed_dive.when - gettimezoneoffset(displayed_dive.when));
|
QDateTime time = QDateTime::fromTime_t(displayed_dive.when, Qt::UTC);
|
||||||
ui.invalidLabel->setText("Dive:" + time.toString() + "\n");
|
ui.invalidLabel->setText("Dive:" + time.toString() + "\n");
|
||||||
|
|
||||||
Q_FOREACH (const QString &fileName, fileNames) {
|
Q_FOREACH (const QString &fileName, fileNames) {
|
||||||
|
@ -378,7 +376,7 @@ void ShiftImageTimesDialog::updateInvalid()
|
||||||
|
|
||||||
// We've found invalid image
|
// We've found invalid image
|
||||||
timestamp = picture_get_timestamp(fileName.toUtf8().data());
|
timestamp = picture_get_timestamp(fileName.toUtf8().data());
|
||||||
time.setTime_t(timestamp + m_amount - gettimezoneoffset(displayed_dive.when));
|
time.setTime_t(timestamp + m_amount);
|
||||||
ui.invalidLabel->setText(ui.invalidLabel->text() + fileName + " " + time.toString() + "\n");
|
ui.invalidLabel->setText(ui.invalidLabel->text() + fileName + " " + time.toString() + "\n");
|
||||||
allValid = false;
|
allValid = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -578,6 +578,7 @@ bool QMLManager::checkDate(DiveObjectHelper *myDive, struct dive * d, QString da
|
||||||
QString oldDate = myDive->date() + " " + myDive->time();
|
QString oldDate = myDive->date() + " " + myDive->time();
|
||||||
if (date != oldDate) {
|
if (date != oldDate) {
|
||||||
QDateTime newDate;
|
QDateTime newDate;
|
||||||
|
newDate.setTimeSpec(Qt::UTC);
|
||||||
// what a pain - Qt will not parse dates if the day of the week is incorrect
|
// what a pain - Qt will not parse dates if the day of the week is incorrect
|
||||||
// so if the user changed the date but didn't update the day of the week (most likely behavior, actually),
|
// so if the user changed the date but didn't update the day of the week (most likely behavior, actually),
|
||||||
// we need to make sure we don't try to parse that
|
// we need to make sure we don't try to parse that
|
||||||
|
@ -668,7 +669,7 @@ parsed:
|
||||||
// add a hundred years.
|
// add a hundred years.
|
||||||
if (newDate.addYears(100) < QDateTime::currentDateTime().addYears(1))
|
if (newDate.addYears(100) < QDateTime::currentDateTime().addYears(1))
|
||||||
newDate = newDate.addYears(100);
|
newDate = newDate.addYears(100);
|
||||||
d->dc.when = d->when = newDate.toMSecsSinceEpoch() / 1000 + gettimezoneoffset(newDate.toMSecsSinceEpoch() / 1000);
|
d->dc.when = d->when = newDate.toMSecsSinceEpoch() / 1000;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
qDebug() << "none of our parsing attempts worked for the date string";
|
qDebug() << "none of our parsing attempts worked for the date string";
|
||||||
|
|
|
@ -353,6 +353,7 @@ DivePlannerPointsModel::DivePlannerPointsModel(QObject *parent) : QAbstractTable
|
||||||
tempGFLow(100)
|
tempGFLow(100)
|
||||||
{
|
{
|
||||||
memset(&diveplan, 0, sizeof(diveplan));
|
memset(&diveplan, 0, sizeof(diveplan));
|
||||||
|
startTime.setTimeSpec(Qt::UTC);
|
||||||
}
|
}
|
||||||
|
|
||||||
DivePlannerPointsModel *DivePlannerPointsModel::instance()
|
DivePlannerPointsModel *DivePlannerPointsModel::instance()
|
||||||
|
@ -540,7 +541,7 @@ void DivePlannerPointsModel::setStartDate(const QDate &date)
|
||||||
void DivePlannerPointsModel::setStartTime(const QTime &t)
|
void DivePlannerPointsModel::setStartTime(const QTime &t)
|
||||||
{
|
{
|
||||||
startTime.setTime(t);
|
startTime.setTime(t);
|
||||||
diveplan.when = startTime.toTime_t();
|
diveplan.when = startTime.toTime_t();
|
||||||
displayed_dive.when = diveplan.when;
|
displayed_dive.when = diveplan.when;
|
||||||
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1));
|
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue