使用Sequelize读取PostgreSQL UTC时区数据时的时间戳差异问题
问题描述
PostgreSQL配置文件postgresql.conf已将timezone设为UTC,且查询时执行了SET TIME ZONE 'UTC';和SET timezone TO 'UTC';命令,数据库中存储的时间戳如2023-08-25 08:37:02.000,通过Sequelize查询时,获取到的时间会比原时间晚8小时(变为2023-08-25 00:37:02.000)。
已尝试在Sequelize配置中添加dialectOptions和timezone参数,但未解决问题。需求是确保获取的时间戳与PostgreSQL配置一致,保持UTC时区。
特殊现象:使用findByPk能获取正确时间,但findAndCountAll查询时时间戳异常。
复现代码
SQL部分
create table test_time( alert_time timestamp ); insert into test_time VALUES('2023-8-26 06:29:40'); select * from test_time; -- 2023-08-26 06:29:40.000 show timezone; -- Asia/Taipei SET TIME ZONE 'UTC'; SET timezone TO 'UTC'; show timezone; --UTC
Sequelize配置代码
const webConfig = require('../config/WebConfig'); const dbConfig = webConfig.dbConfig; const debug = webConfig.debug; const DataTypes = require('sequelize'); const {Op} = require('sequelize'); const initModels = require('./init-models'); const sequelize = new DataTypes(dbConfig.DB, dbConfig.USER, dbConfig.CODE, { host: dbConfig.HOST, port: dbConfig.PORT, dialect: dbConfig.dialect, logging: debug, dialectOptions: {useUTC:false}, }); //initModels are generated by sequelize-auto. const db = initModels(sequelize); db.op = Op; db.sequelize = sequelize; module.exports = db;
查询代码
const db = require('../models'); const TestTime = db.test_time; exports.query = async (obj) => { try { const testTime = await TestTime.findAndCountAll(obj); return testTime; } catch (e) { console.log('\x1b[31m%s\x1b[0m', e); } };
排查分析
- 时区配置冲突:当前Sequelize配置中
dialectOptions.useUTC: false会强制驱动不使用UTC,而数据库已设为UTC,这会导致驱动将数据库返回的UTC时间转换为本地时区(如Asia/Taipei),后续处理又出现反转,最终产生时间差。 - sequelize-auto模型差异:
findByPk与findAndCountAll的行为差异,大概率是sequelize-auto生成的模型中,timestamp字段的处理逻辑不一致——findByPk可能直接返回原始值,而findAndCountAll触发了模型的时区转换钩子。 - 连接级时区未同步:虽然执行了
SET TIME ZONE 'UTC',但这是会话级配置,如果Sequelize创建新连接时未自动同步该配置,新连接的时区仍为默认的Asia/Taipei,导致查询时间转换异常。
解决方法
1. 修正Sequelize时区配置
修改Sequelize初始化配置,明确指定时区为UTC,调整驱动参数:
const sequelize = new DataTypes(dbConfig.DB, dbConfig.USER, dbConfig.CODE, { host: dbConfig.HOST, port: dbConfig.PORT, dialect: dbConfig.dialect, logging: debug, timezone: '+00:00', // 明确设置为UTC时区 dialectOptions: { useUTC: true, // 驱动使用UTC处理时间 dateStrings: true, // 强制返回字符串格式时间,避免自动转换 typeCast: true }, });
2. 检查并修正sequelize-auto生成的模型
打开init-models生成的test_time.js模型文件,检查alert_time字段定义,确保类型为DataTypes.DATE且无额外时区转换配置。正确定义示例:
alert_time: { type: DataTypes.DATE, allowNull: true }
若模型中存在defaultValue或get钩子进行时区转换,需移除这些逻辑,确保字段直接返回数据库存储的UTC时间。
3. 全局设置连接时区
在Sequelize初始化后,添加连接钩子,确保每个新连接自动设置时区为UTC:
// 钩子方式 sequelize.addHook('beforeConnect', (config) => { config.timezone = '+00:00'; }); // 或执行全局SQL语句 sequelize.query("SET TIME ZONE 'UTC';", { raw: true });
4. 验证并调整结果格式
修改配置后重新执行findAndCountAll查询,若返回的是字符串格式时间,可手动转换为带UTC标识的Date对象:
const result = await TestTime.findAndCountAll(obj); result.rows.forEach(row => { row.alert_time = new Date(row.alert_time + 'Z'); // 添加Z标识UTC时间 });
内容的提问来源于stack exchange,提问作者huang7017
相关产品推荐
相关产品推荐

