В настоящее время у нас есть стандартный способ работы с .NET DateTime
с учетом TimeZone: всякий раз, когда мы производим его, DateTime
мы делаем это в UTC (например, используя DateTime.UtcNow
), и всякий раз, когда мы его отображаем, мы конвертируем обратно из UTC в местное время пользователя. ,
Это прекрасно работает, но я читал о DateTimeOffset
том, как он фиксирует местное и UTC время в самом объекте. Итак, вопрос в том, какие преимущества использования DateTimeOffset
по сравнению с тем, что мы уже делали?
Ответы:
DateTimeOffset
является представлением мгновенного времени (также известного как абсолютное время ). Под этим я подразумеваю момент времени, универсальный для всех (не считая високосных секунд или релятивистских эффектов замедления времени ). Другой способ представить мгновенное время - этоDateTime
где.Kind
естьDateTimeKind.Utc
.Это отличается от календарного времени (также известного как гражданское время ), которое является позицией в чьем-то календаре, и по всему миру существует множество различных календарей. Мы называем эти календари часовыми поясами . Календарное время представлено элементом ,
DateTime
где.Kind
этоDateTimeKind.Unspecified
, илиDateTimeKind.Local
. И.Local
имеет смысл только в тех случаях, когда у вас есть подразумеваемое понимание того, где расположен компьютер, который использует результат. (Например, рабочая станция пользователя)Итак, почему
DateTimeOffset
вместо UTCDateTime
? Это все о перспективе. Давайте использовать аналогию - мы будем притворяться фотографами.Представьте, что вы стоите на временной шкале календаря, направляя камеру на человека на мгновенной временной шкале, расположенной перед вами. Вы устанавливаете камеру в соответствии с правилами вашего часового пояса, которые периодически меняются из-за перехода на летнее время или из-за других изменений в юридическом определении вашего часового пояса. (У вас нет устойчивой руки, поэтому ваша камера дрожит.)
Человек, стоящий на фотографии, увидит угол, под которым видна ваша камера. Если бы другие фотографировали, они могли бы быть с разных сторон. Это то, что представляет
Offset
частьDateTimeOffset
.Поэтому, если вы маркируете свою камеру «Восточное время», иногда вы указываете с -5, а иногда с -4. Во всем мире есть камеры, все они помечены разными вещами, и все они указывают на одну и ту же мгновенную шкалу времени под разными углами. Некоторые из них находятся рядом друг с другом (или друг над другом), поэтому просто знать смещение недостаточно, чтобы определить, к какому часовому поясу относится время.
А как насчет UTC? Ну, это единственная камера, у которой гарантированно устойчивая рука. Это на штативе, прочно закрепленном на земле. Это никуда не денется. Мы называем его угол перспективы нулевым смещением.
Итак, что говорит нам эта аналогия? Это обеспечивает некоторые интуитивные руководящие принципы-
Если вы представляете время относительно определенного места, в частности, представьте его в календарном времени с помощью
DateTime
. Просто убедитесь, что вы никогда не перепутаете один календарь с другим.Unspecified
должно быть ваше предположение.Local
полезно только исходя изDateTime.Now
. Например, я могу получитьDateTime.Now
и сохранить его в базе данных - но когда я получаю его, я должен предположить, что это такUnspecified
. Я не могу полагать, что мой локальный календарь - тот же самый календарь, из которого он был первоначально взят.Если вы всегда должны быть уверены в моменте, убедитесь, что вы представляете мгновенное время. Используйте
DateTimeOffset
для принудительного применения или используйте UTCDateTime
по соглашению.Если вам нужно отследить мгновенное время, но вы также хотите знать, «во сколько время пользователь думал, что это было в его локальном календаре?» - тогда вы должны использовать
DateTimeOffset
. Это очень важно, например, для систем хронометража - как для технических, так и для юридических вопросов.Если вам когда-либо понадобится изменить ранее записанный файл
DateTimeOffset
- у вас недостаточно информации только для одного смещения, чтобы гарантировать, что новое смещение по-прежнему актуально для пользователя. Вы также должны сохранить идентификатор часового пояса (подумайте - мне нужно имя этой камеры, чтобы я мог сделать новый снимок, даже если положение изменилось).Следует также отметить, что для этого у Noda Time есть представление
ZonedDateTime
, в то время как библиотека базовых классов .Net не имеет ничего подобного. Вам нужно будет хранить как a, такDateTimeOffset
иTimeZoneInfo.Id
значение.Иногда вам может потребоваться указать календарное время, которое является локальным для «того, кто смотрит на него». Например, при определении того, что сегодня означает. Сегодня всегда полночь - полночь, но они представляют собой почти бесконечное количество перекрывающихся диапазонов на мгновенной временной шкале. (На практике у нас есть ограниченное количество часовых поясов, но вы можете выразить смещения вплоть до отметки). Поэтому в этих ситуациях убедитесь, что вы понимаете, как ограничить «кто спрашивает?» вопрос до одного часового пояса, или сделайте перевод их обратно в мгновенное время по мере необходимости.
Вот еще несколько маленьких подробностей об
DateTimeOffset
этой аналогии, а также несколько советов, как сохранить ее прямо:Если вы сравниваете два
DateTimeOffset
значения, они сначала нормализуются к нулевому смещению перед сравнением. Другими словами,2012-01-01T00:00:00+00:00
и2012-01-01T02:00:00+02:00
относятся к одному и тому же мгновенному моменту и, следовательно, эквивалентны.Если вы делаете какие - либо модульного тестирования и должны быть уверены в офсетной, испытания как в
DateTimeOffset
стоимости, и.Offset
собственности отдельно.В платформу .Net встроено одностороннее неявное преобразование, которое позволяет передавать a
DateTime
в любойDateTimeOffset
параметр или переменную. При этом, вопросы . Если вы передаете тип UTC, он будет иметь нулевое смещение, но если вы передадите либо, либо , он будет локальным . Фреймворк в основном говорит: «Ну, вы попросили меня перевести календарное время в мгновенное, но я понятия не имею, откуда это пришло, поэтому я просто собираюсь использовать местный календарь». Это огромная ошибка, если вы загружаете неопределенное на компьютер с другим часовым поясом. (ИМХО - это должно вызвать исключение - но это не так.).Kind
.Local
.Unspecified
DateTime
Бесстыдная вилка:
Многие люди поделились со мной, что они находят эту аналогию чрезвычайно полезной, поэтому я включил ее в свой курс по Pluralsight, «Основы даты и времени» . Вы найдете пошаговое руководство по аналогии с камерой во втором модуле «Вопросы контекста» в клипе под названием «Время календаря и мгновенное время».
источник
DateTimeOffset
в C #, то вы должны сохранить этоDATETIMEOFFSET
в SQL Server.DATETIME2
или простоDATETIME
(в зависимости от требуемого диапазона) хорошо для обычныхDateTime
значений. Да - вы можете определить местное время из любой пары timezone + dto или utc. Разница в том, что вы всегда хотите вычислять правила для каждого решения или же хотите их пересчитать? Во многих случаях (иногда по юридическим причинам) DTO - лучший выбор.DateTimeOffset.Now
на сервере, вы действительно получите смещение сервера. Дело в том, чтоDateTimeOffset
тип может сохранить это смещение. Вы также можете легко сделать это на клиенте, отправить его на сервер, и тогда ваш сервер узнает смещение клиента.От Microsoft:
Источник: «Выбор между DateTime, DateTimeOffset, TimeSpan и TimeZoneInfo» , MSDN
Мы используем
DateTimeOffset
почти все, так как наше приложение имеет дело с определенными моментами времени (например, когда запись была создана / обновлена). В качестве примечания мы также используемDATETIMEOFFSET
SQL Server 2008.Я считаю
DateTime
полезным, когда вы хотите иметь дело только с датами, только со временем или с любым из них в общем смысле. Например, если у вас есть сигнал тревоги, который вы хотите включить каждый день в 7 часов утра, вы можете сохранитьDateTime
егоDateTimeKind
вUnspecified
потому что хотите, чтобы он утра, независимо от летнего времени. Но если вы хотите представить историю возникновения тревог, вы должны использоватьDateTimeOffset
.Будьте осторожны при использовании сочетания
DateTimeOffset
иDateTime
особенно при назначении и сравнении между типами. Кроме того, сравнивайте толькоDateTime
экземпляры, которые одинаковы,DateTimeKind
потому чтоDateTime
при сравнении игнорируется смещение часового пояса.источник
Kind
того же, сравнение может быть ошибочным. Если обе стороныDateTimeKind.Unspecified
не знают, что они пришли из одного и того же часового пояса. Если обе стороны верныDateTimeKind.Local
, большинство сравнений будут хорошими, но вы все равно можете иметь ошибки, если одна сторона неоднозначна в местном часовом поясе. Действительно, толькоDateTimeKind.Utc
сравнения являются надежными, и да,DateTimeOffset
как правило, предпочтительнее. (Ура!)DateTime может хранить только два разных времени, местное время и UTC. Свойство Kind указывает, какой.
DateTimeOffset расширяет это, имея возможность хранить местное время из любой точки мира. Он также хранит смещение между этим местным временем и UTC. Обратите внимание, что DateTime не может этого сделать, если вы не добавите в свой класс дополнительного члена для хранения этого смещения 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 явно включает смещение. Оно может не включать имя часового пояса, но, по крайней мере, оно включает смещение, и если вы его сериализуете, вы получите явно включенное смещение в вашем значении вместо того, каким бы ни было местное время сервера.
источник
The most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
Этот кусок кода от Microsoft объясняет все:
источник
Большинство ответов хорошие, но я подумал добавить еще несколько ссылок на MSDN для получения дополнительной информации.
источник
Основным отличием является то, что
DateTimeOffset
может использоваться в сочетании сTimeZoneInfo
преобразованием местного времени в часовые пояса, отличные от текущего.Это полезно для серверного приложения (например, ASP.NET), к которому обращаются пользователи в разных часовых поясах.
источник
Единственная отрицательная сторона DateTimeOffset, которую я вижу, заключается в том, что Microsoft «забыла» (намеренно) поддерживать ее в своем классе XmlSerializer. Но с тех пор он был добавлен в служебный класс XmlConvert.
XmlConvert.ToDateTimeOffset
XmlConvert.ToString
Я говорю «вперед» и используйте DateTimeOffset и TimeZoneInfo из-за всех преимуществ, просто будьте осторожны при создании сущностей, которые будут или могут быть сериализованы в или из XML (тогда все бизнес-объекты).
источник