在select语句中将Datetime列从UTC转换为本地时间

Nug*_*ugs 189 sql sql-server

我正在做一些SQL选择查询,并希望将我的UTC日期时间列转换为本地时间,以便在我的查询结果中显示为本地时间.注意,我不希望通过代码进行此转换,而是在我对数据库进行手动和随机SQL查询时.

Mic*_*eyn 296

您可以在SQL Server 2008或更高版本上执行以下操作:

SELECT CONVERT(datetime, 
               SWITCHOFFSET(CONVERT(datetimeoffset, 
                                    MyTable.UtcColumn), 
                            DATENAME(TzOffset, SYSDATETIMEOFFSET()))) 
       AS ColumnInLocalTime
FROM MyTable
Run Code Online (Sandbox Code Playgroud)

你也可以做一些不那么冗长的事情:

SELECT DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), GETDATE()), MyTable.UtcColumn) 
       AS ColumnInLocalTime
FROM MyTable
Run Code Online (Sandbox Code Playgroud)

不管你做什么,不要使用-减去日期,因为该操作不是原子操作,你会偶尔得到应有的种族系统日期时间与当地的日期时间之间的条件不确定的结果在不同的时间被检查(即非原子) .

请注意,此答案不考虑DST.如果您想要包含DST调整,请参阅以下SO问题:

如何在SQL Server中创建夏令时开始和结束功能

  • 有没有办法让这个帐户节省日光? (33认同)
  • 我在这里看不到时区的名称,所以这是不正确的.假设你可以通过算术转换到当地时间是一个非常糟糕的主意 (11认同)
  • 对于任何使用sql azure的人来说,这种方法不起作用,因为日期/时间函数都返回UTC,所以比较GETDATE()和GETUTCDATE()不会给你任何东西,你的结果和你开始的一样. (8认同)
  • @MichaelGoldshteyn如果你有一个时区偏移,你仍然不知道一个时间是什么逻辑时区.时区是社会事物,可以由政府在不同点改变.时区的偏移量可以在时间/历史的不同时间点(夏季时间是常见的)不同.您正在通过UTC的当前偏移来抵消时间...另一点是,这将给出服务器的时区而不是客户端,如果您在其他国家/地区托管,这可能是另一个问题. (6认同)
  • @Niloofar简单的答案是你没有,也不应该.日期时间应始终以UTC格式存储(最好是基于数据库服务器的时钟,而不是网络服务器,应用程序服务器,而不是客户端的时钟).显示该日期时间是UI层的一个功能,它负责将日期时间转换为您想要的格式(必要时包括时区). (4认同)
  • @Jonny,据我所知,问题是从 UTC 转换为本地时间,考虑到所使用的 SQL Server 函数不需要提及时区。没有提到从 UTC 转换为任意时区,在这种情况下,需要提及时区(或者更好的是,它的偏移量)。 (2认同)

Mat*_*ear 53

如果您说的是本地日期时间Eastern Standard Time并且您想从 UTC 转换为该时间,那么在 Azure SQL 和 SQL Server 2016 及更高版本中,您可以执行以下操作:

SELECT YourUtcColumn AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time' AS
       LocalTime
FROM   YourTable
Run Code Online (Sandbox Code Playgroud)

可以通过以下方式找到时区名称的完整列表:

SELECT * FROM sys.time_zone_info 
Run Code Online (Sandbox Code Playgroud)

是的,时区的命名很糟糕 - 即使是Eastern Standard Time,也考虑了夏令时。

  • 时区和偏移量也可以在 SQL Server 中找到:SELECT * FROM sys.time_zone_info (2认同)
  • 包含“AT TIME ZONE 'UTC””不是多余的吗?我会完全忽略它。其他答案没有考虑 DST(夏令时),因此在查看日期时间(在最近的夏令时之前)时,它们会显示错误。然而,这效果很好。对于那些说不理会日期并在前端显示本地 DST 的人,我想说我同意 - 但是 - 当您在 SSMS 中工作并在后端运行查询并研究原始数据时,有时您需要转换这些 UTC,以便在与您的时区相关的具体事件发生时进行故障排除。 (2认同)

Aid*_*ela 44

我没有发现这些示例中的任何一个有助于将日期时间存储为UTC到指定时区的日期时间(不是服务器的时区,因为Azure SQL数据库以UTC身​​份运行).这就是我处理它的方式.它并不优雅,但它很简单,无需维护其他表格即可为您提供正确的答案:

select CONVERT(datetime, SWITCHOFFSET(dateTimeField, DATEPART(TZOFFSET, 
dateTimeField AT TIME ZONE 'Eastern Standard Time')))
Run Code Online (Sandbox Code Playgroud)

  • 仅适用于2016年并使用系统注册表.但Azure的出色解决方案. (4认同)
  • 这也有点奇怪,但一些非常基本的测试看起来也可能有效 - `dateTimeField AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time'` - 只是链接 `AT TIME ZONE` 语句。(@NateJ 上面提到了这一点,我现在看到了) (4认同)
  • 到目前为止,这似乎是唯一适用于 Azure 问题的方法 (2认同)
  • 这项功能适用于UTC中存储的天蓝色时间,谢谢大家 (2认同)
  • 以下是可用于时区的字符串列表:/sf/answers/553593771/ (2认同)
  • 这是非常聪明的,因为它适用于可能在 DST 期间的较旧(历史)日期。“AT TIME ZONE”语法仅在 SQL 2016 或更高版本中有效(当然还有 Azure SQL DB!)。就我个人而言,在 StackOverflow 中针对此类问题的所有各种答案和建议的解决方案中,我选择了这个作为我的用例。 (2认同)
  • 如果使用 SQL 2016 和 `AT TIME ZONE` 语法,请考虑 /sf/answers/3145907551/——您可以通过简单地连接多个 `at time zone <blah> 来**chain** 多个转换`s. (2认同)

Ron*_*ith 21

如果您需要服务器位置以外的转换,此功能允许您传递标准偏移量并记录美国夏令时:

-- =============================================
-- Author:      Ron Smith
-- Create date: 2013-10-23
-- Description: Converts UTC to DST
--              based on passed Standard offset
-- =============================================
CREATE FUNCTION [dbo].[fn_UTC_to_DST]
(
    @UTC datetime,
    @StandardOffset int
)
RETURNS datetime
AS
BEGIN

    declare 
        @DST datetime,
        @SSM datetime, -- Second Sunday in March
        @FSN datetime  -- First Sunday in November

    -- get DST Range
    set @SSM = datename(year,@UTC) + '0314' 
    set @SSM = dateadd(hour,2,dateadd(day,datepart(dw,@SSM)*-1+1,@SSM))
    set @FSN = datename(year,@UTC) + '1107'
    set @FSN = dateadd(second,-1,dateadd(hour,2,dateadd(day,datepart(dw,@FSN)*-1+1,@FSN)))

    -- add an hour to @StandardOffset if @UTC is in DST range
    if @UTC between @SSM and @FSN
        set @StandardOffset = @StandardOffset + 1

    -- convert to DST
    set @DST = dateadd(hour,@StandardOffset,@UTC)

    -- return converted datetime
    return @DST

END

GO
Run Code Online (Sandbox Code Playgroud)

  • 罗恩史密斯我知道这是一个古老的帖子,但我很好奇硬编码'0314'和'1107'代表DST范围.它似乎是硬编码的日子,由于DTS而改变.当它应该是计算日期时,为什么你会硬编码呢,因为天数根据日历的第二个星期日和11月的第一个星期日的日期而变化.硬编码的日子会使这段代码存在根本缺陷. (2认同)
  • 好问题:)这些是3月的第二个星期日和11月的第一个星期日可以发生的最长日期.以下行将变量设置为实际日期. (2认同)

PPa*_*ann 8

好吧,如果您将数据作为 UTC 日期存储在数据库中,您可以执行以下简单操作

select 
 [MyUtcDate] + getdate() - getutcdate()
from [dbo].[mytable]
Run Code Online (Sandbox Code Playgroud)

从服务器的角度来看,它始终是本地的,并且您不会摸索 AT TIME ZONE 'your time zone name',如果您的数据库移动到另一个时区(例如客户端安装),则硬编码的时区可能会困扰您。

  • 虽然正如你提到的那样可能有风险,但我不敢相信你对此没有任何赞成票 - 节省了我很多时间 (2认同)

小智 6

如果在数据库上启用CLR是一个选项以及使用sql server的时区,它可以很容易地用.Net编写.

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlDateTime fn_GetLocalFromUTC(SqlDateTime UTC)
    {
        if (UTC.IsNull)
            return UTC;

        return new SqlDateTime(UTC.Value.ToLocalTime());
    }
}
Run Code Online (Sandbox Code Playgroud)

UTC日期时间值进入,并且相对于服务器的本地日期时间值出现.空值返回null.


Pav*_*nko 6

使用新的SQL Server 2016机会:

CREATE FUNCTION ToLocalTime(@dtUtc datetime, @timezoneId nvarchar(256))
RETURNS datetime
AS BEGIN

return @dtUtc AT TIME ZONE 'UTC' AT TIME ZONE @timezoneId

/* -- second way, faster

return SWITCHOFFSET(@dtUtc , DATENAME(tz, @dtUtc AT TIME ZONE @timezoneId))

*/

/* -- third way

declare @dtLocal datetimeoffset
set @dtLocal = @dtUtc AT TIME ZONE @timezoneId
return dateadd(minute, DATEPART (TZoffset, @dtLocal), @dtUtc)

*/

END
GO
Run Code Online (Sandbox Code Playgroud)

但clr程序的工作速度提高了5倍:' - (

注意一个TimeZone的Offset可以改为冬季或夏季.例如

select cast('2017-02-08 09:00:00.000' as datetime) AT TIME ZONE 'Eastern Standard Time'
select cast('2017-08-08 09:00:00.000' as datetime) AT TIME ZONE 'Eastern Standard Time'
Run Code Online (Sandbox Code Playgroud)

结果:

2017-02-08 09:00:00.000 -05:00
2017-08-08 09:00:00.000 -04:00
Run Code Online (Sandbox Code Playgroud)

您不能只添加常量偏移量.


Mik*_*ike 6

这些都不适合我,但下面这个 100% 有效。希望这可以帮助其他像我一样尝试转换它的人。

CREATE FUNCTION [dbo].[fn_UTC_to_EST]
(
    @UTC datetime,
    @StandardOffset int
)
RETURNS datetime
AS
BEGIN

declare 
    @DST datetime,
    @SSM datetime, -- Second Sunday in March
    @FSN datetime  -- First Sunday in November
-- get DST Range
set @SSM = DATEADD(dd,7 + (6-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 2,0))%7)),DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 2,0))+'02:00:00' 
set @FSN = DATEADD(dd, (6-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 10,0))%7)),DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 10,0)) +'02:00:00'

-- add an hour to @StandardOffset if @UTC is in DST range
if @UTC between @SSM and @FSN
    set @StandardOffset = @StandardOffset + 1

-- convert to DST
set @DST = dateadd(hour,@StandardOffset,@UTC)

-- return converted datetime
return @DST

END
Run Code Online (Sandbox Code Playgroud)


小智 6

最简单的答案并不总是在底部,但这一次是,并且可以在上面的评论中隐藏的某个地方看到。

使用您自己的“AT TIME ZONE”来捕获列/数据字段的 TzOffset,而不是当前的 SYSDATETIME。

在下面的数据中,有 2 个查询,第一个查询是 2 月数据(DST 关闭,阿姆斯特丹冬季)+1 差异,第二个查询是阿姆斯特丹 4 月数据,所以+2 小时差异。

SELECT TOP 2 
    MONTH(receiveTimeUTC) AS MonthInWinterOrSpring,
    receiveTimeUTC,
    CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset,  receiveTimeUTC), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) AS LocalTimeWrongNoDST,
    CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset,  receiveTimeUTC), DATENAME(TzOffset, receiveTimeUTC AT TIME ZONE 'Central European Standard Time' ))) AS LocalTimeWithDST
FROM
    sensordetails 
ORDER BY 
    id

SELECT TOP 2 
    MONTH(receiveTimeUTC) AS MonthInWinterOrSpring, 
    receiveTimeUTC,  
    CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset,  receiveTimeUTC), DATENAME(TzOffset, SYSDATETIMEOFFSET()))) AS LocalTimeWrongNoDST,
    CONVERT(datetime, SWITCHOFFSET(CONVERT(datetimeoffset,  receiveTimeUTC), DATENAME(TzOffset, receiveTimeUTC AT TIME ZONE 'Central European Standard Time' ))) AS LocalTimeWithDST
FROM
    sensordetails 
ORDER BY
    id DESC
Run Code Online (Sandbox Code Playgroud)

结果:

格式化结果,指示二月数据上的红色错误NoDST。

所以这是一个 T-SQL(SQL Server 答案),不需要存储过程或函数。


vik*_*on0 5

没有简单的方法可以以正确和通用的方式做到这一点。

首先,必须了解偏移量取决于相关日期、时区和夏令时。 GetDate()-GetUTCDate仅在服务器的 TZ 处为您提供今天的偏移量,这无关紧要。

我只看到了两个有效的解决方案,而且我搜索了很多。

1) 一个自定义 SQL 函数,带有几个基本数据表,例如每个 TZ 的时区和 DST 规则。工作但不是很优雅。我不能发布它,因为我没有代码。

编辑:这是此方法的示例 https://gist.github.com/drumsta/16b79cee6bc195cd89c8

2) 将 .net 程序集添加到数据库中,.Net 可以很容易地做到这一点。这工作得很好,但缺点是您需要在服务器级别配置多个参数,并且配置很容易被破坏,例如,如果您恢复数据库。我使用这种方法,但我不能发布它,因为我不拥有代码。


Mik*_*ike 5

此功能将 UTC 时间转换为经过 DST 调整的 EST 时间。您可以在此函数中更改您设计的时区名称,或从注册表中获取它:

Create Function fnConvertUTCTimetoESTTime(
    @UTCTime as datetime
)
returns datetime
as
begin
    return convert(datetime, convert(varchar(23), @UTCTime AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time', 121), 121)
end
go

select dbo.fnConvertUTCTimetoESTTime ('2020-3-8 5:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-3-8 6:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-3-8 7:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-3-8 8:00:00.000')

--returns 0:00am, 1:00am, 3:00am, 4:00am

select dbo.fnConvertUTCTimetoESTTime ('2020-11-1 4:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-11-1 5:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-11-1 6:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-11-1 7:00:00.000')

--returns 0:00am, 1:00am, 1:00am, 2:00am
Run Code Online (Sandbox Code Playgroud)

请注意,您不能只返回“@UTCTime AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time'”作为结果,因为此结果实际上是 EST 格式的 UTC 时间(当您比较此“假”EST 时间或包含它在订单子句中,它将被转换回 UTC 时间)。


归档时间:

查看次数:

376478 次

最近记录:

5 年,9 月 前