1. Знайомство з типом DateTime
Дата й час — це така базова цеглинка будь-якої бізнес-логіки, що її не можна ігнорувати навіть у найпростіших проєктах. Дуже часто потрібно знати, коли користувач зареєструвався, коли надіслати лист, як правильно опрацювати розклад рейсів; і часові пояси — всюди потрібні дати! Але, як показує практика, робота з датою й часом — одна з найпідступніших тем у програмуванні. 😅
C# пропонує два особливо важливих типи для роботи з часом: DateTime і DateTimeOffset. Якщо коротко — один для простих сценаріїв, інший для завдань, пов’язаних із часовими поясами та точним зберіганням часу.
Що таке DateTime?
Тип DateTime — це структура, визначена у просторі імен System. Вона призначена для зберігання дати й часу з точністю до 100 наносекунд (однієї десятимільярдної секунди).
- Тип значення (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; // пʼятниця
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. Як зберігаються дати під капотом
Щоб зручно додавати та віднімати дати, у .NET їх зберігають як кількість «тіків», що минули з початку літочислення. Діапазон значень:
| Властивість | Значення |
|---|---|
| Мінімальна дата | = 0001-01-01 00:00:00 |
| Максимальна дата | = 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?
- Поточний момент часу
DateTimeOffset now = DateTimeOffset.Now; // Локальний час + зміщення DateTimeOffset utc = DateTimeOffset.UtcNow; // UTC (нульове зміщення) - Явне створення
// Рік, місяць, день, година, хвилина, секунда, зміщення (наприклад, +2 години) DateTimeOffset custom = new DateTimeOffset(2025, 5, 24, 20, 0, 0, TimeSpan.FromHours(2)); - Перетворення з 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); // Для Берліна (UTC+2)
7. Коли використовувати DateTime, а коли — DateTimeOffset?
Філософія вибору
Коли йдеться про щось просте й локальне — наприклад, день народження, дату публікації статті або дедлайн для опитування до 15:00, найчастіше достатньо DateTime. У таких випадках абсолютний час не критичний, важливо лише, що подія відбулася у певний день і годину, без прив’язки до часового поясу.
А от якщо треба зафіксувати конкретний момент часу з урахуванням часового поясу, наприклад, у логах, транзакціях або розподілених системах — краще використовувати DateTimeOffset. Він не дасть заплутатися під час передавання часу між системами, особливо якщо вони перебувають у різних часових поясах.
| Сценарій | Тип | Чому |
|---|---|---|
| День народження | |
Важлива лише дата, пояс неважливий (зазвичай) |
| Час логіну | |
Важливо знати точний момент і зміщення |
| Термін дії товару | |
Дата закінчення, пояс неважливий |
| Час події у журналі подій | |
Бажано завжди зберігати зі зміщенням |
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ