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 允许在 LocalTime、LocalDateTime、Instant 和 ZonedDateTime 类的 plus/minus 方法中使用 Duration。
主要区别 — 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.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)开头。
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() 方法以 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 库提供了额外的格式化能力。
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.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()。注意:当值大于 292 年时,toNanos() 可能溢出 long。对于大型 Duration,使用 toSeconds() 或 toMinutes()。
使用 Duration.between(startTime, endTime)。如果 endTime 小于 startTime(夜班),Duration 将是负数。添加 24 小时:duration.plusHours(24),如果你认为 endTime 是第二天。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。