JavaRush /Курсы /C# SELF /Основные типы работы со временем

Основные типы работы со временем

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

1. Знакомство с типом DateTime

Дата и время — это такой базовый кирпичик любой бизнес-логики, что его нельзя игнорировать даже в самых простых проектах. Бывает часто нужно узнать, когда пользователь зарегистрировался, когда отправить письмо, правильно обработать расписание рейсов, временные зоны — везде даты! Но, как показывает практика, работа с датой и временем — одна из самых коварных тем в программировании. 😅

C# предлагает два особенно важных типа для работы со временем: DateTime и DateTimeOffset. Если говорить в двух словах — один для простых сценариев, другой для задач, связанных с временными зонами и точным хранением времени.

Что такое DateTime?

Тип DateTime — это структура, определённая в пространстве имён System. Она предназначена для хранения даты и времени с точностью до 100 наносекунд (1/10-ти миллиардная секунды).

  • Тип значения (struct), то есть копируется по значению.
  • Содержит как дату, так и время.
  • Позволяет хранить как просто дату (2025-05-24), так и дату с временем (2025-05-24 15:17:42).
  • «Не знает» о временных зонах, если явно не сказано.

2. Создаем DateTime

Вариантов несколько, рассмотрим главные.

1. Текущая дата и время

DateTime now = DateTime.Now;           // Локальное время компьютера
DateTime utcNow = DateTime.UtcNow;     // Время по Гринвичу (UTC)
  • Now — это "как на вашем компьютере", с учётом локальной часовой зоны Windows.
  • UtcNow — "универсальное", то есть мировое координированное время. Очень удобно для хранения времени, например, в базах данных или системах, которые работают по всему миру.

2. Конструкторы

// Год, месяц, день
var date1 = new DateTime(2025, 5, 24);

// Год, месяц, день, часы, минуты, секунды
var date2 = new DateTime(2025, 5, 24, 12, 0, 0);

// Год, месяц, день, часы, минуты, секунды, миллисекунды
var date3 = new DateTime(2025, 5, 24, 12, 0, 0, 500);

Месяцы не нумеруются с нуля! То есть январь — это 1, декабрь — 12.

3. Парсинг из строки

Очень часто дата приходит в виде строки! Например, пользователь вводит дату вручную.

string userInput = "2025-05-24 18:30";
DateTime parsed = DateTime.Parse(userInput); // Может бросить исключение!

Для безопасного парсинга — используем TryParse:

string input = "2025-05-24 18:30";
if (DateTime.TryParse(input, out DateTime result))
{
    Console.WriteLine($"Преобразовано успешно: {result}");
}
else
{
    Console.WriteLine("Не удалось преобразовать строку в дату.");
}

4. Получение даты "без времени"

DateTime today = DateTime.Today; // Текущий день, но время = 00:00:00

Извлечение компонентов даты и времени

Все свойства можно получить быстро:

var dt = new DateTime(2025, 5, 24, 15, 17, 42);

int year = dt.Year;         // 2025
int month = dt.Month;       // 5 (май)
int day = dt.Day;           // 24
int hour = dt.Hour;         // 15
int minute = dt.Minute;     // 17
int second = dt.Second;     // 42
DayOfWeek dow = dt.DayOfWeek; // Friday

3. Работа с типом DateTime

Сложение и вычитание дат

Время в программировании — штука быстрая, и чтобы не считать "а сколько же часов до дедлайна", используем методы класса:

DateTime now = DateTime.Now;
DateTime tomorrow = now.AddDays(1); // Завтра
DateTime afterFiveHours = now.AddHours(5); // Через 5 часов
DateTime inTenMinutes = now.AddMinutes(10); // Через 10 минут

Разность двух дат: TimeSpan

Разница между двумя датами — это отдельная величина, тип TimeSpan:

DateTime deadline = new DateTime(2026, 6, 1);
DateTime now = DateTime.Now;

TimeSpan untilDeadline = deadline - now;

Console.WriteLine($"До дедлайна осталось дней: {untilDeadline.Days}");
Console.WriteLine($"До дедлайна осталось часов: {untilDeadline.TotalHours:N2}");

TimeSpan — это "разница", а не "конкретная дата/время"!

Проверка даты в будущем/прошлом

if (deadline > now)
    Console.WriteLine("Дедлайн ещё впереди!");
else
    Console.WriteLine("Дедлайн уже прошёл! Срочно сдаём работу!");

4. Как хранятся даты под капотом

Для удобства сложения и вычитания дат, программисты решили хранить все даты как количество секунд прошедших с начала нашей эры. Значения диапазона:

Свойство Значение
Минимальная дата
DateTime.MinValue
= 0001-01-01 00:00:00
Максимальная дата
DateTime.MaxValue
= 9999-12-31 23:59:59.9999999
Единица измерения 1 тик = 100 наносекунд

DateTime сохраняет не просто количество секунд, а количество "тиков" (ticks) с особой даты — 1 января 0001 года (да-да, программисты не знают про динозавров, их летоисчисление строже). Один тик = 100 наносекунд.

5. Тип DateTimeOffset

Иногда типа DateTime оказывается мало. Рассмотрим классический случай: компания работает в двух странах, у них разные временные зоны, а все встречи запланированы "по-моему времени". Как быть?

Время, записанное как DateTime, зачастую "абстрактное": это просто набор циферок и тик, пока вы не укажете явно, в какой зоне это было. Например, если вы получили "2025-05-24 15:00", вы не можете быть уверены — это по времени Найроби? Багдада? Берлина? (а если ещё и летнее время — держитесь крепче!).

Поэтому для задач, связанных с точным моментом во времени, а не просто с "датой календаря", появился второй тип — DateTimeOffset. Создание DateTimeOffset с указанием смещения (например, Найроби +3 к UTC):


var localTime = new DateTimeOffset(2025, 5, 24, 15, 0, 0, TimeSpan.FromHours(3));

Используйте DateTimeOffset, если:

  • Нужно знать точный момент времени (например, для событий в логе, привязанных к серверному времени).
  • Программа работает с пользователями в разных временных зонах.
  • Надо хранить/передавать время с указанием зоны (например, для транзакций, событий, расписаний).

6. Как создать DateTimeOffset?

  1. Текущий момент времени
    DateTimeOffset now = DateTimeOffset.Now; // Локальное время + смещение
    DateTimeOffset utc = DateTimeOffset.UtcNow; // UTC (смещение 0)
    
  2. Явное создание
    // Год, месяц, день, час, минута, секунда, смещение (например, +2 часа)
    DateTimeOffset custom = new DateTimeOffset(2025, 5, 24, 20, 0, 0, TimeSpan.FromHours(2));
    
  3. Конвертация из DateTime
    DateTime dt = DateTime.Now;
    DateTimeOffset dto = new DateTimeOffset(dt); // Локальное смещение подхватится автоматически
    

Извлечение компонентов

DateTimeOffset dto = DateTimeOffset.Now;
var datePart = dto.Date;      // Только дата
var timePart = dto.TimeOfDay; // Только время
var offset = dto.Offset;      // Смещение относительно UTC, например, 03:00:00

Преобразование между зонами времени

Магия смещений:

// Получаем дату в UTC
var now = DateTimeOffset.Now;
var utc = now.ToUniversalTime();
Console.WriteLine(utc); // Время по Гринвичу

// Переводим в другую зону
var berlinTime = utc.ToOffset(TimeSpan.FromHours(2));
Console.WriteLine(berlinTime); // Для Берлина (CET +2)

7. Когда использовать DateTime, а когда — DateTimeOffset?

Философия выбора

Когда речь идёт о чём-то простом и локальном — например, дне рождения, дате публикации статьи или дедлайне на опрос до 15:00, чаще всего достаточно DateTime. В таких случаях абсолютное время не критично, и важно лишь, что событие произошло в определённый день и час, без привязки к часовой зоне.

А вот если нужно зафиксировать конкретный момент времени с учётом часового пояса, например, в логах, транзакциях или распределённых системах — лучше использовать DateTimeOffset. Он не даст запутаться при передаче времени между системами, особенно если они находятся в разных часовых поясах.

Сценарий Тип Почему
День рождения
DateTime
Важна только дата, зона не важна (обычно)
Время логина
DateTimeOffset
Важно знать точный момент и смещение
Срок действия товара
DateTime
Дата окончания, зона не важна
Время события в логе
DateTimeOffset
Желательно всегда хранить со смещением
2
Задача
C# SELF, 15 уровень, 0 лекция
Недоступна
Вывод текущей даты и времени в различных форматах
Вывод текущей даты и времени в различных форматах
2
Задача
C# SELF, 15 уровень, 0 лекция
Недоступна
Добавление и вычитание времени
Добавление и вычитание времени
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Александр Уровень 39
12 ноября 2025
хз, в логах на серваке время храним в utc и никогда никаких проблем не было. DateTimeOffset штука конечно интересная, но видимо не так часто необходимая.