我正在尝试编写一个查询,该查询仅根据 UTC 日期时间字段的本地日期部分对记录进行分组。
例如,如果我的表包含10/19/2012 2:00:00
,那么它应该被分组为10/18/2012
,因为我的当地时间是 EST (-5h) 并且我只对字段的日期部分感兴趣。
我知道我DateAdd(day, DateDiff(day, 0, MyDate), 0)
只能从日期时间字段中获取日期部分,并且可以DateAdd(minute, DateDiff(minute, GetUtcDate(), GetDate()), MyUtcDate)
将 UTC 日期时间转换为本地日期时间。
但是将两者结合起来严重冒犯了我。
在 SQL Server 2005 中,有没有比这更好的方法来获取 UTC DateTime 字段的 Date 部分,转换为本地时间?
SELECT DateAdd(day, DateDiff(day, 0, DateAdd(minute, DateDiff(minute, GetUtcDate(), GetDate()), MyUtcDate)), 0)
, Count(*)
FROM MyTable
GROUP BY DateAdd(day, DateDiff(day, 0, DateAdd(minute, DateDiff(minute, GetUtcDate(), GetDate()), MyUtcDate)), 0)
此答案未考虑夏令时更改、闰秒的添加,并且对不是与 UTC 整小时偏移的时区不敏感。有关SQL Server 2016 及更高版本的更准确方法,请参阅我关于此问题的第二个答案。
如果你不反对让一个函数做脏活,这有助于使语句更清晰:
然后,您可以执行以下操作:
上面的代码将截断输入到函数的日期的任何时间部分(这是设计使然)。因此,正如我在回答开头所指出的,任何实施分钟的时区,例如加拿大的爱德华王子岛 (UTC -4:30)、印度 (UTC -4:30) 和加德满都 (UTC +5:45 ),不会看到正确的结果。
请注意,在大量行上使用这样的函数会影响性能。如下所示的
AT TIME ZONE
构造不会受到该问题的影响,尽管它绝不是灵丹妙药,性能方面。通常,我使用这个表达式
但在 SQL Server 2008 上,这简化为
除了在这里给出替代表达式之外,如果你是一个 T-SQL 纯粹主义者,嫁给了 DATE 函数来操纵日期时间,我认为除了使用标量函数之外,没有更简洁的方法来获取本地时间。
在 SQL Server 2016 及更高版本中,使用
AT TIME ZONE 'timezone'
构造在时区之间转换日期时间值。AT TIME ZONE
考虑到多年来为符合夏令时等所需的更改。这第一位设置了一些示例数据供我们使用:
接下来,我们获取样本数据并将其填充到中间临时表中,
AT TIME ZONE
用于首先在“中部标准时间”时区创建日期列的副本,然后仅创建该时间的日期部分。按日期分组的查询变得超级简单:
行样本的分组输出:
这些行的基础数据:
此数据显示了 2022 年从标准时间到夏令时的过渡: