如何在不使用本地时区的情况下解析日期?
How to parse date without using Local Time Zone?
从服务器我得到以下值:
epochMillis=1556532279322
iso8601=2019-04-29T10:04:39.322Z
当我执行 serverTimeDateFormat.parse(iso8601)
时,结果是 Mon Apr 29 10:04:39 GMT+02:00 2019
而对于 serverTimeDateFormat.parse(iso8601).time
,结果是 1556525079322
,这与我从服务器获得的结果不同(比 UNIX 时间晚 2 小时),而我在时区 + 2 小时。
当我用 serverTimeDatFormat.format(1556525079322)
重新格式化时,结果是 2019-04-29T10:04:39.322Z
我知道 SimpleDateFormat
使用的是本地时区,但为什么结果晚了 2 小时?我如何在不考虑时区的情况下解析 Date
?我不明白这一切的逻辑。
我的代码:
private val serverTimeDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",Locale.ENGLISH)
val iso8601 = "2019-04-29T10:04:39.322Z"
val epochMillis = 1556532279322
serverTimeDateFormat.parse(iso8601).time
问题在于您的 SimpleDateFormat 的模式。最后,您有 'Z'
,这表明要解析的日期字符串中应该有文字 "Z"。但是,日期末尾的"Z"有特殊含义,即it signifies the UTC timezone. Hence, you should parse it as a timezone designator so that the correct date value will be obtained. You can do this with the pattern XXX
(See JavaDocs).
private val serverTimeDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX",Locale.ENGLISH)
val iso8601 = "2019-04-29T10:04:39.322Z"
print( serverTimeDateFormat.parse(iso8601).time ) // 1556532279322
Runnable example on pl.kotl.in
附录:虽然上面的代码应该适合您,但如果可能的话,您应该考虑将 ThreeTen Android Backport 添加到您的项目中。这将使您能够访问由 JSR310 添加到 Java/Kotlin 的较新时间 类(在 Android API >=26 中默认也可用)。 类 通常更容易 API,并且默认使用 ISO8601,因此您根本不需要任何格式化程序:
print( ZonedDateTime.parse(iso8601).toInstant().toEpochMilli() )
避免遗留日期时间类
您使用的 糟糕 日期时间 类 多年前已被现代 java.time 取代类 在 JSR 310 中定义。
Date::toString
谎言
why is the result 2 hours behind
其实还不到两个小时。
问题在于,虽然 java.util.Date
表示 UTC 中的时刻,但其 toString
方法在生成表示日期时间对象值的文本时动态应用 JVM 当前的默认时区.虽然本意是好的,但这个反特征令人困惑地产生了 Date
对象具有那个时区的错觉。
换句话说,Date::toString
在撒谎。在这些遗留问题中发现的许多糟糕的设计决策之一 类。也是从不使用这些遗留物的众多原因之一 类。
java.time
Instant
解析自 UTC 中 1970 年第一时刻的纪元参考以来的毫秒数作为 Instant
。
Instant instant = Instant.ofEpochMilli( 1556532279322 );
你的另一个输入,一个标准的 ISO 8601 字符串,也可以被立即解析。
Instant instant = Instant.parse( "2019-04-29T10:04:39.322Z" ) ;
ZonedDateTime
要通过特定地区(时区)的人们使用的挂钟时间查看同一时刻,请应用 ZoneId
以获得 ZonedDateTime
对象。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ) ;
instant
和 zdt
对象代表同一时刻。阅读同一时刻的两种方式,就像在冰岛和魁北克的 phone 上交谈的两个人同时看一眼墙上的时钟时,每个人都会看到不同的时间。
从服务器我得到以下值:
epochMillis=1556532279322
iso8601=2019-04-29T10:04:39.322Z
当我执行 serverTimeDateFormat.parse(iso8601)
时,结果是 Mon Apr 29 10:04:39 GMT+02:00 2019
而对于 serverTimeDateFormat.parse(iso8601).time
,结果是 1556525079322
,这与我从服务器获得的结果不同(比 UNIX 时间晚 2 小时),而我在时区 + 2 小时。
当我用 serverTimeDatFormat.format(1556525079322)
重新格式化时,结果是 2019-04-29T10:04:39.322Z
我知道 SimpleDateFormat
使用的是本地时区,但为什么结果晚了 2 小时?我如何在不考虑时区的情况下解析 Date
?我不明白这一切的逻辑。
我的代码:
private val serverTimeDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",Locale.ENGLISH)
val iso8601 = "2019-04-29T10:04:39.322Z"
val epochMillis = 1556532279322
serverTimeDateFormat.parse(iso8601).time
问题在于您的 SimpleDateFormat 的模式。最后,您有 'Z'
,这表明要解析的日期字符串中应该有文字 "Z"。但是,日期末尾的"Z"有特殊含义,即it signifies the UTC timezone. Hence, you should parse it as a timezone designator so that the correct date value will be obtained. You can do this with the pattern XXX
(See JavaDocs).
private val serverTimeDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX",Locale.ENGLISH)
val iso8601 = "2019-04-29T10:04:39.322Z"
print( serverTimeDateFormat.parse(iso8601).time ) // 1556532279322
Runnable example on pl.kotl.in
附录:虽然上面的代码应该适合您,但如果可能的话,您应该考虑将 ThreeTen Android Backport 添加到您的项目中。这将使您能够访问由 JSR310 添加到 Java/Kotlin 的较新时间 类(在 Android API >=26 中默认也可用)。 类 通常更容易 API,并且默认使用 ISO8601,因此您根本不需要任何格式化程序:
print( ZonedDateTime.parse(iso8601).toInstant().toEpochMilli() )
避免遗留日期时间类
您使用的 糟糕 日期时间 类 多年前已被现代 java.time 取代类 在 JSR 310 中定义。
Date::toString
谎言
why is the result 2 hours behind
其实还不到两个小时。
问题在于,虽然 java.util.Date
表示 UTC 中的时刻,但其 toString
方法在生成表示日期时间对象值的文本时动态应用 JVM 当前的默认时区.虽然本意是好的,但这个反特征令人困惑地产生了 Date
对象具有那个时区的错觉。
换句话说,Date::toString
在撒谎。在这些遗留问题中发现的许多糟糕的设计决策之一 类。也是从不使用这些遗留物的众多原因之一 类。
java.time
Instant
解析自 UTC 中 1970 年第一时刻的纪元参考以来的毫秒数作为 Instant
。
Instant instant = Instant.ofEpochMilli( 1556532279322 );
你的另一个输入,一个标准的 ISO 8601 字符串,也可以被立即解析。
Instant instant = Instant.parse( "2019-04-29T10:04:39.322Z" ) ;
ZonedDateTime
要通过特定地区(时区)的人们使用的挂钟时间查看同一时刻,请应用 ZoneId
以获得 ZonedDateTime
对象。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ) ;
instant
和 zdt
对象代表同一时刻。阅读同一时刻的两种方式,就像在冰岛和魁北克的 phone 上交谈的两个人同时看一眼墙上的时钟时,每个人都会看到不同的时间。