Duration — незмінний клас з пакету java.time, що представляє тривалість між двома моментами часу в секундах та наносекундах. Duration вимірює часові кількості на основі часу — години, хвилини, секунди, мілісекунди та наносекунди. Згідно зі специфікацією Oracle Java 17 (2024), на відміну від Period (який вимірює роки-місяці-дні), Duration працює з точними одиницями часу і не залежить від календаря.
Головне
Duration — це клас, що моделює кількість часу в секундах та наносекундах. Він представляє часову тривалість, тобто фізичну кількість секунд, не прив’язану до календаря. Duration можна уявити як «125 хвилин» або «2 години 5 хвилин» — на відміну від Period, який би сказав «2 місяці».
Внутрішнє представлення Duration складається з двох полів: long seconds (секунди) та int nanos (наносекунди, від 0 до 999999999). Значення може бути від’ємним — це означає тривалість назад у часі. Максимальне значення — ±31557014167219200 секунд.
За даними Baeldung (2024), Duration — ключовий клас для розрахунку тривалості операцій, налаштування таймаутів та вимірювання продуктивності. Duration є незмінним та потокобезпечним, що дозволяє використовувати його в багатопотоковому середовищі без синхронізації.
Клас реалізує інтерфейси Comparable, TemporalAmount та TemporalUnit. TemporalAmount дозволяє використовувати Duration у методах plus/minus класів LocalTime, LocalDateTime, Instant та ZonedDateTime.
Головна відмінність полягає в тому, що Duration вимірює точну кількість секунд (на основі часу), тоді як Period вимірює календарні одиниці (на основі дати): роки, місяці, дні. Duration каже: «пройшло 86400 секунд». Period каже: «пройшов 1 день». Різниця проявляється під час переведення на літній час — 1 день в Period завжди є 1 календарний день, а 86400 секунд в Duration можуть відповідати 23 або 25 годинам під час DST.
Duration використовується для вимірювання фізичного часу: таймаути підключення, час виконання запиту, інтервали між двома Instant. Period використовується для календарних розрахунків: вік людини (Period.between(dateOfBirth, today)), термін дії контракту.
Duration працює з секундами та наносекундами, тому його можна ділити на частини (години, хвилини). Period працює з роками, місяцями та днями — неподільні календарні одиниці. Згідно з Oracle Java Tutorial (2024), вибір між Duration та Period залежить від типу завдання: точний час проти календарних дат.
Найпоширеніший спосіб — Duration.between(Temporal start, Temporal end). Temporal може бути Instant, LocalTime, LocalDateTime, ZonedDateTime — будь-який тип, що реалізує Temporal. Метод повертає Duration, що представляє різницю start - end (може бути від’ємною).
Статичні фабричні методи: Duration.ofSeconds(long), ofMinutes(long), ofHours(long), ofDays(long), ofMillis(long), ofNanos(long). Також є of(long amount, TemporalUnit unit) для довільних одиниць — ChronoUnit.HOURS, ChronoUnit.MINUTES та інших.
Метод parse(CharSequence) приймає рядок у форматі ISO-8601: «PT1H30M» (1 година 30 хвилин), «PT45S» (45 секунд), «P2DT3H» (2 дні 3 години). Рядок завжди починається з «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 підтримує повний набір арифметичних операцій. Методи plus(Duration) та minus(Duration) додають або віднімають іншу тривалість. Методи plusDays(), plusHours(), plusMinutes(), plusSeconds(), plusMillis(), plusNanos() — для точкового додавання одиниць.
Для множення та ділення використовуються multipliedBy(long) та dividedBy(long). Duration.multipliedBy(2) подвоїть тривалість. Duration.dividedBy(3) ділить на три частини з округленням вниз. Метод negated() інвертує знак — додатній стає від’ємним і навпаки.
Метод abs() повертає Duration з абсолютним (додатним) значенням. isNegative() та isZero() — перевірки. toDays(), toHours(), toMinutes(), toSeconds(), toMillis(), toNanos() — перетворення в відповідні одиниці.
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 реалізує інтерфейс Comparable, що дозволяє порівнювати тривалості природним чином. Метод compareTo() повертає від’ємне число, нуль або додатне число. isNegative() та isZero() — швидкі перевірки. Для явного порівняння використовуйте equals() — два Duration рівні, якщо їхні секунди та наносекунди збігаються.
Оскільки Duration може бути від’ємним, порівняння «більше» або «менше» працюють з урахуванням знака. -5 хвилин менше, ніж 2 хвилини. Метод abs() корисний для порівняння «абсолютних» довжин незалежно від напряму.
В Kotlin Duration підтримує оператори порівняння через перевантаження операторів: a < b, a > b, a <= b. Також plus та minus доступні як оператори: a + b, a - b.
val short = Duration.ofMinutes(5)
val long = Duration.ofMinutes(10)
if (short < long) {
Log.d("Duration", "5 хвилин менше ніж 10")
}
val negative = Duration.ofMinutes(-3)
Log.d("Duration", "Від’ємний: ${negative.isNegative()}")
Перший приклад — налаштування періодичної синхронізації з сервером. Duration використовується для розрахунку інтервалу між синхронізаціями та перевірки, чи не перевищено ліміт часу без оновлення.
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)
}
Другий приклад — вимірювання часу виконання операції для логування продуктивності.
fun measureExecution(
tag: String,
block: () -> Unit
) {
val start = Instant.now()
block()
val duration = Duration.between(start, Instant.now())
Log.d(tag, "Виконано за ${duration.toMillis()} мс")
}
Третій приклад — розрахунок часу залишку таймера (наприклад, зворотний відлік до закінчення акції).
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
}
Метод toString() повертає Duration у форматі ISO-8601: «PT1H30M» (1 година 30 хвилин), «PT45.5S» (45.5 секунд). Цей формат зручний для машинного обміну, але не для відображення користувачеві.
Для читабельного людьми формату використовуйте toDays(), toHours(), toMinutes(), toSeconds() з подальшим ручним складанням рядка. Наприклад: «${days} д ${hours} год ${minutes} хв». Зверніть увагу, що toHours() повертає загальну кількість годин, а не години в межах дня.
Для розбивки Duration на компоненти використовуйте формулу: val hours = duration.toHours(); val minutes = duration.toMinutes() % 60; val seconds = duration.seconds % 60. За даними Apache Commons Lang (2024), бібліотека DurationFormatUtils надає додаткові можливості форматування.
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")
}
}
Перша помилка — плутанина між Duration та Period при роботі з датами. Duration вимірює секунди, тому Duration.ofDays(1) — це завжди 24 години (86400 секунд), незалежно від переведення на літній час. Якщо потрібен календарний день, використовуйте Period.ofDays(1).
Друга помилка — втрата наносекунд під час перетворення. Duration може зберігати наносекунди, але toMillis() та toSeconds() відкидають їх. Для точних розрахунків використовуйте toNanos() або працюйте безпосередньо з Duration без перетворення в примітиви.
Третя помилка — ігнорування від’ємних Duration. Duration.between(start, end) повертає start - end. Якщо start після end, Duration буде від’ємним. Метод abs() допомагає отримати абсолютне значення, а isNegative() перевіряє порядок аргументів.
Четверта помилка — неправильне форматування Duration для інтерфейсу користувача. Duration.toString() повертає ISO-8601, який нечитабельний. Завжди форматуйте Duration вручну для відображення користувачеві, використовуючи toHours(), toMinutes() та toSeconds() з правильним залишком від ділення.
Часто запитувані питання
Так, Duration може бути від’ємним. Duration.between(start, end) повертає start - end. Якщо start після end, Duration буде від’ємним. Використовуйте abs() для отримання абсолютного значення або isNegative() для перевірки.
Використовуйте метод plus(Duration) або оператор + в Kotlin: duration1 + duration2. Результат — нова Duration. Метод minus(Duration) віднімає одну тривалість від іншої. Усі операції незмінні та повертають новий об’єкт.
Duration.ofDays(1) — це завжди 24 години (86400 секунд). Period.ofDays(1) — це 1 календарний день, який під час DST може бути 23 або 25 годин. Для точних розрахунків часу використовуйте Duration, для календарних — Period.
Використовуйте метод toMillis(). Він повертає long — кількість мілісекунд в Duration. Для наносекунд використовуйте toNanos(). Увага: toNanos() може переповнити long при значеннях > 292 роки. Для великих Duration використовуйте toSeconds() або toMinutes().
Використовуйте Duration.between(startTime, endTime). Якщо endTime менше startTime (нічна зміна), Duration буде від’ємною. Додайте 24 години: duration.plusHours(24), якщо передбачається, що endTime — це наступний день.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також