День 1087. #ЗаметкиНаПолях
Зачем Использовать DateTimeOffset
DateTime повсеместно используется как тип свойств сущностей и полей базы данных, но если вас действительно волнует временная часть значения, она часто неоднозначна. В каком часовом поясе указана дата? Она сохраняется как UTC? Всегда? Вы уверены? DateTimeOffset решает эту проблему.
Хранение значений DateTime
Тип DateTime всегда подразумевает время локальной машины. Когда вы запрашиваете .Today или .Now, он использует локальные системные часы. Это может легко вызвать проблемы между машинами с разным временем и/или часовыми поясами:
var now = new DateTime(2022,1,20,18,11,30);
Console.WriteLine(now);
// 1/20/2022 6:11:30 PM
Console.WriteLine(now.ToLocalTime());
// 1/20/2022 9:11:30 PM
Console.WriteLine(now.ToUniversalTime());
// 1/20/2022 3:11:30 PM
Изначальное время — 18:11 20 января 2022 года на моем компьютере в московском часовом поясе. Переменная now не содержит данных о часовом поясе. Функции ToLocalTime и ToUniversalTime предполагают, что данные, с которыми они работают, представлены в формате UTC или по местному времени соответственно. В результате время, полученное с помощью ToLocalTime, неверно, так как оно добавляет 3 часа к фактическому времени 18:11.
Обратите внимание, что, когда вы сохраняете дату и время в своём приложении, они будут храниться относительно часового пояса машины, если только вы не будете следить за использованием времени UTC везде. Но даже в этом случае постфактум, глядя на данные, нельзя будет сказать, точно ли это время по UTC.
Что произойдёт, когда один и тот же код запустится на машинах разработчиков в разных часовых поясах? Что произойдёт при запуске тестов? А если вы переместите своё облачное приложение из одного региона в другой?
По всем этим причинам может оказаться целесообразным хранить даты в вашем приложении, используя тип DateTimeOffset.
DateTimeOffset является типом как в .NET, так и в SQL Server (в других базах данных тоже есть эквиваленты). Основное отличие между ним и более простым DateTime в том, что он включает смещение часового пояса от UTC. Таким образом, при взгляде на DateTimeOffset всегда ясно, какое время имеется в виду, будь то UTC или местное время.
Добавим к примерам выше аналоги с DateTimeOffset:
var nowHere = new DateTimeOffset(now);
Console.WriteLine(nowHere);
// 1/20/2022 6:11:30 PM +03:00
Console.WriteLine(nowHere.ToLocalTime());
// 1/20/2022 6:11:30 PM +03:00
Console.WriteLine(nowHere.ToUniversalTime());
// 1/20/2022 3:11:30 PM +00:00
Мы видим 2 изменения:
- значение ToLocalTime правильное,
- строковые представления даты включают смещение часового пояса.
DateTimeOffset в SQL Server
DateTimeOffset использует переменную точность и поэтому может занимать больше места, чем DateTime. Если вы хотите увидеть разницу, выполните следующие запросы:
select GetDate()
select SYSDATETIME()
select SYSDATETIMEOFFSET()
Результаты (заметьте разницу в точности):
2022-01-20 18:30:18.227
2022-01-20 18:30:18.2292271
2022-01-20 18:30:18.2292271 -05:00
EF Core прекрасно поддерживает DateTimeOffset.
Итого
Значения DateTime не имеют сведений о смещении часового пояса или его отсутствии (в UTC). Если вам нужно знать, когда что-то произошло на самом деле, с большей точностью, чем просто приблизительная дата, и вы не можете быть на 100% уверены, что ваши даты ВСЕГДА хранятся в формате UTC, вам следует рассмотреть возможность использования DateTimeOffset для представления значений даты и времени. Этот тип указывает смещение, таким образом, может устранить множество ошибок, от которых страдают многие системы реального мира.
Источник: https://ardalis.com/why-use-datetimeoffset/