Как правильно работать с часовым поясом?
Я много читаю о часовом поясе , смещение, utc, местное время, функции javascript, 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º около DST.
Используя мой текущий подход. Будет DST быть уважаемым?
2 ответа:
Одна очень важная вещь, которую нужно понять о дате / времени, заключается в том, что нет одного правильного способа делать все. Общий ответ "использование мирового" составляет не всегда применимо. Контекст очень важен, и существуют различные методы и подходы, основанные на том, что представляют ценности, с которыми вы работаете. Если вы можете уточнить, для чего они используются в вашем приложении, я обновлю свой ответ соответствующим образом. В то же время, я постараюсь обратиться к конкретным пунктам, которые вы воспитали уже:
#1-Часовой Пояс Сервера
Сохранение вашего сервера в UTC является лучшей практикой, и это то, что вы можете ожидать от поставщиков облачных услуг, таких как Azure или AWS. Но это не то, от чего вы должны зависеть. Ваш сервер должен иметь возможность установить любой часовой пояс, не затрагивая ваше приложение. Пока часы синхронизированы с сервером NTP, выбор часового пояса не должен иметь значения.
Так как же вы это гарантируете? Простой, просто убедитесь, что ваше приложение избегает всего следующего:
DateTime.Now
DateTimeKind.Local
TimeZone
(весь класс)TimeZoneInfo.Local
DateTime.ToLocalTime()
DateTime.ToUniversalTime()
(поскольку предполагается, что входные данные являются локальными)- разное. другие методы, которые предполагают локальный вход или выход, такие как
TimeZoneInfo.ConvertTimeToUtc(DateTime)
(эта конкретная перегрузка не принимает часовой пояс, поэтому она предполагает локальный часовой пояс)Смотрите также мой пост в блоге: дело против значение datetime.Теперь .
Обратите внимание, что я не включилDateTimeOffset.Now
в список. Хотя это немного дизайнерский запах, он все еще "безопасен" в использовании.#2-Что хранить
Я предлагаю вам прочитать мой ответ на DateTime vs DateTimeOffset. Это должно прояснить некоторые вещи. Не отрыгивая все это, главное заключается в том, что, хотя оба точно представляют точку во времени,DateTimeOffset
обеспечивает перспективу, в то время как UTCDateTime
этого не делает.Ты тоже спросили, когда вы должны хранить
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, что в любом случае отличная идея (IMHO).
#4-Переход На Летнее И Зимнее Время
С вашего текущего подхода, переход на летнее время будет соблюдаться в определении тока правила перехода на летнее время часовой пояс, который пользователь установил для своего локального браузера. (Опять же, обратитесь к моему сообщению в блоге, чтобы узнать, почему это так).
Преобразование из любого значения со смещением (будь то
-03:00
илиZ
), которое проходит через объектDate
(что, как я полагаю, сделает угловой фильтр), будет правильно преобразовано в конкретную метку времени unix.Ошибки, которые могут возникнуть при преобразовании DST для предыдущих правил DST, вызваны переходом от временной метки unix внутри объекта
Date
к локальной часовой пояс всегда будет предполагать, что правилоcurrent DST применимо, даже если время попало в период, который имел другое правило.
На самом деле это зависит от конкретного приложения, которое вы пишете, но самый простой/надежный подход IMO-хранить/вычислять все ваши даты с помощью UTC и конвертировать их обратно в местный часовой пояс, когда вы показываете его пользователю.