我正在尝试在SQL Server 2016和Azure SQL中使用新的AT TIME ZONE syntax。我只想把伦敦的当前时间调整为datetime
,并根据夏令时进行调整。在运行以下所有命令时,伦敦的时间是凌晨3点半。
第一步是获得datetimeoffset
,我可以按照以下方式成功完成:
DECLARE @dto datetimeoffset
SET @dto = (SELECT GETUTCDATE() AT TIME ZONE 'GMT Standard Time')
SELECT @dto
这会返回一个我期望的值:
2016-04-04 02:27:54.0200000 +01:00
接下来,我想将其转换为datetime
,这是我的应用程序所期望的。我尝试过三种不同的方法,但没有一种方法可以让我得到我想要的结果:
SELECT SWITCHOFFSET(@dto,'+00:00')
-- Returns 2016-04-04 01:27:54.0200000 +00:00
SELECT CONVERT(datetime, @dto)
-- Returns 2016-04-04 02:27:54.020
SELECT CONVERT(datetime2, @dto)
-- Returns 2016-04-04 02:27:54.0200000
我觉得我错过了一些明显的东西 - 是否有一种简单的方法可以获取datetimeoffset
并仅返回该偏移处的日期/时间部分?
答案 0 :(得分:22)
代码的第一行包含错误:
SELECT GETUTCDATE() AT TIME ZONE 'GMT Standard Time'
GETUTCDATE()
返回datetime
,其中没有时区偏移信息。因此,如the MSDN documentation:
如果提供 inputdate 而没有偏移信息,则该函数会应用时区的偏移量,假设在目标时区中提供了 inputdate 值。
因此,即使您检索了UTC时间,您也错误地断言该值是在伦敦时间(在此日期夏令时为UTC + 1)。
处理此问题的最简单方法是将UTC时间作为datetimeoffset
开始。
SELECT SYSDATETIMEOFFSET() AT TIME ZONE 'GMT Standard Time'
这会调用AT TIME ZONE
的转换功能,该文档在文档中说明:
如果 inputdate 作为 datetimeoffset 值提供,则
AT TIME ZONE
子句会使用时区转换规则将其转换为目标时区。
请注意,如果您的数据实际上来自某个地方的datetime
字段,则可能需要使用两个部分功能,如下所示:
SELECT mydatetimefield AT TIME ZONE 'UTC' AT TIME ZONE 'GMT Standard Time'
对AT TIME ZONE
的第一次调用断言该值为UTC,给第二次调用datetimeoffset
,将其转换为伦敦时间。
其中任何一项的输出均为datetimeoffset
,您可以将其转换为datetime
或datetime2
,与您在原始问题中的显示完全相同。 (请勿使用switchoffset
。)
此外,伦敦的Windows时区标识符始终为"GMT Standard Time"
。它包括格林威治标准时间和英国夏令时,它们之间有适当的过渡。不要尝试将其更改为"GMT Daylight Time"
- 该标识符不存在。有关Windows时区的部分the timezone tag wiki中也介绍了这一点。
答案 1 :(得分:1)
我建议您仅将此字符串存储为字符串,并确认它是本地时间表示形式,否则,如果服务器时间正确,则SQL Server内部存储的时间将是错误的实际/物理时间,但不是同一时区。这就是为什么您不能使用convert来表示相同的原因,因为您实际上是从实际发生的时间更改datetime值,而不仅仅是重新表示它,即Datetime始终存储为UTC,而是在服务器的时区中输入和显示,因此,如果您在datetime字段中输入本地时间,则服务器会将时间解释为服务器时区中的时间,而不是事件的实际时间,如果本地时间不是当前时间,则会导致存储的时间导航/偏差与服务器相同>如果您随后将相同的数据馈送到其他时区的其他系统,则它们的数据将不正确,并且可能会变得混乱。将正确的值存储在datetime字段中,并根据需要将其显示为字符串。
答案 2 :(得分:0)
由于我无法在其他我想分享的地方找到它。通过在AT TIME ZONE中使用datepart(tz),可以获取以分钟为单位的偏移量。
datepart(tz,UTC_Date AT TIME ZONE 'Central Standard Time')
select dateadd(MINUTE,datepart(tz,cast('2018-07-02 17:54:41.537' as datetime) AT Time Zone 'Central Standard Time'),'2018-07-02 17:54:41.537') as CentralTime
返回
CentralTime
2018-07-02 12:54:41.537