前言
在 Kotlin(基于 JVM)中,日期时间处理主要依赖 Java 8 引入的 java.time 包(JSR-310),它取代了老旧且问题较多的 java.util.Date 和 Calendar。
Kotlin 本身不提供新的日期类,但能以更简洁、安全的方式使用这些 Java 类。
核心类一览
| 类名 |
说明 |
LocalDate |
仅日期(年-月-日),无时区 |
YearMonth |
仅年月,无时区 |
LocalTime |
仅时间(时-分-秒-纳秒),无时区 |
LocalDateTime |
日期 + 时间,无时区 |
ZonedDateTime |
日期 + 时间 + 时区(含夏令时规则) |
OffsetDateTime |
日期 + 时间 + UTC 偏移量 |
Instant |
UTC 时间戳(自 1970-01-01T00:00:00Z 起) |
Duration |
基于秒/纳秒的时间量(如 2 小时) |
Period |
基于年-月-日的日历量(如 1 年 3 个月) |
DateTimeFormatter |
线程安全的格式化与解析工具 |
ZoneId / ZoneOffset |
表示时区或偏移量 |
注意:
遗留类如 java.util.Date、Calendar 和 SimpleDateFormat 已不推荐使用。
日期与字符串互转
| 方式 |
优点 |
缺点 |
SimpleDateFormat |
简单直接,兼容老代码 |
非线程安全,API 设计老旧 |
java.time + Date.from() / Date.toInstant() |
线程安全、功能强大、可读性好 |
代码稍长,需理解新时间 API |
使用 java.time(推荐)
Date 转 LocalDateTime
1 2 3 4
| val localDateTime = date .toInstant() .atZone(ZoneId.systemDefault()) .toLocalDateTime()
|
LocalDateTime → 字符串
1 2 3 4 5 6 7
| fun dateToStringModern(localDateTime: LocalDateTime, pattern: String = "yyyy-MM-dd HH:mm:ss"): String { return localDateTime.format(DateTimeFormatter.ofPattern(pattern)) }
val localDateTime = Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() println(dateToStringModern(localDateTime, "yyyy-MM-dd"))
|
字符串 → LocalDateTime
1 2 3 4 5 6 7
| fun stringToDateModern(str: String, pattern: String = "yyyy-MM-dd HH:mm:ss"): LocalDateTime { val formatter = DateTimeFormatter.ofPattern(pattern) return LocalDateTime.parse(str, formatter) }
val date = stringToDateModern("2025-12-08 14:30:00")
|
LocalDateTime → Date
1
| Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant())
|
使用 java.util.Date(遗留方式)
Date → 字符串
1 2 3 4 5 6 7
| fun dateToString(date: Date, pattern: String = "yyyy-MM-dd HH:mm:ss"): String { return SimpleDateFormat(pattern, Locale.getDefault()).format(date) }
println(dateToString(Date())) println(dateToString(Date(), "yyyy/MM/dd"))
|
字符串 → Date
1 2 3 4 5 6 7
| fun stringToDate(str: String, pattern: String = "yyyy-MM-dd HH:mm:ss"): Date? { return try { SimpleDateFormat(pattern, Locale.getDefault()).parse(str) } catch (e: Exception) { null } }
|
年月与字符串互转
1 2 3 4 5 6
| val yearMonthStr = YearMonth.now().format(DateTimeFormatter.ofPattern("yyyy-MM"))
val yearMonth = YearMonth.parse("2025-06", DateTimeFormatter.ofPattern("yyyy-MM"))
|
获取某日所在周起止
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
fun LocalDate.weekStartMonday(): LocalDate { val daysSinceMonday = this.dayOfWeek.value - DayOfWeek.MONDAY.value return this.minusDays(daysSinceMonday.toLong()) }
fun LocalDate.weekEndSunday(): LocalDate { val daysToAdd = DayOfWeek.SUNDAY.value - this.dayOfWeek.value return this.plusDays(daysToAdd.toLong()) }
val today = LocalDate.now() println(today.weekStartMonday()) println(today.weekEndSunday())
|
获取某月起止
1 2 3 4 5 6 7
| fun getFirstAndLastDay(dateStr: String, pattern: String = "yyyy-MM"): Pair<LocalDate, LocalDate> { val yearMonth = YearMonth.parse(dateStr, DateTimeFormatter.ofPattern(pattern)) return yearMonth.atDay(1) to yearMonth.atEndOfMonth() }
val (firstDay, lastDay) = getFirstAndLastDay("2025-06")
|
获取两个日期之间的所有日期
1 2 3 4 5
| val (firstDay, lastDay) = getFirstAndLastDay("2025-06")
val allDayList = generateSequence(firstDay) { it.plusDays(1) } .takeWhile { !it.isAfter(lastDay) } .toList()
|
LocalDate 详解
LocalDate 是 Java 8 引入的 java.time 包中用于表示不带时间、不带时区的日期(年-月-日) 的不可变类。
创建 LocalDate
1 2 3 4 5 6 7
| val today = LocalDate.now() val date1 = LocalDate.of(2025, 12, 8) val date2 = LocalDate.parse("2025-12-08") val date3 = LocalDate.ofYearDay(2025, 342)
val date4 = LocalDate.parse("2025年12月08日", DateTimeFormatter.ofPattern("yyyy年MM月dd日"))
|
格式化为字符串
1 2 3 4 5 6 7 8
| val date = LocalDate.of(2025, 12, 8)
date.toString() date.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")) date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"))
date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.CHINA))
|
获取字段
1 2 3 4 5 6 7 8 9 10
| val date = LocalDate.of(2025, 12, 8)
date.year date.monthValue date.month date.dayOfMonth date.dayOfWeek date.dayOfYear date.lengthOfMonth() date.isLeapYear
|
日期计算(加减)
所有操作返回新对象(不可变):
1 2 3 4 5 6 7 8 9 10 11 12 13
| val date = LocalDate.of(2025, 12, 8)
date.plusDays(1) date.plusMonths(1) date.plusYears(1)
date.minusDays(1) date.minusMonths(2)
date.plus(Period.ofWeeks(2))
|
比较日期
1 2 3 4 5 6 7 8 9 10 11 12
| val d1 = LocalDate.of(2025, 12, 8) val d2 = LocalDate.of(2025, 12, 10)
d1.isBefore(d2) d1.isAfter(d2) d1.isEqual(d2) d1.compareTo(d2)
if (d1 in d2.minusDays(5)..d2.plusDays(5)) { println("d1 在 d2 前后5天内") }
|
💡 LocalDate 实现了 Comparable,可直接用于 sorted()、min()、max() 等。
调整日期(withXXX)
1 2 3 4 5 6
| val date = LocalDate.of(2025, 12, 8)
date.withDayOfMonth(1) date.with(TemporalAdjusters.lastDayOfMonth()) date.with(TemporalAdjusters.next(DayOfWeek.MONDAY)) date.with(TemporalAdjusters.firstDayOfMonth())
|
需要导入:import java.time.temporal.TemporalAdjusters
与其他类型互转
1 2 3 4 5 6 7 8
| val ldt = date.atStartOfDay()
val utilDate = Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant())
val parsed = LocalDate.parse("08-12-2025", DateTimeFormatter.ofPattern("dd-MM-yyyy"))
|
小贴士
LocalDate 没有时区概念,适合表示生日、节假日、合同起止日等
- 所有方法都是不可变操作,不会修改原对象
- 推荐配合
DateTimeFormatter 和 TemporalAdjusters 使用高级功能