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 е непроменяем и thread-safe, което позволява използването му в многонишкова среда без синхронизация.
Класът имплементира интерфейсите Comparable, TemporalAmount и TemporalUnit. TemporalAmount позволява използване на Duration в методите plus/minus на класовете LocalTime, LocalDateTime, Instant и ZonedDateTime.
Основната разлика — Duration измерва точния брой секунди (time-based), докато Period измерва календарни единици (date-based): години, месеци, дни. Duration казва: „изминаха 86400 секунди”. Period казва: „измина 1 ден”. Разликата се проявява при прехода към лятно часово време — 1 ден в Period е винаги равен на 1 ден в календара, а 86400 секунди в Duration могат да съответстват на 23 или 25 часа при DST.
Duration се използва за измерване на физическо време: таймаути за връзка, време за изпълнение на заявка, интервал между два Instant. Period се използва за календарни изчисления: възраст на човек (Period.between(датаНаРаждане, днес)), срок на валидност на договор.
Duration работи с секунди и наносекунди, така че може да бъде разделен на части (часове, минути). Period работи с години, месеци и дни — неделими календарни единици. Според Oracle Java Tutorial (2024), изборът между Duration и Period зависи от типа задача: точно време vs календарни дати.
Най-разпространеният начин — 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()} ms")
}
Третият пример — изчисляване на оставащото време на таймер (например, за отброяване до края на промоция).
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 за UI. 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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също