TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.77K subscribers
Post #1968 1.65K
День 1619. #ЗаметкиНаПолях
Время в .NET. Начало
Время – важная часть наших программ. Отслеживание часовых поясов и тестирование кода, зависящего от времени, - вечная головная боль разработчиков.

История
DateTime была основной структурой для хранения даты и времени в .NET, начиная с версии 1.1. У неё есть большой недостаток - отсутствие часового пояса. Чтобы решить эту проблему, было добавлено свойство Kind с возможными значениями: Local (по умолчанию), Utc или Unspecified.

Для конвертации в UTC нужно вызвать метод ToUniversalTime() или использовать свойство DateTime.UtcNow. Как .NET понимает разницу в часовых поясах, если DateTime не предоставляет эту информацию? Метод ToUniversalTime берёт часовой пояс из операционной системы, что может приводить к проблемам. Если создать локальное время на сервере в одном часовом поясе, а потом переместить его на другой сервер (например, на клиента API), то результат вызова ToUniversalTime на этих серверах будут разным.

Поэтому Microsoft выпустили «Рекомендации по написанию кода с использованием даты и времени в платформе .NET Framework», переложив всю ответственность на разработчиков:
«Разработчик несёт ответственность за отслеживание информации о часовом поясе, связанным со значением DateTime, с помощью некоторого внешнего механизма. Обычно это достигается путём определения другого поля или переменной, которые вы используете для записи информации о часовом поясе при сохранении значения типа DateTime.»

.NET ожидает, что информация о часовом поясе будет связана со свойством Kind во время восстановления даты и времени: DateTimeKind.Local с TimeZoneInfo.Local, DateTimeKind.Utc с TimeZoneInfo.Utc и DateTimeKind.Unspecified в остальных случаях. Пользовательский часовой пояс, как рекомендует Microsoft, требует использования DateTimeKind.Unspecified:
var nowInNY = DateTime.Now;
var nyTZ = TimeZoneInfo
.FindSystemTimeZoneById("Eastern Standard Time");
nowInNY = DateTime
.SpecifyKind(nowInNY, DateTimeKind.Unspecified);
var utc = TimeZoneInfo
.ConvertTimeToUtc(nowInNY, nyTZ);

Альтернативным решением будет создавать и хранить дату только в UTC:
var utc = DateTime.UtcNow;
и преобразовывать её в локальную:
var localTime = utc.ToLocalTime();
или в нужный часовой пояс:
var nyTime = TimeZoneInfo
.ConvertTimeFromUtc(utc, nyTZ);

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

В .NET 2 была введена структура DateTimeOffset. Она состоит из:
- структуры DateTime
- свойства Offset, хранящего смещение относительно UTC.

Однако проблема с серверами в разных часовых поясах никак не решается через DateTimeOffset.Now. Всё дело в переходе на летнее время. Для разных дат смещение в одном и том же часовом поясе будет разным. Кроме того, правила перехода на летнее время могут меняться (нам ли в РФ не знать). А значит невозможно определить происхождение даты и времени, просто взглянув на значение смещения. Поэтому для международного ПО лучше хранить часовой пояс для возможного пересчёта смещения по новым правилам. Для этого подходит класс TimeZoneInfo.

Окончание следует…

Источник:
https://www.infoq.com/articles/dotnet-unit-tests-time-timezone/
  • 👍 18
More from @netdeveloperdiary
  1. Oct 10, 2026День 2810. #ЧтоНовенького #VSCode Более Быстрый и Лёгкий C# Dev Kit Мы, разработчики, люби…
  2. Oct 9, 2026День 2809. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Окончание Начало 3.…
  3. Oct 8, 2026День 2808. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Начало В ИТ есть се…
  4. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
  5. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  6. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →