расчет часового пояса - PullRequest
       19

расчет часового пояса

2 голосов
/ 11 января 2010

У меня есть таблица, в которой хранятся коды магазинов и их часовой пояс. Теперь, основываясь на заданной локальной дате, мне нужно знать, была ли эта дата преобразована в локальную дату магазинов в выходные или нет. Теперь я уже знаю, как получить часть выходных. Я борюсь с преобразованием. Я на самом деле запутался. Моя таблица имеет, например, следующие два значения:

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

Наш sql сервер находится в Лос-Анджелесе. Я использовал следующий код для получения даты 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 ]

4 голосов
/ 12 января 2010

Не стоит начинать с местного времени. Начните прямо с UTC времени:

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

Франкфурт на час опережает UTC (UTC + 1) , когда летнее время не действует , поэтому вы добавляете один час:

print DATEADD(HOUR, 1, @UTCDate);

Помните, что часовые пояса не имеют 60-минутных интервалов, Мумбаи - UTC + 5:30, а Непал - UTC + 5:45. Вы также должны учитывать дневные сбережения, и они регулярно меняются. Например, аргентинцы предпочитают использовать дневной свет из года в год, исходя из количества воды, хранящегося на гидроэлектростанциях.

Подводя итог: всегда используйте UTC и оставьте локализацию времени для отображения и отчетности клиента.

2 голосов
/ 11 января 2010

Если у вас есть UTCDate, он одинаков для всех часовых поясов ... То есть, когда в Нью-Йорке 1:00 UTC, во Франкфурте также 1:00 UTC. Чтобы получить местное время для любой тимнезоны, просто добавьте смещение (это значение, которое вы имеете в своей таблице) из UTC DateTime ... то есть, когда это 1:00 UTC, это 2 часа утра во Франкфурте. Чтобы запомнить, нужно ли добавлять или вычитать, просто помните, что его всегда Раньше Восток .

0 голосов
/ 02 июня 2016

Вот реализация SQL-Only, которую я недавно собрал, которую вы можете использовать (форумы предполагают, что CLR - единственный метод, поскольку TSQL неоправданно сложен для достижения этой цели - не совсем так). Я реализовал через встроенную функцию, которая избегает RBAR (Вы можете профилировать и проверить это, чтобы подтвердить).

Производительность великолепна даже по сравнению с распределенными многораздельными представлениями старой школы. Убедитесь, что ваша индексация хороша, даже при работе со строками в полях DateTime (чтобы обойти зависимости DatePart от Year) я получаю желаемые результаты поиска. Некоторые из лежащих в основе секционированных таблиц имеют размер более 80 ГБ.

Конечно, вам нужно будет добавлять строки часовых поясов по мере необходимости, и не забывайте обновлять даты начала и окончания перехода на летнее время (они могут измениться). В обоих случаях для часового пояса и перехода на летнее время смещения выполняются в минутах, поэтому это работает для всех сценариев, с которыми я сталкивался до сих пор.

Наконец, смещение Летнего времени всегда является положительным числом, обратите внимание, что функция учитывает это, чтобы соответствовать эмпирическому правилу (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
...