AT TIME ZONE 不能直接跨时区转换,必须经 UTC 中转:先将无时区 datetime2 按源时区解释为 UTC,再转为目标时区本地时间;时区名须用 Windows 标识符,且需注意夏令时与历史偏移。

AT TIME ZONE 不能直接转换任意两个时区,必须经过 UTC 中转
SQL Server 的 AT TIME ZONE 不是“从 A 时区直接转到 B 时区”的函数,它只做两件事:把一个无时区的 datetime2 解释为某个时区的本地时间(→ UTC),或把 UTC 时间转换为指定时区的本地时间(UTC →)。所以跨时区换算本质是两步:原时间 AT TIME ZONE '源时区' → 转成 UTC datetime2,再 AT TIME ZONE '目标时区'。
常见错误是写成 GETDATE() AT TIME ZONE 'China Standard Time' AT TIME ZONE 'Pacific Standard Time' —— 这会先按 CST 解释当前服务器时间(错!因为 GETDATE() 返回的是本地服务器时区时间,不是 CST),再转 PST,结果完全不可靠。
- 必须确保输入时间是
datetime2类型且不带时区信息(即datetime2值本身是“纯时间点”,不隐含解释) - 若原始数据来自应用层,建议统一存为 UTC,避免源头歧义
- 时区名称必须用 Windows 时区标识符(如
'China Standard Time',不是'Asia/Shanghai'),可用sys.time_zone_info查看支持列表
如何正确把北京时间下午 3 点转成纽约时间
假设你有一条明确的北京时间(CST)时间点:2024-06-15 15:00:00,想得到对应纽约(Eastern Time)本地时间。关键在于:这个“15:00”是 CST 的本地时间,不是服务器时间,也不是 UTC。
SELECT
CAST('2024-06-15T15:00:00' AS datetime2) AT TIME ZONE 'China Standard Time'
AT TIME ZONE 'Eastern Standard Time' AS ny_time;
执行过程:
• 第一步:把字符串转成 datetime2,再用 AT TIME ZONE 'China Standard Time' 将其解释为 CST 本地时间 → 得到带偏移的 datetimeoffset(即 UTC 时间)
• 第二步:该 datetimeoffset 自动作为 UTC 输入,再经 AT TIME ZONE 'Eastern Standard Time' 转为 ET 本地时间
- 注意:夏季用
'Eastern Daylight Time'更准确;SQL Server 不自动处理夏令时切换,它依赖系统时区库,而 Windows 时区名(如'Eastern Standard Time')本身已包含 DST 规则 - 如果原始时间字段是
datetime类型,务必先转成datetime2,否则精度丢失且AT TIME ZONE会静默失败 - 结果列类型是
datetimeoffset,若只要本地时间值(去掉偏移),可外裹CAST(... AS datetime2)
批量转换表中带时区标记的时间字段
当表里已有 datetime2 列 + 单独的 timezone_name 列(如 'Tokyo Standard Time'、'GMT Standard Time'),需动态转成统一目标时区(如 UTC 或 ‘W. Europe Standard Time’)。这时不能直接在 AT TIME ZONE 中用变量或列名——它不接受表达式作为时区参数。
可行方案是用 CASE 显式枚举(适用于时区种类有限),或改用 TRY_CAST + 多次 UNION ALL 拆解(不推荐),更稳妥的是在应用层处理,或用标量函数封装(但有性能代价):
SELECT
id,
event_time,
timezone_name,
CASE timezone_name
WHEN 'China Standard Time' THEN event_time AT TIME ZONE 'China Standard Time'
WHEN 'Tokyo Standard Time' THEN event_time AT TIME ZONE 'Tokyo Standard Time'
WHEN 'GMT Standard Time' THEN event_time AT TIME ZONE 'GMT Standard Time'
ELSE NULL
END AT TIME ZONE 'W. Europe Standard Time' AS time_in_rome
FROM events;
-
AT TIME ZONE的时区参数必须是字符串字面量,不能是变量、列或拼接结果 - 大量不同源时区时,建议提前将源时间统一转为 UTC 存储,后续转换更高效、可索引
- 注意
sys.time_zone_info中的current_utc_offset是当前偏移,不反映历史变化(如 1986 年中国未实行夏令时),所以历史时间转换需额外校验
为什么有时转换后时间没变,甚至倒退一小时
典型现象:把某时间从 'Pacific Standard Time' 转到 'Pacific Daylight Time',结果却显示相同时间点,或出现“2:30 AM 重复”“跳过 2:00 AM”等异常。这不是 bug,而是 Windows 时区数据库对夏令时过渡的精确建模所致。
例如:2024 年 3 月 10 日凌晨 2:00,美国太平洋时间进入夏令时,时钟拨快至 3:00 —— 那么 '2024-03-10T02:30:00' AT TIME ZONE 'Pacific Standard Time' 实际不存在(标准时间下该时刻被跳过),SQL Server 会按规则映射到最近有效时间(通常是 3:30)。
- 用
sys.time_zone_info查看各时区的is_currently_dst和历史utc_offset变化点 - 避免在 DST 切换小时内做精确时间比对(如排班、日志对齐),优先用 UTC 存储和计算
- 测试时别只用今天,一定要覆盖 3 月/11 月前后几天,验证是否触发了 DST 边界逻辑
AT TIME ZONE 再准也没用。

















