我正在做一些SQL选择查询,并希望将我的UTC日期时间列转换为本地时间,以便在我的查询结果中显示为本地时间。注意,我不希望通过代码进行这种转换,而是当我对我的数据库进行手动和随机SQL查询时。
当前回答
没有一种简单的方法能以正确而通用的方式做到这一点。
首先,必须理解偏移量取决于所讨论的日期、时区和夏令时。 GetDate()-GetUTCDate只提供今天服务器TZ的偏移量,这是不相关的。
我只见过两种有效的解决方案,我已经搜索了很多。
1)一个自定义SQL函数,包含几个基本数据表,如每个TZ的时区和夏令时规则。 工作,但不是很优雅。我不能发布,因为我没有代码。
编辑:下面是这个方法的一个例子 https://gist.github.com/drumsta/16b79cee6bc195cd89c8
2)添加一个.net程序集到db, .net可以很容易地做到这一点。这工作得很好,但缺点是你需要在服务器级配置几个参数,配置很容易被破坏,例如,如果你恢复数据库。 我使用这个方法,但我不能张贴它,因为我没有自己的代码。
其他回答
我发现当有大量数据时,一次性函数的方法太慢了。因此,我通过连接到一个允许计算小时差的表函数来实现它,它基本上是带有小时偏移量的datetime分段。一年是4行。这个表格函数
dbo.fn_getTimeZoneOffsets('3/1/2007 7:00am', '11/5/2007 9:00am', 'EPT')
将返回这个表:
startTime endTime offset isHr2
3/1/07 7:00 3/11/07 6:59 -5 0
3/11/07 7:00 11/4/07 6:59 -4 0
11/4/07 7:00 11/4/07 7:59 -5 1
11/4/07 8:00 11/5/07 9:00 -5 0
它确实考虑了夏时制。下面是它如何使用的示例,完整的博客文章在这里。
select mt.startTime as startUTC,
dateadd(hh, tzStart.offset, mt.startTime) as startLocal,
tzStart.isHr2
from MyTable mt
inner join dbo.fn_getTimeZoneOffsets(@startViewUTC, @endViewUTC, @timeZone) tzStart
on mt.startTime between tzStart.startTime and tzStart.endTime
没有一种简单的方法能以正确而通用的方式做到这一点。
首先,必须理解偏移量取决于所讨论的日期、时区和夏令时。 GetDate()-GetUTCDate只提供今天服务器TZ的偏移量,这是不相关的。
我只见过两种有效的解决方案,我已经搜索了很多。
1)一个自定义SQL函数,包含几个基本数据表,如每个TZ的时区和夏令时规则。 工作,但不是很优雅。我不能发布,因为我没有代码。
编辑:下面是这个方法的一个例子 https://gist.github.com/drumsta/16b79cee6bc195cd89c8
2)添加一个.net程序集到db, .net可以很容易地做到这一点。这工作得很好,但缺点是你需要在服务器级配置几个参数,配置很容易被破坏,例如,如果你恢复数据库。 我使用这个方法,但我不能张贴它,因为我没有自己的代码。
对于任何仍然试图解决这个问题的人,这里有一个在SQL Server 2017中工作的概念证明
declare
@StartDate date = '2020-01-01'
;with cte_utc as
(
select
1 as i
,CONVERT(datetime, @StartDate) AS UTC
,datepart(weekday, CONVERT(datetime, @StartDate)) as Weekday
,datepart(month, CONVERT(datetime, @StartDate)) as [Month]
,datepart(YEAR, CONVERT(datetime, @StartDate)) as [Year]
union all
Select
i + 1
,dateadd(d, 1, utc)
,datepart(weekday, CONVERT(datetime, dateadd(d, 1, utc))) as Weekday
,datepart(month, CONVERT(datetime, dateadd(d, 1, utc))) as [Month]
,datepart(YEAR, CONVERT(datetime, dateadd(d, 1, utc))) as [Year]
from
cte_utc
where
(i + 1) < 32767
), cte_utc_dates as
(
select
*,
DENSE_RANK()OVER(PARTITION BY [Year], [Month], [Weekday] ORDER BY Utc) WeekDayIndex
from
cte_utc
), cte_hours as (
select 0 as [Hour]
union all
select [Hour] + 1 from cte_hours where [Hour] < 23
)
select
d.*
, DATEADD(hour, h.Hour, d.UTC) AS UtcTime
,CONVERT(datetime, DATEADD(hour, h.Hour, d.UTC) AT TIME ZONE 'UTC' AT TIME ZONE 'Central Standard Time') CST
,CONVERT(datetime, DATEADD(hour, h.Hour, d.UTC) AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time') EST
from
cte_utc_dates d, cte_hours h
where
([Month] = 3 and [Weekday] = 1 and WeekDayIndex = 2 )-- dst start
or
([Month] = 11 and [Weekday] = 1 and WeekDayIndex = 1 )-- dst end
order by
utc
OPTION (MAXRECURSION 32767)
GO
这样就可以获得与夏令时同步的服务器时间
declare @dt datetime
set @dt = getutcdate() -- GMT equivalent
sysdatetimeoffset会考虑夏令时
select [InputTime] = @dt
, [LocalTime2] = dateadd(mi, datediff(mi, sysdatetimeoffset(),getdate()), @dt)
对于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
推荐文章
- 如何在Ruby On Rails中使用NuoDB手动执行SQL命令
- 查询JSON类型内的数组元素
- 确定记录是否存在的最快方法
- 获得PostgreSQL数据库中当前连接数的正确查询
- 在SQL选择语句Order By 1的目的是什么?
- 从现有模式生成表关系图(SQL Server)
- 我如何循环通过一组记录在SQL Server?
- 数据库和模式的区别
- 如何在SQL Server中一次更改多个列
- 如何从命令行通过mysql运行一个查询?
- 外键约束可能导致循环或多条级联路径?
- 使用LIMIT/OFFSET运行查询,还可以获得总行数
- 当恢复sql时,psql无效命令\N
- 货币应该使用哪种数据类型?
- 如何选择每一行的列值不是独特的