使用DATEADD后如何转换值?解决GMT转CST时日期复现问题
问题分析与解决方案
这个问题我之前也碰到过,核心是没搞清楚SQL Server里数据类型转换的逻辑,我来给你拆解下:
为什么你的查询会带回日期?
你先把arrival字段(datetime类型)转成了VARCHAR(8)的字符串时间(比如12:35:43),但当你对这个字符串使用DATEADD函数时,SQL Server会隐式地把字符串转换回datetime类型——因为DATEADD要求输入参数是日期/时间类数据类型。转换过程中,SQL会给这个时间字符串补上默认日期(在你的场景里恰好补了原字段的日期4-6-2018),所以最终结果又变回了带日期的datetime值。
最优解决方案:先调整时区,再提取时间
正确的顺序应该是先对原始的datetime字段做时区调整,再提取时间部分,这样完全避免隐式转换的问题,效率也最高:
SELECT CONVERT(VARCHAR(8), DATEADD(hour, -5, arrival)) AS arrival_time FROM locations
这个语句会先把arrival减5小时(从GMT转CST),再把结果转成8位的时间字符串,最终得到的就是你想要的HH:MM:SS格式,不带日期。
备选方案:用TIME类型处理时间部分
如果你更倾向于用专门的时间类型来操作(比字符串更高效),可以先把datetime转成TIME类型,调整时区后再按需转成字符串:
-- 返回TIME类型的结果(无日期) SELECT DATEADD(hour, -5, CONVERT(TIME, arrival)) AS arrival_time FROM locations -- 如果需要字符串格式,再加一层转换 SELECT CONVERT(VARCHAR(8), DATEADD(hour, -5, CONVERT(TIME, arrival))) AS arrival_time FROM locations
这种方式利用了SQL Server的TIME数据类型,本身就只存储时间信息,调整时区后不会带上多余的日期。
为什么不推荐你原来的反向操作?
如果你坚持先提取时间再调整时区,虽然能实现,但需要多做一次类型转换,而且容易出错:
SELECT CONVERT(VARCHAR(8), DATEADD(hour, -5, CONVERT(TIME, arrival))) FROM locations
这里必须先把字符串时间转成TIME类型(而不是依赖隐式转换),再调整时区,最后再转回字符串。但这种方法比第一种多了一次转换,效率更低,没必要舍近求远。
内容的提问来源于stack exchange,提问作者IowaMatt
相关产品推荐
相关产品推荐

