用Kotlin Duration来优化时间单位换算
时间单位换算在JVM中我们可以使用Java提供的TimeUnit,kotlin的Duration,以及直接数学运算实现。
kotlin.time.Duration是 Kotlin 标准库中的一个类,用于表示时间度量,它可以用于计算时间差等场景。该类提供了一些方便的方法来创建、比较和操作时间间隔。
先带入一个需求看看Duration是如何简单的优雅的实现的:
计算3天后的时间戳
//不使用Duration
@Test
fun time_millis() {
val currentTimeMillis = System.currentTimeMillis()
//1天24小时 每小时60分钟 1分钟60秒 每秒1000毫秒
val millisOfOneDay: Long = 24 * 3600 * 1000
println("format:${currentTimeMillis + 3 * millisOfOneDay}")
}
//Java TimeUnit
@Test
fun time_millis_java() {
val currentTimeMillis = System.currentTimeMillis()
val threeDaysOfMillis = TimeUnit.DAYS.toMillis(3)
println("format:${currentTimeMillis + threeDaysOfMillis}")
}
//使用Duration
@Test
fun time_millis_duration() {
val currentTimeMillis = System.currentTimeMillis()
val feature = currentTimeMillis.milliseconds + 3.days
println("format:${feature.inWholeMilliseconds}")
}
这样一对比Duration简化了时间单位换算的过程。尤其是较为复杂的时间运算和换算中会让你非常惊喜。虽然Java TimeUnit也可以增加可读性,但是不支持TimeUnit直接运算,后面我们会介绍。一起来学习下kotlin的时间度量api吧。
如何创建Duration
创建Duration最方便是使用Int, Long, Double的扩展属性: nanoseconds, microseconds, milliseconds, seconds, minutes, hours, days。Duration 可以是正、负、零、正无穷大或负无穷大。示例:
fun main() {
val fiveHundredMilliseconds: Duration = 500.milliseconds
val zeroSeconds: Duration = 0.seconds
val tenMinutes: Duration = 10.minutes
val negativeNanosecond: Duration = (-1).nanoseconds
val infiniteDays: Duration = Double.POSITIVE_INFINITY.days
val negativeInfiniteDays: Duration = Double.NEGATIVE_INFINITY.days
println(fiveHundredMilliseconds) // 500ms
println(zeroSeconds) // 0s
println(tenMinutes) // 10m
println(negativeNanosecond) // -1ns
println(infiniteDays) // Infinity
println(negativeInfiniteDays) // -Infinity
}
Duration执行单位换算
DurationUnit
kotlin标准库中定义了DurationUnit枚举类,表示支持的时间度量的单位
- NANOSECONDS
- MICROSECONDS
- MILLISECONDS
- SECONDS
- MINUTES
- HOURS
- DAYS
Duration 类中的一些方法可以接受DurationUnit作为参数,以便在不同的时间单位之间进行转换。例如,可以使用toLong方法将一个Duration对象转换为指定的时间单位(注意 时间戳 Duration.toInt可能会精度不够),如下所示:
fun main() {
//30分钟转换秒
val duration = 30.minutes
val seconds = duration.toLong(DurationUnit.SECONDS)
println(seconds)
// 1800
// 8小时转换天
val days = 12.hours.toDouble(DurationUnit.DAYS)
println("$days")
//0.5
}
inWhole 系列函数执行换算
当然kotlin还有更简单的实现方案,inWhole系列扩展属性,正好是对上面的toLong(DurationUnit)封装。另外也可以用inSeconds: Double但在1.5版本已经废弃了,建议用.toDouble(DurationUnit)。
- inWholeNanoseconds
- inWholeMicroseconds
- inWholeMilliseconds
- inWholeSeconds
- inWholeMinutes
- inWholeHours
- inWholeDays
fun main() {
val duration = 30.minutes
println(duration.inWholeHours)
// 0
println(duration.inWholeSeconds)
// 1800
}
Duration执行运算
Duration支持四则运算和逻辑运算,先看看官方四则运算示例。
fun main() {
//sampleStart
val fiveSeconds: Duration = 5.seconds
val thirtySeconds: Duration = 30.seconds
println(fiveSeconds + thirtySeconds)
// 35s
println(thirtySeconds - fiveSeconds)
// 25s
println(fiveSeconds * 2)
// 10s
println(thirtySeconds / 2)
// 15s
println(thirtySeconds / fiveSeconds)
// 6.0
println(-thirtySeconds)
// -30s
println((-thirtySeconds).absoluteValue)
// 30s
//sampleEnd
}
Duration对象无论是什么单位都可以相互运算
Duration对象无论是什么单位都可以相互运算
Duration对象无论是什么单位都可以相互运算
回头看看开头的例子,3.days和long.milliseconds可以直接相加而去计算时间戳,这确实大大简化了不同时间运算中单位换算的工作,太好用啦。
逻辑运算
比较 Duration 对象,使用比较运算符 (<, >,==):
fun main() {
val thirtyMinutes: Duration = 30.minutes
val halfHour: Duration = 0.5.hours
println(thirtyMinutes == halfHour)
// true
println(3000.microseconds < 25000.nanoseconds)
// false
}
分解计算
duration需要转换为其他对象时候,可以使用toComponents来获得分解值来构造其他对象
val duration: Duration
val result = duration.toComponents { seconds, nanoseconds -> ... }
duration.toComponents { minutes, seconds, nanoseconds -> }
duration.toComponents { hours, minutes, seconds, nanoseconds -> }
duration.toComponents { days, hours, minutes, seconds, nanoseconds -> }
下面的示例是把时间转换为字符串 时分格式
@Test
fun kotlin_time_setup3() {
val minutes: Duration = 78.minutes
println(minutes.toComponents { hours, minutes, _, _ -> "${hours}h:${minutes}m" })
// 1h:18m
}
在lambda 表达式中未使用的参数可以用
_来消除,当然了IDE也会提示你的。
获取字符串形式
获取字符串表示形式,以便您可以打印、序列化、传输或存储它。支持国际标准化组织的日期和时间的表示方法ISO 8601。为什么不叫格式化,因为不像DateFormatter支持Locale.setDefault()之后可以输出多语言格式。
要获取字符串表示形式,请使用 .toString() 函数。 默认情况下,使用现有的每个单位报告时间。 例如:1h 0m 45.677s 或 -(6d 5h 5m 28.284s)
要配置输出,请使用 .toString(...) 函数,并将所需的 DurationUnit 和小数位数作为函数参数。.toIsoString()输出ISO 8601时间格式:
@Test
fun kotlin_time_setup4() {
// Print in seconds with 2 decimal places
println(5887.milliseconds.toString(DurationUnit.SECONDS, 2))
// 5.89s
println(86420.seconds.toIsoString())
//PT24H0M20S
}
输出单位和对应的字符串代码源码如下:
internal fun DurationUnit.shortName(): String = when (this) {
DurationUnit.NANOSECONDS -> "ns"
DurationUnit.MICROSECONDS -> "us"
DurationUnit.MILLISECONDS -> "ms"
DurationUnit.SECONDS -> "s"
DurationUnit.MINUTES -> "m"
DurationUnit.HOURS -> "h"
DurationUnit.DAYS -> "d"
else -> error("Unknown unit: $this")
}
字符串形式转Duration
通过.toString(...)和.toIsoString()输出的字符串,可以通过parse系列函数转为Duration。parseOrNull和parseIsoStringOrNull可以保证解析失败时候空安全不会抛出异常。
@Test
fun kotlin_time_setup4() {
val duration = Duration.parse("5.89s")
println(duration.inWholeSeconds)
//5
val durationNullable = Duration.parseOrNull("5.89s")
durationNullable?.inWholeSeconds
val durationIso = Duration.parseIsoString("PT24H0M20S")
val durationIsoNullable = Duration.parseIsoStringOrNull("PT24H0M20S")
println(durationIso.inWholeSeconds)
//86420
}
实战案例
我们经常在社交APP中看到对发布内容的时间显示功能:“刚刚,2周前,2个月前”。那么这个功能在Duration来实现就非常简单了,这里就不讨论闰年和2月特殊情况。
@Test
fun kotlin_time_setup5() {
val serviceApiMillis = 1689015000000 //发布时的时间戳
val currentTimeMillis = System.currentTimeMillis()
val format = (currentTimeMillis - serviceApiMillis).milliseconds
.toComponents { days, hours, minutes, _, _ ->
when {
days > 365 -> "${days / 365}年前"
days > 30 -> "${days / 30}个月前"
days > 7 -> "${days / 7}周前"
days > 0 -> "${days}天前"
hours > 0 -> "${hours}小时前"
minutes > 0 -> "${minutes}分钟前"
else -> "刚刚"
}
}
println("format:$format")
}
Java TimeUnit
java提供了TimeUnit类来进行时间单位转换,但是不支持TimeUnit对象的直接运算。
//天数转秒数
TimeUnit.DAYS.toSeconds(1);
//天数转小时
TimeUnit.DAYS.toHours(1);
//秒数转毫秒数
TimeUnit.SECONDS.toMillis(60);
//秒数转分钟
TimeUnit.SECONDS.toMinutes(60);
//当前线程休眠三秒,底层调用的是Thread.sleep()
TimeUnit.SECONDS.sleep(3);
//天数转秒数,另一种转换方式
TimeUnit.DAYS.convert(1,TimeUnit.SECONDS);
总结
通过学习和实战总结一波,kotlin.time.Duration可以帮助我们更简单的实现时间运算和转换,所以在工作中如果遇到时间换算和运算问题,第一时间先试试Duration吧。
官方文档: book.kotlincn.net/text/time-m…
强烈推荐
源码剖析和实战:用Kotlin通杀“一切”进率换算