Details
-
Bug
-
Resolution: Invalid
-
P2: Important
-
None
-
6.7.2, 6.8.0
-
None
-
-
8d8805214 (dev), b0d73b947 (6.8), 4be1b3666 (6.7)
Description
The firebird docu says:
Storage of Time Zone Types
The time zone types are stored as values at UTC (offset 0), using the structure of TIME or TIMESTAMP + two extra bytes for time zone information (either an offset in minutes, or the id of a named time zone). Storing as UTC allows Firebird to index and compare two values in different time zones.
But the two functions (fromTimeStampTz, toTimeStampTz) in qsql_ibase.cpp doesn't respect this:
QDateTime fromTimeStampTz(char *buffer) { ... if (!timeZoneName.isEmpty()) return QDateTime(d, t, QTimeZone(timeZoneName)); // should convert from UTC here else return {}; ... } ISC_TIMESTAMP_TZ toTimeStampTz(const QDateTime &dt) { ... // dt is local time, but should be converted to UTC ts.utc_timestamp.timestamp_time = midnight.msecsTo(dt.time()) * 10; ts.utc_timestamp.timestamp_date = basedate.daysTo(dt.date()); ts.time_zone = qIanaIdToFbTzIdMap()->value(dt.timeZone().id().simplified(), 0); ... }
Howto prove the bug:
auto db = QSqlDatabase::addDatabase("QIBASE", "test"); db.setDatabaseName("employee"); db.setHostName("localhost"); db.setPort(3050); db.setUserName("SYSDBA"); db.setPassword("masterkey"); QVERIFY(db.open()); const auto now = QDateTime::currentDateTime(); QSqlQuery qry{db}; QVERIFY2(qry.exec(u"select current_timestamp from rdb$database"_s), qPrintable(getErrorMessage(qry.lastError()))); QVERIFY2(qry.isActive(), qPrintable(getErrorMessage(qry.lastError()))); QVERIFY2(qry.next(), qPrintable(getErrorMessage(qry.lastError()))); const auto then = qry.value(0).toDateTime(); QCOMPARE(now.date(), then.date()); QCOMPARE(now.offsetFromUtc(), then.offsetFromUtc()); QCOMPARE(now.isDaylightTime(), then.isDaylightTime()); // this reveals the bug QCOMPARE(now.time().hour(), then.time().hour());
How to fix it:
QDateTime fromTimeStampTz(char *buffer) { ... if (!timeZoneName.isEmpty()) { // stored timestamp is in UTC const auto utc = QDateTime(d, t, Qt::UTC); return utc.toTimeZone(QTimeZone(timeZoneName)); } else return {}; ... } ISC_TIMESTAMP_TZ toTimeStampTz(const QDateTime &dt) { ... // convert to UTC before storing const auto dtUtc = dt.toUTC(); ISC_TIMESTAMP_TZ ts; ts.utc_timestamp.timestamp_time = midnight.msecsTo(dtUtc.time()) * 10; ts.utc_timestamp.timestamp_date = basedate.daysTo(dtUtc.date()); ts.time_zone = qIanaIdToFbTzIdMap()->value(dt.timeZone().id().simplified(), 0); ... }
Attachments
For Gerrit Dashboard: QTBUG-128493 | ||||||
---|---|---|---|---|---|---|
# | Subject | Branch | Project | Status | CR | V |
589612,9 | SQL/Firebird: Fix interpretation of time stamp with timezone | dev | qt/qtbase | Status: MERGED | +2 | 0 |
590371,2 | SQL/Firebird: Fix interpretation of time stamp with timezone | 6.8 | qt/qtbase | Status: MERGED | +2 | 0 |
590456,3 | SQL/Firebird: Fix interpretation of time stamp with timezone | 6.7 | qt/qtbase | Status: MERGED | +2 | 0 |
590995,2 | SQL/Firebird: Fix interpretation of time stamp with timezone | 6.8.0 | qt/qtbase | Status: ABANDONED | 0 | 0 |
590996,2 | SQL/Firebird: Fix interpretation of time stamp with timezone | 6.7.3 | qt/qtbase | Status: ABANDONED | 0 | 0 |