从SQL 2005 Server访问TimeZoneInfo

时间:2009-03-05 12:37:11

标签: asp.net sql sql-server clr timezone

.NET TimeZoneInfo类很棒,我认为它可以解决我在SQL 2005数据库中记录来自多个时区的数据的所有问题。

要将数据库中的UTC日期时间转换为任何其他时区,我只需使用TimeZoneInfo.FindSystemTimeZoneById()将时区转换为TimeZoneInfo类,然后调用TimeZoneInfo.ConvertTimeFromUtc()。辉煌!我只是从SQL .NET CLR中调用它!

但是...... TimeZoneInfo的主机保护属性为MayLeakOnAbort。

当我使用VS 2008创建SQL函数或存储过程时,我甚至看不到system.TimeZoneInfo类永远不会使用它。我还假设即使我能以某种方式引用TimeZoneInfo类,如果我尝试在SQL Sever 2005中注册程序集,我可能会得到某种安全性异常。

帮助!有没有办法从SQL Server 2005访问TimeZoneInfo类及其所有的财富?

注意:我刚刚在第一个答案之后添加了这个警告:

我们在世界各地都有网站。我们需要在数据库中存储本地时间和UTC时间,以防止可能需要在站点级别进行趋势分析的事件。趋势可能包括一年超过52,000个数据点,因此,为了提高效率,我不能只在数据库中存储UTC时间并转换客户端上的每个数据点。因此,我需要能够在数据库中将任何时区的本地时间转换为UTC时间。

7 个答案:

答案 0 :(得分:3)

我刚刚在SQL 2008数据库上完成了这项工作。

首先,我必须将数据库设置为值得信任并验证所有者是否正确。

use [myDB]
go
alter database [myDB] set trustworthy on
go

exec sp_changedbowner 'sa'
go

接下来,我创建了一个.NET解决方案

Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
Imports System.Collections.ObjectModel
Imports System.Runtime.InteropServices

Partial Public Class StoredProcedures
    <Microsoft.SqlServer.Server.SqlProcedure()> _
    Public Shared Sub sp_ConvertTime(ByVal UTCTime As DateTime, ByVal ZoneID As String, <Out()> ByRef Output As DateTime)
    Dim sp As SqlPipe = SqlContext.Pipe

    Dim ConvertedTime As DateTime
    Dim tzUTC = TimeZoneInfo.FindSystemTimeZoneById("UTC")
    Dim tzNew = TimeZoneInfo.FindSystemTimeZoneById(ZoneID)

    ConvertedTime = TimeZoneInfo.ConvertTime(UTCTime, tzUTC, tzNew)

    Output = ConvertedTime
    sp.Send(ConvertedTime)

    ConvertedTime = Nothing
    tzUTC = Nothing
    tzNew = Nothing
    sp = Nothing

End Sub
End Class

在部署之前,我将权限级别设置为不安全

接下来,我将其部署出来,检查了“输出”窗口中的“构建错误”并更正了这些错误。

这是SQL测试

DECLARE @UTCTime datetime
DECLARE @ZoneID varchar(21)
DECLARE @NewTime datetime

SET @UTCTime = GETUTCDATE()
SET @ZoneID = 'Central Standard Time'

exec sp_ConvertTime @UTCTime, @ZoneID, @NewTime OUTPUT
select @NewTime AS NewTime

答案 1 :(得分:1)

我不知道您是否可以从SQL访问TimeZoneInfo类,但通常认为在数据库中坚持使用UTC是很好的做法,客户端会在当地时间进行转换。

因此,每次写入数据库时​​,都会将本地时间转换为UTC,每次从数据库读取时,都会将UTC转换为本地时间。

编辑:根据我对问题的理解,我仍然建议的解决方案是:

1)将日期以UTC格式存储在数据库中。 2)计算客户端的本地时间。

2可以通过多种方式完成。建议的方法是在DataColumn类(*)中将DateTimeMode设置为Local(或Utc)。如果您需要本地时间的报告,请使用本地,如果您需要UTC,请使用UTC。

(*)请注意,Visual Studio中的设计器存在问题,请参阅博文:http://bethmassi.blogspot.com/2006/01/serializing-data-across-time-zones-in.html, 错误报告:http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=96118

答案 2 :(得分:1)

我遇到了同样的问题,因为我想在报告服务正在使用的查询中转换本地和UTC。我经历了与你正在经历的相同的挣扎。我的解决方案......

我开始编写一个独立的应用程序,它通过TimeZoneInfo对象并将条目写入我的数据库中的TimeZoneInfo表。我在开始年份和结束年份之间存储了每年的所有偏移量(包括夏令时偏移量)(这些是独立应用程序的参数)。

从这个表中,我可以创建一些sql函数,这些函数将在utc或local和timezone中取一个日期,使用TimeZoneInfo查找表来获得正确的年份和时区的偏移量,并返回日期时间转换为UTC或本地。

不幸的是,我还没有完成。我不得不创建一个CLR函数,该函数使用一个对SQL Server是安全的库(与TimeZoneInfo对象不同)返回系统的当前时区。我目前无法访问我的代码,但我相信我使用了TimeZone对象。

http://msdn.microsoft.com/en-us/library/system.timezone_members.aspx

总而言之,我有一个CLR函数返回系统的时区,这个应用程序生成一个时区查找表,其中包含多年的DLS特定信息。我用一个存储过程来完成所有这些操作,该存储过程包含一个时区和一个转换日期,并且从那以后它一直很好用。

我知道这是一项巨大的工作,可以做一些看起来非常简单的事情,但它以安全的方式完成了工作。

答案 3 :(得分:1)

这是一个解决方案:

  1. 创建一个CLR存储过程或UDF,它包装了TimeZoneInfo类的功能。请遵循以下指南:http://www.codeproject.com/KB/cs/CLR_Stored_Procedure.aspx
  2. TimeZoneInfo需要.NET 3.5。但是,System.Core v3.5将不会在Sql Server 2005中通过验证。因此,您必须为System.Core执行CREATE ASSEMBLY。查看详细信息:http://weblogs.asp.net/paulomorgado/archive/2009/06/13/playing-with-sql-server-clr-integration-part-iv-deploying-to-sql-server-2005.aspx
  3. 请注意,您需要将System.Core注册为UNSAFE ...因此DBA可能会遇到问题。

    此外,即使您部署到Sql Server 2008(包括.NET 3.5),您的自定义程序集也必须是UNSAFE,因为它使用来自TimeZoneInfo的不安全方法: http://social.msdn.microsoft.com/Forums/en/sqlnetfx/thread/d0515862-eb87-4a13-bab4-0e343983823a

    我尝试了这个并得到了关于MayLeakOnAbort的消息。

    如果UNSAFE在您的环境中没问题,您应该可以这样做。

答案 4 :(得分:0)

我们一直在寻找这个,但一直没能找到一个好方法。我认为这是在SQL 2008中要添加的内容列表中,如果我记得从一段时间后再看到它。

答案 5 :(得分:0)

您是否在Channel9上查看了这篇文章?似乎在CLR功能中做你正在寻找的东西......虽然我认为你不会得到你想要访问的所有好东西......但这是一个开始。

http://channel9.msdn.com/playground/Sandbox/139123/

问题是该线程中的一个海报提到的是因为p / invoke它是一个不安全的程序集。

答案 6 :(得分:0)

经过多年的同样问题的斗争,我终于决定为SQL Server Time Zone Support建立一个解决方案。该项目使用标准的IANA时区as listed here

例如:

SELECT Tzdb.UtcToLocal('2015-07-01 00:00:00', 'America/Los_Angeles')

我意识到这并不是完全所要求的内容,但我认为它同样可以很好地解决问题。