【问题标题】:Convert UTC Time to Local time in SQL Server 2014 When Using Aggregate Function使用聚合函数时在 SQL Server 2014 中将 UTC 时间转换为本地时间
【发布时间】:2017-09-05 00:54:36
【问题描述】:

问题:需要查询从世界各地收集的大量时间序列数据以生成不同的报告。

时间序列表如下所示:LoggerId (int), Datetime (Datetime), Value (decimal)。

日期时间以 UTC 格式存储,但需要转换为本地时间。在某些情况下,需要使用 AVG、SUM 汇总数据,以获得每日/每周/每月平均、总计等数据。

用户可以随时更改报告中使用的数据的时区。例如,用户 A 可能决定在时区 A 中查看 logger #1 的数据,而用户 B 可能选择在时区 B 中查看相同的数据。

到目前为止,由于夏令时使事情变得非常复杂,我无法在 SQL Server 2014 中找到一个好的解决方案。我知道在 SQL Server 2016 中有一个新的“在时区”功能,这可能是我需要的,但它在 2014 年不可用。

我们将不胜感激。

以下是一些示例数据:

LoggerId、日期时间UTC、值

1, 2017-04-10 10:00:00, 10

1, 2017-04-10 11:00:00, 20

2017-04-10 12:00:00, 30

1, 2017-04-10 13:00:00, 40


显示新西兰时间 (UTC+12) 中记录器 1 的每日总数的查询的预期结果:

LoggerId、DatetimeLocal、值

1, 2017-04-10, 30

1, 2017-04-11, 70

【问题讨论】:

  • ,发布您的示例数据和预期输出
  • 我稍微简化了问题并添加了一些示例数据。谢谢。
  • 您需要正确考虑夏令时吗?
  • 是的。 DST 是我首先遇到这个问题的主要原因。我会更新这个问题。谢谢
  • 因为这个问题在等待了将近一年之后没有合适的答案。我最终将原始数据(没有聚合)查询到一个列表中,并使用 TimezoneInfo 类在 .NET 中转换日期时间,然后在 .NET 代码中聚合数据。它速度较慢,但​​我的应用程序的整体性能是可以接受的。感谢您的帮助!

标签: sql asp.net sql-server sql-server-2014 windows-server-2008-r2


【解决方案1】:

看看下面返回夏令时的函数,你可以根据你的要求进行修改。您只需传递 UTC 日期时间和偏移量。 :

 Create FUNCTION [dbo].[UDTToLocalTime_Test](@UDT AS DATETIME,@offs as SMALLINT)  
 RETURNS DATETIME
 AS
BEGIN 
--====================================================
--Set the Timezone Offset (NOT During DST [Daylight Saving Time])
--====================================================
DECLARE @Offset AS SMALLINT
SET @Offset = @offs
--====================================================
--Figure out the Offset Datetime
--====================================================
DECLARE @LocalDate AS DATETIME
SET @LocalDate = DATEADD(hh, @Offset, @UDT)
--====================================================
--Figure out the DST Offset for the UDT Datetime
--====================================================
DECLARE @DaylightSavingOffset AS SMALLINT
DECLARE @Year as SMALLINT
DECLARE @DSTStartDate AS DATETIME
DECLARE @DSTEndDate AS DATETIME
--Get Year
SET @Year = YEAR(@LocalDate)
--Get First Possible DST StartDay
IF (@Year > 2006) SET @DSTStartDate = CAST(@Year AS CHAR(4)) + '-03-08 02:00:00'
ELSE SET @DSTStartDate = CAST(@Year AS CHAR(4)) + '-04-01 02:00:00'
--Get DST StartDate 
WHILE (DATENAME(dw, @DSTStartDate) <> 'sunday') SET @DSTStartDate = DATEADD(day, 1,@DSTStartDate)
--Get First Possible DST EndDate
IF (@Year > 2006) SET @DSTEndDate = CAST(@Year AS CHAR(4)) + '-11-01 02:00:00'
ELSE SET @DSTEndDate = CAST(@Year AS CHAR(4)) + '-10-25 02:00:00'
--Get DST EndDate 
WHILE (DATENAME(dw, @DSTEndDate) <> 'sunday') SET @DSTEndDate = DATEADD(day,1,@DSTEndDate)
--Get DaylightSavingOffset
SET @DaylightSavingOffset = CASE WHEN @LocalDate BETWEEN @DSTStartDate AND @DSTEndDate THEN 1 ELSE 0 END
--====================================================
--Finally add the DST Offset 
--====================================================
RETURN DATEADD(hh, @DaylightSavingOffset, @LocalDate)
END

只需要这样调用:

 select  [dbo].[UDTToLocalTime_Test](getutcdate(),+4) as DateSTim

很久以前我找到了这个解决方案,并且在我的场景中运行良好,希望它也能帮助你。

【讨论】:

  • 这似乎基于某种切换实现了两组 DST 规则,但我不确定这适用于哪个国家/地区。听起来 OP 需要与多个时区一起工作,因此,如果您沿着这条路线走,您需要为 所有国家/地区 实施规则,其中一些不可能经受住未来的考验为了。此外,假设 DATENAME 正在返回英语,这绝不是给定的。
  • 感谢您的解决方案。有没有一种为不同国家自动维护时区规则的好方法? DST 的一个问题是它们似乎一直在变化。我的另一个担心是解决方案的性能。需要先做一些基准测试。
【解决方案2】:

如果您不介意在数据库中使用不安全的 CLR 程序集,那么您可以简单地编写自己的 .net CLR 标量函数来为您执行转换。下面是一个来自 Dicko2.0 的 GitHub 项目的链接,它演示了如何实现自己的 CLR:

https://github.com/HostedSolutions/SQLDates

以下 OneDrive 链接包含上述 CLR 项目的完整安装脚本:

SQLDatesFunc

下面是一个使用程序集的例子:

DECLARE @utcDateTime DATETIME = GETUTCDATE();

DECLARE @cstDateTime DATETIME;
DECLARE @estDateTime DATETIME;

SET @cstDateTime = [dbo].[ConvertToLocalTimeZone]('Central Standard Time', @utcDateTime);
SET @estDateTime = [dbo].[ConvertToLocalTimeZone]('Eastern Standard Time', @utcDateTime);

SELECT @cstDateTime AS CST, @estDateTime AS EST;

【讨论】:

  • 谢谢。我也在考虑 CLR 方法。我以前没有使用过,所以需要做一些调查。我主要担心的是它与 SQL 的性能对比,以及它如何集成到我们当前的开发环境中。
  • @Robert 不用担心,就性能而言,CLR 选项的性能将优于在 SQL Server 中创建的任何自定义 UDF,并将为您处理所有边缘情况,例如 DST。我自己的测试表明,对一百万条记录调用该函数只会使查询速度减慢大约一秒钟。关于集成,您只需要运行我提供的脚本(它将为您创建和安装程序集,无需编译)。
猜你喜欢
  • 2014-04-14
  • 1970-01-01
  • 2018-07-27
  • 2023-03-31
  • 2020-01-11
  • 2014-07-18
  • 2021-02-04
  • 2010-09-15
相关资源
最近更新 更多