UTC и сценарий перехода на летнее время - PullRequest
4 голосов
/ 11 августа 2009

Я использую UTC для хранения данных и значений времени в БД. Значения преобразуются в местное время на клиенте или для часового пояса клиента. Я остановился на этих сценариях из статьи MSDN , в которой отображение времени из UTC создает проблемы при переходе на летнее время.

Кто-то живет на восточном побережье Соединенные Штаты вводят значение как "26 октября 2003 01:10:00 AM".

1) Именно этим утром в связи с летнее время, в 2:00 утра, местные часы сбрасываются на 1:00 утра, создание 25-часового дня. Так как все значения часового времени между 1:00 утра и 2:00 утра происходят дважды на этом конкретное утро - по крайней мере, в большинстве Соединенные Штаты и Канада, компьютер действительно не знает который 1:10 AM имел в виду - тот, который происходит до переключения, или один это происходит через 10 минут после переключатель летнего времени.

2) Аналогично, проблема возникает в весна, когда, на конкретном утро 2:10 AM. Причина в том, что в 2:00 на этом определенное утро, время на местном часы внезапно меняются на 3 часа ночи. Весь 2:00 час никогда не бывает на этот 23-часовой день.

Как вы справились с ситуацией # 1, когда у вас могло быть 4 транзакции, две до переключения и две после переключения при переходе на летнее время? Как отобразить пользователю время для транзакций, так как две последние транзакции могут отображаться раньше, чем первые две транзакции из-за сдвига. Иногда это может оказаться нелогичным, например, в почтовой цепочке.

ДОБАВЛЕНО:

Чтобы добавить дополнительную информацию о контексте, приложения RIA, такие как Silverlight / Flash, запускаемые на клиенте (или любое клиентское приложение, взаимодействующее с сервером через веб-сервис), позволяют пользователю выбирать время доставки или расписание с местным временем компьютера.

Если бы я мог проверить заданное время ввода на недопустимое время, я, вероятно, мог бы предупредить пользователя. Кроме того, для путешественников часовой пояс должен быть найден в определенный момент времени, а не на основе выбора пользователя, поскольку они могут перемещаться между зонами и сохранение своего часового пояса в профиле пользователя не поможет.

Некоторые тестовые образцы C # для оценки времени ввода:

//2:30 am CT to UTC --> 8:30 am  
DateTime dt = new DateTime(2009, 03, 08, 2, 30, 00, DateTimeKind.Local);  

//8:30 am UTC to CT --> 3:30 am.. which is as expected  
DateTime dt1 = new DateTime(2009, 03, 08, 8, 30, 00, DateTimeKind.Utc);  

//check for daylight saving time returns false.. ??  
TimeZoneInfo.Local.IsDaylightSavingTime(dt);  

//check for daylight saving time returns true  
TimeZoneInfo.Local.IsInvalidTime(dt);  

Ответы [ 7 ]

3 голосов
/ 11 августа 2009

Вам необходимо сохранить смещение времени.

В настоящее время время на восточном побережье (формат туда и обратно)

2009-08-11T13:22:13.8493713-04:00

Даже если считается, что восточное побережье находится в -5, в летнее время оно будет в -4.

26 октября в 01:10 время будет

2009-10-26T1:10:00.0000000-04:00

но когда часы выйдут за 02:00 и мы вернемся к обычному времени, ваше время будет

2009-10-26T1:10:00.0000000-05:00

Для обработки смещения .NET, начиная с 2.0sp1, предлагает тип DateTimeOffset. Microsoft SQL Server 2008 также предлагает тип данных datetimeoffset, который поможет вам сохранить это значение. Если вы не используете Microsoft SQL Server 2008, вы можете сохранить дату в виде строки, используя формат туда и обратно:

DateTime.Now.ToString("o")
3 голосов
/ 11 августа 2009

Эти сценарии представляют собой случаи, пропагандирующие использование ТЛЧ. Неважно, что вы отображаете , пока вы сохраняете и сортируете значения в UTC. То есть, если вы правильно используете UTC, проблемы, представленные в этих сценариях, будут решены.

Да, было бы странно видеть такие записи: 12:30, 1:20, 1:10, 3:30, но если так они упорядочены в соответствии с UTC (что на самом деле произошло), я думаю, что это правильный способ сделать это.

ТАК избегает этой проблемы в целом, записывая все в UTC, а затем отображая все в UTC или относительное время (например, "17 минут назад ...").


Если вы ссылаетесь на предоставленные пользователем даты / время, как указано в комментариях, у меня для вас есть плохие новости: это отстой. Я думаю, что самое лучшее и очевидное решение - выбрать правило и использовать его. Если вам действительно нужно справиться с этим идеально, ваш пользовательский интерфейс должен быть расширен, чтобы педантично обрабатывать этот крайний случай, который происходит всего за 1 час в год, а затем только для транзакций, созданных не в реальном времени (потому что, если бы они были реальными -время, вы знаете эквивалент DST).

1 голос
/ 11 августа 2009

Ваш вопрос состоит из двух частей:

  1. Получение пользовательских данных
  2. Отображение данных для пользователей

Эти два должны обрабатываться одинаково, но в некоторых отношениях есть несколько отдельных обходных путей. Что касается 1, самый простой вариант - выяснить, действительно ли вашему бизнесу нужен однозначный способ указать время в конкретный час. Многие приложения просто игнорируют его (когда вы в последний раз использовали селектор даты, который имел для этого положение?) И просто предполагают, что будет достаточно любого последовательного алгоритма угадывания. Вы должны обеспечить защиту (то есть, выдать ошибку), если введен несуществующий час.

Что касается 2, пропущенный час не имеет значения, так как ваша база данных находится в UTC. Повторный час может сбивать с толку, особенно в виде меток времени. Если это того стоит, рассмотрите возможность форматирования строки даты с идентификатором часового пояса, который включает ссылку на смещение летнего времени. В большинстве старых стилей, то есть не-Олсон, названия часовых поясов включают это (EST против EDT, GMT против BST и т. Д.). Этого будет достаточно для устранения неоднозначности в крайнем случае . Это, вероятно, все, что вам нужно, поскольку этот граничный случай может не стоить слишком много испортить дисплей. Если вам нужно немного больше выходных данных, вы также можете отформатировать временную метку со смещением UTC, что сделает переключение очень однозначным в следе временных меток.

1 голос
/ 11 августа 2009

Если у вас есть кто-то, кто вводит данные вручную, удачи, если они не вводят их в UTC. На самом деле не существует «правильного» способа справиться с этим. Если вы имеете дело с датами, которые не были введены пользователем, например, с момента совершения транзакции, просто сохраните их как UTC, и жизнь будет хорошей. :)

1 голос
/ 11 августа 2009

Как отобразить время для пользователя для транзакций, так как две последние транзакции могут отображаться раньше, чем первые две транзакции из-за сдвига.? Иногда это может оказаться нелогичным, например, в почтовой цепочке.

Сортировка по UTC и отображение по местному времени.

Таким образом, пользователь может увидеть такой список:

01:10:00
01:50:00
01:05:00
01:20:00

Либо так, либо покажите и сортируйте их по UTC.

0 голосов
/ 27 сентября 2016

Я решил эту проблему для времени Новой Зеландии, как показано ниже:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:      Vivi Woolford
-- Create date: 27-09-2016
-- Description: This procedure only Works in New Zealand
-- =============================================
CREATE FUNCTION [dbo].[udf_GetLocalTimeFromUTC] 
(   
    @UTCTime DATETIME
)
RETURNS DATETIME
AS
BEGIN
    --Daylight Saving commences on the last Sunday in September, when 2.00am becomes 3.00am. 
    --It ends on the first Sunday in April, when 3.00am becomes 2.00am.
    DECLARE @LocalTime DATETIME 
    DECLARE @OffSet INT = 12

    SELECT @LocalTime = DATEADD(HOUR, @OffSet, @UTCTime)

    IF @LocalTime BETWEEN 
    /*FINISH DAY LIGHT SAVINGS*/
    DATEADD(HOUR, 2, DATEADD(dd, (6-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(@LocalTime)-1900) * 12 + 3,0))%7)),DATEADD(mm,(YEAR(@LocalTime)-1900) * 12 + 3,0)))
    AND     
    /*START DAY LIGHT SAVINGS*/
    DATEADD(HOUR, 2, DATEADD(dd, -1*(DATEPART(dw, DateAdd(day, -1, DateAdd(month, DateDiff(month, 0, @LocalTime)+1, 0)))-1),DateAdd(day, -1, DateAdd(month, DateDiff(month, 0, @LocalTime)+1, 0))))     
    BEGIN
        SELECT @LocalTime = @LocalTime 
    END 
    ELSE
    BEGIN
        SELECT @LocalTime = DATEADD(HOUR, 1, @LocalTime)
    END

    RETURN @LocalTime

END

GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:      Vivi Woolford
-- Create date: 27-09-2016
-- Description: This procedure only Works in New Zealand
-- =============================================
ALTER FUNCTION [dbo].[udf_GetUTCFromLocalTime]
(   
    @LocalTime DATETIME
)
RETURNS DATETIME
AS
BEGIN
    --Daylight Saving commences on the last Sunday in September, when 2.00am becomes 3.00am. 
    --It ends on the first Sunday in April, when 3.00am becomes 2.00am.
    DECLARE @UTCTime DATETIME   
    DECLARE @OffSet INT = 12

    IF @LocalTime BETWEEN 
    /*FINISH DAY LIGHT SAVINGS*/
    DATEADD(HOUR, 2, DATEADD(dd, (6-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(@LocalTime)-1900) * 12 + 3,0))%7)),DATEADD(mm,(YEAR(@LocalTime)-1900) * 12 + 3,0)))
    AND     
    /*START DAY LIGHT SAVINGS*/
    DATEADD(HOUR, 2, DATEADD(dd, -1*(DATEPART(dw, DateAdd(day, -1, DateAdd(month, DateDiff(month, 0, @LocalTime)+1, 0)))-1),DateAdd(day, -1, DateAdd(month, DateDiff(month, 0, @LocalTime)+1, 0))))
    BEGIN
        SELECT @UTCTime = DATEADD(HOUR, -@OffSet, @LocalTime)
    END 
    ELSE 
    BEGIN
        SELECT @UTCTime = DATEADD(HOUR, -1-@OffSet, @LocalTime)
    END

    RETURN @UTCTime

END
go
0 голосов
/ 11 августа 2009

SQL Server имеет новый тип, datetimeoffset, который хорошо справляется с этим, поскольку вы можете иметь одно и то же время с разными смещениями. Для моей базы данных SQL Server 2005 я использую строковый литерал этого типа, nvarchar (25) в форме «ГГГГ-ММ-ДД чч: мм: сс-чч: мм».

Для этого я создал подпрограммы для преобразования этих строк в правильное время.

...