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


82

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

这肯定是一个普遍的问题,所以我很惊讶Google不会提供任何可用的东西。

有指针吗?


我将我的mssql服务器链接到mysql服务器。我想知道是否有可能在查询中运行mysql CONVERT_TZ(time,srczone,dstzone):-)奇怪的是,缺少此函数;它内置在linux中。
雷夫·尼兰德

1
@BuschnicK请参阅下面的答案。实际上,我认为您甚至可以接受它,因此其他人更容易找到它。
2016年

简短的答案:在SQL Server 2016之前没有内置的方法可以执行此操作,因此它将需要早期版本的自定义代码。
lehiester

中等答案:SQL Server 2016之前的所有时间偏移功能仅适用于绝对偏移,不支持由于夏时制而在大多数时区中发生的变量偏移。查询没有任何访问时间偏移的方法,除非碰巧是服务器本地时间的当前偏移,这对于尝试自动转换没有用。
lehiester

Answers:


49

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


19
不再从事此工作-不在那个项目中,不在SQL Server中,不在同一家公司中,甚至不在同一国家;-)因此,它对我没有帮助,但是我会为发现此问题的人投票现在。
BuschnicK

2
@BuschnicK是的,我想,但是我到这里来寻找与您遇到的相同问题的解决方案,所以我决定现在发布一个Aswer,因为有一个真正的解决方案:D
Piotr Owsiak

5
问题是针对SQL Server 2008的。如果您接受此答案,请更新问题。Thx
Robert

1
要转换为UTC,您可以进行“ AT TIME ZONE'UTC”。
Krzyserious

1
@Piotr Owsiak:是的,但是它假定输入时间为UTC,如果您想将本地时间转换为UTC是没有意义的……所以7年过去了,他们仍然认为不值得正确处理。 ..
Stefan Steiger

65

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

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


2
谢谢,这是一个好主意,但是它只能在一个时区(本地计算机的时区)上工作。我们需要它为任意时区工作……
BuschnicK 2010年

51
这不考虑夏令时
Gabriel McAdams

10
没有!差异取决于确切的日期。这取决于夏令时。
usr

3
就我而言,我只需要1个时区,这很好用!谢谢。
M Thelen

10
如果您不知道自己今天的跑步情况,那么效果很好,谢谢
David Adlington 2012年

22

尽管其中一些答案会让您大跌眼镜,但由于夏时制,您无法使用SqlServer 2005及更早版本的任意日期来做您想做的事情。使用当前本地和当前UTC之间的差异将为我提供今天存在的偏移量。我还没有找到一种方法来确定相关日期的偏移量。

就是说,我知道SqlServer 2008提供了一些可能解决该问题的新日期函数,但是使用较早版本的人们需要意识到这些限制。

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


16

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

对于旧版本的SQL Server,您可以使用我的SQL Server时区支持项目在IANA标准时区之间进行转换,如下所示

UTC到本地是这样的:

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)

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


1
真是个好人... Matt的这个项目很棒,不需要CLR。马特(Matt)值得赞扬+100
棘手的2016年

14

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

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

然后,您可以使用该功能SWITCHOFFSET将其从一个时区移至另一个时区,但仍保持相同的UTC值。

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


6
SWITCHOFFSET不考虑夏令时,因此仅在某些情况下有用。
robocat

2
否。但是问题在于是否能够处理切换到所请求的任何时区。
罗布·法利

来自“不同的时区和夏时制设置”问题。我们也在寻找适合当地时间的解决方案。您的建议不能解决夏时制问题吗?
robocat

任何时候,当您需要在客户端上检测时区,然后在指定时区中设置数据库返回时间时,SWITCHOFFSET函数都非常有用。
罗伯·法利

3
@RobFarley在客户端检测时区并使用SWITCHOFFSET仍然会出错。您需要知道转换的日期和时间是否应用了夏令时。仅检测时区并将当前偏移量应用于UTC可能需要一个小时的时间-这是在所有转换都在同一国家/地区的简单情况下。并非每个国家都在同一日期切换到夏令时或从夏令时切换到夏令时。如果您存储本地时间并知道同一国家/地区中原始区域和目标区域之间的时差,SWITCHOFFSET效果很好。
JamieSee'3

12

这是将一个区域转换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+,优点是转换为特定时区时会自动考虑日光


2
我喜欢这个,如果除了显示您可以将多个AT TIME ZONE呼叫(短语?)链接在一起的事实外,别无其他!简单优雅。我之前说过stackoverflow.com/a/44579178/112764可以满足我的需求,但这甚至更好。主要荣誉。
NateJ '18年

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呼叫,但是“从”和“到”就足以进行任何转换,而我们最需要的是
KarthikeyanMlp

4

我倾向于将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 = /^(\d{4})-(\d{2})-(\d{2})((([T ](\d{2}):(\d{2})(:(\d{2})(\.(\d{1,12}))?)?)?)?)?([Zz]|([-+])(\d{2})\:?(\d{2}))?$/;

    //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)发送带有偏移量的值。
user2864740 2013年

值得注意的是,我完全按照相反的方式来做。对于与本地事件相关的DateTime,我存储一个DateTimeOffset。对于与本地事件无关的DateTime,我将DateTime存储在UTC中。前者有两个相关的数据点(当地时间是什么时候,那是什么本地时间),后者只有一个(时间是什么时候)
Martijn 2014年

@Martijn但是,时区无法提供位置,因此无论如何都必须单独存储。
Tracker1 2014年

@ tracker1仅在位置具有知道其时区的方式时才起作用,即使这样,转换也很费劲。
马丁2014年

3

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


1

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

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


12
没有!差异取决于确切的日期。这取决于夏令时。
usr

3
不考虑夏令时。我使用这种解决方案已有一段时间,这引起了重大问题。您必须确定要比较的日期是否在DST中。
杰夫·戴维斯

-1

用法示例:

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.