时区转换的语义归属必须由业务层明确声明,数据库仅执行链式转换;SQL Server需用TODATETIMEOFFSET显式指定源时区,MySQL应避免CONVERT_TZ()而由应用层统一转UTC,PostgreSQL应统一使用timestamptz类型并显式声明源时区。

AT TIME ZONE 在 SQL Server 中不能直接作用于 DATETIME2,必须先用 TODATETIMEOFFSET 构造带时区的时间值,否则结果取决于服务器本地时区,极易出错。
SQL Server 存储过程里用 AT TIME ZONE 前必须显式指定源时区
你传入的 @local_time DATETIME2 本身不带时区信息,SQL Server 会默认按当前会话或服务器时区解释它。比如传入 '2026-08-04 15:00',若服务器在东八区,就会被当成北京时间;若服务器在 UTC,则当成 UTC 时间——完全不可控。
正确做法是让调用方明确传入源时区名(如 'China Standard Time'),然后用 TODATETIMEOFFSET(@local_time, @source_tz) 构造 DATETIMEOFFSET 值。
-
TODATETIMEOFFSET第二个参数必须是 Windows 时区名,不是 IANA 名('Asia/Shanghai'会报错,得用'China Standard Time') - 如果应用层用的是 IANA 时区(如
'Europe/London'),映射逻辑必须放在应用层或 CLR 函数里,SQL Server 原生不识别 - 别依赖
SYSDATETIMEOFFSET()获取“当前会话时区”,它反映的是连接设置,不是业务语义上的源时区
MySQL 存储过程中避免用 CONVERT_TZ() 做核心时区转换
CONVERT_TZ() 在生产环境静默失败风险极高:只要时区表没加载(云数据库默认不开启),它就返回 NULL,且不报错。更麻烦的是,它无法处理夏令时边界(比如美国 2026 年 3 月 9 日凌晨 2 点跳到 3 点)。
真正可控的做法是:写入前由应用层转为 UTC 时间字符串,存进 DATETIME(3) 字段;读出后仍在应用层转目标时区。
- 测试时区表是否就位:
SELECT CONVERT_TZ(NOW(), '+00:00', '+08:00');返回NULL就说明缺失 - 临时补救可用固定偏移计算,如
DATE_SUB(NOW(), INTERVAL 8 HOUR),但夏令时场景下必然偏差 -
TIMESTAMP类型虽自动转 UTC,但依赖会话时区,连接池复用时极易被覆盖,不建议作为跨时区主存储类型
PostgreSQL 存储过程里别混用 NOW() 和 CURRENT_TIMESTAMP
NOW() 返回 timestamp without time zone,CURRENT_TIMESTAMP 返回 timestamptz(即 timestamp with time zone)。前者做 AT TIME ZONE 转换时,本质是在“猜”源时区,结果不可靠。
新建表字段存业务时间,一律用 timestamptz 类型,并用 CURRENT_TIMESTAMP 插入。
- 已有
timestamp字段且数据按本地时间存入,转换前先查SHOW timezone;确认当前数据库时区设置 - 转换时必须显式声明源时区:
column_name AT TIME ZONE 'Asia/Shanghai' AT TIME ZONE 'UTC' - 跨时区调度(如“每天凌晨 2 点执行”)必须基于
timestamptz+AT TIME ZONE,不能依赖服务器本地timezone配置
GETDATE() 或 NOW() 的隐式上下文),就等于把时间准确性交给运气。

















