Как правильно работать с часовым поясом?

Я много читаю о часовом поясе, смещении, UTC, местном времени, функциях JavaScript< /b>, DST, bacon, и я пытаюсь собрать все это вместе, чтобы создать прочную/правильную структуру для моего приложения.

Предположим, мое приложение похоже на StackOverflow.

Вот как я это делаю...

  • Сервер находится в другой стране, поэтому я установил UTC 00:00.
  • Я сохраняю дату как DateTimeOffset.
  • Я не сохраняю TimeZoneID.
  • Дата отправляется клиенту в следующем формате: 2012-07-19T14:30:00-03:00.
  • Я использую угловой фильтр, чтобы преобразовать его в местное время.

У меня есть несколько вопросов по этому поводу...

1º Часовой пояс сервера?

О моем сервере (один сервер) ... должен ли он работать с нейтральным UTC (+00:00)? А что, если в будущем мы переедем на ферму, где серверы работают в разных местах?

2º Что я должен хранить?

В настоящее время я храню только дату как DateTimeOffset. Я читаю о сохранении TimeZoneID, но не вижу в этом никакой пользы. Я что-то пропустил?

Или я должен хранить дату как DateTimeUtc с TimeZoneID и вручную преобразовывать каждую дату с классом TimeZone?

3º Как перейти на местное время?

Безопасно ли конвертировать данные на клиенте? Или преобразования даты всегда должны быть на стороне сервера?

4º О летнем времени.

Используя мой текущий подход. Будет ли соблюдаться летнее время?


person Felipe Miosso    schedule 09.12.2013    source источник
comment
Что вы пытаетесь сохранить? Имеет ли значение исходный часовой пояс? (Например, нужно ли кому-то в другом часовом поясе видеть исходный часовой пояс?) Почему вы сохраняете культуру?   -  person Jon Skeet    schedule 09.12.2013
comment
Нет, это не важно. И культура используется как раз в формате даты.   -  person Felipe Miosso    schedule 09.12.2013
comment
Разве вы не должны позволять клиенту определять формат даты? Если я (как житель Великобритании) увижу дату, введенную пользователем из США, я все равно захочу увидеть ее в британском формате.   -  person Jon Skeet    schedule 09.12.2013
comment
Мой английский не так хорош, поэтому я думаю, что написал это плохо, чтобы понять. Но культура является частью моего объекта сеанса и определяется пользователем. Я собираюсь удалить это из вопроса, так как это не очень важно. Извини за это   -  person Felipe Miosso    schedule 09.12.2013
comment
Что за бекон??   -  person Matt Johnson-Pint    schedule 09.12.2013
comment
Все эти вещи заставляют меня плакать (думаю, я все усложняю), и я знаю, что я не одинок. Итак, раз все любят сало, то оно для всех, кто со мной (киринг)...   -  person Felipe Miosso    schedule 09.12.2013
comment
Хороший. :) Работаю над ответом для вас. будет опубликован в ближайшее время. :)   -  person Matt Johnson-Pint    schedule 09.12.2013


Ответы (2)


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

#1 - Часовой пояс сервера

Хранение вашего сервера в UTC — это лучшая практика, и вы также можете ожидать этого от облачных провайдеров, таких как Azure или AWS. Но это не то, от чего вы должны зависеть. Ваш сервер должен быть настроен на любой часовой пояс, чтобы это не повлияло на ваше приложение. Пока часы синхронизированы с сервером NTP, выбор часового пояса не имеет значения.

Итак, как вы гарантируете это? Просто убедитесь, что ваше приложение избегает всего следующего:

  • DateTime.Now
  • DateTimeKind.Local
  • TimeZone (весь класс)
  • TimeZoneInfo.Local
  • DateTime.ToLocalTime()
  • DateTime.ToUniversalTime() (потому что предполагается, что ввод является локальным)
  • Разное другие методы, предполагающие локальный ввод или вывод, такие как TimeZoneInfo.ConvertTimeToUtc(DateTime) (эта конкретная перегрузка не использует часовой пояс, поэтому предполагается локальный часовой пояс)

См. также мою запись в блоге: Дело против DateTime.Now.

Обратите внимание, что я не включил DateTimeOffset.Now в список. Хотя это немного дизайнерский запах, он по-прежнему «безопасен» в использовании.

#2 - Что хранить

Я предлагаю вам прочитать мой ответ на DateTime vs DateTimeOffset. Это должно прояснить некоторые вещи. Не повторяя всего этого, главное, что, хотя оба точно представляют момент времени, DateTimeOffset обеспечивает перспективу, а UTC DateTime - нет.

Вы также спросили, когда следует хранить файл TimeZoneInfo.Id. Есть как минимум два сценария, когда это необходимо:

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

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

Опять же, точный ответ зависит от того, что именно представляют временные метки. Нет единого кольца, которое бы управляло ими всеми.

#3 - Безопасность клиента

Если это клиент .NET, конечно, вы можете конвертировать там. Но я думаю, вы спрашиваете о клиентском браузере JavaScript.

«Безопасный» — понятие относительное. Если вы просите о точном совершенстве, то нет. JavaScript небезопасен для этого из-за ошибки в спецификации ECMAScript (с ES1 по ES5.1. Работа над ES6 ведется). Вы можете прочитать больше в моем блоге: JavaScript Date type ужасно сломан.

Однако, если вы работаете с относительно актуальными данными, а пользователи вашего приложения не находятся в той части мира, где часовые пояса изменчивы, или вам не требуются точные результаты в 100% случаев, то вы можете «безопасно "используйте JavaScript для преобразования в местный часовой пояс пользователя.

Вы можете избежать некоторых этих проблем с библиотеками, которые реализуют IANA TZDB в JavaScript, например те, которые я перечисляю здесь. Но многие из них все еще зависят от JS Date, поэтому у них все еще есть проблемы. (Примечание: я работаю над библиотекой JS, которая будет противостоять этому, но она еще не готова поделиться).

Преобразования на стороне сервера — гораздо лучший выбор, если вы можете запросить у пользователя часовой пояс. В большинстве случаев я думаю, что это выполнимо.

Вы можете рассмотреть вопрос об использовании средства выбора часового пояса на основе карты, такого как этого или вот этот. Оба из них потребуют от вас использования часовых поясов IANA, что для .NET означает использование Noda Time, что в любом случае является отличной идеей ( ПО МОЕМУ МНЕНИЮ).

# 4 - Летнее время

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

Преобразование из любого значения со смещением (будь то -03:00 или Z), которое проходит через объект Date (что, я полагаю, сделает фильтр Angular), будет правильно преобразовано в конкретную временную метку unix.

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

person Matt Johnson-Pint    schedule 09.12.2013
comment
ты обалденный! Приложение будет чем-то вроде Stackoverflow. - person Felipe Miosso; 09.12.2013
comment
Таким образом, если задействованные временные метки — это время, когда произошло событие (например, время публикации, время комментария и т. д.), вы можете просто сохранить UTC DateTime. Идентификаторы часовых поясов не требуются, поскольку вы не собираетесь изменять историю, а смещения не вредны, но и не всегда полезны. - person Matt Johnson-Pint; 09.12.2013
comment
Также обратите внимание, что С.О. позволяет избежать преобразования часовых поясов, используя стиль вывода 10 минут назад и показывая только значения UTC. Кроме того, они основывают свой ежедневный цикл для очков и других вещей на дне UTC, что было оспорено как возможно несправедливое мета несколько раз, но это то, что они решили. - person Matt Johnson-Pint; 09.12.2013
comment
Я могу подумать, что одно место, которое может изменить ваше решение использовать вместо этого DateTimeOffset, было бы, если вы хотите создавать статистические отчеты, которые показывают наиболее активное время дня, когда люди публикуют сообщения. День UTC — это одно, но вас могут заинтересовать более относительные локальные периоды, такие как утро, полдень, вечер, ночь. Для этого вам нужна перспектива, которую может предоставить DateTimeOffset. - person Matt Johnson-Pint; 09.12.2013
comment
Это именно то, что я искал. Большое спасибо за это! Вы сэкономили мне несколько дней тестирования и поиска! Вы, сэр, заслуживаете всего бекона в мире. Еще раз спасибо! - person Felipe Miosso; 09.12.2013

На самом деле это зависит от фактического приложения, которое вы пишете, но самый простой/надежный подход IMO состоит в том, чтобы хранить/вычислять все ваши даты, используя UTC и конвертировать его обратно в местный часовой пояс, когда вы показываете его пользователю.

person ken2k    schedule 09.12.2013
comment
Безопасно конвертировать с помощью javascript? - person Felipe Miosso; 09.12.2013
comment
@FelipeMiosso, что по своей сути небезопасно в этой операции? - person Mike Perrenoud; 09.12.2013
comment
@FelipeMiosso Не уверен, что вы подразумеваете под «безопасным», но если ваше приложение является веб-приложением, вы можете преобразовать даты UTC в местный часовой пояс с помощью javascript. См. stackoverflow.com/questions/6525538/ - person ken2k; 09.12.2013
comment
stackoverflow.com/ вопросы/2532729/. Может быть, я неправильно понял это, но... в разделе "нельзя". Он говорит, что нехорошо обрабатывать дату на клиенте. - person Felipe Miosso; 09.12.2013