From b0d73b947faf6aab614fe48dfa0a73b80080aa0e Mon Sep 17 00:00:00 2001 From: Andreas Bacher Date: Mon, 9 Sep 2024 21:36:03 +0200 Subject: [PATCH] SQL/Firebird: Fix interpretation of time stamp with timezone The firebird api expects the timestamp (ISC_TIMESTAMP_TZ) of a timestamp with time zone is provided in UTC. Pick-to: 6.7 Task-number: QTBUG-128493 Change-Id: Iacc85ca1141407f5ab73fd0198c7b2db770bf589 Reviewed-by: Christian Ehrlicher Reviewed-by: Johann Anhofer (cherry picked from commit 8d8805214df22bf8dccbb30c9ca4a9953f3a1068) Reviewed-by: Qt Cherry-pick Bot --- src/plugins/sqldrivers/ibase/qsql_ibase.cpp | 10 ++-- .../sql/kernel/qsqlquery/tst_qsqlquery.cpp | 53 +++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp index 8f08b023bbb..aad8cead880 100644 --- a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp +++ b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp @@ -263,16 +263,20 @@ static inline QDateTime fromTimeStampTz(const char *buffer) QByteArray timeZoneName = qFbTzIdToIanaIdMap()->value(fpTzID); if (!timeZoneName.isEmpty()) - return QDateTime(d, t, QTimeZone(timeZoneName)); + { + const auto utc = QDateTime(d, t, QTimeZone(QTimeZone::UTC)); + return utc.toTimeZone(QTimeZone(timeZoneName)); + } else return {}; } static inline ISC_TIMESTAMP_TZ toTimeStampTz(const QDateTime &dt) { + const auto dtUtc = dt.toUTC(); ISC_TIMESTAMP_TZ ts; - ts.utc_timestamp.timestamp_time = dt.time().msecsSinceStartOfDay() * 10; - ts.utc_timestamp.timestamp_date = s_ibaseBaseDate.daysTo(dt.date()); + ts.utc_timestamp.timestamp_time = dtUtc.time().msecsSinceStartOfDay() * 10; + ts.utc_timestamp.timestamp_date = s_ibaseBaseDate.daysTo(dtUtc.date()); ts.time_zone = qIanaIdToFbTzIdMap()->value(dt.timeZone().id().simplified(), 0); return ts; } diff --git a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp index ee34995f8ee..653c898cbc7 100644 --- a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp +++ b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp @@ -259,6 +259,9 @@ private slots: void ibaseInt128_data() { generic_data("QIBASE"); } void ibaseInt128(); + void QTBUG_128493_data() { generic_data("QIBASE"); } + void QTBUG_128493(); + void psqlJsonOperator_data() { generic_data("QPSQL"); } void psqlJsonOperator(); @@ -4868,6 +4871,56 @@ void tst_QSqlQuery::ibaseDateTimeWithTZ() } } +void tst_QSqlQuery::QTBUG_128493() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + if (tst_Databases::getDatabaseType(db) != QSqlDriver::Interbase) + QSKIP("Implemented only for Interbase"); + + if (tst_Databases::getIbaseEngineVersion(db).majorVersion() < 4) + QSKIP("Time zone support only implemented for firebird engine version 4 and greater"); + + + QSqlQuery q{db}; + +#if QT_CONFIG(timezone) + const auto currentDateTime = QDateTime::currentDateTime().toTimeZone(QTimeZone("Europe/Vienna"_ba)); + //Set session time zone + QVERIFY_SQL(q, exec(u"set time zone 'Europe/Vienna'"_s)); +#else + const auto currentDateTime = QDateTime::currentDateTime(); +#endif // QT_CONFIG(timezone) + + QVERIFY_SQL(q, exec(u"select current_timestamp from rdb$database"_s)); + QVERIFY_SQL(q, isActive()); + QVERIFY_SQL(q, next()); + const auto currentDateTimeDB = q.value(0).toDateTime(); + + QCOMPARE(currentDateTime.date(), currentDateTimeDB.date()); + QCOMPARE(currentDateTime.offsetFromUtc(), currentDateTimeDB.offsetFromUtc()); + QCOMPARE(currentDateTime.isDaylightTime(), currentDateTimeDB.isDaylightTime()); + + QCOMPARE(currentDateTime.time().hour(), currentDateTimeDB.time().hour()); + + const QString tableName(qTableName(u"dateTimeTS"_s, __FILE__, db)); + QVERIFY_SQL(q, exec(u"CREATE TABLE "_s + tableName + u"(dt timestamp with time zone)"_s)); + + QVERIFY_SQL(q, prepare(u"INSERT INTO %1 values(:dt)"_s.arg(tableName))); + q.bindValue(":dt", currentDateTime ); + QVERIFY_SQL(q, exec()); + + QVERIFY_SQL(q, exec(u"SELECT cast(dt AS VARCHAR(50)) FROM "_s + tableName)); + QVERIFY_SQL(q, next()); + + const auto currentDateTimeFromDBString = q.value(0).toString(); + auto currentDateTimeFromDB = QDateTime::fromString(currentDateTimeFromDBString, + u"yyyy-MM-dd hh:mm:ss.zzz0 tttt"_s); + + QCOMPARE(currentDateTimeFromDB, currentDateTime); +} + void tst_QSqlQuery::sqliteVirtualTable() { // Virtual tables can behave differently when it comes to prepared