Ответ 1
В описываемом вами сценарии вы получите 10:00. Функция преобразования часового пояса не имела бы представления о том, что значение было первоначально введено как 9:00 AM, потому что вы сохранили время UTC 7:00 утра.
Это иллюстрирует один из случаев, когда совет "всегда хранить UTC" является ошибочным. Когда вы работаете с будущими событиями, это не всегда работает. Проблема в том, что правительства часто меняют свое мнение о часовых поясах. Иногда они дают разумное уведомление (например, Соединенные Штаты, 2007), но иногда они этого не делают (например, Египет, 2014).
Когда вы сделали первоначальное преобразование с локального времени в UTC, вы намеренно решили, что правила часового пояса не будут меняться. Другими словами, вы решили, что вы назначили событие универсальной шкале времени, основанной исключительно на правилах часового пояса, как вы знали их в то время.
Способ избежать этого прост: Будущие события должны быть запланированы по местному времени. Теперь я не имею в виду "локальный для вашего компьютера", а скорее "локальный для пользователя", поэтому вам нужно знать часовой пояс пользователя, и вы также должны хранить идентификатор часового пояса где-то.
Вам также нужно будет решить, что вы хотите сделать, если событие попадает в переход <+ w760 > -forward или fall-back для летнего времени. Это особенно важно для моделей повторения.
В конечном счете, вам нужно выяснить, когда запускать событие. Или в вашем случае вам нужно решить, прошло ли событие или нет. Это можно сделать несколькими способами:
Вариант 1
-
Вы можете вычислить соответствующее значение UTC для каждого локального времени и сохранить его в отдельном поле.
-
В течение некоторого цикла (ежедневно, еженедельно и т.д.) вы можете пересчитать ожидаемые значения UTC из своих локальных значений и ваше текущее понимание правил часовых поясов. Или, если вы вручную применяете обновления часового пояса, вы можете в любой момент перераспределить все.
Вариант 2
-
Вы можете сохранить значения как
ЗначенияDateTimeOffset
, а неDateTime
. Он будет содержать исходное местное время и смещение, которое вы рассчитали на основе правил часового пояса, как вы знали их во время ввода. -
DateTimeOffset
могут быть легко возвращены в UTC, поэтому они, как правило, работают очень хорошо для этого. Вы можете прочитать больше в DateTime vs DateTimeOffset. -
Как и в варианте 1, вы периодически пересматриваете значения или после обновлений данных часового пояса и корректируете смещения для выравнивания с новыми данными часового пояса.
-
Это то, что я обычно рекомендую, особенно если вы используете базу данных, поддерживающую типы
DateTimeOffset
, такие как SQL Server или RavenDB.
Вариант 3
-
Вы можете сохранить значения как локальные
DateTime
. -
При запросе вы будете вычислять текущее время в целевом часовом поясе и сравнивать с этим значением.
DateTime now = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, targetTZ); bool passed = now >= eventTime;
-
Нижняя сторона этой опции заключается в том, что вам может потребоваться сделать много запросов, если у вас есть события во множестве разных часовых поясов.
-
У вас могут также возникнуть проблемы со значениями, близкими к переходу DST с возвратом, поэтому будьте осторожны, если вы используете этот подход.
Я рекомендую против идеи сериализации самого часового пояса. Если часовой пояс изменился, он изменился. Притвориться, что это не так, не является хорошим решением.