Duration — niemutowalna klasa z pakietu java.time, reprezentująca czas trwania pomiędzy dwoma momentami w sekundach i nanosekundach. Duration mierzy ilość czasu opartą na czasie — godziny, minuty, sekundy, milisekundy i nanosekundy. Według specyfikacji Oracle Java 17 (2024), w przeciwieństwie do Period (który mierzy lata-miesiące-dni), Duration działa z dokładnymi jednostkami czasu i nie zależy od kalendarza.
Najważniejsze
Duration — to klasa modelująca ilość czasu w sekundach i nanosekundach. Reprezentuje czas trwania oparty na czasie, czyli fizyczną liczbę sekund, niezwiązaną z kalendarzem. Duration można przedstawić jako „125 minut” lub „2 godziny 5 minut” — w przeciwieństwie do Period, który powie „2 miesiące”.
Wewnętrzna reprezentacja Duration składa się z dwóch pól: long seconds (sekund) i int nanos (nanosekund, od 0 do 999999999). Wartość może być ujemna — oznacza to czas trwania „wstecz” w czasie. Maksymalna wartość to ±31557014167219200 sekund.
Według Baeldung (2024), Duration to kluczowa klasa do obliczania czasu trwania operacji, ustawiania timeoutów i pomiaru wydajności. Duration jest niemutowalna i bezpieczna wątkowo, co pozwala na jej używanie w środowisku wielowątkowym bez synchronizacji.
Klasa implementuje interfejsy Comparable, TemporalAmount i TemporalUnit. TemporalAmount umożliwia używanie Duration w metodach plus/minus klas LocalTime, LocalDateTime, Instant i ZonedDateTime.
Główna różnica — Duration mierzy dokładną liczbę sekund (time-based), a Period mierzy jednostki kalendarzowe (date-based): lata, miesiące, dni. Duration mówi: „minęło 86400 sekund”. Period mówi: „minął 1 dzień”. Różnica ujawnia się przy zmianie czasu na letni — 1 dzień w Period zawsze równa się 1 dniowi w kalendarzu, a 86400 sekund w Duration może odpowiadać 23 lub 25 godzinom przy DST.
Duration jest używane do pomiaru fizycznego czasu: timeouty połączenia, czas wykonania zapytania, interwał pomiędzy dwoma Instant. Period jest używane do obliczeń kalendarzowych: wiek osoby (Period.between(dataUrodzenia, dzisiaj)), okres ważności umowy.
Duration działa z sekundami i nanosekundami, więc można je dzielić na części (godziny, minuty). Period działa z latami, miesiącami i dniami — niepodzielnymi jednostkami kalendarzowymi. Według Oracle Java Tutorial (2024), wybór między Duration a Period zależy od rodzaju zadania: dokładny czas vs daty kalendarzowe.
Najczęstszy sposób — Duration.between(Temporal start, Temporal end). Temporal może być Instant, LocalTime, LocalDateTime, ZonedDateTime — dowolny typ implementujący Temporal. Metoda zwraca Duration reprezentującą różnicę start - end (może być ujemna).
Statyczne metody fabryczne: Duration.ofSeconds(long), ofMinutes(long), ofHours(long), ofDays(long), ofMillis(long), ofNanos(long). Jest też of(long amount, TemporalUnit unit) dla dowolnych jednostek — ChronoUnit.HOURS, ChronoUnit.MINUTES i innych.
Metoda parse(CharSequence) przyjmuje ciąg w formacie ISO-8601: „PT1H30M” (1 godzina 30 minut), „PT45S” (45 sekund), „P2DT3H” (2 dni 3 godziny). Ciąg zawsze zaczyna się od „PT” (Period of Time).
val betweenMoments = Duration.between(
Instant.parse("2026-07-21T10:00:00Z"),
Instant.parse("2026-07-21T14:30:00Z")
)
val fromMinutes = Duration.ofMinutes(90)
val fromHours = Duration.ofHours(2)
val parsed = Duration.parse("PT1H30M")
Duration obsługuje pełny zestaw operacji arytmetycznych. Metody plus(Duration), minus(Duration) dodają lub odejmują inną długość. Metody plusDays(), plusHours(), plusMinutes(), plusSeconds(), plusMillis(), plusNanos() — do dodawania poszczególnych jednostek.
Do mnożenia i dzielenia służą multipliedBy(long) i dividedBy(long). Duration.multipliedBy(2) podwaja czas trwania. Duration.dividedBy(3) dzieli na trzy części z zaokrągleniem w dół. Metoda negated() odwraca znak — dodatnia staje się ujemna i odwrotnie.
Metoda abs() zwraca Duration z wartością bezwzględną (dodatnią). isNegative() i isZero() — sprawdzenia. Do konwersji między Duration służą toDays(), toHours(), toMinutes(), toSeconds(), toMillis(), toNanos() — konwersja do odpowiednich jednostek.
val oneHour = Duration.ofHours(1)
val twoHours = oneHour.plus(Duration.ofMinutes(60))
val halfHour = oneHour.dividedBy(2)
val minutes = twoHours.toMinutes()
val absDuration = (Duration.ofHours(-1)).abs()
Duration implementuje interfejs Comparable, co pozwala na naturalne porównywanie czasów trwania. Metoda compareTo() zwraca liczbę ujemną, zero lub dodatnią. Metody isNegative() i isZero() — szybkie sprawdzenia. Do jawnego porównania używaj equals() — dwa Duration są równe, jeśli mają takie same sekundy i nanosekundy.
Ponieważ Duration może być ujemne, porównanie ‚większy” lub „mniejszy” działa z uwzględnieniem znaku. -5 minut jest mniejsze niż 2 minuty. Metoda abs() jest przydatna, jeśli potrzebujesz porównywać „bezwzględne” długości niezależnie od kierunku.
W Kotlinie Duration obsługuje operatory porównania przez przeciążanie operatorów: a < b, a > b, a <= b. Dostępne są również plus i minus jako operatory: a + b, a - b.
val short = Duration.ofMinutes(5)
val long = Duration.ofMinutes(10)
if (short < long) {
Log.d("Duration", "5 min to mniej niż 10")
}
val negative = Duration.ofMinutes(-3)
Log.d("Duration", "Ujemny: ${negative.isNegative()}")
Pierwszy przykład — konfiguracja okresowej synchronizacji z serwerem. Duration jest używane do obliczania interwału między synchronizacjami i sprawdzania, czy limit czasu bez aktualizacji nie został przekroczony.
data class SyncConfig(
val interval: Duration = Duration.ofMinutes(15),
val retryDelay: Duration = Duration.ofSeconds(30)
)
fun calculateNextSync(
lastSync: Instant,
config: SyncConfig
): Duration {
val elapsed = Duration.between(lastSync, Instant.now())
return config.interval.minus(elapsed)
.coerceAtLeast(Duration.ZERO)
}
Drugi przykład — pomiar czasu wykonania operacji do logowania wydajności.
fun measureExecution(
tag: String,
block: () -> Unit
) {
val start = Instant.now()
block()
val duration = Duration.between(start, Instant.now())
Log.d(tag, "Wykonano w ${duration.toMillis()} ms")
}
Trzeci przykład — obliczanie pozostałego czasu timera (na przykład do odliczania do zakończenia promocji).
class CountdownTimer(
private val expiresAt: Instant
) {
fun getRemainingTime(): Duration {
val remaining = Duration.between(
Instant.now(), expiresAt
)
return remaining.coerceAtLeast(Duration.ZERO)
}
fun isExpired(): Boolean = getRemainingTime() == Duration.ZERO
}
Metoda toString() zwraca Duration w formacie ISO-8601: „PT1H30M” (1 godzina 30 minut), „PT45.5S” (45.5 sekund). Ten format jest wygodny do wymiany maszynowej, ale nie do wyświetlania użytkownikowi.
Do formatowania czytelnego dla człowieka używaj toDays(), toHours(), toMinutes(), toSeconds() z ręcznym składaniem ciągu. Na przykład: „${days} d ${hours} g ${minutes} min”. Zwóć uwagę, że toHours() zwraca całkowitą liczbę godzin, a nie godziny w ciągu dnia.
Do podziału Duration na komponenty używa się wzoru: val hours = duration.toHours(); val minutes = duration.toMinutes() % 60; val seconds = duration.seconds % 60. Według Apache Commons Lang (2024), biblioteka DurationFormatUtils udostępnia dodatkowe możliwości formatowania.
fun formatDuration(duration: Duration): String {
val hours = duration.toHours()
val minutes = duration.toMinutes() % 60
val seconds = duration.seconds % 60
return buildString {
if (hours > 0) append("${hours} h ")
if (minutes > 0) append("${minutes} min ")
append("${seconds} sec")
}
}
Pierwszy błąd — pomylenie Duration i Period podczas pracy z datami. Duration mierzy sekundy, więc Duration.ofDays(1) to zawsze 24 godziny (86400 sekund), niezależnie od zmiany czasu na letni. Jeśli potrzebujesz dnia kalendarzowego, użyj Period.ofDays(1).
Drugi błąd — utrata nanosekund podczas konwersji. Duration może przechowywać nanosekundy, ale toMillis() i toSeconds() je odrzucają. Do dokładnych obliczeń używaj toNanos() lub pracuj z Duration bezpośrednio, bez konwersji na typy prymitywne.
Trzeci błąd — ignorowanie ujemnych Duration. Duration.between(start, end) zwraca start - end. Jeśli start jest po end, Duration będzie ujemna. Metoda abs() pomaga uzyskać wartość bezwzględną, a isNegative() — sprawdzić kolejność argumentów.
Czwarty błąd — nieprawidłowe formatowanie Duration dla UI. Duration.toString() zwraca ISO-8601, który jest nieczytelny. Zawsze formatuj Duration ręcznie do wyświetlenia użytkownikowi, używając toHours(), toMinutes() i toSeconds() z prawidłową resztą z dzielenia.
Często zadawane pytania
Tak, Duration może być ujemne. Duration.between(start, end) zwraca start - end. Jeśli start jest po end, Duration będzie ujemna. Użyj abs() aby uzyskać wartość bezwzględną lub isNegative() do sprawdzenia.
Użyj metody plus(Duration) lub operatora + w Kotlinie: duration1 + duration2. Wynik — nowa Duration. Metoda minus(Duration) odejmuje jedną długość od drugiej. Wszystkie operacje są niemutowalne i zwracają nowy obiekt.
Duration.ofDays(1) to zawsze 24 godziny (86400 sekund). Period.ofDays(1) to 1 dzień kalendarzowy, który przy DST może mieć 23 lub 25 godzin. Do obliczeń z dokładnym czasem używaj Duration, do kalendarzowych — Period.
Użyj metody toMillis(). Zwraca long — liczbę milisekund w Duration. Dla nanosekund użyj toNanos(). Uwaga: toNanos() może przekroczyć zakres long przy wartościach > 292 lat. Dla dużych Duration używaj toSeconds() lub toMinutes().
Użyj Duration.between(startTime, endTime). Jeśli endTime jest mniejsze niż startTime (zmiana nocna), Duration będzie ujemna. Dodaj 24 godziny: duration.plusHours(24), jeśli zakładasz, że endTime to następny dzień.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również