Joda Time to Java Time Migration 在迁移时显示不同的结果

Joda Time to Java Time Migration show different result while migration

给定以下代码

public static void main(String[] args) {
     org.joda.time.format.DateTimeFormatter _timestampFomatNYCJoda = org.joda.time.format.DateTimeFormat.forPattern("yyyyMMdd HHmmss.SSS").withZone(DateTimeZone.forID("America/New_York"));
     DateTimeFormatter _timestampFomatNYC = DateTimeFormatter.ofPattern("yyyyMMdd HHmmss.SSS").withZone(ZoneId.of("America/New_York"));
     LocalDateTime localDateTime = LocalDateTime.now();
     org.joda.time.LocalDateTime jodaLocalDateTime = new org.joda.time.LocalDateTime();
     System.out.println("System Time " + new Date());
     System.out.println("Java Version " +  localDateTime.format(_timestampFomatNYC));
     System.out.println("Joda Version " +  _timestampFomatNYCJoda.print(jodaLocalDateTime.toDateTime(DateTimeZone.UTC)));
} 

为什么 Java 版本和 Joda 版本不匹配?我在 IST 时钟上 运行。

下面是输出

System Time Fri Mar 27 17:01:33 IST 2020
Java Version 20200327 170133.933
Joda Version 20200327 130133.938

我可以重现你的结果。我也可以解释它们。 Joda-Time 和 java.time 被设计成在这种情况下表现不同。让我们依次看一下。

乔达时间

在 Joda-Time 中 DateTimeFormatter.withZone() 为您提供了一个带有 覆盖区域 的格式化程序,也就是说,一个 总是 [=68= 的区域] 用于格式化日期和时间。换句话说,任何日期和时间都将转换到此区域进行打印。文档说:

When printing, this zone will be used in preference to the zone from the datetime that would otherwise be used.

当您执行 new org.joda.time.LocalDateTime() 时,您会得到一个 LocalDateTime 代表默认时区的当前日期和时间。某些 class 名称中的 Local 表示 没有时区或与 UTC 的偏移量。我认为您的值一定等于 2020-03-27T17:01:33.938.

显然,当您使用带有覆盖区域的格式化程序格式化 LocalDateTime 时会发生什么,格式化程序假定您的 LocalDateTime 是 UTC(而您的不是)并将其转换从那里,在你的情况下到 America/New_York 时区。由于纽约实行夏令时 (DST),偏移量为 -04:00,因此 17:01 变为 13:01。

这是错误的结果。当你所在时区的时间是17:01时,不是17:01UTC,所以转换是建立在错误的前提下的。也不是13:01在纽约,所以转换后的结果是骗人的。

java.time

使用 java.time 在格式化程序上设置覆盖区与格式化类似,但这里有一个重要的区别:覆盖区仅在打印标识瞬间的日期时间对象时使用(a时间点)。来自文档:

When formatting, if the temporal object contains an instant, then it will be converted to a zoned date-time using the override zone. Whether the temporal is an instant is determined by querying the INSTANT_SECONDS field. If the input has a chronology then it will be retained unless overridden. If the input does not have a chronology, such as Instant, then the ISO chronology will be used.

… In all other cases, the override zone is added to the temporal, replacing any previous zone, but without changing the date/time.

再次 LocalDateTime.now() 为您提供当天的当前日期和时间(比通过 Joda-Time 查询早几毫秒),2020-03-27T17:01:33.933Local 仍然表示没有偏移量或时区。

因为你的LocalDateTIme没有偏移量或时区,它不能识别一个明确的时间点,一个瞬间。因此,在格式化时,日期和时间都不会改变。由于您的格式模式不包含时区或偏移量,因此会打印 none 。因此,您只需获取您所在时区(而不是纽约)的日期和时间,20200327 170133.933.

获取纽约时区的日期和时间

    DateTimeFormatter timestampFormat
            = DateTimeFormatter.ofPattern("yyyyMMdd HHmmss.SSS");
    ZonedDateTime timeInNy = ZonedDateTime.now(ZoneId.of("America/New_York"));
    System.out.println(timeInNy.format(timestampFormat));

当我刚才运行这段代码时,输​​出是:

20200327 122359.683

文档链接