如何转换org.joda.time.DateTime到java.time.ZonedDateTime

时间:2015-03-05 12:33:34

标签: java datetime timezone jodatime java-time

我有一个存储了joda time DateTime对象的数据源。我需要将它们转换为java ZonedDateTime对象,保留原始时区。

仅保留偏移量是不够的,因为某些DateTime对象表示每日重复性任务,并且这些任务必须在特定时区的特定时间针对每个日期发生。因此,它们必须遵循指定的TimeZone过渡,例如夏季和冬季。我不能告诉DateTime对象的最终用法,所以我需要保持所有对象的时区信息是安全的。

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

所有

  

ord.joda.time.DateTimeZone.getId()

映射到

中可用的ID
  

java.time.ZoneId

2 个答案:

答案 0 :(得分:17)

并非所有来自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();

答案 1 :(得分:8)

如果您正在使用夏令时转换,则应该避免单独提供每个字段。请改用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日格林尼治标准时间2点03:00从格林威治标准时间+ 1开始,从夏季时间(GMT + 2)过渡到冬季时间(格林尼治标准时间+ 1)。在那一天,您有2个02:00的实例 - 较早的一个GMT + 2和后一个GMT + 1。

由于您使用的是ZoneIds而不是偏移量,因此无法知道您想要的两个实例中的哪一个。默认情况下,在转换期间假定第一个。如果您提供hourOfDay以及ZoneId,则格林威治标准时间02:00和格林威治标准时间02:00均将转换为格林威治标准时间02:00 +。