【问题标题】:SQL Server 2008 - How to convert GMT(UTC) datetime to local datetime?SQL Server 2008 - 如何将 GMT(UTC) 日期时间转换为本地日期时间?
【发布时间】:2011-08-29 05:04:05
【问题描述】:

我有一个插入过程,它传入GETDATE() 作为值之一,因为每个插入也存储它何时被插入。这是托管在使用 GMT 的 SQL Azure 上。

现在,当我收到消息时,我将每个消息的 GMT 日期存储在它们的时间戳列中,当您访问我的页面时,如何将其转换为本地 datetime

谢谢。

【问题讨论】:

  • 服务器时间变了吗?可以查吗?
  • 嗯,它在 SQL Azure 上运行。我将如何检查?
  • 你不能简单地做一个“SELECT GETDATE() GO”看看是否符合你的期望吗?
  • 你不能 Azure 应该运行 GMT。你得到的是那个时间还是当地时间?
  • @Slandau,当你说“当我在 Azure 中执行简单查询时它不能正常工作”时,你所说的“不能正常工作”是什么意思?

标签: sql sql-server tsql sql-server-2008 datetime


【解决方案1】:

你可以这样做:

declare @InputUtcDateTime datetime2 = '2011-05-20 06:30:18'

declare @LocalDateTime datetime2 = dateadd(minute, datepart(TZoffset, sysdatetimeoffset()), @InputUtcDateTime)
print @LocalDateTime

declare @InputUtcDateTime datetime2 = '2011-05-20 06:30:18'

declare @LocalDateTime datetime2 = dateadd(minute, datediff(minute, sysutcdatetime(), sysdatetime()), @InputUtcDateTime)
print @LocalDateTime

【讨论】:

  • 这两个都不正确。他们依赖的假设是,今天本地时区的偏移量与输入 UTC 日期所指时间的本地时区偏移量相同。由于当地时区可能受 DST 影响,因此过去的偏移量可能与现在不同。
  • @wasabi +1 - 您对这种方法的局限性是正确的。但是我不同意这是不正确的。这取决于您要达到的目标。
  • 在给定任意输入时间的情况下,尝试获取本地时区的正确时间?
  • 要处理 DST 转换,您需要这样的查找表:codeproject.com/Articles/14769/…
  • 正如@wasabi 解释的那样,这种方法行不通。它假定服务器的本地时间始终与 GMT/UTC 有相同的偏移 - 即使对于格林威治,由于夏令时,这不是真的!
【解决方案2】:

以 MST 为例...考虑到每个 DTM 已经存储在 GMT 中,这简化了事情..

SWITCHOFFSET(CONVERT(DATETIMEOFFSET, [ColumnName]), '-07:00')

现在,如果您的本地日期/时间不是 GMT/UTC,您可能需要使用以下...

SWITCHOFFSET(TODATETIMEOFFSET([ColumnName], datepart(tz,sysdatetimeoffset())),'+00:00')

这是细分。

  • SWITCHOFFSET - 将 DateTimeOffset 值转换为不同的时区,同时保留偏移量。
  • TODATETIMEOFFSET - 将 DateTime 值转换为指定时区的 DateTimeOffset 值。
  • DATEPART - 在这种情况下,获取本地日期时间的时区部分。
  • '+00:00' - 目标偏移量,在第二个示例中是 UTC/GMT 目标,从本地...前一个示例是到 MST。

注意/警告:我不认为这是夏令时的原因,这对您来说可能是个问题。如果不需要绝对保存,您可能想简单地添加一个辅助列,并进行粗略的转换并安全地进行。

您可能希望将逻辑抽象为函数调用,以考虑到 DST 的保存...不过,这应该不会太难。

【讨论】:

    【解决方案3】:

    这是一个处理历史数据的函数。我是为英国夏令时写的 - 不幸的是,这发生在 3 月和 10 月的最后一个星期日,这使得逻辑有点复杂。

    基本上,硬编码日期部分 01/03 是在寻找 3 月的最后一个星期日,而 01/10 是在寻找 10 月的最后一个星期日(这是时钟前进和后退的时间)。注意:如果您的服务器使用美国本土日期,请将这两个日期部分反转为 03/01 和 10/01!!!!

    因此,您向它提供 UTC 日期,它会自动计算出历史日期是 BST 还是 GMT。不是在大数据集上使用的最佳方法,但它是一种解决方案。

    运行此脚本以创建函数并在您的选择中内联调用它。 SQL 2008 有用户定义函数的问题,它似乎在代码下放了一条红线,但只要你使用 dbo 前缀它仍然会运行它(SELECT dbo.UTCConvert(yourdate) 运行它)

    CREATE FUNCTION [dbo].[UTCConvert] 
    (
    
        @p1 datetime
    )
    RETURNS datetime
    AS
    BEGIN
    
        DECLARE @Result datetime
    
    
    RETURN CASE 
    WHEN
    @p1 >
    (DATEADD(day,DATEDIFF(day,'19000107',DATEADD(month,DATEDIFF(MONTH,0,'01/03/' + CAST(DATEPART(year,@p1) as CHAR)),30))/7*7,'19000107'))
    AND
    @p1<
    (DATEADD(day,DATEDIFF(day,'19000107',DATEADD(month,DATEDIFF(MONTH,0,'01/10/' + CAST(DATEPART(year,@p1) as CHAR)),30))/7*7,'19000107'))
    THEN (DATEADD(HH, 1, @p1)) 
    ELSE @p1
    END
    END
    

    【讨论】:

    • 如果您的服务器使用美国本土日期,因为它位于美国,您很可能会遇到更严重的头痛。奇怪的是,如果您正在查看此问题,则您正在存储至少一个适用夏令时的位置的日期。这意味着至少您必须担心 2007 年轮班日期的变化。您可能不得不担心多个时区,可能是亚利桑那州,甚至可能是纳瓦霍族。您确实希望在 SQL Server 中存储 UTC 或 datetimeoffset 并在业务层中进行日期数学运算。
    【解决方案4】:

    除了夏令时问题,为什么不简化:

    yourDateTime - getutcdate() + getdate()
    

    【讨论】:

      【解决方案5】:
      CREATE FUNCTION [dbo].[fn_DateTime_GMTFromLocal](@LocalTime DATETIME)
      RETURNS DATETIME2(3)
      AS
      BEGIN
      /*
      =============================================
       Author:        Mark Griffiths
       Create date:   29/05/2018
       Description:   BST runs from 02:00AM on the last Sunday of March to the same time on the last Sunday of October.       
          The Series of DATEDIFFs and DATEADDS below function as follows
          1   ●   Count the number of months there have been between the given date and start of computer time
          2   ●   Add that number of months to the end of the first month to get the end of the given month
          3   ●   Count the number of days there have been between the end of the given month and the first Saturday
          4   ●   Add that number of days to the calculated end of the given month
          5   ●   Add Two hours to that time as the clocks go back at 02:00 in the morning
      
          I know that the tabbing below makes it all look odd, but the description above is the best way I could find to comment things, given the nesting...
          The comments in the code below should help find the nesting levels and the numbers refer to the bullet points above.
      =============================================
      -- Test Variables --
      DECLARE @GMTime DATETIME2(3) = '2018-05-01 12:00:00.000'
      */
      
          DECLARE @RealTime As DATETIME2(3)
          DECLARE @Year VARCHAR(4)
          SET @Year = CONVERT(VARCHAR,DATEPART(YEAR,@GMTime))
          DECLARE @StartOfBST AS DATETIME
          DECLARE @EndOfBST AS DATETIME
          SELECT
              @StartOfBST =
              DATEADD     -----------------------------------------------------------------------------------------
                  (                                                       --                                      |
                   HOUR                                                   --                                      |
                  ,2                                                      --                                      |
                  ,DATEADD    -----------------------------------------------------------------------------       |
                      (                                                   --                              |       |
                       DAY                                                --                              |       |
                      ,DATEDIFF       -------------------------------------------------------------       |       |
                          (                                               --                      |       |       |
                           DAY                                            --                      |       |       |
                          ,'19000107'                                     --                      |       |       5
                          ,DATEADD            ---------------------------------------------       |       |       |
                              (                                           --              |       3       4       |
                               MONTH                                      --              |       |       |       |
                              ,DATEDIFF(MONTH,0,CONVERT(DATE,'03/01/' + @Year)) -- 1      2       |       |       |
                              ,CONVERT(DATE,'01/31/1900')                 --              |       |       |       |
                              )               ---------------------------------------------       |       |       |
                          )/7*7       -------------------------------------------------------------       |       |
                          ,'19000107'                                     --                              |       |
                      )               ---------------------------------------------------------------------       |
                  ),      -----------------------------------------------------------------------------------------
          @EndOfBST =
              DATEADD(HOUR,2,DATEADD(day,DATEDIFF(day,'19000107',DATEADD(month,DATEDIFF(MONTH,0,CONVERT(DATE,'10/01/' + @Year)),30))/7*7,'19000107'))
          SET @RealTime = CASE
                          WHEN @GMTime BETWEEN @StartOfBST AND @EndOfBST THEN DATEADD(HOUR,-1,@GMTime)
                          ELSE @GMTime
                      END
      RETURN @RealTime;
      --SELECT @RealTime
      END
      

      *编辑:将 CONVERT(DATE,'01/30/1900') 更改为 CONVERT(DATE,'01/31/1900'),因为自公元前 45 年以来,一月已有 31 天。这有时会导致 2019 年的结果不正确,以及 3 月的最后一个星期日是 31 日的其他任何时候。

      【讨论】:

      • 这非常有效,尽管我必须在最后将 -1 更改为 +1 才能将 UTC 时间更改为 GMT
      • 是的,您可以采用任何一种方式——我创建了两个函数,每种方式一个,并在我的脚本中分别引用它们——它们的区别仅在于+-1。 PS。令我惊讶的是,接受的答案(被赞成)不起作用!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-07
      • 1970-01-01
      • 2021-05-02
      • 2013-02-14
      • 2021-09-28
      • 1970-01-01
      • 2016-07-29
      相关资源
      最近更新 更多