开发者

sql timezone calculation

开发者 https://www.devze.com 2022-12-16 05:26 出处:网络
I have a table which stores the storecodes and their timezone. Now based on a given local date, I need to know if that date converted to stores local date was a in a weekend or not. Now I already know

I have a table which stores the storecodes and their timezone. Now based on a given local date, I need to know if that date converted to stores local date was a in a weekend or not. Now I already know how to get the weekend part. I am struggling with the conversion. I am actually confused. My table has for example the following two values:

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

Our sql server is located in LA. I used the following code to get the UTC date:

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

If I understood everything correct, I can now simply add the required hours to the @UTCDate to get the @UTCDate of that local timezone, correct?

For frankfurt it would be:

print DATEADD(HOUR, 1, @UTCDate)

Now this returns me the UTCDate for Frankfurt. How would I get the local date of Frankfurt though?

Edit: I am using Sql 2005.

Edit2: Complete example that is still confusing to me:

DECLARE @LocalDate DATETIME, @UTCDate DATETIME
SET @LocalDate = GetDate()
-- convert local date to utc date
SET @UTCDa开发者_高级运维te = DATEADD(Hour, DATEDIFF(Hour, GETUTCDATE(), GetDate()), @LocalDate)
print GetDate()
print @UTCDate
print DATEADD(HOUR, 1, @UTCDate)

Output:

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

Now does this mean, that if its 12:32PM in LA, then its 5:32AM in Franfurt? That seems be incorrect though. It should be 9:32PM in Franfurt.


You shouldn't start with local time. Start directly with UTC time:

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

Frankfurt is one hour ahead of UTC (UTC + 1) when summer time is not in effect so you add one hour:

print DATEADD(HOUR, 1, @UTCDate);

Remember that time zones are not on 60 minutes intervals, Mumbai is UTC + 5:30 and Nepal is UTC + 5:45. You must also account for daylight savings, and those change regularly. Argentine for instance opts to use the daylight on a year-by-year basis based on the ammount of water stored in its hydro power plants.

To sum up: always use UTC and leave the localisation of time to the client display and reporting.


If you have a UTCDate, it is the same for all timezones... I.e., when it's 1 am UTC in New York, it is also 1 am UTC in Frankfort. To get local time for any timnezone just add the offset (that's the value you have in your table) from the UTC DateTime... i.e., when it's 1 AM UTC, it's 2 am local in Frankfort. To remember whether to add or subtract, just remember that its always Earlier East.


Here is a SQL-Only implementation I recently put together you can use (Forums suggest that CLR is the only method since TSQL is needlessly complicated to achieve this in - not really afaik). I implemented via an inline function which avoids RBAR (You can profile and test this to confirm).

Performance is great even over old-school Distributed Partitioned Views too. Make sure your indexing is good for it, even on the string manipulations on the DateTime Fields (To bypass the Year DatePart dependencies) I get the desired seeks. Some of the underlying partitioned tables are over 80GB in size.

Of course, you will need to add your timezone rows as you need and remember to keep the daylight savings start and end dates updated (They can change). In both cases of timezone and daylight savings, offsets are in minutes so this works for all scenarios I have bumped into so far.

Lastly, the Daylight savings offset is always a positive number, note the function caters for this to suite the rule of thumb (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
0

精彩评论

暂无评论...
验证码 换一张
取 消