如何在不使用本地时区的情况下解析日期?

时间:2019-04-29 10:12:20

标签: android date kotlin simpledateformat

从服务器我得到以下值:

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

2 个答案:

答案 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在说谎。这些遗留类中发现的许多不良设计决策之一。以及从不使用这些遗留类的众多原因之一。

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 ) ) ;

instantzdt对象都表示相同的同时力矩。两种方式可以读取同一时刻,因为两个人在冰岛和魁北克的电话上交谈时,他们会同时在墙上扫视时在墙上的时钟上看到不同的时间。