1. 简介
在开发软件时,处理时间间隔(duration)是常见需求。Kotlin 提供了 Duration
类,封装了一套强大且易用的时间间隔操作 API。
本文将深入介绍 Kotlin 中的 Duration
类,帮助你高效、准确地处理各类时间计算场景,比如任务调度、超时控制、耗时统计等,避免踩坑 ❌。
2. 什么是 Duration?
✅ Duration
表示一段具体的时间长度,可以是正数、负数、零,甚至是无限长(infinite)。
这类时间间隔通过 DurationUnit
枚举来定义单位,支持:
DAYS
HOURS
MINUTES
SECONDS
MILLISECONDS
MICROSECONDS
NANOSECONDS
⚠️ 注意:超过一天的时间统一以“天”为单位表示,即使实际是周或月。Kotlin 并不直接支持
YEARS
或MONTHS
单位(尽管 ISO8601 支持),这是为了规避闰年、月份天数不一致等复杂问题。
与用于标记某个具体时刻的日期时间类型(如 LocalDateTime
)不同,Duration
只关心“经过了多久”,而不关联任何具体时间点。例如,10.days
表示整整 10 天(即 240 小时),它描述的是一个时间段。
典型应用场景
- ✅ 定时任务调度(scheduling tasks)
- ✅ 接口超时设置
- ✅ 耗时监控(measuring elapsed time)
- ✅ 时间相关的业务逻辑计算
3. 创建 Duration 实例
Kotlin 提供了多种方式创建 Duration
,以下是常用方法。
3.1 使用伴生对象扩展函数(推荐)
Kotlin 在 Duration
的伴生对象中提供了简洁的扩展函数,可直接对数字调用:
val tenMinutes = 10.minutes
val tenSeconds = 10.seconds
val tenHours = 10.hours
val tenDays = 10.days
val tenMillis = 10.milliseconds
val tenMicros = 10.microseconds
val tenNanos = 10.nanoseconds
此外,还可以使用预定义常量:
val zero = Duration.ZERO // 零时长
val infinite = Duration.INFINITE // 无限时长
✅
Duration.INFINITE
特别适合配置永不超时的场景,比如某些后台任务或长轮询接口。
3.2 使用 toDuration()
方法
另一种方式是通过 toDuration()
扩展函数,显式传入单位:
val tenMinutes = 10.toDuration(DurationUnit.MINUTES)
val tenSeconds = 10.toDuration(DurationUnit.SECONDS)
该方法适用于动态单位场景,代码更清晰,尤其在单位来自变量时推荐使用。
3.3 从 ISO8601 字符串解析
✅ 支持从 ISO8601 格式的字符串创建 Duration
。
格式说明:
P
表示 Period 开始T
表示 Time 部分开始- 示例:
P10D
→ 10 天PT10M
→ 10 分钟(无天数部分)P10DT1H
→ 10 天 1 小时P10DT1H5M7S
→ 10 天 1 小时 5 分 7 秒
转换代码如下:
val tenMinDuration = Duration.parseIsoString("PT10M")
val tenDays = Duration.parseIsoString("P10D")
val tenDaysAndOneHour = Duration.parseIsoString("P10DT1H")
val tenDaysWithAllUnits = Duration.parseIsoString("P10DT1H5M7S")
⚠️ 注意:Kotlin 的
Duration.parseIsoString()
仅支持到D
(天),不支持Y
(年)和M
(月),这点与完整 ISO8601 标准有差异,使用时需注意。
4. Duration 操作
Duration
提供了丰富的操作方法,涵盖转换、运算、比较等。
4.1 单位转换与格式化
可以将 Duration
转换为指定单位的整数值:
val tenMinutes = 10.minutes
assertEquals(10L, tenMinutes.inWholeMinutes)
assertEquals(600L, tenMinutes.inWholeSeconds)
也可以转换为 ISO8601 字符串:
val tenSeconds = 10.seconds
assertEquals("PT10S", tenSeconds.toIsoString())
val tenDaysAndOneHour = Duration.parseIsoString("P10DT1H")
assertEquals("PT241H", tenDaysAndOneHour.toIsoString())
⚠️ 注意:
toIsoString()
会将所有时间归一化为小时表示(即D
被转为H
),所以P10D
会变成PT240H
。这是设计行为,不是 bug。
若需按天、小时、分钟等分别提取,可用 toComponents()
:
val seventyMinutes = 70.minutes
val asStr = seventyMinutes.toComponents { hrs, min, sec, nanos -> "${hrs}:${min}" }
assertEquals("1:10", asStr)
该方法支持 lambda 回调,便于格式化输出或业务处理。
4.2 算术运算
Duration
支持常见的算术操作,单位会自动转换:
val tenMinutes = 10.minutes
val fiveHours = 5.hours
val sum = tenMinutes + fiveHours
assertEquals(310L, sum.inWholeMinutes)
val diff = fiveHours - tenMinutes
assertEquals(290L, diff.inWholeMinutes)
val triple = tenMinutes.times(3)
assertEquals(30L, triple.inWholeMinutes)
val sixth = tenMinutes.div(100)
assertEquals(6, sixth.inWholeSeconds)
✅ 除了
+
、-
等操作符,也可使用plus()
和minus()
方法,效果相同。
这种统一的运算模型极大简化了混合单位的处理逻辑。
4.3 比较操作
支持直接使用比较操作符:
val tenMinutes = 10.minutes
val fiveHours = 5.hours
assertTrue { fiveHours > tenMinutes }
assertFalse { fiveHours < tenMinutes }
assertTrue { fiveHours == 300.minutes }
同时也提供语义化判断方法:
isInfinite()
:是否无限长isNegative()
:是否为负数isFinite()
:是否有限(非无限)isZero()
:是否为零
这些方法在条件判断中非常实用。
4.4 计算两个 DateTime 之间的时间差
可以通过 Java 的 java.time.Duration.between()
计算两个时间点之间的间隔,并转换为 Kotlin 的 Duration
:
val datetime1 = LocalDateTime.now()
val datetime2 = LocalDateTime.now().minusDays(1).minusHours(1)
val duration = java.time.Duration.between(datetime2, datetime1).toKotlinDuration()
val expectedDuration = 25.hours
assertEquals(expectedDuration, duration)
关键点:
- 使用
java.time.Duration.between(startTime, endTime)
- 调用
.toKotlinDuration()
扩展函数转换为 Kotlin 类型
4.5 实际应用示例
测量代码执行耗时
@ExperimentalTime
fun main() {
val elapsedTime = kotlin.time.measureTime {
Thread.sleep(510)
}
println(elapsedTime) // 输出类似 "510ms"
}
⚠️ 需启用
@ExperimentalTime
注解,生产环境建议封装成工具类。
Coroutine 中的延迟执行
@OptIn(ExperimentalTime::class)
fun main() = runBlocking {
val delayDuration = 1000.milliseconds
println("Task will execute after a delay of $delayDuration")
delay(delayDuration)
println("Task executed")
}
delay()
函数接受 Duration
类型参数,比传 long 更直观、安全。
5. 总结
Kotlin 的 Duration
类为时间间隔管理提供了现代化、类型安全的解决方案。相比原始的 Long
+ 单位注释方式,它具备以下优势:
- ✅ 语法简洁:
10.minutes
直观易读 - ✅ 类型安全:避免单位混淆
- ✅ 运算友好:支持加减乘除、比较
- ✅ 标准兼容:支持 ISO8601 解析与格式化
- ✅ 场景丰富:适用于耗时统计、超时控制、协程调度等
在实际项目中,建议全面替换 Long
表示时间的做法,统一使用 Duration
,提升代码可维护性与健壮性 ✅。