【问题标题】:Parse ISO timestamp using Java 8 java.time api (standard edition only)使用 Java 8 java.time api 解析 ISO 时间戳(仅限标准版)
【发布时间】:2014-11-02 08:11:21
【问题描述】:

在示例中,我无法从字符串中获取从纪元开始的毫秒数。到目前为止,我已经尝试了这三种不同的方式,示例显示了最新的尝试。似乎总是归结为TemporalAccessor 不支持ChronoField。如果我能成功构建 Instant 实例,我可以使用toEpochMilli()

String dateStr = "2014-08-16T05:03:45-05:00"
TemporalAccessor creationAccessor = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(dateStr);
Instant creationDate = Instant.from(creationAccessor);

请给出简明的答案(不要从头开始构建格式化程序)并仅使用 java 8 标准发行版(我可以使用 Joda,但要避免依赖)。

编辑:上面代码中的 Instant.from 抛出:java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor: {OffsetSeconds=-18000},ISO resolved to 2014-08-16T05:03:45 of type java.time.format.Parsed

【问题讨论】:

  • 您发布的代码编译并运行良好。您使用的是什么版本的 Java 8?

标签: java datetime java-8 string-parsing java-time


【解决方案1】:

您的代码从 Java 8 更新 51 开始有效

您的代码现在可以运行,从 Mac OS X Mountain Lion 上的 Java 8 Update 51 开始。 HolgerAnswer 表示早期版本的 Java 中可能存在错误。可以理解,因为 java.time 框架在 Java 8 中是全新的。

这是您的代码的修改副本。

String dateStr = "2014-08-16T05:03:45-05:00";
TemporalAccessor creationAccessor = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse( dateStr );
Instant instant = Instant.from( creationAccessor );
long millisSinceEpoch = instant.toEpochMilli( );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant, ZoneOffset.of( "-05:00" ) );

转储到控制台。

System.out.println( "dateStr: " + dateStr );
System.out.println( "instant: " + instant );
System.out.println( " millis: " + millisSinceEpoch );
System.out.println( "    zdt: " + zdt );

运行时。

dateStr: 2014-08-16T05:03:45-05:00
instant: 2014-08-16T10:03:45Z
 millis: 1408183425000
    zdt: 2014-08-16T05:03:45-05:00

规范方法:
parse(CharSequence text, TemporalQuery<T> query)

您可能希望使用其他方法完成解析。

DateTimeFormatter 的类文档提到通常的解析方式应该是调用DateTimeFormatter::parse(CharSequence text, TemporalQuery<T> query) 而不是DateTimeFormatter::parse(CharSequence text)

所以不要这样:

String input = "2007-12-03T10:15:30+01:00[Europe/Paris]" ;
TemporalAccessor temporalAccessor = DateTimeFormatter.ISO_DATE_TIME.parse( input ) ;

...这样做,我们添加第二个参数,参数是method reference in Java 8 syntax,以调用转换from 方法(在本例中为ZonedDateTime :: from):

String input = "2007-12-03T10:15:30+01:00[Europe/Paris]" ;
ZonedDateTime zdt = DateTimeFormatter.ISO_DATE_TIME.parse( input , ZonedDateTime :: from ) ;

转储到控制台。

System.out.println("input: " + input );
System.out.println("  zdt: " + zdt );

运行时。

input: 2007-12-03T10:15:30+01:00[Europe/Paris]
  zdt: 2007-12-03T10:15:30+01:00[Europe/Paris]

【讨论】:

    【解决方案2】:

    这似乎是我在之前的所有测试版本(包括jdk1.8.0_20b19)中发现的一个错误,但在最终的jdk1.8.0_20 中没有。所以downloading an up-to-date jdk version 会解决这个问题。在最新的jdk1.9中也解决了。

    请注意,旧的 Java 7 方式适用于所有版本:

    long epochMillis = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")
                      .parse(dateStr).getTime();
    

    它还支持获取Instant:

    Instant i=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").parse(dateStr).toInstant();
    long epochMillis = i.toEpochMilli();
    

    但是,如前所述,一个简单的更新就能让您的 Java 8 代码正常工作。

    【讨论】:

      【解决方案3】:

      Instant.from(creationAccessor).toEpochMili() 应该至少根据ThreeTen javadoc for TemporalInstant 对您进行排序。 ThreeTen 是 javax.time 的参考实现。通过发表评论让我知道这是否适合您。

      【讨论】:

      • 取决于您想要的准确度。 toEpochMillis 将起作用,但据记载,在未来的某个时候(2040 年左右),这将引发异常。让我们希望 java 还在。
      • 到 2040 年左右,它有望被 javax.datetimetwentythirtyeight 取代。
      • 我越看给定时间和wikipedia's sample,我越怀疑这是一个JDK错误... sigh
      【解决方案4】:

      由于无法使用 long 正确表示 Instant(他们将 API 设计为没有 Y 2040 问题,当 long 不再足够时),您必须结合使用两种方法

      getEpochSecond()

      getNano()

      前者为您提供从纪元开始的秒数,后者为您提供自同一秒以来经过的纳秒数。

      【讨论】:

      • 这些都是 Instant 上的方法,我无法获得实例。如果可以,那么 Instant#toEpochMilli() 将是答案。
      • 长毫秒值将持续远远超过 2040 年。您将它与 2038 年到期的 int 秒值混淆了。
      猜你喜欢
      • 1970-01-01
      • 2019-05-31
      • 2018-08-29
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      • 1970-01-01
      • 2013-04-05
      • 2015-06-27
      相关资源
      最近更新 更多