从服务器我得到以下值:
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小时)不同,而我处于timeZone + 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
答案 0 :(得分:1)
问题在于您的SimpleDateFormat的模式。最后,您有'Z'
,它表示要解析的日期字符串中应该有一个文字“ Z”。但是,日期末尾的“ Z”具有特殊含义,即it signifies the UTC timezone。因此,您应该将其解析为时区指示符,以便获得正确的日期值。您可以使用模式XXX
(请参见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() )
答案 1 :(得分:1)
您使用的是可怕的日期时间类,而该类在几年前被JSR 310中定义的现代 java.time 类所取代。
Date::toString
说谎为什么结果落后2小时
实际上并没有落后两个小时。
问题在于,尽管java.util.Date
表示UTC时刻,但其toString
方法动态地应用JVM的当前默认时区,同时生成表示日期时间对象值的文本。尽管出于良好目的,但此反功能却令人困惑地产生了具有该时区的Date
对象的错觉。
换句话说,Date::toString
在说谎。这些遗留类中发现的许多不良设计决策之一。以及从不使用这些遗留类的众多原因之一。
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
对象都表示相同的同时力矩。两种方式可以读取同一时刻,因为两个人在冰岛和魁北克的电话上交谈时,他们会同时在墙上扫视时在墙上的时钟上看到不同的时间。