Java 8日期时间API(java.time)和Joda-Time之间的区别

300

我知道有关于java.util.Date和Joda-Time的问题,但是经过一番搜索,我没有找到关于Java 8引入的java.time API(由JSR 310定义)和Joda-Time之间差异的讨论。

我听说Java 8的java.time API更加简洁,而且可以做更多的事情,但我找不到比较两者的例子。

  • java.time能做什么Joda-Time不能做的?
  • java.time比Joda-Time做得更好的是什么?
  • java.time的性能是否更好?

8
这句话的意思是“这与Java 8无关,而是与Java 7有关。那个问题的答案只是简单地编辑了Java 8,没有提供很多细节。我的问题是特别针对Java 8的新日期和时间API,而不是Java 7的java.util.Date。我只是希望得到一个将Java 8与JodaTime进行比较的答案。” - Zack
2
也许这份Oracle文档可以帮到您。 - MarioDS
如果您使用的是Java 7,则使用附加库;如果您使用的是Java 8,则使用内置库。由于这两个库基本上都是由同一个人设计的,我不确定您期望有什么主要区别。 - Peter Lawrey
2
@PeterLawrey:Joda-Time和Java 8日期和时间API实际上非常不同。 - jarnbjo
5
你也可以阅读 Stephen Colbourne 的博客文章,他是这两个项目的主要作者。文章链接为:http://blog.joda.org/2009/11/why-jsr-310-isn-joda-time_4941.html - Matt Johnson-Pint
4个回答

467

常见特点

a)两个库都使用不可变类型。Joda-Time还提供了额外的可变类型,如MutableDateTime

b)此外:这两个库都受到设计研究"时间和金钱" from Eric EvansMartin Fowler有关领域驱动样式的思想的启发,因此它们更多或少地追求一种流畅的编程风格(虽然不总是完美的;-))。

c)使用这两个库,我们可以获得真正的日历日期类型(称为LocalDate)、真正的墙壁时间类型(称为LocalTime)以及组合(称为LocalDateTime)。与旧的java.util.Calendarjava.util.Date相比,这是一个非常大的优势。

d) 两个库都采用以方法为中心的方法,这意味着它们鼓励用户使用getDayOfYear()而不是get(DAY_OF_YEAR)。与java.util.Calendar相比,这会导致许多额外的方法(尽管后者由于过度使用int而根本不安全)。

性能

请参见@OO7的另一个答案,指向Mikhail Vorontsov的分析,尽管第3点(异常捕获)可能已经过时-请参见this JDK-bug。不同的性能(通常有利于JSR-310)主要是因为Joda-Time的内部实现始终使用类似机器时间的长整型(以毫秒为单位)。

空值

Joda-Time经常将NULL用作系统时区、默认语言环境、当前时间戳等的默认值,而JSR-310几乎总是拒绝NULL值。

精度

JSR-310处理纳秒精度,而Joda-Time仅支持毫秒精度。

支持的字段:

Java-8(JSR-310)中的一些类(例如ChronoFieldWeekFields)提供了有关支持字段的概述,而Joda-Time在此领域相对较弱 - 请参见DateTimeFieldType。 Joda-Time最大的缺点是缺少本地化的周相关字段。这两种字段实现设计的共同特点是都基于长整型值(没有其他类型,甚至没有枚举类型)。

枚举

JSR-310提供了枚举,例如DayOfWeekMonth,而Joda-Time没有这样做,因为它主要是在2002年至2004年之间开发的,早于Java 5

时区API

a) JSR-310比Joda-Time提供更多的时区特性。后者无法以编程方式访问时区偏移转换的历史记录,而JSR-310可以做到这一点。

b) 提醒您:JSR-310已将其内部时区存储库移动到新位置并使用不同格式。旧的库文件夹lib/zi不再存在。

调整器与属性

JSR-310引入了TemporalAdjuster接口作为一种规范化的方式来外部化时间计算和操作,特别是对于库或框架编写者,这是一种很好而相对简单的方式来嵌入JSR-310的新扩展(一种类似于以前java.util.Date的静态帮助类的等价物)。
然而,对于大多数用户来说,这个功能的价值非常有限,因为编写代码的负担仍然在用户身上。基于新的TemporalAdjuster概念的内置解决方案并不多,目前只有带有有限操作集的辅助类TemporalAdjusters(和枚举Month或其他时间类型)。
Joda-Time提供了一个字段包,但实践表明,新的字段实现非常难以编写。另一方面,Joda-Time提供了所谓的属性,使得某些操作比JSR-310更容易和更优雅,例如property.withMaximumValue()
日历系统
JSR-310提供了四种额外的日历系统。其中最有趣的是乌鲁木齐(沙特阿拉伯使用)。其他三个是:民国(台湾)、日本(仅自1871年以来的现代日历!)和泰国佛教(只在1940年之后正确)。
Joda-Time基于计算基础提供伊斯兰日历,而不是像乌鲁木齐那样基于观测的日历。Joda-Time还以类似的形式提供了泰国佛教日历,但没有提供民国和日本日历的支持。否则,Joda-Time还提供了科普特和埃塞俄比亚日历(但没有任何国际化支持)。
更有趣的是,Joda-Time还提供格里高利历, 朱利安历和混合格里高利-朱利安历。然而,对于真实历史计算的重要特征,如日期历史中不同的年份起点根本不被支持(同样的批评也适用于旧版java.util.GregorianCalendar),因此其实用价值有限。
其他日历,如希伯来日历波斯日历印度教日历在这两个库中完全缺失。 纪元日 JSR-310具有JulianFields类,而Joda-Time(版本2.0)在DateTimeUtils类中提供了一些辅助方法。

时钟

JSR-310没有接口(这是一个设计错误),但有一个抽象类java.time.Clock,可用于任何时钟依赖注入。相反,Joda-Time提供了接口MillisProvider和一些辅助方法DateTimeUtils。因此,Joda-Time也能够支持使用不同时钟(模拟等)的测试驱动模型。

持续时间算术

两个库都支持在一个或多个时间单位中计算时间间隔。然而,当处理单单位持续时间时,JSR-310风格显然更好(并且基于long而不是int):

JSR-310 => long days = ChronoUnit.DAYS.between(date1, date2);

Joda-Time => int days = DAYS.daysBetween(date1, date2).getDays();

处理多个时间单位的方式也不同。即使计算结果也可能不同 - 参见这个已关闭的Joda-Time issue。虽然JSR-310使用一种非常简单和有限的方法,只使用类Period(基于年、月和日的持续时间)和Duration(基于秒和纳秒),但Joda-Time使用了一种更复杂的方式,使用PeriodType类来控制一个持续时间(Joda-Time称其为“Period”)应该表示为哪些单位。虽然PeriodType-API的使用方式有些笨拙,但JSR-310根本没有提供类似的方式。尤其是在JSR-310中尚不可能定义混合日期和时间持续时间(例如基于天和小时)。因此,在从一个库迁移到另一个库时要小心。尽管部分类名相同,但讨论中的库是不兼容的。
间隔

JSR-310不支持此功能,而Joda-Time有限的支持。另请参阅SO-answer

格式化和解析

比较这两个库的最佳方法是查看同名类DateTimeFormatterBuilder(JSR-310)和DateTimeFormatterBuilder(Joda-Time)。 JSR-310变体稍微更强大(还可以处理任何类型的TemporalField,只要字段实现者成功编写了一些扩展点,例如resolve())。然而,最重要的区别在于 - 我认为:

JSR-310可以更好地解析时区名称(格式模式符号z),而Joda-Time在早期版本中根本无法做到这一点,现在只能以非常有限的方式进行。 JSR-310的另一个优点是支持独立的月份名称,这在俄语或波兰语等语言中非常重要。Joda-Time没有 访问此类资源 - 即使在Java-8平台上也没有。 JSR-310中的模式语法也比Joda-Time更灵活,允许使用可选部分(使用方括号),更加面向CLDR标准,并提供填充(字母符号p)和更多字段。否则应注意,Joda-Time可以使用PeriodFormatter格式化持续时间,而JSR-310无法做到这一点。
希望这个概述有所帮助。收集的所有信息主要是由于我的努力和调查,以设计和实现更好的日期和时间库(没有完美的)。 2015-06-24更新: 与此同时,我已经抽出时间为Java中不同的时间库编写并发布一个表格概述。 这些表格还包括Joda-Time v2.8.1和Java-8(JSR-310)之间的比较。它比本文更详细。

16
这是一篇很棒的文章,非常感谢分享这些信息。如果只在普通情况下选择Joda和JSR-310中的一个,你会选择哪个?我现在面临这个选择。我正在考虑JSR-310,只是因为它更新,而我尽可能支持“向前发展”。 - kevinarpe
9
如果我只能在JSR-310和Joda-Time之间做出选择,我可能会更倾向于JSR-310,因为Joda-Time几乎停止了任何进一步的开发(不会有新的大功能--只有错误修复和小型更新)。而且,JSR-310的设计更加现代化(并且具有更好的内部质量)。顺便说一下,这并不令人惊讶,我的真正决定更倾向于我的自己的库Time4J--请参见我发布帖子底部给出的表格概述链接。拥有替代选择总是有益的,并且强烈取决于您需要哪些功能。 - Meno Hochschild
Duration不是Java8版本的Interval吗? - Christian Hujer
1
@ChristianHujer 不可以查询 java.time.Duration 的开始或结束,与以时间轴为锚点的间隔相反。这种 JSR-310 类型只是自未知起始时间以来经过的秒数和纳秒数的一对。 - Meno Hochschild

45
Java 8 日期/时间:
Java 8的类是围绕人类时间构建的。这使得它们在人类日期时间的算术运算/转换方面非常快速。
在Java 8的实现中,像getDayOfMonth这样的日期/时间组件获取器的复杂度为O(1)。
在Java 8 ea b121中,解析OffsetDateTime/OffsetTime/ZonedDateTime非常慢,这是由于JDK内部抛出和捕获异常。
一组包:java.time.*,java.time.chrono.*,java.time.format.*,java.time.temporal.*,java.time.zone.*
时间戳、日期和时间部分、日期和时间解析器和格式化器、时区、不同的年表(日历)。
现有的类存在问题,比如Date不支持国际化或本地化。它们是可变的!
更简单、更健壮。
时钟可以被注入。
时钟可以根据不同的属性创建 - 静态时钟、模拟时钟、低精度时钟(整秒、整分钟等)。
时钟可以根据特定的时区创建。Clock.system(Zone.of("America/Los_Angeles"))。
使处理日期和时间的代码可测试。
使测试独立于时区。

Joda-Time:

  1. Joda-Time在内部使用机器时间。基于int/long值的手动实现会更快。
  2. Joda-Time的getter方法在每次调用时需要进行计算,这使得Joda-Time在这种情况下成为瓶颈。
  3. Joda-Time由不可变类组成,它处理瞬间、日期和时间、部分时间和持续时间。它具有灵活性和良好的设计。
  4. 将日期表示为瞬间。但是一个日期和时间可能对应多个瞬间。在夏令时结束时会有重叠的小时。也可能没有任何对应的瞬间。在夏令时开始时会有间隔的小时。必须对简单操作进行复杂的计算。
  5. 大多数方法接受null作为有效值。这会导致隐含的错误。

有关更详细的比较,请参见:

Java 8日期/时间库性能(以及Joda-Time 2.3和j.u.Calendar)。 & Java 8中的新日期和时间API


当你说“使测试独立于时区”时,你具体指的是什么? - Zack
@Zack:如果你测试的代码在不同的时区下表现不同,那么你可能需要强制测试在一个与操作系统提供的默认时区不同的时区下运行。 - jarnbjo
哈哈 - 我以为你说joda内部是在时间机器上运行而不是机器时间...我不会感到惊讶。 - Kyranstar
4
你的意思是什么是“人类时间”?时间本来就是由机器计算的。 - IgorGanapolsky

17

Joda-Time现已进入维护模式

虽然这不是问题的直接答案,但Joda-Time项目已经不再在积极开发中了。该团队建议用户迁移到更新的java.time API。请参阅Oracle的教程

从官方GitHub项目页面中得知:

Joda-time已经不再积极开发,除了保持时区数据的最新状态外。从Java SE 8开始,要求用户迁移到java.time(JSR-310)- JDK的核心部分,用于替换此项目。对于Android用户,java.time已添加到API 26+。需要支持更低API级别的项目可以使用ThreeTenABP库。


1

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