Duration — что это, вычисление продолжительности в Android

Автор: IT Sectr Опубликовано: 2026-07-13 Время чтения: 9 мин

Duration — immutable класс из пакета java.time, представляющий продолжительность между двумя моментами времени в секундах и наносекундах. Duration измеряет time-based количество времени — часы, минуты, секунды, миллисекунды и наносекунды. По данным спецификации Oracle Java 17 (2024), в отличие от Period (который измеряет годы-месяцы-дни), Duration работает с точными единицами времени и не зависит от календаря.

Главное

  • Duration — immutable класс для измерения продолжительности в секундах и наносекундах.
  • В отличие от Period, Duration работает с time-based единицами (часы, минуты, секунды), а не с date-based (дни, месяцы, годы).
  • Создаётся через Duration.between(start, end), Duration.ofMinutes(), Duration.ofHours() и другие статические фабрики.
  • Поддерживает арифметические операции: plus(), minus(), multipliedBy(), dividedBy(), negated().
  • Для Android разработки Duration удобен для расчёта таймаутов, интервалов синхронизации и длительности операций.

Что такое Duration?

Duration — это класс, моделирующий количество времени в секундах и наносекундах. Он представляет time-based продолжительность, то есть физическое количество секунд, не привязанное к календарю. Duration можно представить как "125 минут" или "2 часа 5 минут" — в отличие от Period, который скажет "2 месяца".

Внутреннее представление Duration состоит из двух полей: long seconds (секунды) и int nanos (наносекунды, от 0 до 999999999). Значение может быть отрицательным — это означает продолжительность "назад" во времени. Максимальное значение — ±31557014167219200 секунд.

По данным Baeldung (2024), Duration — ключевой класс для расчёта длительности операций, настройки таймаутов и измерения производительности. Duration immutable и thread-safe, что позволяет использовать его в многопоточной среде без синхронизации.

Класс реализует интерфейсы Comparable, TemporalAmount и TemporalUnit. TemporalAmount позволяет использовать Duration в методах plus/minus классов LocalTime, LocalDateTime, Instant и ZonedDateTime.

Duration vs Period: в чём разница?

Главное различие — Duration измеряет точное количество секунд (time-based), а Period измеряет календарные единицы (date-based): годы, месяцы, дни. 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 зависит от типа задачи: точное время vs календарные даты.

Создание Duration

Самый распространённый способ — 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).

kotlin
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

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() — проверки. между двумя Duration работает toDays(), toHours(), toMinutes(), toSeconds(), toMillis(), toNanos() — преобразование в соответствующие единицы.

kotlin
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

Duration реализует интерфейс Comparable, что позволяет сравнивать продолжительности естественным образом. Метод compareTo() возвращает отрицательное число, ноль или положительное. Методы isNegative() и isZero() — быстрые проверки. Для явного сравнения используйте equals() — два Duration равны, если совпадают секунды и наносекунды.

Поскольку Duration может быть отрицательным, сравнение "больше" или "меньше" работает с учётом знака. -5 минут меньше 2 минут. Метод abs() полезен, если нужно сравнивать "абсолютные" длины независимо от направления.

В Kotlin Duration поддерживает операторы сравнения через operator overloading: a < b, a > b, a <= b. Также доступны plus и minus как операторы: a + b, a - b.

kotlin
val short = Duration.ofMinutes(5)
val long = Duration.ofMinutes(10)

if (short < long) {
    Log.d("Duration", "5 min is less than 10")
}

val negative = Duration.ofMinutes(-3)
Log.d("Duration", "Negative: ${negative.isNegative()}")

Duration в реальных задачах Android

Первый пример — настройка периодической синхронизации с сервером. Duration используется для расчёта интервала между синхронизациями и проверки, не превышен ли лимит времени без обновления.

kotlin
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)
}

Второй пример — измерение времени выполнения операции для логирования производительности.

kotlin
fun measureExecution(
    tag: String,
    block: () -> Unit
) {
    val start = Instant.now()
    block()
    val duration = Duration.between(start, Instant.now())
    Log.d(tag, "Executed in ${duration.toMillis()} ms")
}

Третий пример — расчёт оставшегося времени таймера (например, для отсчёта до окончания акции).

kotlin
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
}

Преобразование Duration в строку

Метод toString() возвращает Duration в формате ISO-8601: "PT1H30M" (1 час 30 минут), "PT45.5S" (45.5 секунд). Этот формат удобен для машинного обмена, но не для отображения пользователю.

Для человекочитаемого формата используйте toDays(), toHours(), toMinutes(), toSeconds() с последующей ручной сборкой строки. Например: "${days} д ${hours} ч ${minutes} мин". Обратите внимание, что toHours() возвращает общее количество часов, а не часы в пределах дня.

Для разбивки Duration на компоненты используется formula: val hours = duration.toHours(); val minutes = duration.toMinutes() % 60; val seconds = duration.seconds % 60. По данным Apache Commons Lang (2024), библиотека DurationFormatUtils предоставляет дополнительные возможности форматирования.

kotlin
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 может быть отрицательным. Duration.between(start, end) возвращает start - end. Если start после end, Duration будет отрицательной. Используйте abs() для получения абсолютного значения или isNegative() для проверки.

Как сложить две Duration?

Используйте метод plus(Duration) или оператор + в Kotlin: duration1 + duration2. Результат — новая Duration. Метод minus(Duration) вычитает одну продолжительность из другой. Все операции immutable и возвращают новый объект.

Чем Duration.ofDays(1) отличается от Period.ofDays(1)?

Duration.ofDays(1) — это всегда 24 часа (86400 секунд). Period.ofDays(1) — это 1 календарный день, который при DST может быть 23 или 25 часов. Для расчётов с точным временем используйте Duration, для календарных — Period.

Как преобразовать Duration в миллисекунды?

Используйте метод toMillis(). Он возвращает long — количество миллисекунд в Duration. Для наносекунд используйте toNanos(). Внимание: toNanos() может переполнить long при значениях > 292 лет. Для больших Duration используйте toSeconds() или toMinutes().

Как рассчитать Duration между двумя LocalTime?

Используйте Duration.between(startTime, endTime). Если endTime меньше startTime (ночная смена), Duration будет отрицательной. Добавьте 24 часа: duration.plusHours(24), если предполагается, что endTime — это следующий день.

Итоги

  • Duration — immutable класс для time-based продолжительности с точностью до наносекунд, идеален для измерения интервалов.
  • Основное отличие от Period — Duration измеряет секунды (физическое время), Period — календарные единицы.
  • Создаётся через Duration.between() для разницы между моментами или через статические фабрики ofMinutes, ofHours, ofDays.
  • Поддерживает арифметику: plus, minus, multipliedBy, dividedBy, а также сравнение через Comparable и abs.
  • В Android Duration применяется для расчёта таймаутов, интервалов синхронизации, таймеров и измерения производительности.
  • При отображении пользователю форматируйте Duration вручную — toString() выводит ISO-8601, нечитаемый для человека.
  • Учитывайте, что Duration может быть отрицательным — проверяйте isNegative() и используйте abs() для корректных расчётов.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также