DateTime vs DateTimeOffset

В настоящее время у нас есть стандартный способ иметь дело с .net DateTimes в методе TimeZone: всякий раз, когда мы создаем DateTime мы делаем это в UTC (например, используя DateTime.UtcNow ), и всякий раз, когда мы показываем его, мы конвертируем обратно с UTC на местное время пользователя.

Это работает отлично, но я читал о DateTimeOffset и о том, как он фиксирует время локального и UTC в самом объекте. Итак, вопрос в том, каковы были бы преимущества использования DateTimeOffset сравнении с тем, что мы уже делали?

DateTimeOffset представляет собой представление мгновенного времени (также известного как абсолютное время ). Таким образом, я имею в виду момент времени, который является универсальным для всех (не считая скачкообразных секунд или релятивистских эффектов замедления времени ). Другим способом представления мгновенного времени является DateTime где .KindDateTimeKind.Utc .

Это отличается от календарного времени (также известного как гражданское время ), что является позицией в чьем-то календаре, и существует множество разных календарей по всему миру. Мы называем эти временные зоны календарей. Время календаря представлено DateTime где .KindDateTimeKind.Unspecified или DateTimeKind.Local . И .Local имеет смысл только в сценариях, где у вас подразумевается понимание того, где находится компьютер, который использует результат. (Например, рабочая станция пользователя)

Итак, почему DateTimeOffset вместо UTC DateTime ? Это все о перспективах. Давайте используем аналогию – мы будем притворяться фотографами.

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

Человек, стоящий на фотографии, увидит угол, из которого появилась ваша камера. Если другие фотографируют, они могут быть под разными углами. Это то, что представляет собой Offset часть DateTimeOffset .

Поэтому, если вы помечаете свою камеру «Восточное время», иногда вы указываете от -5, а иногда вы указываете от -4. Есть камеры по всему миру, все обозначенные разными вещами, и все они указывают на одну и ту же мгновенную временную шкалу под разными углами. Некоторые из них находятся рядом с (или сверху) друг с другом, поэтому просто знать, что смещение недостаточно для определения того, к какому временному времени относится время.

А как насчет UTC? Ну, это единственная камера, которая гарантирована устойчивой рукой. Он на штативе, крепко привязан к земле. Это никуда не денется. Мы называем его угол зрения нулевым смещением.

Мгновенное время против визуализации календаря

Итак, что говорит эта аналогия? Он содержит некоторые интуитивные рекомендации.

  • Если вы представляете время относительно некоторого места в частности, представляйте его в календарном времени с помощью DateTime . Просто убедитесь, что вы никогда не путаете один календарь с другим. Unspecified должно быть вашим предположением. Local полезен только с DateTime.Now . Например, я могу получить DateTime.Now и сохранить его в базе данных, но когда я его извлечу, я должен предположить, что он не Unspecified . Я не могу полагаться на то, что мой местный календарь – это тот же календарь, из которого он был первоначально взят.

  • Если вы всегда должны быть уверены в этом, убедитесь, что вы представляете мгновенное время. Используйте DateTimeOffset для принудительного использования или используйте UTC DateTime по соглашению.

  • Если вам нужно отслеживать мгновение мгновенного времени, но вы также хотите знать: «В какое время пользователь подумал, что это был в их местном календаре?» – тогда вы должны использовать DateTimeOffset . Это очень важно для систем учета времени, например, как для технических, так и юридических проблем.

  • Если вам когда-либо понадобится изменить ранее записанный DateTimeOffset – у вас недостаточно информации только в смещении, чтобы убедиться, что новое смещение все еще актуально для пользователя. Вы также должны сохранить идентификатор часового пояса (подумайте – мне нужно имя этой камеры, чтобы я мог снимать новое изображение, даже если позиция изменилась).

    Следует также отметить, что Noda Time имеет для этого представление ZonedDateTime , в то время как библиотека базового classа .Net не имеет ничего подобного. Вам нужно будет сохранить значение DateTimeOffset и значение TimeZoneInfo.Id .

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

Вот несколько других небольших бит о DateTimeOffset которые поддерживают эту аналогию, и некоторые советы по ее сохранению:

  • Если вы сравниваете два значения DateTimeOffset , они сначала нормализуются до нулевого смещения перед сравнением. Другими словами, 2012-01-01T00:00:00+00:00 и 2012-01-01T02:00:00+02:00 относятся к одному и тому же мгновенному моменту и поэтому эквивалентны.

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

  • Существует одностороннее неявное преобразование, встроенное в .Net-структуру, которая позволяет передавать DateTime в любой параметр или переменную DateTimeOffset . При этом .Kind значение .Kind . Если вы передадите тип UTC, он будет переноситься с нулевым смещением, но если вы пройдете либо .Unspecified или .Unspecified , он будет считаться локальным . Рамки в основном говорят: «Ну, вы попросили меня преобразовать время календаря в мгновенное время, но я понятия не имею, откуда это взялось, поэтому я просто собираюсь использовать местный календарь». Это огромная ошибка, если вы загружаете неопределенный DateTime на компьютер с другим часовым поясом. (ИМХО – это должно выставить исключение – но это не так.)

Бесстыдный штекер:

Многие люди поделились со мной тем, что они считают эту аналогию чрезвычайно ценной, поэтому я включил ее в мой курс Pluralsight, Date and Time Fundamentals . Вы найдете пошаговое руководство по аналогии с камерой во втором модуле «Контекстные вопросы» в клипе под названием «Время календаря против мгновенного времени».

От Microsoft:

Эти значения для значений DateTimeOffset гораздо более распространены, чем значения для значений DateTime. В результате DateTimeOffset следует рассматривать как тип даты и времени по умолчанию для разработки приложений.

источник: «Выбор между DateTime, DateTimeOffset, TimeSpan и TimeZoneInfo» , MSDN

Мы используем DateTimeOffset почти для всех, так как наше приложение имеет дело с определенными моментами времени (например, когда запись была создана / обновлена). В качестве дополнительной заметки мы также используем DATETIMEOFFSET в SQL Server 2008.

Я вижу, что DateTime полезен, когда вы хотите иметь дело только с датами, только временами, или иметь дело либо в общем смысле. Например, если у вас есть будильник, который вы хотите отключать каждый день в 7 утра, вы можете сохранить это в DateTime используя DateTimeKind Unspecified потому что вы хотите, чтобы он ушел в 7 утра, независимо от DST. Но если вы хотите представить историю возникновения тревоги, вы должны использовать DateTimeOffset .

Соблюдайте осторожность при использовании комбинации DateTimeOffset и DateTime особенно при назначении и сравнении типов. Кроме того, сравнивайте только экземпляры DateTime которые являются тем же самым DateTimeKind потому что DateTime игнорирует смещение часового пояса при сравнении.

DateTime может хранить только два отдельных раза, местное время и UTC. Свойство Kind указывает, какой.

DateTimeOffset расширяет это, сохраняя локальные времена из любой точки мира. Он также сохраняет смещение между этим местным временем и UTC. Обратите внимание, что DateTime не может это сделать, если вы не добавите дополнительный член в свой class, чтобы сохранить это смещение UTC. Или только работайте с UTC. Что само по себе является прекрасной идеей кстати.

Есть несколько мест, где DateTimeOffset имеет смысл. Первый – когда вы имеете дело с повторяющимися событиями и летним временем. Предположим, я хочу настроить будильник, чтобы выходить в 9 утра каждый день. Если я использую правило «магазин как UTC, отображаемое как локальное время», тогда будильник будет уходить в другое время, когда действует летнее время.

Возможно, есть и другие, но приведенный выше пример – это тот, который я использовал в прошлом (это было до добавления DateTimeOffset к BCL – в то время мое решение заключалось в том, чтобы явно хранить время в локальном часовом поясе и сохраните информацию о часовом поясе рядом с ней: в основном, что делает DateTimeOffset внутренне).

Самое важное отличие заключается в том, что DateTime не хранит информацию о часовом поясе, а DateTimeOffset.

Хотя DateTime различает UTC и Local, абсолютно нет явного смещения часового пояса, связанного с ним. Если вы выполняете сериализацию или преобразование, будет использоваться часовой пояс сервера. Даже если вы вручную создаете локальное время, добавив минуты для смещения времени UTC, вы все равно можете получить бит на этапе сериализации, потому что (из-за отсутствия какого-либо явного смещения в DateTime) он будет использовать смещение часового пояса сервера.

Например, если вы сериализуете значение DateTime с помощью Kind = Local, используя Json.Net и формат даты ISO, вы получите строку типа 2015-08-05T07:00:00-04 . Обратите внимание, что последняя часть (-04) не имеет никакого отношения к вашему DateTime или любому смещению, которое вы использовали для его расчета … это просто чисто смещение часового пояса сервера.

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

Большинство ответов хороши, но я подумал о добавлении дополнительных ссылок MSDN для получения дополнительной информации.

  • Краткая история DateTime – Энтони Мур из команды BCL
  • Выбор между Datetime и DateTime Offset – с помощью MSDN
  • Не забывайте, что SQL Server 2008 имеет новый тип данных как DateTimeOffset
  • .NET Framework включает типы DateTime , DateTimeOffset и TimeZoneInfo , все из которых могут быть использованы для создания приложений, работающих с датами и временем.
  • Выполнение арифметических операций с датами и временем-MSDN

Основное отличие заключается в том, что DateTimeOffset может использоваться совместно с TimeZoneInfo для преобразования в локальные времена в часовых поясах, отличных от текущего.

Это полезно для серверного приложения (например, ASP.NET), доступ к которому осуществляется пользователями в разных часовых поясах.

Единственная отрицательная сторона DateTimeOffset, которую я вижу, – это то, что Microsoft «забыла» (по дизайну), чтобы поддерживать ее в своем classе XmlSerializer. Но с тех пор он добавлен в class утилиты XmlConvert.

XmlConvert.ToDateTimeOffset

XmlConvert.ToString

Я говорю, продолжайте использовать DateTimeOffset и TimeZoneInfo из-за всех преимуществ, просто будьте осторожны при создании объектов, которые будут или могут быть сериализованы в XML или из него (все бизнес-объекты затем).

  • Как я могу анализировать даты и преобразовывать часовые пояса в Perl?
  • Java SimpleDateFormat ("yyyy-MM-dd'T'HH: mm: ss'Z '") дает часовой пояс как IST
  • Что означает «Z» в отметке Unix «120314170138Z»?
  • Преобразование даты и времени в другой часовой пояс в java
  • MySQL CONVERT_TZ ()
  • Преобразование даты времени UTC в локальное время
  • Почему вычитание этих двух раз (в 1927 году) дает странный результат?
  • Предотrotation изменения часового пояса при десериализации значения DateTime
  • Разбор даты и времени ISO8601 (включая TimeZone) в Excel
  • Interesting Posts

    Как Random является System.Guid.NewGuid ()? (Возьми два)

    Конечная локальная переменная не может быть назначена

    Удаленный рабочий стол: полноэкранный режим ограничен разрешением моего основного монитора. Как я могу получить полноэкранный сеанс rdp на моем втором (большем) экране?

    3 Мониторы на Sapphire Radeon HD 6870

    Возможно ли выполнить последовательность сборки x86 из C #?

    Как проверить, связано ли событие click – JQuery

    Как предотвратить очистку вывода терминала при выходе из сеанса SSH?

    Как отправить запрос POST с помощью волейбола со строкой?

    Как я могу рассчитать возраст человека в год, месяц, дни?

    В чем разница между module_init и subsys_initcall при инициализации драйвера?

    OpenCV получает значение пиксельного канала от изображения Mat

    Символы Emoji не отображаются правильно в Windows 7

    Как сохранить предупреждения и ошибки как выходные данные из функции?

    Включить доступ для вспомогательных устройств программным способом на 10,9

    Есть ли проблемы с огромными папками?

    Давайте будем гением компьютера.