SQL Server中应优先用SYSDATETIME()替代GETDATE()以获得更高精度和兼容性;PostgreSQL中应使用CURRENT_TIMESTAMP而非NOW();MySQL需确保时区表加载才能正确使用CONVERT_TZ();跨库迁移时须统一转换为ISO 8601 UTC格式。

SQL Server 里 GETDATE() 和 SYSDATETIME() 别混用
两者都返回当前时间,但精度和时区行为完全不同:GETDATE() 是 datetime 类型(精度到 3.33ms,无时区),SYSDATETIME() 返回 datetime2(7)(100ns 精度,仍无时区)。如果后续要做时区转换,用错类型会导致隐式转换丢失精度或报错。
实操建议:
- 需要毫秒级以上精度、或准备转成
datetimeoffset的场景,一律用SYSDATETIME() - 老系统兼容旧逻辑(比如和
datetime字段直接比较),才考虑GETDATE() - 别在计算中混用:比如
SYSDATETIME() - GETDATE()会触发隐式转换,可能截断小数秒
PostgreSQL 中 NOW() 默认不带时区,但 CURRENT_TIMESTAMP 带
这是个容易被忽略的坑:NOW() 和 CURRENT_TIMESTAMP 都返回当前时间,但前者类型是 timestamp without time zone,后者是 timestamp with time zone。一旦你用 NOW() 存时间,再想按 UTC 或其他时区做加减或比较,就得手动 AT TIME ZONE 转换,否则结果可能偏移。
实操建议:
- 新建表字段存“业务发生时间”时,优先用
timestamptz类型,并用CURRENT_TIMESTAMP插入 - 已有
timestamp字段且数据已按本地时间存入,转换前先确认数据库timezone设置(SHOW timezone),再用column_name AT TIME ZONE 'Asia/Shanghai'显式对齐 - 存储过程里做跨时区调度(比如凌晨 2 点跑任务),必须用
timestamptz+AT TIME ZONE,不能依赖服务器本地时区
MySQL 存储过程中用 CONVERT_TZ() 前先检查时区表是否加载
CONVERT_TZ('2024-01-01 12:00:00', '+08:00', '+00:00') 看似能用,但如果 MySQL 没加载时区表(mysql.time_zone* 表为空),它会静默失败——返回 NULL,而且不报错。很多 Docker 镜像或精简版安装默认不加载。
实操建议:
- 执行
SELECT CONVERT_TZ(NOW(), '+08:00', '+00:00');测试,返回NULL就说明时区表缺失 - 加载方式:用系统时区文件导入,例如
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql(路径依系统而异) - 如果无法改服务器配置,退而求其次:用固定偏移计算,比如
DATE_SUB(NOW(), INTERVAL 8 HOUR),但注意夏令时场景下会出错
跨数据库迁移时,datetimeoffset 在 SQL Server 和 PostgreSQL 的语义不等价
SQL Server 的 datetimeoffset 是原生类型,带完整时区偏移(如 2024-01-01 12:00:00.0000000 +08:00);PostgreSQL 的 timestamptz 实际存的是 UTC 时间,显示时按会话 timezone 渲染。两者导出导入时若不做归一化,日期会平移。
实操建议:
- 导出 SQL Server 数据时,用
CONVERT(varchar, dt, 126) + 'Z'把datetimeoffset转成 ISO 8601 UTC 字符串(如'2024-01-01T12:00:00.0000000Z') - 导入 PostgreSQL 时,用
col::timestamptz自动识别Z后缀,避免被当成本地时间解析 - 别直接用
CAST或TO_TIMESTAMP处理带偏移的字符串,容易漏掉时区信息
事情说清了就结束。时区不是“设对一个参数就能一劳永逸”的事,它藏在函数选择、类型定义、数据导入导出、甚至容器镜像配置里——漏掉任意一环,时间就悄悄偏了。

















