Duration — 是什么,在 Android 中计算持续时间

作者: IT Sectr 发布日期: 2026-07-13 阅读时间: 9 分钟

Duration — 来自 java.time 包的不可变类,以秒和纳秒为单位表示两个时刻之间的持续时间。Duration 测量基于时间的时间量 — 小时、分钟、秒、毫秒和纳秒。根据 Oracle Java 17 (2024) 的规范,与 Period(测量年-月-日)不同,Duration 使用精确的时间单位工作,不依赖于日历。

主要要点

  • Duration — 不可变类,用于以秒和纳秒为单位测量持续时间。
  • 与 Period 不同,Duration 使用基于时间的单位(小时、分钟、秒),而非基于日期的单位(天、月、年)。
  • 通过 Duration.between(start, end)、Duration.ofMinutes()、Duration.ofHours() 和其他静态工厂方法创建。
  • 支持算术运算:plus()、minus()、multipliedBy()、dividedBy()、negated()。
  • 对于 Android 开发,Duration 方便用于计算超时、同步间隔和操作持续时间。

什么是 Duration?

Duration — 是一个以秒和纳秒为单位建模时间量的类。它表示基于时间的持续时间,即物理秒数,不受日历绑绁。Duration 可以表示为“125分钟”或“2小时 5 分钟” — 与 Period 不同,后者会说“2个月”。

Duration 的内部表示由两个字段组成:long seconds(秒)和 int nanos(纳秒,从 0 到 999999999)。值可以是负数 — 这意味着时间向“后”的持续时间。最大值为 ±31557014167219200 秒。

根据 Baeldung (2024),Duration 是计算操作持续时间、设置超时和测量性能的关键类。Duration 是不可变和线程安全的,这使得它可以在多线程环境中无需同步地使用。

该类实现了 ComparableTemporalAmountTemporalUnit 接口。TemporalAmount 允许在 LocalTime、LocalDateTime、Instant 和 ZonedDateTime 类的 plus/minus 方法中使用 Duration。

Duration 与 Period:有什么区别?

主要区别 — Duration 测量精确的秒数(基于时间),而 Period 测量日历单位(基于日期):年、月、天。Duration 说:“已过去 86400 秒”。Period 说:“已过去 1 天”。差异在转换为夏令时时显现 — Period 中的 1 天始终等于日历中的 1 天,而 Duration 中的 86400 秒在 DST 时可能对应 23 或 25 小时。

Duration 用于测量物理时间:连接超时、请求执行时间、两个 Instant 之间的间隔。Period 用于日历计算:个人年龄(Period.between(生日, 今天))、合同有效期。

Duration 使用秒和纳秒工作,因此可以拆分为部分(小时、分钟)。Period 使用年、月和天工作 — 不可分割的日历单位。根据 Oracle Java Tutorial (2024),选择 Duration 还是 Period 取决于任务类型:精确时间与日历日期。

创建 Duration

最常见的方法 — Duration.between(Temporal start, Temporal end)。Temporal 可以是 Instant、LocalTime、LocalDateTime、ZonedDateTime — 任何实现 Temporal 的类型。该方法返回表示 start - end 差异的 Duration(可以是负数)。

静态工厂方法: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() — 检查方法。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 通过运算符重载支持比较运算符: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分钟小于10")
}

val negative = Duration.ofMinutes(-3)
Log.d("Duration", "负数: ${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, "执行于 ${duration.toMillis()} 毫秒")
}

第三个例子 — 计算定时器剩余时间(例如,倒计时直到活动结束)。

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() 方法以 ISO-8601 格式返回 Duration:“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 库提供了额外的格式化能力。

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() 用于检查参数顺序。

错误四 — 为 UI 错误地格式化 Duration。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) 方法从一个持续时间中减去另一个。所有操作都是不可变的并返回新对象。

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()。注意:当值大于 292 年时,toNanos() 可能溢出 long。对于大型 Duration,使用 toSeconds() 或 toMinutes()。

如何计算两个 LocalTime 之间的 Duration?

使用 Duration.between(startTime, endTime)。如果 endTime 小于 startTime(夜班),Duration 将是负数。添加 24 小时:duration.plusHours(24),如果你认为 endTime 是第二天。

总结

  • Duration — 不可变类,用于基于时间的持续时间,精确度高达纳秒,理想用于测量间隔。
  • 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自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读