Ответ 1
Во-первых, я должен сказать, что я впечатлен! Это очень хорошо написанная запись, и вы, похоже, изучили многие проблемы, связанные с этим вопросом.
Ваш подход хорош. Тем не менее, я предложу следующее для вас, чтобы рассмотреть как улучшения.
-
Модельное связующее может быть улучшено.
-
Я бы назвал его
ZonedDateTimeModelBinder
, так как вы применяете его для создания значенийZonedDateTime
. -
Вы хотите использовать
bindingContext
, чтобы получить значение, а не ожидать, что вход всегда будет находиться вrequest.Form.Get("DateTime")
. Вы можете увидеть пример этого в привязке модели WebAPI, которую я написал дляLocalDate
. Модельные вставки MVC аналогичны. -
В этом примере вы также увидите, как я использую возможности Node Time parsing вместо
DateTime.Parse
. Вы можете подумать о том, чтобы сделать что-то, что у вас, используяLocalDateTimePattern
. -
Убедитесь, что вы понимаете, как работает
AtLeniently
, а также что мы изменили его поведение для предстоящей версии 2.0 (по уважительной причине). См. "Изменения преобразователя Lenient" внизу руководство по миграции. Если это имеет значение в вашем домене, вы можете рассмотреть возможность использования нового поведения сегодня, внедряя свой собственный резольвер. -
Вы могли бы подумать, что могут быть контексты, в которых текущий часовой пояс пользователя не является таковым для данных, с которыми вы работаете в настоящее время. Возможно, администратор работает с некоторыми другими пользовательскими данными. Поэтому вам может потребоваться перегрузка, которая принимает идентификатор часового пояса в качестве параметра.
-
-
В обычном случае вы можете попробовать зарегистрировать привязку модели по всему миру, что позволит сэкономить вам несколько нажатий клавиш на ваших контроллерах:
ModelBinders.Binders.Add(typeof(ZonedDateTime), new ZonedDateTimeModelBinder());
Вы всегда можете использовать атрибут, если есть параметр для передачи.
-
В нижней части кода
ZonedDateTime.FromDateTimeOffset(dto).ToInstant().InZone(tz)
работает нормально, но может быть сделано с меньшим количеством кода. Любой из них эквивалентен:-
ZonedDateTime.FromDateTimeOffset(dto).WithZone(tz)
-
Instant.FromDateTimeOffset(dto).InZone(tz)
-
-
Это звучит как производственное приложение, и поэтому я бы нашел время, чтобы настроить возможность обновления ваших собственных данных часового пояса.
-
Смотрите руководство пользователя о том, как использовать файлы NZD вместо встроенной копии в
DateTimeZoneProviders.Tzdb
. -
Хорошим подходом является конструктор-инъекция
IDateTimeZoneProvider
и зарегистрировать его в контейнере DI по вашему выбору. -
Обязательно подпишитесь на Список объявлений из IANA, чтобы вы знали, когда публикуются новые обновления TZDB. Файлы Noda Time NZD обычно следуют через короткое время.
-
Или вы могли бы придумать и написать что-то проверить последний файл .NZD и автоматически обновить свою систему, так как если вы понимаете, что (если что-либо) должно произойти на вашей стороне после обновления. (Это входит в игру, когда приложение включает планирование будущих событий.)
-
-
Свойства приятеля WRT - Да, я согласен, что они PITA. Но, к сожалению, в настоящее время EF не имеет лучшего подхода, поскольку он не поддерживает сопоставления пользовательских типов. EF6, вероятно, никогда не будет этого, но он отслеживается в aspnet/EntityFramework # 242 для EF7.
Теперь, со всем сказанным, вы можете идти о вещах несколько иначе. Я сделал это, и да - это сложно. Упрощенный подход:
-
Не используйте типы времени Noda в ваших сущностях вообще. Просто используйте
DateTimeOffset
вместоZonedDateTime
. -
Вовлекайте
ZonedDateTime
и часовой пояс пользователя только в том месте, где вы выполняете логику приложения.
Недостатком такого подхода является то, что он загрязняет воды в отношении вашего домена. Иногда бизнес-логика находит свой путь в сервисах, а не остается в сущности, где она принадлежит. Или, если он остается в сущности, теперь вам нужно передать параметр timeZoneId
различным методам, в которых вы, возможно, и не думали об этом иначе. Иногда это приемлемо, но иногда нет. Это зависит от того, сколько работы оно создает для вас.
Наконец, я рассмотрю эту часть:
Теперь у нас есть приложение Noda Time. Объект ZonedDateTime упрощает выполнение специальных вычислений и запросов с часовым поясом.
Это правильное предположение?
Да и нет. Прежде чем зайти слишком глубоко, применив все вышеперечисленное к вашему приложению, вы можете попробовать несколько операций в изоляции с помощью ZonedDateTime
.
В первую очередь ZonedDateTime
обеспечивает учет часового пояса при преобразовании в другие типы и из других, а также при выполнении математических операций, которые включают мгновенное время (с использованием объектов Duration
).
Там, где это действительно не помогает, приходится работать с календарным временем. Например, если я хочу "добавить один день" - мне нужно подумать о том, означает ли это "добавить продолжительность 24 часа" или "добавить период в один календарный день". В течение большинства дней это будет одно и то же, но не в дни, содержащие переходы DST. Там они могут быть 23, 23,5, 24, 24,5 или 25 часов в зависимости от часового пояса. ZonedDateTime
не позволит вам напрямую добавить Period
. Вместо этого вы должны получить LocalDateTime
, затем добавить период, а затем повторно применить часовой пояс, чтобы вернуться к ZonedDateTime
.
Итак, подумайте о том, нужно ли вам одинаково везде или нет. Если ваша логика приложения строго о календарных днях, вы можете найти ее лучше всего, написанную исключительно с точки зрения LocalDate
. Возможно, вам придется работать через различные свойства и методы, чтобы фактически использовать эту логику, но по крайней мере логика моделируется в самой чистой форме.
Надеюсь, что это поможет, и, надеюсь, это будет полезная статья для других читателей. Удачи, и не стесняйтесь обращаться ко мне за помощью.