SQL Server 2016+ 支持 AT TIME ZONE,但仅适用于 DATETIMEOFFSET 类型,且需显式指定源时区;对无时区时间须先用 TODATETIMEOFFSET 构造,再链式转换,否则易因服务器时区或夏令时规则导致错误。
AT TIME ZONE 在 SQL Server 中是否可用?
不能直接用。SQL Server 2016+ 虽然支持
,但它只作用于
类型,且要求输入值本身已带时区偏移(即必须是
,不是
或
)。如果你传入的是无时区的
,SQL Server 会默认按当前服务器时区解释它,再转换——这在跨时区存储过程里极易出错。
常见错误现象:
看似合理,实际会先按服务器本地时区(比如东八区)把字符串转成
,再转 UTC,结果可能多扣或少扣 8 小时。
务必先用
显式指定源时区,再用
转目标时区
不要依赖服务器
的时区上下文;存储过程可能被不同时区客户端调用,
才反映当前会话时区(但也不可靠,最好由调用方传入源时区)
若源时间来自应用层(如用户提交的“北京时间下午3点”),应让应用明确传入时区名或 UTC 偏移(如
或
),而非靠数据库猜
如何安全地在存储过程中做「用户本地时间 → UTC → 目标时区」转换
典型场景:用户在东京下单,订单时间存为 UTC;客服在纽约查看时需显示为当地时间。关键在于三段分离:确认源时区、转 UTC、再转目标时区。
实操建议:
存储过程参数定义为
+
(如
)+
(如
)
第一步:用
构造带时区的时间值(注意:SQL Server 内置时区名必须用 Windows 时区名,不是 IANA 名;
会报错,得用
)
第二步:链式调用
先转 UTC,再转目标时区:
若需兼容 IANA 时区(如应用用的是
),必须在应用层或 CLR 函数中映射,SQL Server 原生不识别
为什么有时 AT TIME ZONE 返回 NULL 或报错?
两个高频原因:时区名拼写错误、输入值为 NULL 或非法日期格式。
Windows 时区名大小写不敏感,但空格和连字符必须完全匹配。例如
正确,
或
都失败。错误信息通常是:
' is not valid.
用系统视图
查可用名:
—— 结果是
,不是
对
输入返回
,不会报错;但若前一步
的第二个参数是
,则整个表达式返回
,容易被忽略
性能影响:每次调用
都触发时区规则查表(含夏令时逻辑),高频调用建议缓存常用时区转换结果,或提前在应用层完成
跨时区存储过程最易忽略的一点
时区转换不是纯数学偏移,而是基于历史规则的查表运算。SQL Server 的
包含自 1980 年以来的夏令时变更记录,但不包含更早年份(如 1975 年东京是否实行夏令时?它不认)。如果业务涉及历史数据(比如金融日志追溯到 1990 年前),
可能给出错误结果,且无警告。
此时必须放弃 SQL Server 原生方案,改用应用层时区库(如 C# 的
或 Python 的
)预处理,再把确定的
传入存储过程。
AT TIME ZONEDATETIMEOFFSETDATETIMEOFFSETDATETIME2SMALLDATETIMEDATETIME2CONVERT(DATETIME2, '2024-05-01 10:00:00') AT TIME ZONE 'China Standard Time' AT TIME ZONE 'UTC'DATETIMEOFFSETTODATETIMEOFFSETAT TIME ZONEGETDATE()SYSDATETIMEOFFSET()'Asia/Shanghai''+08:00'@local_time DATETIME2@source_tz VARCHAR(50)'Tokyo Standard Time'@target_tz VARCHAR(50)'Eastern Standard Time'TODATETIMEOFFSET(@local_time, @source_tz)'Asia/Tokyo''Tokyo Standard Time'AT TIME ZONETODATETIMEOFFSET(@local_time, @source_tz) AT TIME ZONE 'UTC' AT TIME ZONE @target_tz'Europe/London''Pacific Standard Time''pacific standard time''Pacific Standard'The timezone parameter 'xxxsys.time_zone_infoSELECT * FROM sys.time_zone_info WHERE name LIKE '%Beijing%''China Standard Time''Beijing Time'AT TIME ZONENULLNULLTODATETIMEOFFSETNULLNULLAT TIME ZONEsys.time_zone_infoAT TIME ZONETimeZoneInfozoneinfoDATETIMEOFFSET