
本文详解如何在 Kotlin/Java 中正确将形如 "00:05:10.000" 的时分秒毫秒字符串解析为自当日 00:00:00 起的总秒数(整数),避免因时区默认行为导致的负值或偏移错误。
本文详解如何在 kotlin/java 中正确将形如 `"00:05:10.000"` 的时分秒毫秒字符串解析为自当日 00:00:00 起的总秒数(整数),避免因时区默认行为导致的负值或偏移错误。
在处理视频时长、日志耗时或定时任务等场景中,常需将 HH:mm:ss.SSS 格式的时间字符串(如 "00:00:10.000")转换为自当日零点起经过的总秒数(取整),例如 10、310、3623。看似简单,但直接使用 SimpleDateFormat 解析极易出错——你遇到的负值问题,根源在于:SimpleDateFormat 默认绑定本地时区,而 parse() 返回的是该字符串在本地时区所代表的绝对时间戳(毫秒值),并非“从当天 00:00:00 开始的持续时间”。
例如,在东八区(UTC+8)运行时,"00:00:10.000" 会被解析为 1970-01-01 00:00:10.000 CST 对应的时间戳,其毫秒值远小于 UTC 时间戳,除以 1000 后可能为负(尤其在跨时区或系统时区设置异常时)。
✅ 正确做法是显式指定时区为 UTC,确保解析始终以“当日 00:00:00 UTC”为基准,从而将输入视为纯粹的持续时间(duration)而非日历时间(instant):
fun String.convertTimeIntoSeconds(): Long {
val dateFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.ROOT)
dateFormat.timeZone = TimeZone.getTimeZone("UTC") // 关键:强制使用 UTC 时区
val date = dateFormat.parse(this)
return date.time / 1000L // 转换为秒(自动截断毫秒部分)
}
? 补充说明:
- 使用 Locale.ROOT 可避免不同语言环境下月份/星期缩写导致的解析失败;
- date.time / 1000L 是整数除法,天然舍弃毫秒(如 10.999 → 10 秒),符合预期;
- 若需保留毫秒精度,可改用 date.time / 1000.0 返回 Double。
⚠️ 注意事项:
- 此方法适用于 HH ≤ 23 的标准时间格式;若需支持超过 24 小时的累计时长(如 "25:30:45.000"),应改用 java.time.Duration(推荐 Java 8+ / Kotlin with JDK 8+):
fun String.toTotalSeconds(): Long { val parts = this.split(":") val hours = parts[0].toLong() val minutes = parts[1].toLong() val secondsWithMs = parts[2].split(".") val seconds = secondsWithMs[0].toLong() val millis = if (secondsWithMs.size > 1) secondsWithMs[1].padEnd(3, '0').take(3).toLong() else 0L return hours * 3600 + minutes * 60 + seconds + if (millis >= 500) 1 else 0 // 四舍五入到秒 } - SimpleDateFormat 非线程安全,生产环境建议每次调用新建实例,或改用线程安全的 DateTimeFormatter(Java 8+)。
总之,时区意识是时间解析的基石。一行 dateFormat.timeZone = TimeZone.getTimeZone("UTC") 即可规避绝大多数隐式时区陷阱,让时间字符串真正表达“经过了多少秒”,而非“某个时刻距 Epoch 的毫秒数”。











