我正在做一些SQL选择查询,并希望将我的UTC日期时间列转换为本地时间,以便在我的查询结果中显示为本地时间。注意,我不希望通过代码进行这种转换,而是当我对我的数据库进行手动和随机SQL查询时。
当前回答
我没有发现任何这些示例有助于将日期时间存储为UTC到指定时区(不是服务器的时区,因为Azure SQL数据库以UTC运行)中的日期时间。我是这样处理的。它并不优雅,但它很简单,无需维护其他表就能给出正确答案:
select CONVERT(datetime, SWITCHOFFSET(dateTimeField, DATEPART(TZOFFSET,
dateTimeField AT TIME ZONE 'Eastern Standard Time')))
其他回答
下面的版本考虑了夏令时、UTC抵消,并且没有锁定到特定的年份。
---------------------------------------------------------------------------------------------------
--Name: udfToLocalTime.sql
--Purpose: To convert UTC to local US time accounting for DST
--Author: Patrick Slesicki
--Date: 3/25/2014
--Notes: Works on SQL Server 2008R2 and later, maybe SQL Server 2008 as well.
-- Good only for US States observing the Energy Policy Act of 2005.
-- Function doesn't apply for years prior to 2007.
-- Function assumes that the 1st day of the week is Sunday.
--Tests:
-- SELECT dbo.udfToLocalTime('2014-03-09 9:00', DEFAULT)
-- SELECT dbo.udfToLocalTime('2014-03-09 10:00', DEFAULT)
-- SELECT dbo.udfToLocalTime('2014-11-02 8:00', DEFAULT)
-- SELECT dbo.udfToLocalTime('2014-11-02 9:00', DEFAULT)
---------------------------------------------------------------------------------------------------
ALTER FUNCTION udfToLocalTime
(
@UtcDateTime AS DATETIME
,@UtcOffset AS INT = -8 --PST
)
RETURNS DATETIME
AS
BEGIN
DECLARE
@PstDateTime AS DATETIME
,@Year AS CHAR(4)
,@DstStart AS DATETIME
,@DstEnd AS DATETIME
,@Mar1 AS DATETIME
,@Nov1 AS DATETIME
,@MarTime AS TIME
,@NovTime AS TIME
,@Mar1Day AS INT
,@Nov1Day AS INT
,@MarDiff AS INT
,@NovDiff AS INT
SELECT
@Year = YEAR(@UtcDateTime)
,@MarTime = CONVERT(TIME, DATEADD(HOUR, -@UtcOffset, '1900-01-01 02:00'))
,@NovTime = CONVERT(TIME, DATEADD(HOUR, -@UtcOffset - 1, '1900-01-01 02:00'))
,@Mar1 = CONVERT(CHAR(16), @Year + '-03-01 ' + CONVERT(CHAR(5), @MarTime), 126)
,@Nov1 = CONVERT(CHAR(16), @Year + '-11-01 ' + CONVERT(CHAR(5), @NovTime), 126)
,@Mar1Day = DATEPART(WEEKDAY, @Mar1)
,@Nov1Day = DATEPART(WEEKDAY, @Nov1)
--Get number of days between Mar 1 and DST start date
IF @Mar1Day = 1 SET @MarDiff = 7
ELSE SET @MarDiff = 15 - @Mar1Day
--Get number of days between Nov 1 and DST end date
IF @Nov1Day = 1 SET @NovDiff = 0
ELSE SET @NovDiff = 8 - @Nov1Day
--Get DST start and end dates
SELECT
@DstStart = DATEADD(DAY, @MarDiff, @Mar1)
,@DstEnd = DATEADD(DAY, @NovDiff, @Nov1)
--Change UTC offset if @UtcDateTime is in DST Range
IF @UtcDateTime >= @DstStart AND @UtcDateTime < @DstEnd SET @UtcOffset = @UtcOffset + 1
--Get Conversion
SET @PstDateTime = DATEADD(HOUR, @UtcOffset, @UtcDateTime)
RETURN @PstDateTime
END
GO
这可以在没有函数的情况下完成。下面的代码将把UTC时间转换为考虑夏令时的山地时间。相应地调整所有的-6和-7数字到您的时区(即对于EST,您将分别调整为-4和-5)
--Adjust a UTC value, in the example the UTC field is identified as UTC.Field, to account for daylight savings time when converting out of UTC to Mountain time.
CASE
--When it's between March and November, it is summer time which is -6 from UTC
WHEN MONTH ( UTC.Field ) > 3 AND MONTH ( UTC.Field ) < 11
THEN DATEADD ( HOUR , -6 , UTC.Field )
--When its March and the day is greater than the 14, you know it's summer (-6)
WHEN MONTH ( UTC.Field ) = 3
AND DATEPART ( DAY , UTC.Field ) >= 14
THEN
--However, if UTC is before 9am on that Sunday, then it's before 2am Mountain which means it's still Winter daylight time.
CASE
WHEN DATEPART ( WEEKDAY , UTC.Field ) = 1
AND UTC.Field < '9:00'
--Before 2am mountain time so it's winter, -7 hours for Winter daylight time
THEN DATEADD ( HOUR , -7 , UTC.Field )
--Otherwise -6 because it'll be after 2am making it Summer daylight time
ELSE DATEADD ( HOUR , -6 , UTC.Field )
END
WHEN MONTH ( UTC.Field ) = 3
AND ( DATEPART ( WEEKDAY , UTC.Field ) + 7 ) <= DATEPART ( day , UTC.Field )
THEN
--According to the date, it's moved onto Summer daylight, but we need to account for the hours leading up to 2am if it's Sunday
CASE
WHEN DATEPART ( WEEKDAY , UTC.Field ) = 1
AND UTC.Field < '9:00'
--Before 9am UTC is before 2am Mountain so it's winter Daylight, -7 hours
THEN DATEADD ( HOUR , -7 , UTC.Field )
--Otherwise, it's summer daylight, -6 hours
ELSE DATEADD ( HOUR , -6 , UTC.Field )
END
--When it's November and the weekday is greater than the calendar date, it's still Summer so -6 from the time
WHEN MONTH ( UTC.Field ) = 11
AND DATEPART ( WEEKDAY , UTC.Field ) > DATEPART ( DAY , UTC.Field )
THEN DATEADD ( HOUR , -6 , UTC.Field )
WHEN MONTH ( UTC.Field ) = 11
AND DATEPART ( WEEKDAY , UTC.Field ) <= DATEPART ( DAY , UTC.Field )
--If the weekday is less than or equal to the calendar day it's Winter daylight but we need to account for the hours leading up to 2am.
CASE
WHEN DATEPART ( WEEKDAY , UTC.Field ) = 1
AND UTC.Field < '8:00'
--If it's before 8am UTC and it's Sunday in the logic outlined, then it's still Summer daylight, -6 hours
THEN DATEADD ( HOUR , -6 , UTC.Field )
--Otherwise, adjust for Winter daylight at -7
ELSE DATEADD ( HOUR , -7 , UTC.Field )
END
--If the date doesn't fall into any of the above logic, it's Winter daylight, -7
ELSE
DATEADD ( HOUR , -7 , UTC.Field )
END
对于Azure SQL和@@Version >= SQL Server 2016用户,下面是一个使用AT TIME ZONE的简单函数。
CREATE FUNCTION [dbo].[Global_Convert_UTCTimeTo_LocalTime]
(
@LocalTimeZone VARCHAR(50),
@UTCDateTime DATETIME
)
RETURNS DATETIME
AS
BEGIN
DECLARE @ConvertedDateTime DATETIME;
SELECT @ConvertedDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE @LocalTimeZone
RETURN @ConvertedDateTime
END
GO
对于@LocalTimeZone可以采用的值类型,请转到此链接或转到KEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
使用新的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
但clr程序的工作速度快5倍:'-(
请注意,一个时区的偏移量可以更改为冬季时间或夏季时间。例如
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'
结果:
2017-02-08 09:00:00.000 -05:00
2017-08-08 09:00:00.000 -04:00
你不能只是添加常数偏移量。
最简单的答案并不总是在底部,但这一次是,并且可以在上面的评论中看到。 使用您自己的“AT TIME ZONE”来捕获列/数据字段的TzOffset,而不是当前的SYSDATETIME。 在下面的数据中,2个查询,一个关于feb数据(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
结果:
所以这是一个T-SQL (SQL Server Answer),不需要函数的storedproc。
推荐文章
- 如何在Ruby On Rails中使用NuoDB手动执行SQL命令
- 查询JSON类型内的数组元素
- 确定记录是否存在的最快方法
- 获得PostgreSQL数据库中当前连接数的正确查询
- 在SQL选择语句Order By 1的目的是什么?
- 从现有模式生成表关系图(SQL Server)
- 我如何循环通过一组记录在SQL Server?
- 数据库和模式的区别
- 如何在SQL Server中一次更改多个列
- 如何从命令行通过mysql运行一个查询?
- 外键约束可能导致循环或多条级联路径?
- 使用LIMIT/OFFSET运行查询,还可以获得总行数
- 当恢复sql时,psql无效命令\N
- 货币应该使用哪种数据类型?
- 如何选择每一行的列值不是独特的