如何将org.joda.time.DateTime转换为java.time.ZonedDateTime

22
我有一个数据源,其中存储了 joda time DateTime 对象。我需要将它们转换为 java ZonedDateTime 对象,并保留原始时区信息。
仅保留偏移量是不够的,因为某些 DateTime 对象代表每日重复任务,这些任务必须在特定时区的特定时间发生。因此,它们必须遵循指定时区的转换,例如夏令时和冬令时。我无法确定 DateTime 对象的最终用途,因此需要保留所有对象的时区信息以确保安全。
如何从 org.joda.time.DateTime 转换为 java.time.ZonedDateTime?
所有 ord.joda.time.DateTimeZone.getId() 是否都映射到 java.time.ZoneId 可用的 ID?

参见:https://dev59.com/_6Pia4cB1Zd3GeqP4M-8 - Aaron Digulla
2个回答

33

如果你要处理夏令时转换,就应该避免单独提供每个字段。可以使用epochMillis进行转换,如下面的示例所示。

Instant instant = Instant.ofEpochMilli(dt.getMillis());
ZoneId zoneId = ZoneId.of(dt.getZone().getID(), ZoneId.SHORT_IDS);
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, zoneId);
否则您在转换日期时将损失一个小时。例如,德国于2017年10月29日03:00 GMT+2从夏令时(GMT+2)转换为冬令时(GMT+1),这变成了02:00 GMT+1。在那一天,您会有两个02:00的实例-早期的是GMT+2,稍后的是GMT+1。
由于您正在使用ZoneIds而不是偏移量来工作,所以无法知道您想要哪个02:00实例。在转换过程中,默认情况下假定第一个实例。如果您同时提供ZoneId和hourOfDay,那么02:00 GMT+2和02:00 GMT+1都将被转换为02:00 GMT+2。

1
这应该是被接受的答案,比当前接受的答案更准确,而且可能更有效率。你最后一行的意思是等同于 instant.atZone(zoneId) 吗? - Ken Williams

20

并非所有的Joda-Time中的时区字符串都与java.time匹配,但绝大多数都可以,因为它们都基于IANA tz数据。请比较DateTimeZone.getAvailableIDs()ZoneId.getAvailableZoneIds()以确定不匹配的部分。可以使用ZoneId.of(String, Map)映射其他标识符。

要以最有效的方式进行主要转换,您必须传递每个字段:

ZonedDateTime zdt = ZonedDateTime.ofLocal(
    LocalDateTime.of(
        dt.getYear(),
        dt.getMonthOfYear(),
        dt.getDayOfMonth(),
        dt.getHourOfDay(),
        dt.getMinuteOfHour(),
        dt.getSecondOfMinute(),
        dt.getMillisOfSecond() * 1_000_000),
    ZoneId.of(dt.getZone().getID(), ZoneId.SHORT_IDS),
    ZoneOffset.ofTotalSeconds(dt.getZone().getOffset(dt) / 1000));

注意在这种情况下使用 ZoneId.SHORT_IDS 作为 Map 的用法。

对于一个处理大多数用例,但性能较低的更简单的解决方案,请使用以下内容:

ZonedDateTime zdt = dt.toGregorianCalendar().toZonedDateTime();

有没有特定的子集不在其中之一?也许一些旧的 POSIX 兼容区域?我希望所有现代“Area/Locality”风格的区域都可以在两者中使用。 - Matt Johnson-Pint
2
唯一的区别是ROC被ROK所取代。可以使用ZoneId.SHORT_IDS访问EST、HST和MST时区。否则,任何差异都只是来自Joda-Time中时间区域数据库的后续版本。 - JodaStephen
1
@JodaStephen 与其使用ZoneId.of(dt.getZone().getID(), ZoneId.SHORT_IDS)),不如使用dt.toTimeZone().toZoneId(),这样似乎将繁琐的细节分别交给了JodaTime和Java处理。 - NomeN
1
这段代码在夏令时转换期间无法正常工作((year, month, day, hour, minute, second, millis, zone)不能定义一个时间点)。@MuhipTezcan的答案似乎是正确的。 - Piotr Findeisen
通过传递偏移量来处理夏令时问题。 - JodaStephen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接