SimpleDateFormat转换GMT ISO8601时间戳为Unix epoch失败怎么办
问题背景
需要解析符合ISO8601规范的GMT时间戳:2022-01-12T17:17:34.512492+0000,目标转换为Unix epoch时间,使用java.text.SimpleDateFormat实现时两种方案均无法得到正确结果:
错误方案1
- 实现代码:
val parsed = "2022-01-12T17:17:34.512492+0000" val df: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'ZZZZ'") val date = df.parse(parsed.toString) val epoch = date.getTime
- 运行异常:
java.text.ParseException: Unparseable date: "2022-01-12T17:17:34.512492+0000"
错误方案2
- 实现代码:
val parsed = "2022-01-12T17:17:34.512492+0000" val df: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'+0000'") val date = df.parse(parsed.toString) val epoch = date.getTime println(epoch)
- 运行输出epoch值:
1642004766492 - 错误表现:将该epoch值转换为可读日期为
Wednesday, 12 January 2022 16:26:06.492,时、分、秒数值与原时间戳完全不符。
问题根源
两种方案失败的核心原因有两个:
SimpleDateFormat格式串中单引号包裹的内容为字面量匹配规则:第一种方案用'ZZZZ'表示匹配字符串中连续的ZZZZ四个字符,和时间戳末尾实际的+0000完全不匹配,因此直接抛出解析异常。SimpleDateFormat的S模式符仅支持毫秒(3位长度)解析,不支持6位微秒级精度:写6个S时,解析器会将.512492识别为512492毫秒,换算后额外叠加了数百秒的时间偏移,直接导致时分秒结果错误。同时第二种方案将+0000写死为字面量,遇到其他时区偏移的时间戳会直接解析失败,无通用性。
正确解决方案
方案1:Java 8+ 标准时间API(推荐,无第三方依赖)
Java 8开始内置的java.time包(JSR310规范)原生支持ISO8601格式和纳秒级精度解析,不需要手动编写复杂格式串,代码最简洁、容错性最高:
import java.time.OffsetDateTime val parsed = "2022-01-12T17:17:34.512492+0000" // 原生识别ISO8601格式,自动解析6位微秒和时区偏移 val offsetDateTime = OffsetDateTime.parse(parsed) // 转换为Unix epoch毫秒值,与Date.getTime()返回语义完全一致 val epoch = offsetDateTime.toInstant.toEpochMilli
运行得到正确epoch值为1642007854512,转回UTC时间为2022-01-12T17:17:34.512Z,与原时间戳完全匹配。
如果需要解析非标准格式的同类时间戳,可以使用DateTimeFormatter自定义匹配规则,它的S模式符支持任意长度的秒小数解析:
import java.time.OffsetDateTime import java.time.format.DateTimeFormatter val parsed = "2022-01-12T17:17:34.512492+0000" val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSXX") val offsetDateTime = OffsetDateTime.parse(parsed, formatter) val epoch = offsetDateTime.toInstant.toEpochMilli
方案2:兼容旧版本Java的SimpleDateFormat实现
如果受环境限制只能使用SimpleDateFormat,需要先将时间串中的6位微秒截断为3位毫秒,再使用正确的时区匹配规则解析:
import java.text.SimpleDateFormat import java.util.TimeZone val parsed = "2022-01-12T17:17:34.512492+0000" // 正则截断6位小数秒为3位,保留原时区偏移 val adjustedTimeStr = parsed.replaceAll("(\\.\\d{3})\\d{3}([+-]\\d{4})", "$1$2") val df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") // 显式设置UTC时区避免系统默认时区干扰解析结果 df.setTimeZone(TimeZone.getTimeZone("UTC")) val date = df.parse(adjustedTimeStr) val epoch = date.getTime
该方案可以得到正确的毫秒级epoch值,但会丢失微秒精度,且正则匹配对非标准格式时间串兼容性差,优先使用方案1。
内容的提问来源于stack exchange,提问作者IamYipi
相关产品推荐
相关产品推荐

