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

Android DateUtils格式化历史时间异常问题排查

Android中DateUtils转换相对时间的问题及解决办法

需求场景

需要将以下ISO格式的历史时间字符串:

2023-01-06T14:52:41.633
2023-01-06T00:00:00
2022-12-23T00:00:00
2022-12-22T11:17:43.94

转换为「Today、1 Day Ago、3 Days Ago」这类日期维度的相对时间短语。尝试使用android.text.format.DateUtils实现,但结果出现错误。

错误代码示例

val then = LocalDateTime.parse(productionDate)
doLogging{"xxooxx past date = ${productionDate}"}

doLogging{"xxooxx ${DateUtils.getRelativeTimeSpanString(then.toEpochSecond(ZoneOffset.UTC), LocalDateTime.now().toEpochSecond(ZoneOffset.UTC),0L, DateUtils.FORMAT_ABBREV_RELATIVE)}"}
doLogging{"xxooxx \t\t ${DateUtils.getRelativeTimeSpanString(then.toEpochSecond(ZoneOffset.UTC), LocalDateTime.now().toEpochSecond(ZoneOffset.UTC),MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE)}"}
doLogging{"xxooxx \t\t ${DateUtils.getRelativeTimeSpanString(then.toEpochSecond(ZoneOffset.UTC), LocalDateTime.now().toEpochSecond(ZoneOffset.UTC),HOUR_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE)}"}
doLogging{"xxooxx \t\t ${DateUtils.getRelativeTimeSpanString(then.toEpochSecond(ZoneOffset.UTC), LocalDateTime.now().toEpochSecond(ZoneOffset.UTC),DAY_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE)}"}
doLogging{"xxooxx \t\t ${DateUtils.getRelativeTimeSpanString(then.toEpochSecond(ZoneOffset.UTC), LocalDateTime.now().toEpochSecond(ZoneOffset.UTC),WEEK_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE)}"}

错误结果示例

2023-01-11 11:38:08.143 D  xxooxx past date = 2023-01-11T08:33:18.047
2023-01-11 11:38:08.162 D  xxooxx 11 sec. ago
2023-01-11 11:38:08.163 D  xxooxx    0 min. ago
2023-01-11 11:38:08.164 D  xxooxx    0 hr. ago
2023-01-11 11:38:08.165 D  xxooxx    Today
2023-01-11 11:38:08.165 D  xxooxx    0 wk. ago
  • 本应显示为「Today」的2023-01-11T08:33:18.047,被错误显示为「11 sec. ago」
  • 旧时间2021-11-04T00:00:00被错误识别为「10小时前」

问题根源

时间戳单位不匹配是核心问题:

  • DateUtils.getRelativeTimeSpanString()的前两个参数要求传入毫秒级时间戳,但代码中使用toEpochSecond()得到的是秒级时间戳,两者相差1000倍。
  • 秒级时间戳被当作毫秒计算时,时间差会被大幅缩小,导致原本跨小时的时间差被识别为秒级,跨年的时间差被识别为小时级。

解决方案(基于DateUtils实现)

完全可以用DateUtils实现需求,只需修正时间戳单位并调整参数:

1. 修正时间戳转换逻辑

将LocalDateTime转换为毫秒级时间戳:

val then = LocalDateTime.parse(productionDate)
// 转换为带UTC时区的时间,再转成毫秒级时间戳
val thenMillis = then.atZone(ZoneOffset.UTC).toInstant().toEpochMilli()
// 获取当前时间的毫秒级时间戳
val nowMillis = System.currentTimeMillis()

2. 调用DateUtils时设置合适参数

如果需要优先显示日期维度的相对时间(如Today、X Days Ago),使用DAY_IN_MILLIS作为最小分辨率,并配合对应的格式化标志:

val relativeTime = DateUtils.getRelativeTimeSpanString(
    thenMillis,
    nowMillis,
    DateUtils.DAY_IN_MILLIS,
    // 组合标志:缩写相对时间 + 显示日期维度
    DateUtils.FORMAT_ABBREV_RELATIVE or DateUtils.FORMAT_SHOW_DATE
)

3. 进阶优化:忽略时分秒,只按日期判断

如果希望同一天内的任何时间都显示为「Today」,可以先截断时间到日期维度:

// 截断到天,忽略时分秒
val truncatedThen = then.truncatedTo(ChronoUnit.DAYS)
val thenMillis = truncatedThen.atZone(ZoneOffset.UTC).toInstant().toEpochMilli()

修正后效果

  • 2023-01-11T08:33:18.047会正确显示为「Today」
  • 2021-11-04T00:00:00会显示为符合实际的相对时间(如「1年前」)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 15:10:27