Підрозділ 12.3
DateOnly та TimeOnly
Розглядає DateOnly і TimeOnly у .NET 6: конструктори, властивості, методи додавання, перетворення з DateTime, Parse, TryParse і форматоване виведення.
12.3. DateOnly та TimeOnly
Починаючи з .NET 6, мова отримала два спеціалізовані типи: DateOnly — лише дата без часу, і TimeOnly — лише час без дати. Обидва є структурами (struct) і розташовані в просторі імен System.
Навіщо вони потрібні, якщо є DateTime? Причина принципова: DateTime завжди містить обидва компоненти — і дату, і час. Для ситуацій, де час не має значення, використання DateTime породжує небажані питання: а що означає 00:00:00 у полі «дата народження»? Це північ UTC? Місцевий час? Відсутність часу? При передачі таких значень між серверами різних часових поясів виникають помилки конвертації.
DateOnly і TimeOnly усувають цю двозначність: вони фізично не можуть містити компонент, якого не повинно бути.

DateOnly — тільки дата
Структура DateOnly представляє календарну дату без будь-якої прив'язки до часу. Це ідеальний тип для: дати народження пацієнта, дати госпіталізації або виписки (якщо точний час не важливий), дати проведення аналізу, дати складання документа.
Конструктори
// Рік, місяць, день
DateOnly birth = new DateOnly(1958, 4, 15);
DateOnly today = DateOnly.FromDateTime(DateTime.Today); // з DateTime
// Із рядка
DateOnly parsed = DateOnly.Parse("11.06.2026");
// Із порядкового номера дня від 01.01.0001
DateOnly fromN = DateOnly.FromDayNumber(738920);При використанні конструктора без параметрів структура ініціалізується датою 01.01.0001:
DateOnly empty = new DateOnly();
Console.WriteLine(empty); // 01.01.0001Конструктор також приймає об'єкт календаря — System.Globalization.Calendar. Наприклад, розрахунок за юліанським календарем:
using System.Globalization;
DateOnly julianDate = new DateOnly(2026, 1, 6, new JulianCalendar());
Console.WriteLine(julianDate); // 19.01.2026 — той самий день за григоріанськимЦе корисно при роботі зі старими медичними архівами або документами, де дати записані за іншою системою числення.
Властивості DateOnly
| Властивість | Опис |
|---|---|
Year |
Рік |
Month |
Місяць (1-12) |
Day |
День (1-31) |
DayOfWeek |
День тижня (DayOfWeek.Monday тощо) |
DayOfYear |
Порядковий номер дня у році |
DayNumber |
Кількість днів від 01.01.0001 |
DateOnly date = new DateOnly(2026, 6, 11);
Console.WriteLine(date.Year); // 2026
Console.WriteLine(date.Month); // 6
Console.WriteLine(date.Day); // 11
Console.WriteLine(date.DayOfWeek); // Thursday
Console.WriteLine(date.DayOfYear); // 162
Console.WriteLine(date.DayNumber); // 738931Методи DateOnly
Арифметика дат:
DateOnly admission = new DateOnly(2026, 6, 11);
DateOnly discharge = admission.AddDays(7); // 18.06.2026
DateOnly nextVisit = discharge.AddMonths(1); // 18.07.2026
DateOnly nextYear = admission.AddYears(1); // 11.06.2027
DateOnly yesterday = admission.AddDays(-1); // 10.06.2026Різниця між датами — через DayNumber:
DateOnly start = new DateOnly(2026, 6, 1);
DateOnly end = new DateOnly(2026, 6, 18);
int daysBetween = end.DayNumber - start.DayNumber; // 17Форматування та конвертація:
DateOnly date = new DateOnly(2026, 6, 11);
Console.WriteLine(date.ToShortDateString()); // 11.06.2026
Console.WriteLine(date.ToLongDateString()); // 11 червня 2026 р.
Console.WriteLine(date.ToString("yyyy-MM-dd")); // 2026-06-11
// Комбінування з часом → DateTime
TimeOnly time = new TimeOnly(9, 30);
DateTime appointment = date.ToDateTime(time); // 11.06.2026 09:30:00Порівняння:
DateOnly d1 = new DateOnly(2026, 6, 1);
DateOnly d2 = new DateOnly(2026, 7, 1);
bool earlier = d1 < d2; // true
bool equal = d1 == d1; // trueTimeOnly — тільки час
Структура TimeOnly представляє час доби в діапазоні від 00:00:00.0000000 до 23:59:59.9999999, без прив'язки до конкретної дати. Це ідеальний тип для: часу прийому пацієнта (понеділок 09:00), робочих годин відділення, розкладу змін медперсоналу.
Важлива відмінність від TimeSpan (розд. 12.4): TimeOnly завжди представляє час на годиннику (0 – 23:59), тоді як TimeSpan може бути більшим за 24 години або від'ємним — це тривалість, а не час доби.
Конструктори
// Різна точність
TimeOnly t1 = new TimeOnly(9, 30); // 09:30:00
TimeOnly t2 = new TimeOnly(14, 45, 20); // 14:45:20
TimeOnly t3 = new TimeOnly(8, 0, 0, 500); // 08:00:00.500
// Із DateTime
TimeOnly fromDt = TimeOnly.FromDateTime(DateTime.Now);
// Із рядка
TimeOnly parsed = TimeOnly.Parse("14:30");
// Із TimeSpan
TimeOnly fromTs = TimeOnly.FromTimeSpan(new TimeSpan(9, 30, 0));При використанні конструктора без параметрів — 0:00:
TimeOnly empty = new TimeOnly();
Console.WriteLine(empty); // 0:00Властивості TimeOnly
| Властивість | Опис |
|---|---|
Hour |
Година (0-23) |
Minute |
Хвилини (0-59) |
Second |
Секунди (0-59) |
Millisecond |
Мілісекунди (0-999) |
Ticks |
Тіки від початку дня |
TimeOnly time = new TimeOnly(14, 23, 30);
Console.WriteLine(time.Hour); // 14
Console.WriteLine(time.Minute); // 23
Console.WriteLine(time.Second); // 30Методи TimeOnly
Арифметика:
TimeOnly start = new TimeOnly(9, 0);
TimeOnly end = start.AddHours(8); // 17:00 (завершення зміни)
TimeOnly mid = start.AddMinutes(30); // 09:30
// AddHours з переповненням через 24:00 не кидає виняток — обертається
TimeOnly night = new TimeOnly(23, 0);
TimeOnly next = night.AddHours(2); // 01:00 (наступний день)IsBetween — перевірка, чи потрапляє час у проміжок. Корисно для перевірки, чи прийом проводиться в межах робочого часу:
TimeOnly workStart = new TimeOnly(8, 0);
TimeOnly workEnd = new TimeOnly(17, 0);
TimeOnly appoint = new TimeOnly(10, 30);
bool inWork = appoint.IsBetween(workStart, workEnd); // trueКонвертація:
TimeOnly time = new TimeOnly(14, 30, 0);
TimeSpan ts = time.ToTimeSpan(); // 14:30:00 як TimeSpan
Console.WriteLine(time.ToShortTimeString()); // 14:30
Console.WriteLine(time.ToLongTimeString()); // 14:30:00Комбінування DateOnly і TimeOnly
Найпоширеніший сценарій — призначення прийому: є дата і є час, потрібно отримати повний DateTime:
DateOnly date = new DateOnly(2026, 6, 15); // дата прийому
TimeOnly time = new TimeOnly(10, 30); // час прийому
DateTime appointment = date.ToDateTime(time); // 15.06.2026 10:30:00
Console.WriteLine(appointment);Зворотня операція — розкладання DateTime на компоненти:
DateTime dt = new DateTime(2026, 6, 15, 10, 30, 0);
DateOnly d = DateOnly.FromDateTime(dt); // 15.06.2026
TimeOnly t = TimeOnly.FromDateTime(dt); // 10:30:00Цей підхід особливо зручний при зберіганні розкладу в базі даних: дата і час прийому зберігаються в окремих колонках (типи SQL date і time), і при читанні легко об'єднуються назад.