sql时区计算

时间:2010-01-11 20:20:08

标签: sql sql-server sql-server-2005 timezone

我有一个存储商店代码及其时区的表格。现在根据给定的本地日期,我需要知道转换为商店本地日期的日期是否是一个周末。现在我已经知道如何度过周末。我正在努力进行转换。我其实很困惑。我的表有例如以下两个值:

Store / TimeZone(Standard)
100 / 1 (This is frankfurt)
200 / 2 (This is tel aviv)

我们的sql server位于洛杉矶。我使用以下代码来获取UTC日期:

DECLARE @LocalDate DATETIME, @UTCDate DATETIME
SET @LocalDate = GetDate()
-- convert local date to utc date
SET @UTCDate = DATEADD(Hour, DATEDIFF(Hour, GETUTCDATE(), GETDATE()), @LocalDate)

如果我理解了一切正确,我现在可以简单地将所需的小时数添加到@UTCDate以获取当地时区的@UTCDate,对吗?

对于法兰克福,它将是:

print DATEADD(HOUR, 1, @UTCDate)

现在这将返回法兰克福的UTCDate。我怎么能得到法兰克福当地的日期?

编辑:我使用的是Sql 2005.

Edit2:完整的示例仍然让我感到困惑:

DECLARE @LocalDate DATETIME, @UTCDate DATETIME
SET @LocalDate = GetDate()
-- convert local date to utc date
SET @UTCDate = DATEADD(Hour, DATEDIFF(Hour, GETUTCDATE(), GetDate()), @LocalDate)
print GetDate()
print @UTCDate
print DATEADD(HOUR, 1, @UTCDate)

输出:

Jan 11 2010 12:32PM
Jan 11 2010  4:32AM
Jan 11 2010  5:32AM

现在这意味着,如果它在洛杉矶时间下午12:32,那么它在弗兰福特上午5:32?这似乎是不正确的。应该是法兰克福的下午9:32。

3 个答案:

答案 0 :(得分:4)

你不应该从当地时间开始。直接从UTC时间开始:

DECLARE  @UTCDate DATETIME 
SET @UTCDate = GETUTCDATE();

法兰克福比UTC(UTC + 1)提前一小时夏令时无效所以你添加一小时:

print DATEADD(HOUR, 1, @UTCDate);

请记住,时区间隔不是60分钟,孟买是UTC + 5:30,尼泊尔是UTC + 5:45。您还必须考虑夏令时,并定期更改。例如,阿根廷选择根据其水电站储存的水量逐年使用日光。

总结:始终使用UTC并将时间本地化留给客户端显示和报告。

答案 1 :(得分:2)

如果你有一个UTCDate,那么对于所有时区都是一样的...即,当它在纽约的UTC时间凌晨1点时,它也是法兰克福的UTC时间凌晨1点。要获得任何timnezone的本地时间,只需从UTC DateTime添加偏移量(即您在表中的值)...即,当它在UTC时间上午1点时,它在法兰克福当地时间凌晨2点。要记住是添加还是减去,请记住它始终 早期东部

答案 2 :(得分:0)

这是我最近放在一起的SQL-Only实现,你可以使用(论坛建议CLR是唯一的方法,因为TSQL实现这一点是不必要的复杂 - 不是真正的afaik)。我通过内联函数实现了避免RBAR(您可以对其进行分析和测试以确认)。

即使是老式的分布式分区视图,性能也很出色。 确保您的索引对它有好处,即使在DateTime字段上的字符串操作(绕过Year DatePart依赖项)我得到了所需的搜索。一些底层分区表的大小超过80GB。

当然,您需要根据需要添加时区行,并记住保持夏令时的开始和结束日期更新(它们可以更改)。 在时区和夏令时这两种情况下,偏移都是几分钟,所以这适用于我到目前为止遇到的所有情况。

最后,夏令时偏移总是一个正数,请注意该功能可以满足这一要求(Spring Forward,Fall Back)

If Not Exists (Select Name from sys.objects where name = 'tblTimeZones' and type = 'U')
        Begin
        Create Table tblTimeZones(
            [ID] Int Identity (0,1) NOT NULL,
            [UserID] Int NOT NULL,
            [Description] NVarchar(128) NOT NULL,
            [TZ_OffSet_Mins] Int NOT NULL,
            [Use_DST] Bit NOT NULL,
            [DST_AddOffSet] Int NOT NULL,
            [DST_StartDate] DateTime NOT NULL Constraint DF_DST_StartDate Default ('1900-01-01 00:00:00.000'),
            [DST_EndDate] DateTime NOT NULL Constraint DF_DST_EndDate Default ('1900-01-01 00:00:00.000'),
            Constraint PK_tblTimeZones Primary Key NonClustered (ID),
            Constraint UQ_tblTimeZones_Description Unique Clustered ([Description])
        )
        End
        Go

    If Exists (Select Name from sys.objects where name = 'fncV1_iCalcDateInTimeZone' and type = 'IF')
    Begin
        Drop Function fncV1_iCalcDateInTimeZone
    End
    Go

    Create Function fncV1_iCalcDateInTimeZone
    (
        @UserID Int, @DateAndTime DateTime, @EntID Int
    )
        Returns Table
        With SchemaBinding
    As

        Return (

            Select TZDateAndTime =

                DateAdd(
                    mi, 
                    tz.TZ_OffSet_Mins +
                    -- Daylight Savings STARTS earlier in the Year than Ends (So, Northern Hemisphere), In Daylight Savings Time Period and Daylight Savings In Use
                        Case when 
                            tz.Use_DST = 1 
                            And SubString(Convert(Varchar(23),tz.DST_StartDate,21), 6, 18) < SubString(Convert(Varchar(23),tz.DST_EndDate,21), 6, 18)

                            And SubString(Convert(Varchar(23),@DateAndTime,21), 6, 18) >= SubString(Convert(Varchar(23),tz.DST_StartDate,21), 6, 18) 
                            And SubString(Convert(Varchar(23),@DateAndTime,21), 6, 18) < SubString(Convert(Varchar(23),tz.DST_EndDate,21), 6, 18) 

                        then tz.DST_AddOffSet 
                        Else 0 
                        End
                    +
                    -- Daylight Savings STARTS later in the Year than Ends (So, Southern Hemisphere), In Daylight Savings Surround Period
                        Case when 
                            tz.Use_DST = 1
                            And SubString(Convert(Varchar(23),tz.DST_StartDate,21), 6, 18) > SubString(Convert(Varchar(23),tz.DST_EndDate,21), 6, 18)
                            And 
                            (
                                SubString(Convert(Varchar(23),@DateAndTime,21), 6, 18) >= SubString(Convert(Varchar(23),tz.DST_StartDate,21), 6, 18)
                                Or 
                                SubString(Convert(Varchar(23),@DateAndTime,21), 6, 18) < SubString(Convert(Varchar(23),tz.DST_EndDate,21), 6, 18)
                            )
                        then tz.DST_AddOffSet
                        Else 0
                        End
                    ,@DateAndTime
                )

            From dbo.tblSomeEntityTable rd
            Inner Join dbo.tblBranch b on rd.BranchID = b.ID
            Inner Join dbo.tblUsers u on u.ID = @UserID
            Inner Join dbo.tblTimeZones tz on tz.ID = case when u.UserTZOverBranchTZ = 1 then u.TimeZoneID else b.TimeZoneID End
            Where 
                rd.ID           = Case when ISNULL(@EntID, -1)        = -1 then rd.ID           else @EntID End
        )

    Go