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 <ch.ehrlicher@gmx.de>
Reviewed-by: Johann Anhofer <johann.anhofer@meon-medical.com>
(cherry picked from commit 8d8805214df22bf8dccbb30c9ca4a9953f3a1068)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Andreas Bacher 2024-09-09 21:36:03 +02:00 committed by Qt Cherry-pick Bot
parent ae4a48decc
commit b0d73b947f
2 changed files with 60 additions and 3 deletions

View File

@ -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;
}

View File

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