如何将ZonedDateTime转换为Joda DateTime

13
我已经切换到threeten来处理日期和时间,但我仍然有一个使用joda将带时区的时间戳写入数据库的第三方工具,我需要将格式从一个转换为另一个。最好的方法是什么? 作为解决办法,我尝试了DateTime.parse(zdt.toString),但它失败了,因为joda不喜欢这种时区格式。 无效格式:“2015-01-25T23:35:07.684Z[Europe/London]”在“[Europe / London]”处存在格式问题。
3个回答

22

请注意,使用DateTimeZone.forID(...)不是安全的,因为通常ZoneOffset.UTC具有ID"Z",无法被DateTimeZone识别,可能会抛出DateTimeParseException异常。

为了将ZonedDateTime转换为DateTime,我建议采用以下方式:

return new DateTime(
    zonedDateTime.toInstant().toEpochMilli(),
    DateTimeZone.forTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone())));

这应该是被接受的答案。我们遇到了完全相同的问题! - ChrisDekker
@ChrisDekker 在所有情况下,不存在完美的时区标识符转换。虽然表达式TimeZone.getTimeZone(zoneId)对于表示UTC的Z文字面值可能比Joda-Time更有效,但这样的表达式对于其他ID可能会无声失败,而不是抛出异常而是任意回退到UTC(这甚至更糟),请参见API文档:“返回:指定的TimeZone,或者如果无法理解给定的ID,则为GMT区域”,我的回答仅涉及OP在问题中提出的特殊示例,但我不保证它适用于每个时区标识符。 - Meno Hochschild
这个答案的主要问题是它假装适用于所有标识符,尽管无法识别的 id 只需通过表达式 TimeZone.getTimeZone(zoneId) 切换到 UTC - 没有任何例外。Joda-Time 和 JDK 的基础时区存储库知道不同的有效 id 集,而这个事实不能被任何“聪明”的转换代码所修复。 - Meno Hochschild

9
ZonedDateTime zdt = 
  ZonedDateTime.of(
    2015, 1, 25, 23, 35, 7, 684000000, 
    ZoneId.of("Europe/London"));

System.out.println(zdt); // 2015-01-25T23:35:07.684Z[Europe/London]
System.out.println(zdt.getZone().getId()); // Europe/London
System.out.println(zdt.toInstant().toEpochMilli()); // 1422228907684

DateTimeZone london = DateTimeZone.forID(zdt.getZone().getId());
DateTime dt = new DateTime(zdt.toInstant().toEpochMilli(), london);
System.out.println(dt); // 2015-01-25T23:35:07.684Z

如果区域 id 转换可能因任何不支持或无法识别的 id 而崩溃,我建议

  • 捕获并记录它,
  • 更新 tz 存储库(对于 Joda:更新到最新版本,对于 JDK:使用 tz-updater 工具)

通常这比只是静默地回退到任意 tz 偏移更好。


对于那些打算使用James Ding的答案中建议的TimeZone.getTimeZone(...)的人,请三思而后行。对于不支持的id切换回UTC可能比崩溃更糟糕。 - Meno Hochschild

2
这是一个 Kotlin 的扩展方法(如果你使用 Kotlin 编写代码),用于执行相同的操作。
fun ZonedDateTime.toDateTime(): DateTime =
    DateTime(this.toInstant().toEpochMilli(), 
        DateTimeZone.forTimeZone(TimeZone.getTimeZone(this.zone)))

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