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

2022-09-02 19:26:33

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

保留偏移量是不够的,因为某些 DateTime 对象表示日常重复性任务,并且这些任务必须在每个日期的特定时区中的特定时间发生。因此,它们必须遵循指定的时区转换,例如夏令时和冬令时。我无法说出 DateTime 对象的最终用法,因此我需要保留所有对象的时区信息以确保安全。

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

全部

ord.joda.time.DateTimeZone.getId()

映射到 ID 的可用位置

java.time.ZoneId


答案 1

如果您使用的是夏令时过渡,则应避免单独提供每个字段。请改用 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。在那一天,您有 2 个 02:00 实例 - 一个是 GMT+2 的早期实例,另一个是 GMT+1 的实例。

由于您使用的是 ZoneId 而不是偏移量,因此无法知道您需要 2 个实例中的哪一个。默认情况下,在转换过程中假定第一个。如果您提供 ZoneId,则 02:00 GMT+2 和 02:00 GMT+1 都将转换为 02:00 GMT+2。hourOfDay


答案 2

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

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

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_IDSMap

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

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