TSQL:如何将本地时间转换为 UTC? (SQL Server 2008)

Posted

技术标签:

【中文标题】TSQL:如何将本地时间转换为 UTC? (SQL Server 2008)【英文标题】:TSQL: How to convert local time to UTC? (SQL Server 2008) 【发布时间】:2010-11-15 08:43:52 【问题描述】:

我们正在处理需要处理来自不同时区和夏令时设置的全球时间数据的应用程序。这个想法是在内部以 UTC 格式存储所有内容,并且只为本地化的用户界面来回转换。 SQL Server 是否提供任何机制来处理给定时间、国家和时区的翻译?

这一定是一个常见问题,所以我很惊讶谷歌不会提供任何可用的东西。

任何指针?

【问题讨论】:

我的 mssql-server 链接到 mysql-server。我想知道是否可以在查询上运行 mysql CONVERT_TZ(time,srczone,dstzone) :-) 奇怪的是这个函数不见了;它内置在 linux 中。 @BuschnicK 请参阅下面的答案。其实我觉得你也能接受,这样别人更容易找到。 简短的回答:在 SQL Server 2016 之前没有内置方法可以做到这一点,因此需要在早期版本上使用自定义代码。 中等答案:SQL Server 2016 之前的所有时间偏移功能仅适用于绝对偏移,不支持由于夏令时而在大多数时区发生的可变偏移。查询没有任何方法可以访问时间偏移量,除非恰好是服务器本地时间的当前偏移量,这对于尝试自动转换是无用的。 【参考方案1】:

这适用于当前与 SQL Server 主机具有相同 UTC 偏移量的日期;它不考虑夏令时的变化。将YOUR_DATE 替换为要转换的本地日期。

SELECT DATEADD(second, DATEDIFF(second, GETDATE(), GETUTCDATE()), YOUR_DATE);

【讨论】:

谢谢,这是个好主意,但它只适用于一个时区——本地机器的时区。不过,我们需要它适用于任意时区...... 这不考虑夏令时 不!差异取决于确切的日期。这取决于夏令时。 就我而言,我只需要 1 个时区,效果很好!谢谢。 如果你知道你今天的跑步不是历史记录,这很好用,谢谢【参考方案2】:

7 年过去了…… 实际上,SQL Server 2016 的这个新功能可以满足您的需求。 它被称为 AT TIME ZONE,它考虑 DST(夏令时)的变化将日期转换为指定的时区。 更多信息在这里: https://msdn.microsoft.com/en-us/library/mt612795.aspx

【讨论】:

不再从事这个工作 - 不在那个项目,不在 SQL Server,不在同一家公司,甚至不在同一个国家;-) 所以它对我没有帮助,但我我会为现在发现这个问题的人投票。 @BuschnicK 是的,我想,但我来这里是为了解决你遇到的同样的问题,所以我决定发布一个回答,因为现在有一个真正的解决方案:D 该问题针对 SQL Server 2008。如果您接受此答案,请更新该问题。谢谢 要转换为 UTC,您可以执行 'AT TIME ZONE 'UTC'。 @Piotr Owsiak:是的,但它假设输入时间是 UTC,如果你想从本地时间转换为 UTC,这是没有意义的......所以 7 年过去了,他们仍然没有'不认为值得妥善处理...【参考方案3】:

虽然其中一些答案会让您大致了解,但由于夏令时,您无法对 SqlServer 2005 及更早版本的任意日期执行您尝试执行的操作。使用当前本地和当前 UTC 之间的差异将为我提供今天存在的偏移量。我还没有找到一种方法来确定相关日期的偏移量。

也就是说,我知道 SqlServer 2008 提供了一些新的日期函数可以解决这个问题,但是使用早期版本的人需要注意这些限制。

我们的方法是保持 UTC 并在客户端执行转换,我们可以更好地控制转换的准确性。

【讨论】:

【参考方案4】:

这里是将一个区域DateTime转换为另一个区域DateTime的代码

DECLARE @UTCDateTime DATETIME = GETUTCDATE();
DECLARE @ConvertedZoneDateTime DATETIME;

-- 'UTC' to 'India Standard Time' DATETIME
SET @ConvertedZoneDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE 'India Standard Time'
SELECT @UTCDateTime AS UTCDATE,@ConvertedZoneDateTime AS IndiaStandardTime

-- 'India Standard Time' to 'UTC' DATETIME
SET @UTCDateTime = @ConvertedZoneDateTime AT TIME ZONE 'India Standard Time' AT TIME ZONE 'UTC'
SELECT @ConvertedZoneDateTime AS IndiaStandardTime,@UTCDateTime AS UTCDATE

注意AT TIME ZONE 仅适用于 SQL Server 2016+,其优点是它在转换为特定条件时会自动考虑日光时区

【讨论】:

我喜欢这个,因为它表明您可以将多个 AT TIME ZONE 调用(短语?)链接在一起!简直优雅。我之前说过***.com/a/44579178/112764 满足了我的需求,但这更好。主要荣誉。 DECLARE @UTCDateTime DATETIME = GETUTCDATE(); DECLARE @ConvertedZoneDateTime DATETIME; -- 'UTC' to 'India Standard Time' to 'Eastern Standard Time' DATETIME SET @ConvertedZoneDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE 'India Standard Time' AT TIME ZONE 'Eastern Standard Time' SELECT @UTCDateTime AS UTCDATE,@ConvertedZoneDateTime AS EasternStandardTime 是的,您可以链接多个 AT TIME ZONE 调用,但是 FromTo 足以进行任何转换,也是我们最需要的【参考方案5】:

对于 SQL Server 2016 及更高版本以及 Azure SQL 数据库,请使用内置的 AT TIME ZONE statement。

对于旧版本的 SQL Server,您可以使用我的 SQL Server Time Zone Support 项目在 IANA 标准时区 as listed here 之间进行转换。

UTC 到 Local 是这样的:

SELECT Tzdb.UtcToLocal('2015-07-01 00:00:00', 'America/Los_Angeles')

UTC 的本地是这样的:

SELECT Tzdb.LocalToUtc('2015-07-01 00:00:00', 'America/Los_Angeles', 1, 1)

当本地时间值受夏令时影响时,数字选项是用于控制行为的标志。这些在项目的文档中有详细描述。

【讨论】:

伙计……Matt 的这个项目很棒,不需要 CLR。这个+100 值得马特称赞【参考方案6】:

SQL Server 2008 有一个名为datetimeoffset 的类型。对于这种类型的东西真的很有用。

http://msdn.microsoft.com/en-us/library/bb630289.aspx

然后您可以使用函数SWITCHOFFSET 将其从一个时区移动到另一个时区,但仍保持相同的 UTC 值。

http://msdn.microsoft.com/en-us/library/bb677244.aspx

罗伯

【讨论】:

SWITCHOFFSET 不考虑夏令时,所以它只在某些情况下有用。 没有。但问题是能够处理切换到请求的任何时区。 来自“不同时区和夏令时设置”的问题。我们也在为当地时间寻找解决方案。你的建议不能解决夏令时问题吗? 任何时候当你需要在客户端检测时区,然后让数据库返回指定时区的时间,SWITCHOFFSET函数就非常有用。 @RobFarley 在客户端检测时区并使用 SWITCHOFFSET 仍然会出错。您需要知道您要转换的日期和时间是否应用了夏令时。简单地检测时区并将当前偏移量应用于 UTC 可能需要一个小时的时间——在一个简单的情况下,您的所有转换都在同一个国家/地区。并非每个国家/地区都在同一日期切换到/切换夏令时。如果您存储本地时间并知道同一国家/地区内原始时区和目标时区之间的差异,则 SWITCHOFFSET 可以很好地工作。【参考方案7】:

我倾向于将 DateTimeOffset 用于与本地事件无关的所有日期时间存储(即:会议/派对等,下午 12 点至下午 3 点在博物馆)。

获取当前 DTO 为 UTC:

DECLARE @utcNow DATETIMEOFFSET = CONVERT(DATETIMEOFFSET, SYSUTCDATETIME())
DECLARE @utcToday DATE = CONVERT(DATE, @utcNow);
DECLARE @utcTomorrow DATE = DATEADD(D, 1, @utcNow);
SELECT  @utcToday [today]
        ,@utcTomorrow [tomorrow]
        ,@utcNow [utcNow]

注意:通过网络发送时,我将始终使用 UTC……客户端 JS 可以轻松地与本地 UTC 进行往来。见:new Date().toJSON() ...

以下 JS 将处理将 ISO8601 格式的 UTC/GMT 日期解析为本地日期时间。

if (typeof Date.fromISOString != 'function') 
  //method to handle conversion from an ISO-8601 style string to a Date object
  //  Date.fromISOString("2009-07-03T16:09:45Z")
  //    Fri Jul 03 2009 09:09:45 GMT-0700
  Date.fromISOString = function(input) 
    var date = new Date(input); //EcmaScript5 includes ISO-8601 style parsing
    if (!isNaN(date)) return date;

    //early shorting of invalid input
    if (typeof input !== "string" || input.length < 10 || input.length > 40) return null;

    var iso8601Format = /^(\d4)-(\d2)-(\d2)((([T ](\d2):(\d2)(:(\d2)(\.(\d1,12))?)?)?)?)?([Zz]|([-+])(\d2)\:?(\d2))?$/;

    //normalize input
    var input = input.toString().replace(/^\s+/,'').replace(/\s+$/,'');

    if (!iso8601Format.test(input))
      return null; //invalid format

    var d = input.match(iso8601Format);
    var offset = 0;

    date = new Date(+d[1], +d[2]-1, +d[3], +d[7] || 0, +d[8] || 0, +d[10] || 0, Math.round(+("0." + (d[12] || 0)) * 1000));

    //use specified offset
    if (d[13] == 'Z') offset = 0-date.getTimezoneOffset();
    else if (d[13]) offset = ((parseInt(d[15],10) * 60) + (parseInt(d[16],10)) * ((d[14] == '-') ? 1 : -1)) - date.getTimezoneOffset();

    date.setTime(date.getTime() + (offset * 60000));

    if (date.getTime() <= new Date(-62135571600000).getTime()) // CLR DateTime.MinValue
      return null;

    return date;
  ;

【讨论】:

+1 我也切换到 DateTimeOffset。它避免了 UTC + 本地转换的许多问题。但是,出于类似的原因,我建议也通过网络(通过 JSON)发送带有偏移量的值。 请注意,我做你做的事情完全反过来。对于与本地事件相关的 DateTime,我存储了一个 DateTimeOffset。对于与本地事件无关的 DateTime,我将 DateTime 存储在 UTC 中。前者有两个相关的数据点(什么时候是当地时间,什么时候是当地时间),后者只有一个(什么时候) @Martijn 但是时区并没有给你位置,无论如何你必须单独存储。 @tracker1 这仅在该位置有办法知道其时区时才有效,即使这样,转换也是一个完全的痛苦。【参考方案8】:

是的,在某种程度上正如here 所详述。 我使用的方法(2008 年之前)是在插入数据库之前在 .NET 业务逻辑中进行转换。

【讨论】:

【参考方案9】:

您可以使用 GETUTCDATE() 函数来获取 UTC 日期时间 可能您可以选择 GETUTCDATE() 和 GETDATE() 之间的差异,并使用此差异将您的日期调整为 UTC

但我同意之前的消息,即在业务层(例如在 .NET 中)控制正确的日期时间要容易得多。

【讨论】:

不!差异取决于确切的日期。这取决于夏令时。 不考虑夏令时。我使用这样的解决方案有一段时间了,它引起了重大问题。您必须确定您要比较的日期是否在 DST。【参考方案10】:

SUBSTRING(CONVERT(VARCHAR(34), SYSDATETIMEOFFSET()), 29, 5)

返回(例如):

-06:0

并非 100% 肯定,这将始终有效。

【讨论】:

【参考方案11】:

示例用法:

SELECT
    Getdate=GETDATE()
    ,SysDateTimeOffset=SYSDATETIMEOFFSET()
    ,SWITCHOFFSET=SWITCHOFFSET(SYSDATETIMEOFFSET(),0)
    ,GetutcDate=GETUTCDATE()
GO

返回:

Getdate SysDateTimeOffset   SWITCHOFFSET    GetutcDate
2013-12-06 15:54:55.373 2013-12-06 15:54:55.3765498 -08:00  2013-12-06 23:54:55.3765498 +00:00  2013-12-06 23:54:55.373

【讨论】:

以上是关于TSQL:如何将本地时间转换为 UTC? (SQL Server 2008)的主要内容,如果未能解决你的问题,请参考以下文章

如何将 UTC 时间转换为本地时间

sql SQL:将UTC时间转换为本地时间

如何根据 bigquery 中的时区列将 UTC 时间转换为本地时区?

如何在 SAP SQL Anywhere 中将 UTC 时间戳转换为命名时区?

将历史日期从 UTC 转换为本地时间

如何将UTC时间转化为本地时间