1. Історичний контекст: як працювали з датами до Java 8
У далекій давнині (до Java 8) для роботи з датами й часом використовували класи java.util.Date, java.util.Calendar, а для форматування — java.text.SimpleDateFormat. Розробники в усьому світі стикалися з труднощами й писали, наприклад:
import java.util.Date;
Date now = new Date();
System.out.println(now); // Виведе щось дивне на кшталт "Wed Jun 05 14:15:22 MSK 2025"
Звучить просто, але насправді все було не так райдужно. Ось лише деякі «радощі» старого API:
- Date — змінюваний (mutable) обʼєкт. Його можна було випадково змінити — і це часто призводило до помилок.
- Місяці в Date і Calendar починалися з нуля (січень — це 0, грудень — 11), а дні — з одиниці.
- SimpleDateFormat не був потокобезпечним: якщо два потоки форматували дату одночасно, можна було отримати непередбачувані результати.
- Багато методів позначили як @Deprecated (застарілі), і IDE постійно показувало жовті попередження.
- Робота з часовими поясами була справжньою проблемою: легко було сплутати локальний час і UTC, а про перехід на літній/зимовий час — краще взагалі не згадувати.
Приклад проблеми:
import java.util.Date;
Date date = new Date(2025, 5, 1); // 2025 рік, 5 місяць (червень?), 1 число
System.out.println(date); // Не те, чого ви очікуєте!
2. Поява java.time: новий підхід
До 2014 року стало очевидно: старий API не просто незручний — він небезпечний. Тому в Java зʼявився новий пакет — java.time, що реалізує специфікацію JSR‑310. Цей API був натхненний популярною бібліотекою Joda-Time і швидко став де‑факто стандартом.
Основні пакети й класи
- java.time — основний пакет, де розташовано всі нові класи для дат і часу.
- java.time.format — для форматування та розбору дат і часу.
- java.time.temporal — для розширених часових операцій.
- java.time.zone — для роботи з часовими поясами.
Ось головні класи нового API:
| Клас | Призначення | Приклад використання |
|---|---|---|
|
Лише дата (рік, місяць, день) | День народження, без часу |
|
Лише час (години, хвилини, секунди) | Час зустрічі, без дати |
|
Дата й час, без часового поясу | Локальна подія |
|
Дата й час з урахуванням часового поясу | Зустріч у Києві за місцевим часом |
|
Абсолютна точка часу (UTC) | Мітка події в журналі |
|
Проміжок часу (години, хвилини, секунди) | Тривалість дзвінка |
|
Період (роки, місяці, дні) | Стаж роботи, вік |
Приклад: створення дати по‑новому
import java.time.LocalDate;
LocalDate today = LocalDate.now();
System.out.println(today); // Наприклад, "2025-06-05"
3. Переваги нового API
Незмінність (immutable)
Усі класи з java.time — незмінні. Це означає, що якщо ви створили обʼєкт LocalDate, змінити його не можна. Будь‑яка операція (наприклад, додати день) повертає новий обʼєкт.
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
System.out.println(today); // 2025-06-05
System.out.println(tomorrow); // 2025-06-06
Явна робота з часовими поясами
У старому API легко було забути, у якому часовому поясі перебуває дата. У java.time усе явно: якщо потрібен часовий пояс — використовуйте ZonedDateTime, якщо не потрібен — використовуйте LocalDateTime.
import java.time.ZonedDateTime;
import java.time.ZoneId;
ZonedDateTime MinskTime = ZonedDateTime.now(ZoneId.of("Europe/Kyiv"));
System.out.println(MinskTime); // 2025-06-05T14:23:45+03:00[Europe/Kyiv]
Зручні методи для обчислень і порівняння
LocalDate today = LocalDate.now();
LocalDate nextMonth = today.plusMonths(1);
boolean isAfter = LocalDate.now().plusDays(1).isAfter(today); // true
Форматування та розбір
Форматування та розбір — через DateTimeFormatter (детальніше — на наступній лекції):
import java.time.format.DateTimeFormatter;
LocalDate today = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
String formatted = today.format(formatter); // "05.08.2025"
4. Сумісність: як жити зі старим кодом
У реальному світі дуже часто доводиться працювати з бібліотеками або старими системами, де використовуються Date і Calendar. На щастя, новий API добре взаємодіє із застарілим кодом: можна перетворювати старі типи на нові й навпаки.
Перетворення Date ↔ Instant
import java.util.Date;
import java.time.Instant;
// Date → Instant
Date legacyDate = new Date();
Instant instant = legacyDate.toInstant();
// Instant → Date
Date dateBack = Date.from(instant);
Перетворення Calendar ↔ ZonedDateTime
import java.util.Calendar;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.util.Date;
// Calendar → ZonedDateTime
Calendar calendar = Calendar.getInstance();
ZonedDateTime zdt = ZonedDateTime.ofInstant(
calendar.toInstant(),
calendar.getTimeZone().toZoneId()
);
// ZonedDateTime → Calendar
Calendar calBack = Calendar.getInstance();
calBack.setTime(Date.from(zdt.toInstant()));
Таблиця: відповідність старих і нових класів
| Старий клас | Новий клас | Коментар |
|---|---|---|
|
|
Абсолютний час |
|
|
Дата й час із часовим поясом |
|
|
Форматування/розбір дат |
5. Практика: перші кроки з java.time
Припустімо, у вас є користувач із датою народження. Збережемо й виведемо цю дату:
import java.time.LocalDate;
public class UserProfile {
private String name;
private LocalDate birthDate;
public UserProfile(String name, LocalDate birthDate) {
this.name = name;
this.birthDate = birthDate;
}
public void printProfile() {
System.out.println("Імʼя: " + name);
System.out.println("Дата народження: " + birthDate);
}
}
public class Main {
public static void main(String[] args) {
UserProfile user = new UserProfile("Аліса", LocalDate.of(1998, 12, 25));
user.printProfile();
}
}
Виведення:
Імʼя: Аліса
Дата народження: 1998-12-25
6. Порівняння: старий API vs новий API
Приклад: додати тиждень до дати народження
Старий спосіб (Date/Calendar):
import java.util.Calendar;
Calendar cal = Calendar.getInstance();
cal.set(1998, Calendar.DECEMBER, 25);
cal.add(Calendar.WEEK_OF_YEAR, 1);
System.out.println(cal.getTime()); // Незручно й неочевидно
Новий спосіб (java.time):
import java.time.LocalDate;
LocalDate birthDate = LocalDate.of(1998, 12, 25);
LocalDate nextWeek = birthDate.plusWeeks(1);
System.out.println(nextWeek); // 1999-01-01
У новому API код коротший, простіший і безпечніший.
7. Типові помилки під час роботи з java.time
Помилка № 1: забули, що обʼєкти незмінні.
Якщо ви викликаєте date.plusDays(1); і не зберігаєте результат, початкова дата не зміниться.
Помилка № 2: плутанина між LocalDate і LocalDateTime.
LocalDate зберігає тільки дату (рік, місяць, день), а LocalDateTime — ще й час. Не плутайте, якщо треба обробити і години, і хвилини.
Помилка № 3: використання старих класів у нових проєктах.
Якщо є можливість — завжди використовуйте java.time. Старі класи потрібні лише для сумісності.
Помилка № 4: неправильна робота з часовими поясами.
Якщо треба зберігати подію, важливу для різних регіонів, використовуйте ZonedDateTime або принаймні Instant. LocalDateTime не містить інформації про часовий пояс!
Помилка № 5: спроба безпосередньо порівнювати LocalDate і LocalDateTime.
Це різні типи даних — порівнювати їх безпосередньо не можна. Спочатку приведіть обидва до одного типу.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ