前置知识: Kotlin

Kotlin与时间

00:00
3 min Intermediate 2026/6/14

kotlinx-datetime

概述

kotlinx-datetime 是 Kotlin 官方的跨平台日期时间库。它基于 ISO 8601 标准提供了统一的 API 来处理日期、时间、时区等概念。与 Java 的 java.time 不同,kotlinx-datetime 从一开始就为 Kotlin 多平台设计,可以在 JVM、JS、Native 等平台上使用。

如果你需要在项目处理日期、时间计算、时区转换,kotlinx-datetime 是比 java.util.Datejava.util.Calendar 更现代、更安全选择

基础概念

  • Instant时间线上的一个瞬时类似时间戳,不关联任何时区
  • LocalDate:不时间和时区的日期,如 2024-01-15
  • LocalTime:不含日期和时区的时间,如 14:30:00
  • LocalDateTime:日期和时间组合,但没有时区信息
  • TimeZone:时区,用于在 Instant 和本地时间之间转换
  • Clock:时钟抽象,用于获取当前时间,方便测试

快速上手

添加依赖

// build.gradle.kts
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0")
}

最基本的使用:

import kotlinx.datetime.*

fun main() {
    // 获取当前时间
    val now = Clock.System.now()
    println("当前时间戳: $now")

    // 获取当前日期(需要指定时区)
    val today = now.toLocalDateTime(TimeZone.currentSystemDefault()).date
    println("今天的日期: $today")

    // 创建指定日期
    val birthday = LocalDate(2000, Month.JANUARY, 15)
    println("生日: $birthday")

    // 日期计算
    val age = today.year - birthday.year
    println("年龄: $age")

    // 时长
    val duration = 30.minutes
    val future = now + duration
    println("30分钟后: $future")
}

详细用法

Instant 时间戳操作

import kotlinx.datetime.*

fun instantDemo() {
    // 获取当前时刻
    val now = Clock.System.now()
    println("当前时刻: $now")

    // 从时间戳创建
    val fromEpoch = Instant.fromEpochSeconds(1700000000)
    println("从时间戳创建: $fromEpoch")

    // 获取时间戳的秒数和毫秒数
    println("秒: ${now.epochSeconds}")
    println("毫秒: ${now.toEpochMilliseconds()}")

    // 时间加减
    val tomorrow = now + 1.days
    val nextHour = now + 1.hours
    val nextMinute = now + 30.minutes

    // 时间差
    val duration = tomorrow - now
    println("差值: $duration")  // 1d

    // 比较时间
    println("明天在现在之后: ${tomorrow > now}")
}

LocalDate 日期操作

import kotlinx.datetime.*

fun localDateDemo() {
    // 创建日期
    val date = LocalDate(2024, Month.JUNE, 15)
    println("日期: $date")

    // 从字符串解析
    val parsed = LocalDate.parse("2024-06-15")
    println("解析: $parsed")

    // 获取日期的各个部分
    println("年: ${date.year}")
    println("月: ${date.month}")        // JUNE
    println("月份数字: ${date.monthNumber}")  // 6
    println("日: ${date.dayOfMonth}")
    println("星期: ${date.dayOfWeek}")  // SATURDAY

    // 日期加减
    val nextWeek = date + DatePeriod(days = 7)
    val nextMonth = date + DatePeriod(months = 1)
    val lastYear = date - DatePeriod(years = 1)

    // 日期差
    val start = LocalDate(2024, Month.JANUARY, 1)
    val end = LocalDate(2024, Month.DECEMBER, 31)
    val period = start.until(end)
    println("相差: ${period.years}年${period.months}月${period.days}日")
}

LocalTime 时间操作

import kotlinx.datetime.*

fun localTimeDemo() {
    // 创建时间
    val time = LocalTime(14, 30, 0)
    println("时间: $time")

    // 带纳秒
    val precise = LocalTime(14, 30, 0, 500000000)
    println("精确时间: $precise")

    // 获取时间的各个部分
    println("时: ${time.hour}")
    println("分: ${time.minute}")
    println("秒: ${time.second}")

    // 从字符串解析
    val parsed = LocalTime.parse("14:30:00")
    println("解析: $parsed")

    // 时间加减
    val later = time + 30.minutes
    val earlier = time - 1.hours
    println("30分钟后: $later")
    println("1小时前: $earlier")
}

时区转换

import kotlinx.datetime.*

fun timeZoneDemo() {
    val now = Clock.System.now()

    // 获取系统默认时区
    val systemTz = TimeZone.currentSystemDefault()
    println("系统时区: $systemTz")

    // 指定时区
    val beijing = TimeZone.of("Asia/Shanghai")
    val tokyo = TimeZone.of("Asia/Tokyo")
    val newYork = TimeZone.of("America/New_York")
    val london = TimeZone.of("Europe/London")

    // 同一时刻在不同时区的本地时间
    val beijingTime = now.toLocalDateTime(beijing)
    val tokyoTime = now.toLocalDateTime(tokyo)
    val newYorkTime = now.toLocalDateTime(newYork)
    val londonTime = now.toLocalDateTime(london)

    println("北京时间: $beijingTime")
    println("东京时间: $tokyoTime")
    println("纽约时间: $newYorkTime")
    println("伦敦时间: $londonTime")

    // 从本地时间转换回 Instant
    val localDateTime = LocalDateTime(2024, 6, 15, 14, 30)
    val instant = localDateTime.toInstant(beijing)
    println("北京时间对应的时刻: $instant")
}

Duration 时长操作

import kotlinx.datetime.*

fun durationDemo() {
    // 创建时长
    val d1 = 30.minutes
    val d2 = 2.hours
    val d3 = 1.days
    val d4 = Duration.seconds(90)
    val d5 = Duration.milliseconds(1500)

    // 时长运算
    val total = d1 + d2
    println("总时长: $total")

    // 时长比较
    println("30分钟 < 2小时: ${d1 < d2}")

    // 时长转换
    println("${d1.inWholeSeconds} 秒")
    println("${d2.inWholeMinutes} 分钟")
    println("${d3.inWholeHours} 小时")

    // 时长乘以倍数
    val triple = d1 * 3
    println("30分钟的3倍: $triple")
}

常见场景

计算年龄

import kotlinx.datetime.*

fun calculateAge(birthday: LocalDate, today: LocalDate = Clock.System.now()
    .toLocalDateTime(TimeZone.currentSystemDefault()).date): Int {
    var age = today.year - birthday.year
    // 如果今年生日还没到,年龄减1
    if (today.monthNumber < birthday.monthNumber ||
        (today.monthNumber == birthday.monthNumber && today.dayOfMonth < birthday.dayOfMonth)) {
        age--
    }
    return age
}

fun main() {
    val birthday = LocalDate(1990, Month.MARCH, 15)
    println("年龄: ${calculateAge(birthday)}")
}

定时任务的时间计算

import kotlinx.datetime.*

fun nextExecutionTime(intervalMinutes: Int): Instant {
    val now = Clock.System.now()
    return now + intervalMinutes.minutes
}

// 计算距离下一个整点的时间
fun timeToNextHour(): Duration {
    val now = Clock.System.now()
    val localNow = now.toLocalDateTime(TimeZone.currentSystemDefault())
    val nextHour = LocalDateTime(
        localNow.date,
        LocalTime(localNow.hour + 1, 0, 0)
    )
    return nextHour.toInstant(TimeZone.currentSystemDefault()) - now
}

日期范围遍历

import kotlinx.datetime.*

// 遍历两个日期之间的所有日期
fun dateRange(start: LocalDate, end: LocalDate): List<LocalDate> {
    val dates = mutableListOf<LocalDate>()
    var current = start
    while (current <= end) {
        dates.add(current)
        current = current + DatePeriod(days = 1)
    }
    return dates
}

fun main() {
    val start = LocalDate(2024, Month.JANUARY, 1)
    val end = LocalDate(2024, Month.JANUARY, 7)
    dateRange(start, end).forEach { println(it) }
}

注意事项

  • kotlinx-datetime 不是 java.time替代:在 JVM 项目中,两者可以共存。kotlinx-datetime 更适合多平台项目
  • Instant 不可变:所有日期时间对象都是不可变的,修改操作返回对象
  • 时区很重要:在 Instant 和本地时间之间转换时,必须指定时区,否则结果不确定
  • 月份从 1 开始:与 Java 的 Calendar(月份从 0 开始)不同,kotlinx-datetime 的月份从 1 开始
  • Duration 精:Duration 的精为纳秒,转换整数值时使用 inWholeSecondsinWholeMinutes方法

进阶用法

自定义 Clock 用于测试

import kotlinx.datetime.*

class FixedClock(private val fixedInstant: Instant) : Clock {
    override fun now(): Instant = fixedInstant
}

fun main() {
    // 固定时间,用于测试
    val testTime = Instant.parse("2024-06-15T12:00:00Z")
    val testClock = FixedClock(testTime)

    // 使用测试时钟
    val now = testClock.now()
    println("测试时间: $now")  // 始终返回固定时间

    // 在生产代码中注入 Clock,测试时替换为 FixedClock
}

序列化与反序列化

import kotlinx.datetime.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class Event(
    val name: String,
    // kotlinx-datetime 自带序列化支持
    val startTime: Instant,
    val date: LocalDate,
    val duration: Duration
)

fun main() {
    val event = Event(
        name = "会议",
        startTime = Clock.System.now(),
        date = LocalDate(2024, Month.JUNE, 15),
        duration = 2.hours
    )

    // 序列化为 JSON
    val json = Json { prettyPrint = true }
    val jsonString = json.encodeToString(event)
    println(jsonString)

    // 从 JSON 反序列化
    val decoded = json.decodeFromString<Event>(jsonString)
    println(decoded)
}

与 Java Time 互操作

import kotlinx.datetime.*
import java.time as jt

fun interoperability() {
    // kotlinx-datetime -> java.time
    val kInstant = Clock.System.now()
    val jInstant = kInstant.toJavaInstant()

    // java.time -> kotlinx-datetime
    val backToKotlin = jInstant.toKotlinInstant()

    // LocalDate 互转
    val kDate = LocalDate(2024, Month.JUNE, 15)
    val jDate = kDate.toJavaLocalDate()
    val backToDate = jDate.toKotlinLocalDate()
}

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式