代码之家  ›  专栏  ›  技术社区  ›  James

SQL Server-将日期字段转换为UTC

  •  47
  • James  · 技术社区  · 14 年前

    我最近更新了我的系统,将日期/时间记录为与以前一样的UTC,它们存储为本地时间。

    现在我需要将所有本地存储的日期/时间转换为UTC。我想知道是否有类似于.NET的内置函数 ConvertTime 方法?

    我尽量避免为自己写一个实用程序。

    有什么建议吗?

    12 回复  |  直到 6 年前
        1
  •  10
  •   SqlRyan    14 年前

    如果它们都是本地的,那么这里是偏移量:

    SELECT GETDATE() AS CurrentTime, GETUTCDATE() AS UTCTime
    

    您应该能够使用以下方法更新所有数据:

    UPDATE SomeTable
       SET DateTimeStamp = DATEADD(hh, DATEDIFF(hh, GETDATE(), GETUTCDATE()), DateTimeStamp)
    

    这会起作用吗,还是我遗漏了这个问题的另一个角度?

        2
  •  74
  •   Roderick Llewellyn    13 年前

    我不相信上面的代码会起作用。原因是它取决于本地时间和UTC时间的当前日期之间的差异。例如,在加利福尼亚州,我们现在使用的是PDT(太平洋夏令时);此时间与UTC之间的时差为7小时。如果现在运行,所提供的代码将为希望转换的每个日期增加7小时。但是,如果转换了历史存储日期或将来的日期,而该日期不在夏令时期间,则当正确的偏移量为8时,它仍将添加7。底线:您不能仅通过查看当前日期在时区(包括不遵守夏令时的UTC)之间正确转换日期/时间。您必须考虑转换的日期本身,以及夏令时是否在该日期生效。此外,白天和标准时间的变化日期也发生了变化(乔治·布什在为美国执政期间改变了日期!)换句话说,任何引用getDate()或getutcdate()的解决方案都不起作用。它必须分析要转换的实际日期。

        3
  •  36
  •   Matt Johnson-Pint    8 年前

    使用SQL Server 2016,现在有了内置的时区支持, AT TIME ZONE 语句。您可以将这些链接起来进行转换:

    SELECT YourOriginalDateTime AT TIME ZONE 'Pacific Standard Time' AT TIME ZONE 'UTC'
    

    或者,这也可以:

    SELECT SWITCHOFFSET(YourOriginalDateTime AT TIME ZONE 'Pacific Standard Time', '+00:00')
    

    其中任何一个都将解释太平洋时间的输入,正确说明DST是否有效,然后转换为UTC。结果将是 datetimeoffset 零偏移。

    More examples in the CTP announcement.

        4
  •  16
  •   Michael Teper Ryan Lee    10 年前

    如前所述,在SQL Server中没有执行时区规则感知日期转换的内置方法(至少从SQL Server 2012开始)。

    您基本上有三个选择来正确执行此操作:

    1. 在SQL Server外部执行转换并将结果存储在数据库中
    2. 在独立表中引入时区偏移量规则,并创建存储过程或UDF以引用规则表以执行转换。你可以找到一个采取这种方法的人 over at SQL Server Central (需要注册)
    3. 您可以创建一个SQL CLR UDF;我将在这里描述这种方法

    虽然SQL Server不提供执行时区规则感知日期转换的工具,.NET框架提供了,只要您可以使用SQL CLR,就可以利用它。

    在Visual Studio 2012中,请确保已安装数据工具(否则,SQL Server项目不会作为选项显示),然后创建新的SQL Server项目。

    然后,添加一个新的SQL clr c用户定义函数,称之为“converttoutc”。Vs将为您生成锅炉板,其外观应如下所示:

    public partial class UserDefinedFunctions
    {
        [Microsoft.SqlServer.Server.SqlFunction]
        public static SqlString ConvertToUtc()
        {
            // Put your code here
            return new SqlString (string.Empty);
        }
    }
    

    我们想在这里做一些改变。首先,我们要返回 SqlDateTime 而不是 SqlString . 其次,我们想做一些有用的事情。:)

    您的修订代码应如下所示:

    public partial class UserDefinedFunctions
    {
        [Microsoft.SqlServer.Server.SqlFunction]
        public static SqlDateTime ConvertToUtc(SqlDateTime sqlLocalDate)
        {
            // convert to UTC and use explicit conversion
            // to return a SqlDateTime
            return TimeZone.CurrentTimeZone.ToUniversalTime(sqlLocalDate.Value);
        }
    }
    

    在这一点上,我们准备尝试一下。最简单的方法是在Visual Studio中使用内置的发布工具。右键单击数据库项目并选择“发布”。设置数据库连接和名称,然后单击“发布”将代码推送到数据库中,或者单击“生成脚本”(如果要为后代存储脚本,或将位推送到生产环境中)。

    一旦您在数据库中拥有了UDF,您就可以看到它在运行:

    declare @dt as datetime
    set @dt = '12/1/2013 1:00 pm'
    select dbo.ConvertToUtc(@dt)
    
        5
  •  6
  •   Ben Gripka Javed Akram    9 年前

    下面是一个测试过程,它将我的数据库从本地时间升级到了UTC时间。升级数据库所需的唯一输入是输入本地时间从UTC时间偏移到@offset的分钟数,如果时区需要通过设置@applydaylightsavings进行夏令时调整,则输入@applydaylightsavings。

    例如,美国中央时间将输入@offset=-360和@apply daylight savings=1,持续6小时,是应用夏令时调整。

    支持数据库功能


    CREATE FUNCTION [dbo].[GetUtcDateTime](@LocalDateTime DATETIME, @Offset smallint, @ApplyDaylightSavings bit) 
    RETURNS DATETIME AS BEGIN 
    
        --====================================================
        --Calculate the Offset Datetime
        --====================================================
        DECLARE @UtcDateTime AS DATETIME
        SET @UtcDateTime = DATEADD(MINUTE, @Offset * -1, @LocalDateTime)
    
        IF @ApplyDaylightSavings = 0 RETURN @UtcDateTime;
    
        --====================================================
        --Calculate the DST Offset for the UDT Datetime
        --====================================================
        DECLARE @Year as SMALLINT
        DECLARE @DSTStartDate AS DATETIME
        DECLARE @DSTEndDate AS DATETIME
    
        --Get Year
        SET @Year = YEAR(@LocalDateTime)
    
        --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)
    
        --Finally add the DST Offset if needed 
        RETURN CASE WHEN @LocalDateTime BETWEEN @DSTStartDate AND @DSTEndDate THEN 
            DATEADD(MINUTE, -60, @UtcDateTime) 
        ELSE 
            @UtcDateTime
        END
    
    END
    GO
    

    升级脚本


    1. 在运行此脚本之前进行备份!
    2. 设置@offset&@applydaylightsavings
    3. 只跑一次!

    begin try
        begin transaction;
    
        declare @sql nvarchar(max), @Offset smallint, @ApplyDaylightSavings bit;
    
        set @Offset = -360;             --US Central Time, -300 for US Eastern Time, -480 for US West Coast
        set @ApplyDaylightSavings = 1;  --1 for most US time zones except Arizona which doesn't observer daylight savings, 0 for most time zones outside the US
    
        declare rs cursor for
        select 'update [' + a.TABLE_SCHEMA + '].[' + a.TABLE_NAME + '] set [' + a.COLUMN_NAME + '] = dbo.GetUtcDateTime([' + a.COLUMN_NAME + '], ' + cast(@Offset as nvarchar) + ', ' + cast(@ApplyDaylightSavings as nvarchar) + ') ;'
        from INFORMATION_SCHEMA.COLUMNS a
            inner join INFORMATION_SCHEMA.TABLES b on a.TABLE_SCHEMA = b.TABLE_SCHEMA and a.TABLE_NAME = b.TABLE_NAME
        where a.DATA_TYPE = 'datetime' and b.TABLE_TYPE = 'BASE TABLE' ;
    
        open rs;
        fetch next from rs into @sql;
        while @@FETCH_STATUS = 0 begin
            exec sp_executesql @sql;
            print @sql;
            fetch next from rs into @sql;
        end
        close rs;
        deallocate rs;
    
        commit transaction;
    end try
    begin catch
        close rs;
        deallocate rs;
    
        declare @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int;
        select @ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
        rollback transaction;
        raiserror (@ErrorMessage, @ErrorSeverity, @ErrorState);
    end catch
    
        6
  •  4
  •   Chris Barlow    10 年前

    如果必须将今天以外的日期转换为不同的时区,则必须处理夏令时。我想要一个不需要担心数据库版本、不需要使用存储函数以及可以轻松移植到Oracle的解决方案。

    我认为沃伦在获得夏令时的正确日期方面走上了正确的道路,但为了使其更适用于多个时区和国家的不同规则,甚至是2006年至2007年在美国发生变化的规则,上述解决方案在此有所不同。注意这不仅有美国的时区,还有中欧。中欧在4月的最后一个星期日和10月的最后一个星期日之后。你还将注意到,美国在2006年遵循的是4月的第一个星期日,10月的最后一个星期日规则。

    这个SQL代码看起来可能有点难看,但只需复制粘贴到SQL Server中并尝试一下。请注意,有三个部分用于年份、时区和规则。如果你想再过一年,就把它加到年会上。对于其他时区或规则也是如此。

    select yr, zone, standard, daylight, rulename, strule, edrule, yrstart, yrend,
        dateadd(day, (stdowref + stweekadd), stmonthref) dstlow,
        dateadd(day, (eddowref + edweekadd), edmonthref)  dsthigh
    from (
      select yrs.yr, z.zone, z.standard, z.daylight, z.rulename, r.strule, r.edrule, 
        yrs.yr + '-01-01 00:00:00' yrstart,
        yrs.yr + '-12-31 23:59:59' yrend,
        yrs.yr + r.stdtpart + ' ' + r.cngtime stmonthref,
        yrs.yr + r.eddtpart + ' ' + r.cngtime edmonthref,
        case when r.strule in ('1', '2', '3') then case when datepart(dw, yrs.yr + r.stdtpart) = '1' then 0 else 8 - datepart(dw, yrs.yr + r.stdtpart) end
        else (datepart(dw, yrs.yr + r.stdtpart) - 1) * -1 end stdowref,
        case when r.edrule in ('1', '2', '3') then case when datepart(dw, yrs.yr + r.eddtpart) = '1' then 0 else 8 - datepart(dw, yrs.yr + r.eddtpart) end
        else (datepart(dw, yrs.yr + r.eddtpart) - 1) * -1 end eddowref,
        datename(dw, yrs.yr + r.stdtpart) stdow,
        datename(dw, yrs.yr + r.eddtpart) eddow,
        case when r.strule in ('1', '2', '3') then (7 * CAST(r.strule AS Integer)) - 7 else 0 end stweekadd,
        case when r.edrule in ('1', '2', '3') then (7 * CAST(r.edrule AS Integer)) - 7 else 0 end edweekadd
    from (
        select '2005' yr union select '2006' yr -- old us rules
        UNION select '2007' yr UNION select '2008' yr UNION select '2009' yr UNION select '2010' yr UNION select '2011' yr
        UNION select '2012' yr UNION select '2013' yr UNION select '2014' yr UNION select '2015' yr UNION select '2016' yr
        UNION select '2017' yr UNION select '2018' yr UNION select '2018' yr UNION select '2020' yr UNION select '2021' yr
        UNION select '2022' yr UNION select '2023' yr UNION select '2024' yr UNION select '2025' yr UNION select '2026' yr
    ) yrs
    cross join (
        SELECT 'ET' zone, '-05:00' standard, '-04:00' daylight, 'US' rulename
        UNION SELECT 'CT' zone, '-06:00' standard, '-05:00' daylight, 'US' rulename
        UNION SELECT 'MT' zone, '-07:00' standard, '-06:00' daylight, 'US' rulename
        UNION SELECT 'PT' zone, '-08:00' standard, '-07:00' daylight, 'US' rulename
        UNION SELECT 'CET' zone, '+01:00' standard, '+02:00' daylight, 'EU' rulename
    ) z
    join (
        SELECT 'US' rulename, '2' strule, '-03-01' stdtpart, '1' edrule, '-11-01' eddtpart, 2007 firstyr, 2099 lastyr, '02:00:00' cngtime
        UNION SELECT 'US' rulename, '1' strule, '-04-01' stdtpart, 'L' edrule, '-10-31' eddtpart, 1900 firstyr, 2006 lastyr, '02:00:00' cngtime
        UNION SELECT  'EU' rulename, 'L' strule, '-03-31' stdtpart, 'L' edrule, '-10-31' eddtpart, 1900 firstyr, 2099 lastyr, '01:00:00' cngtime
    ) r on r.rulename = z.rulename
        and datepart(year, yrs.yr) between firstyr and lastyr
    ) dstdates
    

    对于规则,在第一个、第二个、第三个或最后一个星期日使用1、2、3或l。“日期”部分给出了规则类型L的月份,具体取决于规则、月份的第一天或月份的最后一天。

    我把上面的查询放到一个视图中。现在,只要我想要一个时区偏移或转换为UTC时间的日期,我就加入到这个视图中,并选择以日期格式获取日期。我将这些转换为datetimeoffset,而不是datetime。

    select createdon, dst.zone
        , case when createdon >= dstlow and createdon < dsthigh then dst.daylight else dst.standard end pacificoffsettime
        , TODATETIMEOFFSET(createdon, case when createdon >= dstlow and createdon < dsthigh then dst.daylight else dst.standard end) pacifictime
        , SWITCHOFFSET(TODATETIMEOFFSET(createdon, case when createdon >= dstlow and createdon < dsthigh then dst.daylight else dst.standard end), '+00:00')  utctime
    from (select '2014-01-01 12:00:00' createdon union select '2014-06-01 12:00:00' createdon) photos
    left join US_DAYLIGHT_DATES dst on createdon between yrstart and yrend and zone = 'PT'
    
        7
  •  2
  •   AndyMcKenna    8 年前

    这是我的快速和肮脏版本。我知道我所有的约会都在使用美国东部时区。您可以更改偏移量,或者根据需要使其更智能。我做过一次迁移,所以这就足够了。

    CREATE FUNCTION [dbo].[ConvertToUtc]
    (
        @date datetime
    )
    RETURNS DATETIME
    AS
    BEGIN
        -- Declare the return variable here
        DECLARE @utcDate datetime;
        DECLARE @offset int;
    
        SET @offset = (SELECT CASE WHEN 
                                        @date BETWEEN '1987-04-05 02:00 AM' AND '1987-10-25 02:00 AM'
                                     OR @date BETWEEN '1988-04-03 02:00 AM' AND '1988-10-30 02:00 AM'
                                     OR @date BETWEEN '1989-04-02 02:00 AM' AND '1989-10-29 02:00 AM'
                                     OR @date BETWEEN '1990-04-01 02:00 AM' AND '1990-10-28 02:00 AM'
                                     OR @date BETWEEN '1991-04-07 02:00 AM' AND '1991-10-27 02:00 AM'
                                     OR @date BETWEEN '1992-04-05 02:00 AM' AND '1992-10-25 02:00 AM'
                                     OR @date BETWEEN '1993-04-04 02:00 AM' AND '1993-10-31 02:00 AM'
                                     OR @date BETWEEN '1994-04-03 02:00 AM' AND '1994-10-30 02:00 AM'
                                     OR @date BETWEEN '1995-04-02 02:00 AM' AND '1995-10-29 02:00 AM'
                                     OR @date BETWEEN '1996-04-07 02:00 AM' AND '1996-10-27 02:00 AM'
                                     OR @date BETWEEN '1997-04-06 02:00 AM' AND '1997-10-26 02:00 AM'
                                     OR @date BETWEEN '1998-04-05 02:00 AM' AND '1998-10-25 02:00 AM'
                                     OR @date BETWEEN '1999-04-04 02:00 AM' AND '1999-10-31 02:00 AM'
                                     OR @date BETWEEN '2000-04-02 02:00 AM' AND '2000-10-29 02:00 AM'
                                     OR @date BETWEEN '2001-04-01 02:00 AM' AND '2001-10-28 02:00 AM'
                                     OR @date BETWEEN '2002-04-07 02:00 AM' AND '2002-10-27 02:00 AM'
                                     OR @date BETWEEN '2003-04-06 02:00 AM' AND '2003-10-26 02:00 AM'
                                     OR @date BETWEEN '2004-04-04 02:00 AM' AND '2004-10-31 02:00 AM'
                                     OR @date BETWEEN '2005-04-03 02:00 AM' AND '2005-10-30 02:00 AM'
                                     OR @date BETWEEN '2006-04-02 02:00 AM' AND '2006-10-29 02:00 AM'
                                     OR @date BETWEEN '2007-03-11 02:00 AM' AND '2007-11-04 02:00 AM'
                                     OR @date BETWEEN '2008-03-09 02:00 AM' AND '2008-11-02 02:00 AM'
                                     OR @date BETWEEN '2009-03-08 02:00 AM' AND '2009-11-01 02:00 AM'
                                     OR @date BETWEEN '2010-03-14 02:00 AM' AND '2010-11-07 02:00 AM'
                                     OR @date BETWEEN '2011-03-13 02:00 AM' AND '2011-11-06 02:00 AM'
                                     OR @date BETWEEN '2012-03-11 02:00 AM' AND '2012-11-04 02:00 AM'
                                     OR @date BETWEEN '2013-03-10 02:00 AM' AND '2013-11-03 02:00 AM'
                                     OR @date BETWEEN '2014-03-09 02:00 AM' AND '2014-11-02 02:00 AM'
                                     OR @date BETWEEN '2015-03-08 02:00 AM' AND '2015-11-01 02:00 AM'
                                     OR @date BETWEEN '2016-03-13 02:00 AM' AND '2016-11-06 02:00 AM'
                                     OR @date BETWEEN '2017-03-12 02:00 AM' AND '2017-11-05 02:00 AM'
                                     OR @date BETWEEN '2018-03-11 02:00 AM' AND '2018-11-04 02:00 AM'
                                     OR @date BETWEEN '2019-03-10 02:00 AM' AND '2019-11-03 02:00 AM'
                                     OR @date BETWEEN '2020-03-08 02:00 AM' AND '2020-11-01 02:00 AM'
                                     OR @date BETWEEN '2021-03-14 02:00 AM' AND '2021-11-07 02:00 AM'
                                   THEN 4
                                   ELSE 5 END);
    
        SELECT @utcDate = DATEADD(hh, @offset, @date)
        RETURN @utcDate;
    
    END
    
        8
  •  1
  •   Gil    8 年前

    除非我错过了上面提到的(可能的)东西,否则上面所有的方法都有缺陷,因为它们在从夏时制(比如EDT)切换到标准时间(比如EST)时不考虑重叠。(非常详细)示例:

    [1] EDT 2016-11-06 00:59 - UTC 2016-11-06 04:59
    [2] EDT 2016-11-06 01:00 - UTC 2016-11-06 05:00
    [3] EDT 2016-11-06 01:30 - UTC 2016-11-06 05:30
    [4] EDT 2016-11-06 01:59 - UTC 2016-11-06 05:59
    [5] EST 2016-11-06 01:00 - UTC 2016-11-06 06:00
    [6] EST 2016-11-06 01:30 - UTC 2016-11-06 06:30
    [7] EST 2016-11-06 01:59 - UTC 2016-11-06 06:59
    [8] EST 2016-11-06 02:00 - UTC 2016-11-06 07:00
    

    基于日期和时间的简单小时偏移不会减少它。如果你不知道当地时间是在01:00到01:59之间记录在EDT或EST中,你就没有线索了!例如,让我们使用01:30:如果您在01:31到01:59范围内找到以后的时间,您将不知道您正在查看的01:30是[3还是[6]。在这种情况下,您可以通过一点编码获得正确的UTC时间来查看以前的条目(在SQL中不是很有趣),这是最好的情况…

    假设您已经记录了以下当地时间,并且没有专门指出EDT或EST:

                         UTC time         UTC time         UTC time
                         if [2] and [3]   if [2] and [3]   if [2] before
    local time           before switch    after switch     and [3] after
    [1] 2016-11-06 00:43     04:43         04:43           04:43
    [2] 2016-11-06 01:15     05:15         06:15           05:15
    [3] 2016-11-06 01:45     05:45         06:45           06:45
    [4] 2016-11-06 03:25     07:25         07:25           07:25
    

    时间[2]和[3]可能在上午5点的时间范围内,也可能在上午6点的时间范围内,或者在上午5点的时间范围内,或者在上午6点的时间范围内。…换言之:你被冲洗了,必须在01:00:00到01:59:59之间扔掉所有的读数。在这种情况下,绝对没有办法解决实际的UTC时间!

        9
  •  0
  •   Gaurav Arora    12 年前

    下面的工作原理是,它计算正在运行的服务器的日期和utc date之间的差异,并使用该偏移量计算传递给它的任何日期的UTC等效值。在我的示例中,我尝试在澳大利亚阿德莱德将“2012年11月1日06:00”的UTC等效值转换为-630分钟,如果将其添加到任何日期,将导致任何本地日期的UTC等效值。

    选择dateadd(分钟,datediff(分钟,getdate(),getutcdate())和'1-nov-2012 06:00')

        10
  •  0
  •   Warren    10 年前

    根据您需要往回走多远,您可以构建一个夏令时表,然后加入该表并进行DST敏感转换。这个特殊的方法将EST转换为GMT(即使用5和4的偏移量)。

    select createdon, dateadd(hour, case when dstlow is null then 5 else 4 end, createdon) as gmt
    from photos
    left outer join (
              SELECT {ts '2009-03-08 02:00:00'} as dstlow, {ts '2009-11-01 02:00:00'} as dsthigh
    UNION ALL SELECT {ts '2010-03-14 02:00:00'} as dstlow, {ts '2010-11-07 02:00:00'} as dsthigh
    UNION ALL SELECT {ts '2011-03-13 02:00:00'} as dstlow, {ts '2011-11-06 02:00:00'} as dsthigh
    UNION ALL SELECT {ts '2012-03-11 02:00:00'} as dstlow, {ts '2012-11-04 02:00:00'} as dsthigh
    UNION ALL SELECT {ts '2013-03-10 02:00:00'} as dstlow, {ts '2013-11-03 02:00:00'} as dsthigh
    UNION ALL SELECT {ts '2014-03-09 02:00:00'} as dstlow, {ts '2014-11-02 02:00:00'} as dsthigh
    UNION ALL SELECT {ts '2015-03-08 02:00:00'} as dstlow, {ts '2015-11-01 02:00:00'} as dsthigh
    UNION ALL SELECT {ts '2016-03-13 02:00:00'} as dstlow, {ts '2016-11-06 02:00:00'} as dsthigh
    UNION ALL SELECT {ts '2017-03-12 02:00:00'} as dstlow, {ts '2017-11-05 02:00:00'} as dsthigh
    UNION ALL SELECT {ts '2018-03-11 02:00:00'} as dstlow, {ts '2018-11-04 02:00:00'} as dsthigh
        ) dst
        on createdon >= dstlow and createdon < dsthigh
    
        11
  •  0
  •   KarthikeyanMlp    7 年前

    我们可以转换serverzone DateTime 到UTC和UTC到ServerZone 日期时间

    只需运行以下脚本来理解转换,然后根据需要进行修改

    --Get Server's TimeZone
    DECLARE @ServerTimeZone VARCHAR(50)
    EXEC MASTER.dbo.xp_regread 'HKEY_LOCAL_MACHINE',
    'SYSTEM\CurrentControlSet\Control\TimeZoneInformation',
    'TimeZoneKeyName',@ServerTimeZone OUT
    
    -- ServerZone to UTC DATETIME
    DECLARE @CurrentServerZoneDateTime DATETIME = GETDATE()
    DECLARE @UTCDateTime  DATETIME =  @CurrentServerZoneDateTime AT TIME ZONE @ServerTimeZone AT TIME ZONE 'UTC' 
    --(OR)
    --DECLARE @UTCDateTime  DATETIME = GETUTCDATE()
    SELECT @CurrentServerZoneDateTime AS CURRENTZONEDATE,@UTCDateTime AS UTCDATE
    
    -- UTC to ServerZone DATETIME
    SET @CurrentServerZoneDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE @ServerTimeZone
    SELECT @UTCDateTime AS UTCDATE,@CurrentServerZoneDateTime AS CURRENTZONEDATE
    

    注释 这个: AT TIME ZONE ) 仅在SQL Server 2016上工作+ 这个优势是 自动考虑日光 转换到特定时区时

        12
  •  0
  •   Corbin    6 年前

    Matt Johnson's answer 绝对是Microsoft SQL Server 2016+中的最佳方法。但他建议在时区使用的确有一个弱点需要注意。无法确定DST结束时与UTC之间的正确偏移量。据我所知,没有可能的方法来解决这个问题。考虑下面的例子。

    --Assume that the value of input_date = '2018-11-04 01:00:00'
    --Assume that dates in this field are written in Central Standard Time
    
    select input_date at time zone 'Central Standard Time'
    
    >> '2018-11-04 01:00:00 -05:00'
    

    默认的Microsoft SQL Server行为是假定输入的日期值偏移量为-05:00。然而,根据DST是否结束,这一时间实际上可以表示-05:00或-06:00偏移。没有附带的偏移量是无法确定的,因此Microsoft SQL Server猜测哪些偏移量可能是正确的,哪些偏移量可能不正确。