Qt. QDateTime непредсказуемое поведение с часовыми поясами и addSecs

Я не могу понять, что не так.

QTimeZone zone1(QTimeZone("Europe/Moscow"));
QTimeZone zone2(QTimeZone("Asia/Yekaterinburg"));
QDateTime test = QDateTime(QDate(2016, 11, 11), QTime(15,00), zone1);
qDebug() << test;//QDateTime(2016-11-11 15:00:00.000 MSK Qt::TimeSpec(TimeZone) Europe/Moscow)
test = test.addSecs(5*60);
qDebug() << test;//QDateTime(2016-11-11 15:05:00.000 MSK Qt::TimeSpec(TimeZone) Europe/Moscow)

Это нормально работает с Европой / Москвой (+3), но когда я меняю часовой пояс на Азию / Екатеринбург (+5), это работает очень странно

QTimeZone zone2(QTimeZone("Asia/Yekaterinburg"));
QDateTime test = QDateTime(QDate(2016, 11, 11), QTime(15,00), zone2);
qDebug() << test;//QDateTime(2016-11-11 15:00:00.000  Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
test = test.addSecs(5*60);
qDebug() << test;//QDateTime(2016-11-11 10:05:00.000  Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)

1

Решение

Странно, я воспроизвел это, используя Qt 5.6.0 с этим настроенным примером.

QDateTime test1 = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("Europe/Moscow"));
QDateTime test2 = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("Asia/Yekaterinburg"));
QDateTime test3 = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("Europe/Berlin"));
qDebug() << test1;
qDebug() << test2;
qDebug() << test3;
qDebug() << test1.addSecs(5*60);
qDebug() << test2.addSecs(5*60);
qDebug() << test3.addSecs(5*60);

Выход:

QDateTime(2016-11-11 15:00:00.000 RTZ 2 Qt::TimeSpec(TimeZone) Europe/Moscow)
QDateTime(2016-11-11 15:00:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:00:00.000 MEZ   Qt::TimeSpec(TimeZone) Europe/Berlin)
QDateTime(2016-11-11 18:05:00.000 RTZ 2 Qt::TimeSpec(TimeZone) Europe/Moscow)
QDateTime(2016-11-11 20:05:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:05:00.000 MEZ   Qt::TimeSpec(TimeZone) Europe/Berlin)
QDateTime(2016-11-11 18:05:00.000 RTZ 2 Qt::TimeSpec(TimeZone) Europe/Moscow)

Обратите внимание, что я добавил другой часовой пояс, который является моим местным часовым поясом. Вы, наверное, заметили, что это часовой пояс, который работает (Europe/Berlin).

Следующее, что нужно сделать, — это анализ смещения. Вы видите следующие смещения:

  • Europe/Moscow: + 3 часа
  • Asia/Yekaterinburg: + 5ч

Глядя на некоторые карта часовых поясов Можно заметить, что смещение между двумя зонами точно 2h. Так где же +3h приходящий из?

Излишне говорить, что я провел еще один тест.

QDateTime dt = QDateTime(QDate(2016, 11, 11), QTime(15, 00), QTimeZone("UTC"));
qDebug() << dt;
qDebug() << dt.addSecs(5*60);
qDebug() << dt.addSecs(5*60).toTimeZone(QTimeZone("Asia/Yekaterinburg"));

Выход:

QDateTime(2016-11-11 15:00:00.000 UTC   Qt::TimeSpec(TimeZone) UTC)
QDateTime(2016-11-11 15:05:00.000 UTC   Qt::TimeSpec(TimeZone) UTC)
QDateTime(2016-11-11 20:05:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)

И здесь мы идем: кажется, что Qt не переводит часовой пояс в UTC до вычисления, а использует его как UTC, что приводит к обратному сдвигу с соответствующим смещением.

Но ждать…

QDateTime dt = QDateTime(QDate(2016, 11, 11), QTime(15, 00), Qt::UTC);
qDebug() << dt.toLocalTime();
qDebug() << dt.toTimeZone(QTimeZone("Asia/Yekaterinburg"));
qDebug() << dt.toUTC();

Выход:

QDateTime(2016-11-11 16:00:00.000 MEZ   Qt::TimeSpec(LocalTime))
QDateTime(2016-11-11 20:00:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:00:00.000 UTC   Qt::TimeSpec(UTC))

Теперь похоже, что если конструктор получает часовой пояс, он предполагает, что это UTC, а не предоставленный. Если его не использовать, Qt занимает местное время, что очевидно.

Как это не задокументировано, где это используемый в быть это похоже на ошибку для меня.


Короче говоря, что делать?

Если применимо, попробуйте указать время по местному времени или в формате UTC, преобразовать их в UTC, рассчитать с ними и затем отформатировать до желаемого результата.

QDateTime dt = QDateTime(QDate(2016, 11, 11), QTime(10, 00), Qt::UTC);
qDebug() << dt.toTimeZone(QTimeZone("Asia/Yekaterinburg"));
qDebug() << dt.addSecs(5*60).toTimeZone(QTimeZone("Asia/Yekaterinburg"));

Выход:

QDateTime(2016-11-11 15:00:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
QDateTime(2016-11-11 15:05:00.000 RTZ 4 Qt::TimeSpec(TimeZone) Asia/Yekaterinburg)
1

Другие решения

Другой возможный обходной путь — использовать это бесплатно, с открытым исходным кодом, библиотека C ++ 11/14 который основывается на <chrono>:

#include "tz.h"#include <iostream>

int
main()
{
using namespace date;
using namespace std::chrono;
auto zone1 = make_zoned("Europe/Moscow", local_days{2016_y/11/11} + 15h);
std::cout << zone1 << '\n';
zone1 = zone1.get_local_time() + 5min;
std::cout << zone1 << '\n';

auto zone2 = make_zoned("Asia/Yekaterinburg", local_days{2016_y/11/11} + 15h);
std::cout << zone2 << '\n';
zone2 = zone2.get_local_time() + 5min;
std::cout << zone2 << '\n';
}

Выход:

2016-11-11 15:00:00 MSK
2016-11-11 15:05:00 MSK
2016-11-11 15:00:00 +05
2016-11-11 15:05:00 +05

Вот функции преобразования в / из QDate:

https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes#QDate

1

По вопросам рекламы [email protected]