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
相关产品推荐
相关产品推荐

