1. 概述

在本教程中,我们将学习如何在 Kotlin 中计算两个日期之间的时间差。重点是获取以年、月、日等为单位的差异值。

这类需求在实际开发中非常常见,比如计算用户年龄、订单履约周期、任务耗时等。虽然看似简单,但一不小心就会踩坑——尤其是跨库、跨时区或格式不统一时。本文将介绍两种主流方式:使用 Java 8 内置的 java.time 包和经典的第三方库 Joda-Time。

2. 使用 Java SE 8 的日期时间包(java.time)

推荐方案:从 Java 8 开始,java.time 成为处理日期时间的标准 API,基于 ISO 日历系统,默认支持 yyyy-MM-dd 格式。

该包的核心类包括:

  • LocalDate:仅表示日期(无时间、无时区)
  • LocalDateTime:日期+时间(无时区)
  • ZonedDateTime:带时区的完整时间
  • OffsetDateTime:带偏移量的时间

其中,Period 类专门用于表示“一段持续时间”,单位可以是年、月、日。

我们通常会结合 DateTimeFormatter 来解析字符串格式的日期。以下是具体实现:

val dateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy")

val from = LocalDate.parse("02/01/2007", dateFormatter)
val to = LocalDate.parse("11/25/2022", dateFormatter)

val period = Period.between(from, to)
val years = period.years
val months = period.months
val days = period.days

⚠️ 注意:

  • Period.between() 返回的是“精确”的年月日差值,不是总天数换算。
  • 顺序很重要:第一个参数是起始日期,第二个是结束日期。如果反了,结果可能是负数。

测试验证:

assertEquals(15, years)   // 从 2007 到 2022 是 15 年
assertEquals(9, months)   // 2月到11月相差9个月
assertEquals(24, days)    // 1号到25号差24天

这个结果意味着:从 02/01/200711/25/2022 正好是 15年9个月零24天

3. 使用 Joda-Time 外部库

已过时,仅作兼容参考:Joda-Time 曾是 Java 8 之前事实上的日期处理标准库。Java 8 的 java.time 实际上就是受其启发设计的,因此两者 API 高度相似。

尽管现在官方建议迁移到 java.time,但在维护老项目时仍可能遇到它。

添加依赖

Maven 配置如下:

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.12.1</version>
</dependency>

Gradle 用户可使用:

implementation 'joda-time:joda-time:2.12.1'

核心类说明

  • org.joda.time.LocalDate:与 java.time.LocalDate 功能类似
  • org.joda.time.Period:表示时间段,支持更多字段(如周)
  • org.joda.time.format.DateTimeFormatter:格式化与解析工具

示例代码

val dateFormatter: org.joda.time.format.DateTimeFormatter = DateTimeFormat.forPattern("MM/dd/yyyy")

val from = org.joda.time.LocalDate.parse("02/01/2007", dateFormatter)
val to = org.joda.time.LocalDate.parse("11/25/2022", dateFormatter)

val period = org.joda.time.Period(from, to)

val years = period.years
val months = period.months
val days = period.weeks * 7 + period.days

📌 关键区别:

  • Joda-Time 的 Period 支持 weeks 字段,因此 days 只包含不足一周的部分。
  • 所以要得到完整的“剩余天数”,必须手动把 weeks 转成天数再相加。

这与 java.time.Period 不同,后者直接返回剩余天数(不拆分周)。

4. 总结

方案 是否推荐 特点
java.time ✔️ 强烈推荐 JDK 内置、轻量、现代 API、无需额外依赖
Joda-Time ❌ ⚠️ 仅限遗留系统 历史悠久但已归档,新项目不要引入

✅ 最佳实践建议:

  • 新项目一律使用 java.time
  • 注意日期顺序避免出现负值
  • 如果需要更复杂的计算(如工作日、节假日),可考虑 ThreeTen-Extra 或自定义逻辑

所有示例代码均可在 GitHub 获取:https://github.com/Baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-date-time


原始标题:Difference Between Two Dates in Kotlin