如何将日期时间转换为日期时间偏移?

问题描述 投票:0回答:5

如何将 SQL Server

datetime
值转换为
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 晚四个小时(现在,在美国东部时区,夏令时有效):

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

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

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

sql-server internationalization sql-server-2008-r2
5个回答
84
投票

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

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

AT TIME ZONE
会考虑夏令时在转换日期时是否生效。即使它在 “东部标准时间” 中显示“标准”,它也会为您提供夏令时:

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

总而言之
现在我们可以有一个更好的函数将日期时间转换为日期时间偏移:

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)

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

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

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

3
投票
可能值得创建一个函数;

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;
    

您可以将
AT TIME ZONE


0
投票
的方法。

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

这在

OData v4

 交易中变得尤为明显,它需要 
datetimeoffset

我认为您必须将 
DATEPART(TZOFFSET,SYSDATETIMEOFFSET())

乘以

-2
投票
才能获得正确的时区偏移量。我认为,如果您位于东部时区,时区偏移量应该是 +4:00 而不是 -4:00。是从我的本地服务器到 UTC 的偏移量还是从 UTC 到我的本地服务器的偏移量?

© www.soinside.com 2019 - 2024. All rights reserved.