JavaRush /Курси /JAVA 25 SELF /Огляд API java.time: відмінності від застарілих API

Огляд API java.time: відмінності від застарілих API

JAVA 25 SELF
Рівень 13 , Лекція 1
Відкрита

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:

Клас Призначення Приклад використання
LocalDate
Лише дата (рік, місяць, день) День народження, без часу
LocalTime
Лише час (години, хвилини, секунди) Час зустрічі, без дати
LocalDateTime
Дата й час, без часового поясу Локальна подія
ZonedDateTime
Дата й час з урахуванням часового поясу Зустріч у Києві за місцевим часом
Instant
Абсолютна точка часу (UTC) Мітка події в журналі
Duration
Проміжок часу (години, хвилини, секунди) Тривалість дзвінка
Period
Період (роки, місяці, дні) Стаж роботи, вік

Приклад: створення дати по‑новому

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 добре взаємодіє із застарілим кодом: можна перетворювати старі типи на нові й навпаки.

Перетворення DateInstant

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);

Перетворення CalendarZonedDateTime

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()));

Таблиця: відповідність старих і нових класів

Старий клас Новий клас Коментар
java.util.Date
Instant
Абсолютний час
java.util.Calendar
ZonedDateTime
Дата й час із часовим поясом
java.text.SimpleDateFormat
DateTimeFormatter
Форматування/розбір дат

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.
Це різні типи даних — порівнювати їх безпосередньо не можна. Спочатку приведіть обидва до одного типу.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ