You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

绑定QDateTime到MySQL预处理查询时UTC时间异常排查

问题描述

我有一个包含datetime字段的MySQL数据库表,代码运行环境的系统时区为意大利时区(GMT+1)。当使用QSqlQuery预处理查询并绑定QDateTime变量时,插入操作成功,但查询返回的数据为UTC时间;而绑定QDateTime::toString(Qt::ISODate)生成的字符串时,插入和查询结果均正常。执行SELECT @@GLOBAL.time_zone, @@SESSION.time_zone;查询,结果均为SYSTEM。请问这是驱动问题还是操作有误?

示例代码(Qt 6.8.1 + MySQL 8.0)

auto db = QSqlDatabase(QSqlDatabase::addDatabase("QMYSQL"));
db.setHostName("127.0.0.1");
db.setDatabaseName("test_db");
db.open("test_user", "test_pwd");

QSqlQuery queryCreate("CREATE TABLE test_table (id int AUTO_INCREMENT, date_time datetime NOT NULL, PRIMARY KEY (id));", db);

QSqlQuery queryInsertDt(db);
queryInsertDt.prepare("INSERT INTO test_table (date_time) VALUE (:ts);");
auto now = QDateTime::currentDateTime();
queryInsertDt.bindValue(0, now);
qDebug() << now << queryInsertDt.boundValue(":ts").toString() << queryInsertDt.boundValue(":ts").toDateTime();
// prints: QDateTime(2025-01-28 15:43:33.242 W. Europe Standard Time Qt::LocalTime) "2025-01-28T15:43:33.242" QDateTime(2025-01-28 15:43:33.242 W. Europe Standard Time Qt::LocalTime)
queryInsertDt.exec();

QSqlQuery queryInsertStr(db);
queryInsertStr.prepare("INSERT INTO test_table (date_time) VALUE (:ts);");
queryInsertStr.bindValue(0, now.toString(Qt::ISODate));
qDebug() << now << queryInsertStr.boundValue(":ts").toString() << queryInsertStr.boundValue(":ts").toDateTime();
// prints: QDateTime(2025-01-28 15:43:33.242 W. Europe Standard Time Qt::LocalTime) "2025-01-28T15:43:33" QDateTime(2025-01-28 15:43:33.000 W. Europe Standard Time Qt::LocalTime)
queryInsertStr.exec();

QSqlQuery querySelect(db);
querySelect.exec("SELECT id, date_time FROM test_table;");
int rowCount = 0;
while(querySelect.next())
    qDebug() << "Id" << querySelect.value(0).toInt() << "-" << querySelect.value(1).toString() << querySelect.value(1).toDateTime();
// Id 1 - "2025-01-28T14:43:33.000Z" QDateTime(2025-01-28 14:43:33.000 UTC Qt::UTC)
// Id 2 - "2025-01-28T15:43:33.000Z" QDateTime(2025-01-28 15:43:33.000 UTC Qt::UTC)

QSqlQuery querySelectWhere(db);
querySelectWhere.exec(QString("SELECT id, date_time FROM test_table WHERE HOUR(date_time) = %1;").arg(now.time().hour()));
while(querySelectWhere.next())
    qDebug() << "Id" << querySelectWhere.value(0).toInt() << "-" << querySelectWhere.value(1).toString() << querySelectWhere.value(1).toDateTime();
// Id 2 - "2025-01-28T15:43:33.000Z" QDateTime(2025-01-28 15:43:33.000 UTC Qt::UTC)

QSqlQuery queryDrop("DROP TABLE test_table;", db);
问题分析与解决方案

这不是操作错误,而是Qt的QMYSQL驱动处理QDateTime绑定的时区转换逻辑导致的:

  1. 绑定QDateTime变量时的行为
    Qt的QMYSQL驱动在绑定QDateTime对象时,会默认将本地时间转换为UTC时间后发送给MySQL。即使MySQL的会话时区是SYSTEM(即意大利时区),驱动本身做了额外的时区转换,导致插入的时间是UTC时间(比本地时间晚1小时)。查询时,驱动又将MySQL返回的存储时间直接标记为UTC,所以显示结果为UTC时间。

  2. 绑定字符串时的行为
    当绑定QDateTime::toString(Qt::ISODate)生成的字符串时,驱动直接将字符串传递给MySQL,MySQL会按照会话时区(SYSTEM,意大利时区)解析这个时间字符串,存储为对应本地时间。查询时,返回的时间被驱动正确解析为对应时区的时间。

解决方法

  • 方法一:显式设置MySQL会话时区
    在打开数据库连接后,执行以下SQL语句,强制会话时区与本地时区一致,覆盖驱动的默认转换:

    SET time_zone = '+01:00';
    

    这样驱动在绑定QDateTime时,会基于这个时区进行转换,确保插入和查询的时间正确。

  • 方法二:统一使用字符串绑定
    继续使用QDateTime::toString(Qt::ISODate)生成字符串进行绑定,这种方式绕过驱动的时区转换逻辑,直接让MySQL按会话时区处理时间。

  • 方法三:使用带时区的字段类型
    将表中的datetime字段改为datetime(6)或者timestamp类型,timestamp会自动存储UTC时间,但查询时会根据会话时区转换为本地时间,不过需要注意timestamp的时间范围限制(1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC)。

内容的提问来源于stack exchange,提问作者Lorenzo Aldrighetti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 19:18:14