kotlinx-datetime:Kotlin 中的跨平台日期和时间处理
\n\n1. 这是什么以及为什么需要它
\n\n kotlinx-datetime 是 JetBrains 为 Kotlin 生态系统提供的官方日期和时间处理库。其主要特点是跨平台:同一份代码可在 JVM、JS(浏览器和 Node.js)、Native(iOS、macOS、Windows、Linux)和 Wasm 上运行。这使开发者无需编写平台相关的包装器或为不同目标使用不同的库。\n
\n\n 为什么需要它?Kotlin/JVM 的标准库使用 java.time,但该库在其他平台上不可用。对于 Kotlin/Native 和 Kotlin/JS,开发者要么使用平台 API(NSDate、Date),要么引入第三方库。kotlinx-datetime 通过提供基于 ISO-8601 规范的统一 API 解决了这个问题。它包含表示时间点(Instant)、无时间日期(LocalDate)、无日期时间(LocalTime)、日期时间(LocalDateTime)、时区(TimeZone)和时间段(DateTimePeriod)的类。\n
\n 该库非常适合使用 Kotlin Multiplatform (KMP) 的项目、基于 Ktor 的服务器应用程序、Android 应用程序(作为 java.time 的替代品),甚至脚本和控制台工具。它轻量级、无外部依赖,并得到社区的积极维护。\n
\n2. 安装
\n\n 该库通过 Maven Central 分发。要使用它,您需要在 build.gradle.kts(或 build.gradle)文件中添加依赖项。以下是 Gradle Kotlin DSL 的准确代码。\n
// build.gradle.kts\nrepositories {\n mavenCentral()\n}\n\ndependencies {\n // 主库\n implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")\n\n // 如果您使用多平台项目,请添加到 commonMain:\n // commonMain.dependencies {\n // implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")\n // }\n}\n\n \n 重要提示: 0.6.1 版本是撰写本文时的最新稳定版本。请查看 GitHub 发布页面 以获取最新版本。对于 Gradle Groovy DSL,命令类似,但字符串不带引号。\n
\n\n 如果您使用 Maven,请添加到 pom.xml:\n
<dependency>\n <groupId>org.jetbrains.kotlinx</groupId>\n <artifactId>kotlinx-datetime</artifactId>\n <version>0.6.1</version>\n</dependency>\n\n \n 对于 Gradle Kotlin DSL 的多平台项目 (KMP),请添加到 commonMain:\n
kotlin {\n sourceSets {\n commonMain.dependencies {\n implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")\n }\n }\n}\n\n 3. 快速入门
\n\n 最小工作示例:获取当前时间点、格式化并输出到控制台。\n
\nimport kotlinx.datetime.*\n\nfun main() {\n // 获取当前 UTC 时间点\n val now: Instant = Clock.System.now()\n println("当前 UTC 时间点:$now")\n\n // 转换为本地日期和时间(使用系统时区)\n val localDateTime: LocalDateTime = now.toLocalDateTime(TimeZone.currentSystemDefault())\n println("本地日期时间:$localDateTime")\n\n // 手动创建日期\n val date: LocalDate = LocalDate(2024, 12, 25)\n println("圣诞节:$date")\n}\n\n \n 此示例演示了三个关键操作:获取当前时间、转换为本地时间以及创建特定日期。所有操作均跨平台且无需额外配置。\n
\n4. 主要方法及示例
\n\n 以下是 kotlinx-datetime 中最常用的方法和类,附有详细说明和代码示例。\n
\n\n4.1. Instant:时间点
\n\n Instant 表示 UTC 时间轴上的一个特定点。它不包含时区信息,但可以转换为任何时区的本地时间。\n
import kotlinx.datetime.*\n\nfun instantExamples() {\n // 当前时间\n val now = Clock.System.now()\n println("当前时间:$now")\n\n // 从字符串解析\n val parsed = Instant.parse("2024-12-25T10:30:00Z")\n println("解析后的时间:$parsed")\n\n // 从纪元秒数创建\n val fromEpoch = Instant.fromEpochSeconds(1_700_000_000)\n println("从纪元秒数创建:$fromEpoch")\n\n // 转换为纪元毫秒数\n val epochMillis = now.toEpochMilliseconds()\n println("纪元毫秒数:$epochMillis")\n\n // 比较\n val earlier = Instant.parse("2024-01-01T00:00:00Z")\n val later = Instant.parse("2024-12-31T23:59:59Z")\n println("earlier < later:${earlier < later}") // true\n}\n\n\n 4.2. LocalDate:无时间日期
\n\n LocalDate 表示日期(年、月、日),不包含时间和时区。适用于生日、假期等。\n
import kotlinx.datetime.*\n\nfun localDateExamples() {\n // 创建日期\n val date = LocalDate(2024, 12, 25)\n println("日期:$date")\n\n // 解析\n val parsed = LocalDate.parse("2024-12-25")\n println("解析后的日期:$parsed")\n\n // 当前日期(系统时区)\n val today = Clock.System.todayIn(TimeZone.currentSystemDefault())\n println("今天:$today")\n\n // 日期运算\n val tomorrow = today.plus(1, DateTimeUnit.DAY)\n println("明天:$tomorrow")\n\n val nextWeek = today.plus(1, DateTimeUnit.WEEK)\n println("下周:$nextWeek")\n\n // 日期差\n val daysUntil = today.periodUntil(parsed).days\n println("距离圣诞节还有 $daysUntil 天")\n\n // 星期几\n println("星期几:${today.dayOfWeek}") // MONDAY, TUESDAY, ...\n}\n\n\n 4.3. LocalTime:无日期时间
\n\n LocalTime 表示一天中的时间(时、分、秒、纳秒),不包含日期。\n
import kotlinx.datetime.*\n\nfun localTimeExamples() {\n // 创建时间\n val time = LocalTime(14, 30, 0)\n println("时间:$time")\n\n // 解析\n val parsed = LocalTime.parse("14:30:00")\n println("解析后的时间:$parsed")\n\n // 当前时间\n val now = Clock.System.now()\n val localTime = now.toLocalDateTime(TimeZone.currentSystemDefault()).time\n println("当前时间:$localTime")\n\n // 时间运算\n val later = time.plus(1, DateTimeUnit.HOUR)\n println("一小时后:$later")\n\n // 比较\n val morning = LocalTime(8, 0)\n val evening = LocalTime(20, 0)\n println("早上 < 晚上:${morning < evening}") // true\n}\n\n\n 4.4. LocalDateTime:日期时间
\n\n LocalDateTime 结合了日期和时间,但不包含时区。通常用于用户界面。\n
import kotlinx.datetime.*\n\nfun localDateTimeExamples() {\n // 创建\n val dt = LocalDateTime(2024, 12, 25, 10, 30, 0)\n println("日期时间:$dt")\n\n // 解析\n val parsed = LocalDateTime.parse("2024-12-25T10:30:00")\n println("解析后的日期时间:$parsed")\n\n // 从 Instant 转换\n val now = Clock.System.now()\n val local = now.toLocalDateTime(TimeZone.currentSystemDefault())\n println("本地日期时间:$local")\n\n // 转换为 Instant(需要时区)\n val instant = local.toInstant(TimeZone.UTC)\n println("UTC 时间点:$instant")\n\n // 组件提取\n println("年:${dt.year}, 月:${dt.monthNumber}, 日:${dt.dayOfMonth}")\n println("时:${dt.hour}, 分:${dt.minute}, 秒:${dt.second}")\n}\n\n\n 4.5. TimeZone:时区
\n\n TimeZone 表示时区。支持 IANA 时区标识符(如 "Europe/Moscow"、"America/New_York")和 UTC 偏移量。\n
import kotlinx.datetime.*\n\nfun timeZoneExamples() {\n // 系统时区\n val systemTz = TimeZone.currentSystemDefault()\n println("系统时区:$systemTz")\n\n // 按 ID 获取\n val moscow = TimeZone.of("Europe/Moscow")\n println("莫斯科时区:$moscow")\n\n // UTC 偏移量\n val utcPlus3 = TimeZone.of("UTC+3")\n println("UTC+3:$utcPlus3")\n\n // 可用时区列表\n val ids = TimeZone.availableZoneIds\n println("可用时区数量:${ids.size}")\n println("前 5 个时区:${ids.take(5)}")\n\n // 时区偏移量\n val offset = moscow.offsetAt(Clock.System.now())\n println("莫斯科当前偏移量:$offset") // 例如 +03:00\n}\n\n\n 4.6. DateTimePeriod:时间段
\n\n DateTimePeriod 表示时间段,包含天、小时、分钟、秒和纳秒。用于日期时间运算。\n
import kotlinx.datetime.*\n\nfun periodExamples() {\n // 创建时间段\n val period = DateTimePeriod(days = 10, hours = 5, minutes = 30)\n println("时间段:$period")\n\n // 日期时间运算\n val start = LocalDateTime(2024, 1, 1, 0, 0)\n val end = start.plus(period, TimeZone.UTC)\n println("开始:$start, 结束:$end")\n\n // 两个日期时间之间的差\n val diff = start.periodUntil(end, TimeZone.UTC)\n println("差:$diff") // 10 天 5 小时 30 分钟\n\n // 组件提取\n println("天:${diff.days}, 小时:${diff.hours}, 分钟:${diff.minutes}")\n}\n\n\n 4.7. 格式化和解析
\n\n kotlinx-datetime 通过 toString() 支持 ISO-8601 格式,并通过 parse() 方法支持解析。对于自定义格式,可以使用 DateTimeFormat(实验性 API)。\n
import kotlinx.datetime.*\n\nfun formattingExamples() {\n val instant = Instant.parse("2024-12-25T10:30:00Z")\n\n // ISO-8601 格式\n println("ISO 格式:$instant") // 2024-12-25T10:30:00Z\n\n // 自定义格式(实验性)\n // 注意:此 API 在 0.6.1 中为实验性,未来版本可能更改\n // val formatter = DateTimeFormat.byPattern(\"yyyy-MM-dd HH:mm\")\n // val formatted = formatter.format(instant.toLocalDateTime(TimeZone.UTC))\n // println(\"自定义格式:$formatted\")\n\n // 解析自定义格式\n // val parsed = formatter.parse(\"2024-12-25 10:30\")\n // println(\"解析后的自定义格式:$parsed\")\n}\n\n 5. 最佳实践
\n- \n
- 存储和传输使用 Instant: 在数据库、API 和文件之间交换数据时,始终使用
Instant(UTC)。这避免了时区问题。 \n - 显示使用 LocalDateTime: 在用户界面中,使用
LocalDateTime并转换为用户的时区。 \n - 避免平台特定 API: 在 KMP 项目中,不要使用
java.time、NSDate或Date。仅使用 kotlinx-datetime。 \n - 注意实验性 API: 自定义格式和某些操作(如
DateTimeFormat)标记为@ExperimentalDateTimeFormat。在生产中使用前请检查版本。 \n - 测试时区: 始终使用不同的时区测试您的代码,以确保正确性。 \n
6. 结论
\n\n kotlinx-datetime 是 Kotlin 生态系统中处理日期和时间的强大且便捷的工具。其跨平台特性使其成为 KMP 项目的理想选择,而简洁的 API 则降低了错误风险。从简单的日期运算到复杂的时区转换,该库提供了所有必要的工具。\n
\n\n 开始使用 kotlinx-datetime,告别平台特定的日期时间代码!\n
\n