You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin如何将LocalDate/LocalDateTime格式字符串统一解析为LocalDate

问题描述

编写映射器逻辑时,接收到的日期字符串存在两种可能格式:

  • 纯日期格式:"2021-06-08"
  • 带时间的ISO格式:"2021-06-08T15:00"

业务要求在映射器中始终将该字符串转换为LocalDate类型,当前映射逻辑代码如下:

ExcelColumn.CHANGED_DATE -> rowData
        .nullLocalDate(column.dbColumnName)
        ?.format(excelV2DateFormat)

当前实现的nullLocalDate相关方法代码如下:

fun Entity.nullLocalDate(key: String): LocalDate? = if (this[key] == null) null else localDate(key)

fun Entity.localDate(key: String): LocalDate = when (val v = this[key]) {
  is LocalDateTime -> v.toLocalDate()
  is Instant -> LocalDate.ofInstant(v, ZoneId.of("UTC"))
  is String -> LocalDate.parse(v)
  else -> v as LocalDate
}

已实现的nullLocalDateTime相关处理方法如下:

fun Entity.nullLocalDateTime(key: String): LocalDateTime? = if (this[key] == null) null else localDateTime(key)

fun Entity.localDateTime(key: String, zoneId: ZoneId? = null): LocalDateTime = when (val v = this[key]) {
  is Instant -> LocalDateTime.ofInstant(v, zoneId ?: ZoneId.of("UTC"))
  is String -> LocalDateTime.parse(v)
  else -> v as LocalDateTime
}

现存问题:传入值为"2021-06-08"格式时解析逻辑可正常运行,但传入"2021-06-08T15:00"格式的字符串时会抛出如下异常:

java.time.format.DateTimeParseException: Text '2021-06-08T15:00' could not be parsed, unparsed text found at index 10

需要将两种日期解析逻辑整合,在同一个方法中同时兼容LocalDate、LocalDateTime两种格式的输入,无论输入字符串是哪种格式都能正确转换为LocalDate类型。

实现方案

异常根因是默认LocalDate.parse()仅支持纯yyyy-MM-dd格式的ISO日期字符串,遇到带T及后续时间部分的字符串时,解析完日期段后检测到剩余未解析内容就会抛出解析失败异常。

方案1:最小改动实现(复用现有逻辑)

直接修改localDate方法中String类型的解析分支,解析失败时 fallback 到LocalDateTime解析逻辑,再取日期部分,不需要额外新增依赖或工具类:

fun Entity.localDate(key: String): LocalDate = when (val v = this[key]) {
  is LocalDateTime -> v.toLocalDate()
  is Instant -> LocalDate.ofInstant(v, ZoneId.of("UTC"))
  is String -> runCatching { LocalDate.parse(v) }
    .getOrElse { LocalDateTime.parse(v).toLocalDate() }
  else -> v as LocalDate
}

该方案完全兼容当前已有的两种输入格式,改动量极小,不需要调整其他业务代码。

方案2:格式化器配置实现(性能更优、易扩展)

如果后续可能扩展更多日期格式,推荐使用DateTimeFormatterBuilder预定义支持可选时间段的解析器,避免异常捕获的性能开销,扩展格式时只需要修改格式化器配置即可:

// 全局复用的多格式兼容解析器,避免重复创建实例
private val flexibleLocalDateFormatter = DateTimeFormatterBuilder()
    .append(DateTimeFormatter.ISO_LOCAL_DATE)
    // 标记T+时间部分为可选解析段
    .optionalStart()
    .appendLiteral('T')
    .append(DateTimeFormatter.ISO_LOCAL_TIME)
    .optionalEnd()
    .toFormatter()

fun Entity.localDate(key: String): LocalDate = when (val v = this[key]) {
  is LocalDateTime -> v.toLocalDate()
  is Instant -> LocalDate.ofInstant(v, ZoneId.of("UTC"))
  is String -> LocalDate.parse(v, flexibleLocalDateFormatter)
  else -> v as LocalDate
}

方案对比:

  • 方案1适合当前格式固定、追求最小改动的场景,代码逻辑简单直观
  • 方案2适合日期格式后续可能迭代的场景,不需要新增异常分支,后续支持带时区、带毫秒等格式时仅需调整formatter的可选规则即可

注意:如果输入字符串可能存在带时区偏移的格式(如2021-06-08T15:00+08:00),只需要在方案2的formatter中追加对应可选解析规则,不需要修改when分支逻辑。

内容的提问来源于stack exchange,提问作者Ludwig

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 07:01:09