【问题标题】:Timezone Name Conversion Between SQL Server and JavascriptSQL Server 和 Javascript 之间的时区名称转换
【发布时间】:2020-10-05 10:01:02
【问题描述】:

我有一些数据要报告(为简洁起见):

MyDateTime (UTC)     MyValue
------               -------
2020-10-01 10:00:00  24
2020-10-01 15:00:00  53
2020-10-02 12:00:00  26
etc

所有日期都存储为 UTC。通常,我会尽可能长时间地保持 UTC 时间,并根据用户的区域设置在用户浏览器中转换为本地时间。

但是对于这个特定的报告,我想按整个日期汇总数据:

SELECT CAST(MyDateTime AS DATE), AVG(MyValue)
FROM MyTable
GROUP BY CAST(MyDateTime AS DATE)

所以我需要在截断MyDateTime 列并聚合它之前将日期转换为用户的本地时间。

在 SQL Server 中,我可以将 UTC datetime 转换为本地截断日期,如下所示:

CAST(CONVERT(datetime2, (SeizeTime AT TIME ZONE 'GMT Standard Time'), 1) AS DATE)

SQL Server 有一个AT TIME ZONE 子句接受的时区名称列表,在我的示例中,我使用的是GMT Standard Time,这是英国时间,包括夏令时。

在浏览器中运行的 Javascript 有另一种命名时区的方式:

console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)
--> Europe/London

所以我的问题是在 SQL Server 和 Javascript 之间没有命名时区的通用方法。如何可靠地将Europe/London 映射到GMT Standard Time,以及世界上所有其他可能的时区?

到目前为止,我的解决方法是:

  • 使用硬编码的查找表 - 如果将来时区信息发生变化,可能会中断。
  • 将所有预先聚合的数据发送到浏览器,将时区应用于日期,然后在客户端聚合数据 - 我想避免不必要地移动太多数据。

还有其他我想念的方法吗?

【问题讨论】:

  • 在客户端使用 var date = new Date('2012-11-29 17:00:34 UTC');日期.toString();将是使用该页面的客户的当地时间
  • @sandeepjoshi 谢谢,但是我如何从浏览器中提取时区信息,将其发送到服务器,并在 SQL Server 中使用它来进行我在问题中询问的聚合?跨度>
  • 对不起,我以为您想在客户端执行此操作。我想映射查找表是个好主意,但我想在旧浏览器和 IE11 中不支持它:(。如果您使用与 GMT 的时差转换日期,那么我想您可以发送new Date().getTimezoneOffset()/60,这会给您带来时区差异以小时数为单位。-ve 值表示国家/地区早于 GMT,正数表示落后。
  • @sandeepjoshi 另一个好主意,但问题是new Date().getTimezoneOffset()/60 会及时为您提供时区偏移量。我正在寻找可能跨越夏令时边界的历史数据。
  • 您如何连接到 SQL Server?你有应用层吗?是 .NET、Node.JS 还是其他?

标签: javascript sql-server datetime timezone


【解决方案1】:

鉴于您的用户配置文件中的问题历史,我假设您在应用程序层中使用 .NET 来查询您的 SQL Server。如果不是这种情况,请发表评论,我会相应地调整我的答案。

How to translate between Windows and IANA time zones? 解决了您的问题的核心。简而言之,您可以在应用程序层中使用我的TimeZoneConverter 库将浏览器中来自Intl 的IANA 时区名称转换为SQL Server 可以识别的Windows 时区名称。

不过,我还要在您问题的 SQL 部分补充几点:

  • 在许多情况下,您最好在应用程序层中进行时区转换,而不是在 SQL Server 中。 .NET 的 TimeZoneInfo 类可以处理转换并在 Linux 上运行时理解 IANA 标识符。在 Windows 上运行时,您可以使用 TimeZoneConverter 中的 TZConvert.GetTimeZoneInfo 来使用它们。 (或者,您可以使用Noda Time 进行转换。)

  • 如果您使用的数据点数量相对较少,请从 SQL 中查询它们全部不分组,然后在您的应用程序层中转换它们,然后在那里分组并执行聚合。这将带来内存和网络开销 - 但会使您的查询更高效,转换更容易。

  • 如果您正在处理更大的数据集,那么聚合和转换确实必须在查询时完成。使用 TimeZoneConverter 中的 TZConvert.IanaToWindows,并将其作为参数传递给您的查询,以便与 AT TIME ZONE 一起使用。

  • 为防止查询引擎扫描整个表,您需要在 UTC 值上建立索引。您还需要一个使用该原始值的WHERE 子句,因此您的查询是"sargable"。因此,您需要在执行查询之前将所需的查询范围从客户端的时区转换为 UTC

  • 当源值为datetimedatetime2时,您需要使用AT TIME ZONE两次。例如SomeDateTimeValue AT TIME ZONE 'UTC' AT TIME ZONE 'Pacific Standard Time'。第一个将使用给定的时区创建datetimeoffset,分配正确的偏移量(+00:00),同时保留日期和时间值。第二个会将datetimeoffset 转换为给定时区中的另一个datetimeoffset,相应地调整日期、时间和偏移量。

这是一个演示 SQL 方面的示例。我设置了一个临时表,其中包含一些附加值,以明确最终查询中包含和排除的内容。

--Temp table for demo purposes
CREATE TABLE #MyTable (UtcDateTime datetime2, MyValue int)
INSERT INTO #MyTable VALUES ('2020-10-01 06:00:00', 1000) -- excluded
INSERT INTO #MyTable VALUES ('2020-10-01 07:00:00', 100)  -- included
INSERT INTO #MyTable VALUES ('2020-10-01 10:00:00', 24)   -- included
INSERT INTO #MyTable VALUES ('2020-10-01 15:00:00', 53)   -- included
INSERT INTO #MyTable VALUES ('2020-10-02 12:00:00', 26)   -- included
INSERT INTO #MyTable VALUES ('2020-10-03 06:00:00', 100)  -- included
INSERT INTO #MyTable VALUES ('2020-10-03 07:00:00', 1000) -- excluded
 
-- These are the input values of the query
DECLARE @ClientTZ varchar(50) = 'Pacific Standard Time' -- Converted from 'America/Los_Angeles' in the application layer with TimeZoneConverter
DECLARE @FromClientDate date  = '2020-10-01'
DECLARE @ToClientDate date = '2020-10-02'


-- START OF QUERY

-- First you'll need to know the UTC datetimes of the range you are querying over.
-- This pair of values creates a half-open UTC datetime range to be used in the WHERE clause below.
DECLARE @FromUtcDateTime datetime2 = CAST(@FromClientDate AS datetime2) AT TIME ZONE @ClientTZ AT TIME ZONE 'UTC'
DECLARE @ToUtcDateTime datetime2 = CAST(DATEADD(DAY, 1, @ToClientDate) AS datetime2) AT TIME ZONE @ClientTZ AT TIME ZONE 'UTC'

-- Now you can do the query.  The inner subquery does the filtering and conversion.  The outer query does the aggregation.
SELECT ClientDate, AVG(MyValue) as AvgValue
FROM (
    SELECT CAST(UtcDateTime AT TIME ZONE 'UTC' AT TIME ZONE @ClientTZ AS date) as ClientDate, MyValue
    FROM #MyTable
    WHERE UtcDateTime >= @FromUtcDateTime AND UtcDateTime < @ToUtcDateTime
) q
GROUP BY q.ClientDate

-- END OF QUERY

-- Drop the temp table for demo cleanup
DROP TABLE #MyTable

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-12
    • 1970-01-01
    相关资源
    最近更新 更多