自 DST 日期起获取行的正确方法?

Posted

技术标签:

【中文标题】自 DST 日期起获取行的正确方法?【英文标题】:Proper way of getting rows since a date accounting for DST? 【发布时间】:2020-11-05 11:40:51 【问题描述】:

我有一个日期时间列 changedate,我需要使用它来获取自下午 1 点以来在上周发生更改的行。不幸的是,本专栏采用的是当地时间 (EST)。服务器是 Microsoft SQL Server 2016。

这是我现在的查询:

DECLARE @since datetime = DATEADD(week,-1,SMALLDATETIMEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), DAY(GETDATE()), 13, 0)) AT TIME ZONE 'Eastern Standard Time'
SELECT * FROM table WHERE changedate AT TIME ZONE 'Eastern Standard Time' >= @since

由于我对列和@since 都使用了AT TIME ZONE,这是否能正确解释夏令时的变化?根据我找到的文档,这是我的理解,但我不能 100% 确定它是如何工作的,或者我是否遗漏了一些东西。

【问题讨论】:

【参考方案1】:

首先,确定您要比较的时间:

-- Get the current date in the given time zone
DECLARE @today date = convert(date, sysdatetimeoffset() AT TIME ZONE 'Eastern Standard Time')

-- Get the date one week ago
DECLARE @dateOneWeekAgo date = DATEADD(week, -1, @today)

-- Join the date with the desired time (local to the same time zone)
DECLARE @since datetime = convert(datetime, @dateOneWeekAgo) + convert(datetime, timefromparts(1, 0, 0, 0, 0))

那就对比一下吧:

SELECT * FROM table WHERE changedate >= @since

假设您的changedate 字段是datetimedatetime2。如果是datetimeoffset,则应先将目标值转换为同一时区的datetimeoffset,然后改用:

DECLARE @sinceDTO datetimeoffset = @since AT TIME ZONE 'Eastern Standard Time'

关于你在问题中给出的方法,有两个问题:

getdate() 给出基于服务器本地时区的时间。东部时间可能不是同一天。

你不应该对 where 子句中的表字段应用函数(无论是像 AT TIME ZONE 这样的内部函数还是其他函数),因为它会生成查询 non-sargable。换句话说,SQL 必须扫描整个表,而不是使用索引。表越大,查询速度越慢。

【讨论】:

哇,非常感谢,也感谢您的提示(显示我缺乏 SQL 知识)!因此,如果我理解正确,按照您的描述声明 @since 并将其用作 changedate 的标准(在 EDT 或 EST 中是 datetime)将正确解释该字段中的任何 DST 更改? 假设该字段中的数据确实是在美国和加拿大观察到的东部时间 - 即冬季的 EST (UTC-5),夏季的 EDT (UTC-4) - 然后 是的。但是,如果由于某种原因,该字段中的数据实际上是 UTC-5,则将第一行更改为:DECLARE @today date = convert(date, switchoffset(sysdatetimeoffset(), '-05:00'))。见the switchoffset docs。

以上是关于自 DST 日期起获取行的正确方法?的主要内容,如果未能解决你的问题,请参考以下文章

使用 DST 获取当前一周的开始/结束日期

获取两个日期范围内的上周日(GMT、DST)

获取任何时区的 DST 开始和结束日期

哪个 JS 日期时间选择器正确处理 DST 转换?

在熊猫中获取与给定日期时间最接近的时间戳的行的有效方法

如何在java中获取自定义日期和时间的纪元?