1. Dodawanie i odejmowanie dat i czasu
W programowaniu praca z datami i czasem to nie tylko „ładnie wyświetlić datę na ekranie”. Bardzo często trzeba:
- dowiedzieć się, ile dni zostało do ważnego wydarzenia;
- policzyć wiek użytkownika;
- określić, ile czasu upłynęło między dwoma zdarzeniami;
- sprawdzić, czy termin (deadline) już nadszedł;
- dodać do daty określoną liczbę dni, tygodni, miesięcy lub godzin.
Na przykład w naszej aplikacji chcemy przypomnieć użytkownikowi, że do deadline’u zadania zostały 3 dni. Albo pogratulować mu minionych urodzin (jeśli data jest już w przeszłości).
Do tego w java.time są specjalne klasy i metody, które czynią takie obliczenia prostymi i bezpiecznymi.
Metody podstawowe do dodawania i odejmowania
Wszystkie klasy daty i czasu (LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant) mają metody:
- plusXxx() — dodać (dni, miesiące, lata, godziny, minuty itp.)
- minusXxx() — odjąć (to samo)
import java.time.LocalDate;
LocalDate today = LocalDate.now(); // Dzisiejsza data
LocalDate tomorrow = today.plusDays(1); // Jutro
LocalDate nextMonth = today.plusMonths(1); // Za miesiąc
LocalDate lastWeek = today.minusWeeks(1); // Tydzień temu
System.out.println("Dzisiaj: " + today);
System.out.println("Jutro: " + tomorrow);
System.out.println("Za miesiąc: " + nextMonth);
System.out.println("Tydzień temu: " + lastWeek);
Wszystkie te metody zwracają nowy obiekt, a nie zmieniają obiektu źródłowego. Niemutowalność — to podstawa!
import java.time.LocalTime;
LocalTime now = LocalTime.now();
LocalTime inTwoHours = now.plusHours(2);
LocalTime tenMinutesAgo = now.minusMinutes(10);
System.out.println("Teraz: " + now);
System.out.println("Za 2 godziny: " + inTwoHours);
System.out.println("10 minut temu: " + tenMinutesAgo);
Metody łączone
LocalDate vacation = today.plusMonths(2).plusDays(10);
System.out.println("Urlop: " + vacation);
2. Obliczanie różnicy między datami: Period i Duration
Gdy trzeba dowiedzieć się, ile czasu upłynęło między dwiema datami lub momentami w czasie, na scenę wchodzą dwaj bohaterowie — Period i Duration. Poznajmy ich bliżej.
Jaka jest różnica między Period a Duration?
- Period — do pracy z datami (rok, miesiąc, dzień). Na przykład „3 lata, 2 miesiące i 5 dni”.
- Duration — do pracy z czasem (godziny, minuty, sekundy, nanosekundy). Na przykład „5 godzin 30 minut”.
| Klasa | Do czego służy | Przykład użycia |
|---|---|---|
|
Różnica między datami | Ile lat/miesięcy/dni pomiędzy datami |
|
Różnica między momentami | Ile godzin/minut/sekund między zdarzeniami |
Przykład: obliczamy wiek użytkownika
import java.time.LocalDate;
import java.time.Period;
LocalDate birthday = LocalDate.of(2000, 1, 15); // Urodziny
LocalDate today = LocalDate.now();
Period age = Period.between(birthday, today);
System.out.println("Wiek: " + age.getYears() + " lat, " +
age.getMonths() + " miesięcy, " +
age.getDays() + " dni");
Analogicznie: Period to „ludzka” różnica między datami („mam 24 lata 5 miesięcy 12 dni”).
Przykład: ile czasu pozostało do deadline’u
import java.time.LocalDate;
LocalDate deadline = LocalDate.of(2025, 7, 1);
LocalDate today = LocalDate.now();
if (today.isBefore(deadline)) {
Period left = Period.between(today, deadline);
System.out.println("Do deadline’u pozostało: " +
left.getMonths() + " miesięcy i " +
left.getDays() + " dni");
} else {
System.out.println("Deadline już minął!");
}
Duration: różnica czasu
import java.time.LocalDateTime;
import java.time.Duration;
LocalDateTime start = LocalDateTime.of(2025, 6, 1, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 1, 15, 30, 0);
Duration duration = Duration.between(start, end);
System.out.println("Czas trwania: " + duration.toHours() + " godzin " +
(duration.toMinutes() % 60) + " minut");
Duration mierzy różnicę w sekundach, minutach, godzinach, dniach (ale dni — zawsze po 24 godziny, bez uwzględniania specyfiki kalendarza).
Porównanie: kiedy używać Period, a kiedy Duration?
- Jeśli masz tylko daty (bez czasu) — użyj Period.
- Jeśli masz czas (na przykład LocalDateTime, Instant) — użyj Duration.
3. Porównywanie dat i czasu: isBefore, isAfter, equals
Czasem trzeba po prostu ustalić: czy data już nadeszła, czy jeszcze nie? Zdarzenie jest w przeszłości czy w przyszłości? Do tego wszystkie klasy daty i czasu mają metody:
- isBefore(otherDate)
- isAfter(otherDate)
- isEqual(otherDate) (albo po prostu equals)
import java.time.LocalDate;
LocalDate today = LocalDate.now();
LocalDate deadline = LocalDate.of(2025, 7, 1);
if (today.isBefore(deadline)) {
System.out.println("Wciąż jest czas!");
} else if (today.isEqual(deadline)) {
System.out.println("Dziś deadline!");
} else {
System.out.println("Deadline już minął :(");
}
import java.time.LocalTime;
LocalTime now = LocalTime.now();
LocalTime lunch = LocalTime.of(13, 0);
if (now.isAfter(lunch)) {
System.out.println("Obiad już był!");
} else {
System.out.println("Do obiadu wciąż jest czas.");
}
Ważne: porównywać można tylko obiekty tego samego typu (LocalDate z LocalDate, LocalDateTime z LocalDateTime itd.).
4. Praktyka: zadania na obliczanie różnic i sprawdzanie terminów
Przykład: ile dni zostało do deadline’u zadania
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
LocalDate today = LocalDate.now();
LocalDate deadline = LocalDate.of(2025, 7, 1);
long daysLeft = ChronoUnit.DAYS.between(today, deadline);
if (daysLeft > 0) {
System.out.println("Do deadline’u pozostało " + daysLeft + " dni");
} else if (daysLeft == 0) {
System.out.println("Dziś deadline!");
} else {
System.out.println("Deadline minął " + Math.abs(daysLeft) + " dni temu");
}
ChronoUnit.DAYS.between() — wygodny sposób, by poznać dokładną liczbę dni między dwiema datami (bez uwzględniania miesięcy i lat).
Przykład: ile sekund między dwoma zdarzeniami
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
LocalDateTime start = LocalDateTime.of(2025, 6, 1, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 1, 15, 30, 0);
long seconds = ChronoUnit.SECONDS.between(start, end);
System.out.println("Między zdarzeniami upłynęło " + seconds + " sekund");
Przykład: obliczanie „wieku” zadania
import java.time.LocalDate;
import java.time.Period;
LocalDate created = LocalDate.of(2025, 5, 20);
LocalDate today = LocalDate.now();
Period period = Period.between(created, today);
System.out.println("Zadanie zostało utworzone " +
period.getDays() + " dni, " +
period.getMonths() + " miesięcy i " +
period.getYears() + " lat temu.");
Przykład: długość między dwoma Instant
import java.time.Instant;
import java.time.Duration;
Instant start = Instant.now();
// ... coś robimy ...
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.println("Wykonanie zajęło " + duration.toMillis() + " milisekund");
5. Szczegóły użycia Period i Duration
Specyfika Period
- Period liczy różnicę według kalendarza: między 28 lutego a 1 marca — 1 dzień, nawet jeśli to różne miesiące.
- Można osobno pobrać liczbę lat, miesięcy, dni: getYears(), getMonths(), getDays().
- Jeśli potrzebujesz poznać łączną liczbę dni między datami — użyj ChronoUnit.DAYS.between().
Specyfika Duration
- Duration pracuje z dokładnym czasem (godziny, minuty, sekundy).
- Może współpracować z LocalTime, LocalDateTime, Instant.
- Dla dat bez czasu (LocalDate) użyć nie można — będzie wyjątek.
Przykład: różnica między Period i Duration
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Duration;
import java.time.Period;
LocalDate date1 = LocalDate.of(2025, 2, 28);
LocalDate date2 = LocalDate.of(2025, 3, 1);
Period period = Period.between(date1, date2); // 2 dni (rok przestępny)
long days = java.time.temporal.ChronoUnit.DAYS.between(date1, date2); // 2 dni
System.out.println("Period: " + period.getDays() + " dni");
System.out.println("ChronoUnit: " + days + " dni");
LocalDateTime dt1 = LocalDateTime.of(2025, 6, 1, 23, 0);
LocalDateTime dt2 = LocalDateTime.of(2025, 6, 2, 1, 0);
Duration duration = Duration.between(dt1, dt2); // 2 godziny
System.out.println("Duration: " + duration.toHours() + " godzin");
6. Użycie Duration i Period do prezentacji różnicy w wygodnej formie
Gdy obliczasz różnicę między datami lub czasem dla użytkownika, nie zawsze wygodnie jest pokazywać „123456 sekund”. Zwykle chcemy coś w rodzaju „2 dni 3 godziny 15 minut”.
Przykład: wyświetlenie różnicy między datą i czasem
import java.time.LocalDateTime;
import java.time.Duration;
LocalDateTime start = LocalDateTime.of(2025, 6, 1, 14, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 3, 16, 30);
Duration duration = Duration.between(start, end);
long days = duration.toDays();
long hours = duration.minusDays(days).toHours();
long minutes = duration.minusDays(days).minusHours(hours).toMinutes();
System.out.println("Różnica: " + days + " dni " +
hours + " godzin " + minutes + " minut");
7. Specyfika pracy z ZonedDateTime i Instant
- Różnica między dwoma ZonedDateTime w różnych strefach jest liczona według czasu absolutnego, a nie „ludzkiego” czasu w każdej strefie.
- Przejście na czas letni/zimowy może wpływać na różnicę godzin między datami.
Przykład: różnica między ZonedDateTime
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.Duration;
ZonedDateTime MinskTime = ZonedDateTime.of(2025, 6, 1, 12, 0, 0, 0, ZoneId.of("Europe/Minsk"));
ZonedDateTime nyTime = ZonedDateTime.of(2025, 6, 1, 5, 0, 0, 0, ZoneId.of("America/New_York"));
Duration diff = Duration.between(nyTime, MinskTime);
System.out.println("Różnica: " + diff.toHours() + " godzin");
8. Typowe błędy przy obliczeniach z datami i czasem
Błąd nr 1: Mylenie Period i Duration.
Jeśli spróbujesz użyć Duration dla LocalDate, dostaniesz wyjątek. Dla dat używaj Period, dla czasu — Duration.
Błąd nr 2: Nieuwzględnianie stref czasowych.
Jeśli porównujesz daty i czas w różnych strefach czasowych, możesz otrzymać nieoczekiwane wyniki. Lepiej sprowadzić wszystko do jednej strefy albo używać Instant.
Błąd nr 3: Oczekiwanie, że metody plus/minus modyfikują obiekt.
Wszystkie obiekty java.time są niemutowalne! Pamiętaj, aby przypisać wynik do nowej zmiennej (na przykład today = today.plusDays(1)).
Błąd nr 4: Używanie getDays() w Period i mylenie tego z łączną liczbą dni.
W rzeczywistości to tylko dni resztowe po odjęciu lat i miesięcy. Aby poznać łączną liczbę dni, użyj ChronoUnit.DAYS.between().
Błąd nr 5: Porównywanie obiektów różnych typów.
Metody isBefore, isAfter, isEqual działają tylko dla obiektów tego samego typu (na przykład LocalDate z LocalDate).
GO TO FULL VERSION