JavaRush /Курсы /C# SELF /Работа с таймзонами: Time...

Работа с таймзонами: TimeZoneInfo

C# SELF
15 уровень , 2 лекция
Открыта

1. Введение

Если вы думаете, что таймзоны — это просто "прибавить/вычесть сколько-то часов", вы очень скоро почувствуете всю боль большинства программистов на планете. Часовые пояса, перевод часов на зимнее/летнее время, неожиданные исключения при попытке "перевести время в другой регион" — всё это может привести к багам, которые сложно найти и ещё сложнее объяснить пользователям.

Самое ужасное: в мире нет и никогда не было "универсального" понимания времени — например, две соседние страны могут по-разному относиться к переходу на летнее время, а иногда решения о переходе принимаются посреди года (да, это реально так!). Пример: в 2011 году Самоа "перепрыгнула" через целый день и сменила сторону относительно линии перемены дат.

Но C# и .NET дают мощные инструменты, чтобы хоть как-то с этим бороться. Сегодня мы познакомимся с нашим спасителем — типом TimeZoneInfo и его функциями, которые помогут вам и вашему приложению.

Зачем и когда вам нужна работа с таймзонами

  • Когда пользователи/сервера находятся в разных регионах мира
  • Когда вы храните дату и время в базе данных в UTC, а показываете пользователю в его местном времени
  • Для расчёта расписаний, напоминаний, онлайн-событий ("Вебинар начнётся в 19:00 по Берлину — а в вашем регионе?")
  • При разработке корпоративных/глобальных систем (например, бухгалтерия или CRM для международного бизнеса)

Обзор типа TimeZoneInfo

TimeZoneInfo — это специально созданный класс в .NET, предназначенный для представления информации о часовом поясе: его смещение относительно UTC, название, поддержка переходов на летнее время и т.д.

В отличие от устаревшего класса TimeZone, который знáет только про "местный" и "UTC" часовые пояса, TimeZoneInfo даёт полноценный доступ ко всем часовым поясам, зарегистрированным в системе (и даже к произвольным, созданным вручную).

2. Получение информации о таймзонах

Локальный и UTC пояса

Получить информацию о локальном часовом поясе очень легко:


// Локальный (т.е. где сейчас работает ваша программа)
TimeZoneInfo local = TimeZoneInfo.Local;

// Всегда UTC
TimeZoneInfo utc = TimeZoneInfo.Utc;

Console.WriteLine(local.DisplayName); // Например: (UTC+01:00) Berlin, Vienna
Console.WriteLine(utc.DisplayName);   // (UTC) Coordinated Universal Time

Все доступные таймзоны

Часто нужно показать пользователю список всех возможных таймзон (например, при регистрации в сервисе):


foreach (var tz in TimeZoneInfo.GetSystemTimeZones())
{
    Console.WriteLine($"{tz.Id} | {tz.DisplayName}");
}

Это выведет список типа:

  • Central European Standard Time | (UTC+01:00) Berlin, Vienna
  • Pacific Standard Time | (UTC-08:00) Pacific Time (US & Canada)
  • и т.д.

Визуализация: основные свойства TimeZoneInfo

Свойство Описание
Id
Системный идентификатор таймзоны (используется при поиске)
DisplayName
Описание для пользователя (с часами и городами)
StandardName
Название "обычного" времени
DaylightName
Название времени для перехода на летнее время
BaseUtcOffset
Смещение от UTC (например, +01:00 для Берлина)
SupportsDaylightSavingTime
True, если таймзона поддерживает переходы на летнее/зимнее время

3. Конвертация времени между таймзонами

Вот зачем всё это затевалось! Вам нужно:

  • Перевести время из одной зоны в другую (например, сервер записал в UTC, а отображаем пользователю в его поясе)
  • Правильно обработать перевод на летнее/зимнее время

Общий синтаксис


DateTime utcNow = DateTime.UtcNow;

// Например, пересчитаем это время в центральноевропейский часовой пояс
TimeZoneInfo europeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
DateTime europeTime = TimeZoneInfo.ConvertTimeFromUtc(utcNow, europeZone);

Console.WriteLine($"UTC now: {utcNow}");       // 2024-06-20 10:30:00
Console.WriteLine($"Europe now: {europeTime}"); // 2024-06-20 11:30:00

Важно: Всегда используйте UTC для внутренних расчётов и хранения, и только "на выходе" переводите в нужную зону!

Как узнать идентификатор таймзоны?

В Windows идентификаторы (Id) таймзон специфичны (например, "Central European Standard Time"), а на Linux/Unix — обычно это IANA/Olson-идентификаторы ("Europe/Berlin", "America/New_York").

Список идентификаторов на вашей системе можно получить через TimeZoneInfo.GetSystemTimeZones().

4. Конвертация между произвольными зонами

Допустим, вы получили время в Лондоне (GMT/UTC+0) и хотите перевести его в Токио (UTC+9):


DateTime londonTime = new DateTime(2024, 6, 20, 12, 0, 0, DateTimeKind.Unspecified);

TimeZoneInfo londonZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
TimeZoneInfo tokyoZone  = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");

// Первый шаг: перевести время в UTC (если оно локальное)
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(londonTime, londonZone);

// Второй шаг: перевести UTC в Токийское время
DateTime tokyoTime = TimeZoneInfo.ConvertTimeFromUtc(utc, tokyoZone);

Console.WriteLine($"London: {londonTime} | UTC: {utc} | Tokyo: {tokyoTime}");

Работа с переходами на летнее и зимнее время

TimeZoneInfo учитывает переходы на летнее/зимнее время — если нужная зона их поддерживает.


TimeZoneInfo eastern = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

DateTime dateWinter = new DateTime(2024, 1, 1, 12, 0, 0, DateTimeKind.Unspecified);
DateTime dateSummer = new DateTime(2024, 7, 1, 12, 0, 0, DateTimeKind.Unspecified);

Console.WriteLine( TimeZoneInfo.ConvertTimeToUtc(dateWinter, eastern)); // Смещение UTC-5
Console.WriteLine( TimeZoneInfo.ConvertTimeToUtc(dateSummer, eastern)); // Смещение UTC-4 — летнее время!

Даже если США (из примера выше) откажутся от перехода на летнее время (а такие инициативы бывают каждый год!), база таймзон в вашей ОС всё равно будет обновляться, и ваш код будет работать корректно.

5. Практические советы и частые ошибки

Исключения и подводные камни

  • На месте, где переводятся часы (например, 2:30 ночи в день перехода на летнее/зимнее время), некоторые времена могут быть "несуществующими" или "повторяющимися":
    Например, в день перевода "назад" 2:30 происходит дважды — и оба раза "разные".
  • Не забывайте: список таймзон может меняться обновлениями ОС! Пример: некоторые страны отменяют/вводят переходы на летнее время.
2
Задача
C# SELF, 15 уровень, 2 лекция
Недоступна
Вывод локального и UTC пояса
Вывод локального и UTC пояса
2
Задача
C# SELF, 15 уровень, 2 лекция
Недоступна
Конвертация времени между двумя таймзонами
Конвертация времени между двумя таймзонами
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Артём Ляхов Уровень 60
24 сентября 2025
SupportsDaylightSavingTime не проверяет, используется ли сейчас летнее время в твоём регионе, а лишь говорит: «У этой временной зоны вообще когда-то в истории был DST (Daylight Saving Time) и в её правилах есть переходы». В России переход на летнее время отменили в 2014 году, но многие системные базы (tzdata, Windows time zones) всё ещё содержат исторические правила. Чтобы проверить когда происходили переводы, можно сделать так.

var ends = local
    .GetAdjustmentRules()
    .Select(r => r.DateEnd.ToString("yyyy-MM-dd"));

Console.WriteLine(string.Join(", ", ends));
Вывод будет примерно следующий:

0001-12-31
1915-12-31
1916-07-03
1916-12-31
1918-12-31
1919-07-15
1919-12-31
1929-12-31
1930-06-21
1981-09-30
1982-09-30
...
2013-12-31
2014-10-26