Устранить неоднозначные времена для сохраненных значений даты в базе данных

1

В Великобритании часы были возвращены 1 час 26 октября 2014 года в 02:00. В этом случае каждый в Великобритании наблюдал время между 01:00 и 01:59 дважды в этот день.

Предположим, что у меня есть приложение.NET, где дата и время очень важны в конкретном часовом поясе. В этом случае, что я должен делать, когда вижу 26 октября 01:00 в своей системе хранения данных? Должен ли я хранить часовой пояс, такой как BST и GMT, или мне нужно сохранить значения смещения?

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

Какова наилучшая практика для этих случаев, особенно в.NET-приложениях (были бы оценены как типы значений BCL DateTime, так и решения NodaTime)?

Редактировать 1

Есть несколько предложений для сохранения в UTC. Однако представьте, что мне нужно знать о часовом поясе в этом приложении. Примером может служить заявка на билет. Вы вылетели из одного часового пояса и приземлились на другом. Даже если нам удастся ускорить конвертацию даты и времени в UTC и сохранение часового пояса поблизости, я все еще не могу найти правильное решение для неоднозначных времен, что является целым пунктом этого вопроса. Нижеследующее отлично работает (типы значений BCL, а не NodaTime):

    static void ConvertToAndFromUtcCorrectly()
    {
        const string ukTimeZoneId = "GMT Standard Time";
        TimeZoneInfo ukTimeZone = TimeZoneInfo.FindSystemTimeZoneById(ukTimeZoneId);

        // 3/30/2014 2:00:00 AM (BST)
        DateTime aDateTimeInDst = new DateTime(2014, 3, 30, 2, 0, 0);

        // 3/30/2014 1:00:00 AM (UTC)
        DateTime aDateTimeInDstUtc = TimeZoneInfo.ConvertTimeToUtc(aDateTimeInDst, ukTimeZone);

        // 3/30/2014 2:00:00 AM (BST)
        DateTime backToUkTime = TimeZoneInfo.ConvertTimeFromUtc(aDateTimeInDstUtc, ukTimeZone);
    }

Ниже один из них терпит неудачу:

    static void ConvertToAndFromUtcWrongly()
    {
        const string ukTimeZoneId = "GMT Standard Time";
        TimeZoneInfo ukTimeZone = TimeZoneInfo.FindSystemTimeZoneById(ukTimeZoneId);

        // 10/26/2014 1:00:00 AM (This is meant to be BST, not GMT)
        DateTime ambiguousUkDateTime = new DateTime(2014, 10, 26, 1, 0, 0);

        // 10/26/2014 1:00:00 AM (UTC)
        DateTime ambiguousUkDateTimeUtc = TimeZoneInfo.ConvertTimeToUtc(ambiguousUkDateTime, ukTimeZone);

        // 10/26/2014 1:00:00 AM (WHAT?)
        DateTime backToUkTime = TimeZoneInfo.ConvertTimeFromUtc(ambiguousUkDateTimeUtc, ukTimeZone);
    }

Изменить 2

Чтобы быть более конкретным с моим вопросом здесь: Согласно моему вышеприведенному образцу, как вы можете хранить 26 октября 2014 года 01:00 (что является неоднозначным временем) в системе хранения данных и читать/записывать эти данные в.NET-приложение?

  • 0
    Проверьте эту ссылку SO - Летнее время и лучшие часовые пояса
  • 1
    @TonyStark Как совет «Сохранять глобально, отображать локально» логичен, когда вам нужно знать разные часовые пояса. Думай о рейсах. Вы вылетаете из одного часового пояса и приземляетесь в другом. Как бы вы сохранили полетную посадку и взлетели во всем мире?
Теги:
datetime
timezone
nodatime

3 ответа

2
Лучший ответ

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

Прошлое настоящее

В прошлом или настоящие события, вы можете выбрать между DateTimeOffset или UTC основой DateTime. Хотя оба они представляют собой конкретный однозначный момент времени, DateTimeOffset также отслеживает локальное значение времени. Это хорошо для того, чтобы знать, как было утром или вечером, где была записана метка времени. Если вам не нужны такие вещи, используйте DateTime основе UTC. (Подробнее об этом в DateTime vs DateTimeOffset)

Послание "Персистский глобально, отображение локально" хорошо относится к прошлым/текущим событиям. Вы можете преобразовать временную метку в локальный часовой пояс пользователя или вы можете преобразовать ее в любой часовой пояс, который вы хотите. Это полезно, когда контекст зрителя не обязательно совпадает с контекстом, в котором было записано время.

Пример использования вашей авиакомпании:

  • Вы можете записать время, которое рейс вылетел из Лондона в качестве DateTimeOffset 2014-10-26T01:00:00+01:00. Как вы указали, час часа повторяется в этот день из -за переходного периода. Но поскольку мы зафиксировали смещение, мы знаем, что это время было первым появлением 01:00.

  • Мы могли бы также записать этот же момент, как UTC-based DateTime 2014-10-26T00:00:00Z. Единственная потерянная информация - это фактическое местное время суток.

  • Если мы знаем (отдельно), что данные возникли из часового пояса "Europe/London" (или с использованием идентификатора "GMT Standard Time" если вы используете TimeZoneInfo), тогда мы могли бы заявить, что на этот раз в BST (UTC +01: 00), а не в GMT (UTC + 00: 00).

  • Скажите, что полет длится 7 часов и приземляется в Нью-Йорке. Это будет 2014-10-26T07:00:00Z. Он также может быть выражен с помощью DateTimeOffset 2014-10-26T08:00:00+01:00, но это смещение не подходит для Нью-Йорка. Поэтому мы применяем часовой пояс назначения "America/New_York" (или идентификатор "Восточное стандартное время" с TimeZoneInfo) и получаем 2014-10-26T03:00:00-04:00Z. Теперь вы знаете, что полет прибывает в 3:00 по местному времени в Нью-Йорке.

Будущее

Будущие события намного сложнее по нескольким причинам:

  • Вы не обязательно знаете, какой будет смещение. Вы знаете только то, что, по вашему мнению, будет, исходя из известных в настоящее время правил часового пояса. Некоторые правительства меняют свои временные смещения или правила DST с очень коротким уведомлением, что не всегда позволяет время для обновления систем. В системах планирования пребывание в верхней части обновлений часового пояса абсолютно критично и легко упускается из виду.

  • Большинство событий, связанных с человеком, не могут быть запланированы UTC - особенно повторяющиеся события. Представьте себе ежедневный будильник, чтобы разбудить вас в 7:00 каждое утро. Если вы планируете по UTC, то после перехода он начнет выходить в 6:00 или 8:00 (в зависимости от того, когда вы сделали первоначальное преобразование и имеете ли вы дело с переходом весны или падения).

    • Это исключение. Если событие следует за таким правилом, как "каждый х час" (или меньше), вы можете запланировать по UTC без проблем. Будьте осторожны, однако правила, такие как "каждые х часов в среду", не подходят для этого исключения, потому что даже определение того, является ли среда средой, включает часовые пояса.

  • Лучшим подходом является планирование будущих событий по местному времени, в котором применяется событие. Для этого вы можете использовать DateTime с DateTimeKind.Unspecified.

  • В примере будильника часовой пояс не важен, потому что вы использовали бы какой бы то ни было локальный часовой пояс в настоящий момент (т. TimeZoneInfo.Local). Но в примере авиакомпании вам абсолютно необходимы часовые пояса и дата назначения. Вы должны сохранить полный идентификатор часового пояса в базе данных в виде строки. Имейте в виду, что TimeZoneInfo использует идентификаторы Windows, а идентификаторы типа "Eastern Standard Time" представляют собой EST и EDT.

  • Затем вам нужен способ устранения двусмысленности в случае двусмысленности, созданной переходами спада на летнее время, а также для устранения разрыва, создаваемого переходами с переходом вперед. Обычно это делается алгоритмически или политикой.

    • Например, для будильника было бы нормально использовать первое вхождение (появление дневного света) во время спада и пропустить любые недопустимые значения, создаваемые разрывом пружины.

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

    • Снова подумайте о человеческом факторе. Что бы пассажиры сделали, если бы самолет планировал отправиться в 1:00, а затем выяснили, что это был второй случай. Вероятно, они не были бы счастливы сидеть в аэропорту и ждать. Они также не были бы счастливы, если бы они подумали, что это вторая инстанция, и оказалось, что они оказались первыми, так что они пропустили свой полет. Я буду считать, что многие авиакомпании хотели бы избежать планирования времени отправления в переходный период только для предотвращения несчастных клиентов.

Так что просто подумайте о контексте. Это действительно имеет значение, и нет единого пути, чтобы все было сделано.

  • 0
    Спасибо! Итак, я понимаю, что у Tzdb нет часового пояса как BST, он знает часовой пояс "Европа / Лондон" и есть ли конкретное время в летнее время или нет, верно?
  • 1
    Верный. BST и GMT являются двумя чередующимися сегментами зоны Европа / Лондон. TimeZoneInfo работает так же, только имена были изменены.
0

Вы можете использовать время UTC в своем приложении. Время UTC не имеет летнего времени, поэтому оно полностью линейно.

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

  • 0
    UTC не вариант. Приложение также должно знать часовой пояс. Думай о рейсах. Вы вылетаете из одного часового пояса и приземляетесь в другом.
  • 0
    @tugberk: Если вы хотите отобразить это время в часовом поясе / месте, где оно возникло (а не в местном времени текущего зрителя), вы должны сохранить часовой пояс / место вместе с временем UTC.
Показать ещё 7 комментариев
-1

Как насчет этого для отступления:

Первое появление с 1:00 до 1:59 утра будет по-прежнему совпадать, в то время как второе появление с 1:00 до 1:59 утра может быть переназначено с 13:00 до 13:59 (время квази-PM ) для обычного времени. В военное время второе появление с 0100 по 0159 может быть переназначено с 2400 по 2459. Таким образом, в основное время вы отправляетесь с 13:59 до 2:00, а в период с 2459 до 0200 в военное время.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню