1

I can't realize what's wrong.

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)

This is working fine with Europe/Moscow(+3), but when I change timezone to Asia/Yekaterinburg(+5) it works very strange

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)
Atnes Ness
  • 13
  • 4

2 Answers2

2

Strange thing, I reproduced it using Qt 5.6.0 with this adjusted example.

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);

Output:

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)

Note that I added another time zone, which is my local time zone. You probably note that this is the time zone which works (Europe/Berlin).

Next thing to do is offset analysis. You see the following offsets:

  • Europe/Moscow: +3h
  • Asia/Yekaterinburg: +5h

Looking at some time zone map one might note that the offset between the two zones is exactly 2h. So where is the +3h coming from?

Needless to say, I ran another test.

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"));

Output:

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)

And here we go: It seems like Qt is not transfering the time zone to UTC before calculation but uses it as UTC which results in a back shift with the corresponding offset.

But wait...

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

Output:

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))

Now it looks like if the constructor gets a time zone it assumes it is UTC, not the provided one. If leaving it out Qt takes local time, which is obvious.

As it is not documented where it used to be this seems like a bug to me.


Long story short, what to do?

If applicable, try to provide times as local time or UTC, tranform them to UTC, calcualte with them and then format to desired output.

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"));

Output:

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)
maxik
  • 1,053
  • 13
  • 34
  • I needed to manipulate with time zones just for scheduling. For example my car departures Yekaterinburg and local time is 15:00, so when I arrive to Moscow it's next day 18:00 by local time of Yekaterinburg, and I want to just change timezone and it will lead to changing time to Moscow local. So I understood that the behaviour of QDateTime is very weird, and for my task it's not necessary to setTimezone, I can just apply shifts between old timezone and new one. the only problem is to remembering last timezone – Atnes Ness Nov 16 '16 at 08:05
  • But It doesn't work too... :D `QTimeZone zoneNew(QTimeZone("Europe/Moscow")); QTimeZone zoneOld(QTimeZone("Asia/Yekaterinburg")); QDateTime test = QDateTime::currentDateTimeUtc(); test.setTime(QTime(18, 00)); qDebug() << zoneOld.offsetFromUtc(test); // 0 qDebug() << zoneNew.offsetFromUtc(test); // 10800` Very Weird – Atnes Ness Nov 16 '16 at 08:17
  • @AtnesNess See my last example which worked for me. It puzzles me also what Qt is doing there. – maxik Nov 16 '16 at 21:22
1

Another possible workaround is to use this free, open source, C++11/14 library which builds on <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';
}

The output is:

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

Here are conversion functions to/from QDate:

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

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577