50

如何将 SQL Serverdatetime值转换为datetimeoffset值?


例如,现有表包含的datetime值都是“本地”服务器时间。

SELECT TOP 5 ChangeDate FROM AuditLog

ChangeDate
=========================
2013-07-25 04:00:03.060
2013-07-24 04:00:03.073
2013-07-23 04:00:03.273
2013-07-20 04:00:02.870
2013-07-19 04:00:03.780

我的服务器(恰好)(现在,今天)比 UTC 晚 4 小时(现在,在美国东部时区,夏令时有效):

SELECT SYSDATETIMEOFFSET()

2013-07-25 14:42:41.6450840 -04:00

我想将存储的datetime值转换为datetimeoffset值;使用服务器的当前时区偏移信息。

我想要的价值观是:

ChangeDate               ChangeDateOffset
=======================  ==================================
2013-07-25 04:00:03.060  2013-07-25 04:00:03.0600000 -04:00
2013-07-24 04:00:03.073  2013-07-24 04:00:03.0730000 -04:00
2013-07-23 04:00:03.273  2013-07-23 04:00:03.2730000 -04:00
2013-07-20 04:00:02.870  2013-07-20 04:00:02.8700000 -04:00
2013-07-19 04:00:03.780  2013-07-19 04:00:03.7800000 -04:00

您可以看到理想的特征:

2013-07-19 04:00:03.7800000 -04:00
\_________________________/ \____/
           |                  |
      a "local" datetime      the offset from UTC

但实际值为:

SELECT TOP 5
   ChangeDate,
   CAST(ChangeDate AS datetimeoffset) AS ChangeDateOffset
FROM AuditLog

ChangeDate               ChangeDateOffset
=======================  ==================================
2013-07-25 04:00:03.060  2013-07-25 04:00:03.0600000 +00:00
2013-07-24 04:00:03.073  2013-07-24 04:00:03.0730000 +00:00
2013-07-23 04:00:03.273  2013-07-23 04:00:03.2730000 +00:00
2013-07-20 04:00:02.870  2013-07-20 04:00:02.8700000 +00:00
2013-07-19 04:00:03.780  2013-07-19 04:00:03.7800000 +00:00

具有无效特征:

2013-07-19 04:00:03.7800000 +00:00
\_________________________/ \____/
                              ^
                              |
                             No offset from UTC present

所以我随机尝试其他事情:

SELECT TOP 5
    ChangeDate, 
    CAST(ChangeDate AS datetimeoffset) AS ChangeDateOffset,
    DATEADD(minute, DATEDIFF(minute, GETDATE(), GETUTCDATE()), ChangeDate) AS ChangeDateUTC,
    CAST(DATEADD(minute, DATEDIFF(minute, GETDATE(), GETUTCDATE()), ChangeDate) AS datetimeoffset) AS ChangeDateUTCOffset,
    SWITCHOFFSET(CAST(ChangeDate AS datetimeoffset), DATEDIFF(minute, GETUTCDATE(), GETDATE())) AS ChangeDateSwitchedOffset
FROM AuditLog
ORDER BY ChangeDate DESC

结果:

ChangeDate               ChangeDateOffset                    ChangeDateUTC            ChangeDateUTCOffset                 ChangeDateSwitchedOffset
=======================  ==================================  =======================  ==================================  ==================================
2013-07-25 04:00:03.060  2013-07-25 04:00:03.0600000 +00:00  2013-07-25 08:00:03.060  2013-07-25 08:00:03.0600000 +00:00  2013-07-25 00:00:03.0600000 -04:00
2013-07-24 04:00:03.073  2013-07-24 04:00:03.0730000 +00:00  2013-07-24 08:00:03.073  2013-07-24 08:00:03.0730000 +00:00  2013-07-24 00:00:03.0730000 -04:00
2013-07-23 04:00:03.273  2013-07-23 04:00:03.2730000 +00:00  2013-07-23 08:00:03.273  2013-07-23 08:00:03.2730000 +00:00  2013-07-23 00:00:03.2730000 -04:00
2013-07-20 04:00:02.870  2013-07-20 04:00:02.8700000 +00:00  2013-07-20 08:00:02.870  2013-07-20 08:00:02.8700000 +00:00  2013-07-20 00:00:02.8700000 -04:00
2013-07-19 04:00:03.780  2013-07-19 04:00:03.7800000 +00:00  2013-07-19 08:00:03.780  2013-07-19 08:00:03.7800000 +00:00  2013-07-19 00:00:03.7800000 -04:00
                         ----------------------------------                           ----------------------------------  ----------------------------------
                                              No UTC offset                           Time in UTC          No UTC offset  Time all wrong

它们都没有返回所需的值。

任何人都可以提出一些可以返回我直觉想要的东西吗?

4

4 回答 4

77

编辑:为 SQL Server 2016 更新了更好的答案

SELECT 
   ChangeDate,  --original datetime value
   ChangeDate AT TIME ZONE 'Eastern Standard Time' AS ChangeDateOffset
FROM AuditLog

AT TIME ZONE考虑到夏令时在转换日期时是否有效。即使它在"Eastern Standard Time"显示 "Standard" ,它也会为您提供白天时间:

ChangeDate               ChangeDateOffset
-----------------------  ------------------------------
2019-01-21 09:00:00.000  2019-01-21 09:00:00.000 -05:00
2019-02-21 09:00:00.000  2019-02-21 09:00:00.000 -05:00
2019-03-21 09:00:00.000  2019-03-21 09:00:00.000 -04:00  <-- savings time
2019-04-21 09:00:00.000  2019-04-21 09:00:00.000 -04:00  <-- savings time
2019-05-21 09:00:00.000  2019-05-21 09:00:00.000 -04:00  <-- savings time
2019-06-21 09:00:00.000  2019-06-21 09:00:00.000 -04:00  <-- savings time
2019-07-21 09:00:00.000  2019-07-21 09:00:00.000 -04:00  <-- savings time
2019-08-21 09:00:00.000  2019-08-21 09:00:00.000 -04:00  <-- savings time
2019-09-21 09:00:00.000  2019-09-21 09:00:00.000 -04:00  <-- savings time
2019-10-21 09:00:00.000  2019-10-21 09:00:00.000 -04:00  <-- savings time
2019-11-21 09:00:00.000  2019-11-21 09:00:00.000 -05:00
2019-12-21 09:00:00.000  2019-12-21 09:00:00.000 -05:00

至于如何避免对字符串进行硬编码Eastern Standard Time,并使用服务器的当前时区?你是索尔。

SQL Server 2016 之前的原始答案

我想到了。诀窍是有一个内置的 SQL Server 函数ToDateTimeOffset,它将任意偏移信息附加datetime到任何提供的 .

例如,相同的查询:

SELECT ToDateTimeOffset('2013-07-25 15:35:27', -240)     --  -240 minutes
SELECT ToDateTimeOffset('2013-07-25 15:35:27', '-04:00') --  -4 hours

两者都返回:

2013-07-25 15:35:27.0000000 -04:00

注意:偏移量参数ToDateTimeOffset可以是:

  • an integer, 代表分钟数
  • a string,表示小时和分钟({+|-}TZH:THM格式)

我们需要服务器当前的 UTC 偏移量

接下来,我们需要服务器与 UTC 的当前偏移量。有两种方法可以让 SQL Server 返回integer我们来自 UTC 的分钟数:

DATEPART(TZOFFSET, SYSDATETIMEOFFSET()) 
DATEDIFF(minute, GETUTCDATE(), GETDATE())

两者都返回

-240

将其插入TODATETIMEOFFSET函数中:

SELECT ToDateTimeOffset(
      '2013-07-25 15:35:27',
      DATEPART(TZOFFSET, SYSDATETIMEOFFSET()) --e.g. -240
)

返回datetimeoffset我想要的值:

2013-07-25 15:35:27.0000000 -04:00

总而言之

现在我们可以有一个更好的函数来将 datetime 转换为 datetimeoffset:

CREATE FUNCTION dbo.ToDateTimeOffset(@value datetime2)
    RETURNS datetimeoffset AS
BEGIN
/*
    Converts a date/time without any timezone offset into a datetimeoffset value, 
    using the server's current offset from UTC. 
    
    For this we use the built-in ToDateTimeOffset function; 
    which attaches timezone offset information with a datetimeoffset value.
    
    The trick is to use DATEDIFF(minutes) between local server time and UTC 
    to get the offset parameter.
    
    For example:
        DATEPART(TZOFFSET, SYSDATETIMEOFFSET())
    returns the integer
        -240

    for people in EDT (Eastern Daylight Time), which is 4 hours (240 minutes) behind UTC.
    Pass that value to the SQL Server function:
        TODATETIMEOFFSET(@value, -240)
*/
    
    RETURN TODATETIMEOFFSET(@value, DATEPART(TZOFFSET, SYSDATETIMEOFFSET()))
END;

示例使用

SELECT TOP 5
    ChangeDate, 
    dbo.ToDateTimeOffset(ChangeDate) AS ChangeDateOffset
FROM AuditLog

返回所需的:

ChangeDate               ChangeDateOffset
=======================  ==================================
2013-07-25 04:00:03.060  2013-07-25 04:00:03.0600000 -04:00
2013-07-24 04:00:03.073  2013-07-24 04:00:03.0730000 -04:00
2013-07-23 04:00:03.273  2013-07-23 04:00:03.2730000 -04:00
2013-07-20 04:00:02.870  2013-07-20 04:00:02.8700000 -04:00
2013-07-19 04:00:03.780  2013-07-19 04:00:03.7800000 -04:00

如果内置函数只是这样做,那将是理想的:

TODATETIMEOFFSET(value)

而不必创建“重载”

dbo.ToDateTimeOffset(value)

注意:任何代码都会发布到公共领域。无需归属。

于 2013-07-25T20:02:42.863 回答
3

将本地时间转换为具有当前时间偏移量的 datetimeoffset 似乎需要一些技巧。可能有一种更简单的方法,但这似乎可以做到;

SELECT ChangeDate, 
  CONVERT(DATETIMEOFFSET, CONVERT(VARCHAR, ChangeDate, 120) + 
          RIGHT(CONVERT(VARCHAR, SYSDATETIMEOFFSET(), 120), 6), 120)
FROM AuditLog;

可能值得创建一个函数;

CREATE FUNCTION LOCALIFY(@dt DATETIME) 
  RETURNS DATETIMEOFFSET AS
BEGIN
 RETURN CONVERT(DATETIMEOFFSET, 
          CONVERT(VARCHAR, @dt, 120) + 
          RIGHT(CONVERT(VARCHAR, SYSDATETIMEOFFSET(), 120), 6), 120)
END;

……然后就……

SELECT ChangeDate, dbo.LOCALIFY(ChangeDate) FROM AuditLog;
于 2013-07-25T19:16:20.063 回答
0

OP 的时间有点晚,但这个线程有助于注意转换datetimedatetimeoffset.

我已经使用了一些功能,但也建议使用默认设置为 的字段sysdatetimeoffset(),以便在插入项目时(当前时间戳)将相对于它被放入的时间。然后如果需要修改,更新可以在程序中从源头利用 TZ。

这在需要datetimeoffset.

于 2014-12-09T15:30:41.417 回答
-2

我认为您必须乘以DATEPART(TZOFFSET,SYSDATETIMEOFFSET())才能-1获得正确的时区偏移量。我认为,如果您在东部时区,时区偏移量应该是 +4:00 而不是 -4:00。是从我的本地服务器到 UTC 还是从 UTC 到我的本地服务器的偏移量?

于 2017-06-20T15:02:31.850 回答