【问题标题】:sql timezone calculationsql时区计算
【发布时间】:2011-01-03 22:01:32
【问题描述】:

我有一个存储商店代码及其时区的表。现在基于给定的本地日期,我需要知道转换为存储本地日期的日期是否在周末。现在我已经知道如何获得周末部分了。我正在为转换而苦苦挣扎。我其实很困惑。例如,我的表有以下两个值:

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 分。

【问题讨论】:

  • 请问什么引擎和版本?
  • 对不起,我忘了提 - sql 2005。

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


【解决方案1】:

这是我最近整理的一个仅 SQL 的实现,您可以使用(论坛建议 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

【讨论】:

  • P.S.我们需要根据用户或报告实体所在的分支机构,根据用户对时区的偏好来实施。必要时将其删除...
【解决方案2】:

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

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

法兰克福比 UTC (UTC + 1) 早一小时当夏令时无效时所以你一小时:

print DATEADD(HOUR, 1, @UTCDate);

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

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

【讨论】:

    【解决方案3】:

    如果您有 UTCDate,则所有时区都相同...即,当纽约的 UTC 时间凌晨 1 点时,法兰克福的时间也是 UTC 时间的凌晨 1 点。要获取任何 timnezone 的本地时间,只需添加 UTC DateTime 的偏移量(即您在表中的值)......即,当它是 UTC 时间凌晨 1 点时,它是法兰克福当地时间凌晨 2 点。要记住是加还是减,只要记住它总是早期东部

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多