我有一个存储了joda time DateTime对象的数据源。我需要将它们转换为java ZonedDateTime对象,保留原始时区。
仅保留偏移量是不够的,因为某些DateTime对象表示每日重复性任务,并且这些任务必须在特定时区的特定时间针对每个日期发生。因此,它们必须遵循指定的TimeZone过渡,例如夏季和冬季。我不能告诉DateTime对象的最终用法,所以我需要保持所有对象的时区信息是安全的。
如何从org.joda.time.DateTime转换为java.time.ZonedDateTime?
所有
ord.joda.time.DateTimeZone.getId()
映射到
中可用的IDjava.time.ZoneId
答案 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 +。