Java 8新的日期与时间API
为了简化与精化API,在经历了长期的重构工作后,由Stephen Colebourne所领导的,旨在替换Java复杂的日期API的Java Specification Request(JSR 310)已经被添加到了Java 8的特性列表中,并且预计在这个月发布M6版本。
日期处理是个复杂的问题,特别是Java中的日期处理有着不那么光彩的历史。最初的支持是通过java.util.Date实现的,这是个令人困惑的名字,包含了日期与时间,并且无法实现国际化。此外,它在别的方面也令人沮丧,比如说对属性使用了前后矛盾的偏移量;月份与小时都是基于0,月份中的天数则是基于1,而年则是从1900开始的。
在1997年Java 1.1发布时,它通过JDBC提供了对SQL的支持。它也有一个日期,该日期继承自java.util.Date,但却有不同的含义。IBM在1998年贡献了一个替代API(java.util.Calendar),可以实现国际化并且提供了很棒的灵活性,但代价却是带来了很大的复杂性,即便对于相对简单的问题来说亦如此。更糟的是,java.util.Date与java.util.Calendar中的所有属性都是可变的,这样在从访问器中返回时,Date类型(或是Time与Timestamp类型)的任何属性都需要被克隆一份。
直到2005年,Stephen Colebourne的Joda-Time库才发布,这个库非常流行并且得到了广泛的使用,它极大程度地改进了Java日期库的境遇,但却需要用户使用外部库。JSR 310构建在这个经验之上,为Java SE标准库本身的java.util.Date与java.util.Calendar提供了替代方案。
310是个庞大的API,但使用了一阵后我就发现它前后一致且易于理解。在最新版本中,它通过提供一个高层API(主要由应用使用)以及一个低层 API(主要由框架及其他场景使用)来处理复杂性问题。比如说,开发者可以通过低层API扩展对date-time域(YEAR、MONTH、 DAY_OF_MONTH及HOUR等)的支持,增加新的域,如“hour-of-fortnight”。
层API提供了一系列类,如ZonedDateTime(用于存储带有时区的日期与时间)、LocalDate、LocalTime及 LocalDateTime(用于处理“人类”的日期与时间)、还提供了Instant,用于实现日志等功能所需的时间戳,这类似于 Java.util.Date。所有这些类都是不可变且线程安全的。
现在,JSR 310已经成为Java 8的一部分了,ThreeTen项目也通过OpenJDK建立起来了,用于完成集成工作。提供310参考实现的项目托管在SourceForge上,源代码位于GitHub上。
Java的日期与时间API问题由来已久,这次终于在Java 8中进行了彻底的改进,InfoQ英文站的读者也对此举表达出了自己的看法:
真不错。很高兴看到日期时间API终于更新了。这个API至少涵盖了Joda-Time库的一些特性,然而,我仍旧觉得在真实的使用场景下,诸如工作日/非工作日的差别/计算等工作还是需要由其他第三方API来实现。
事实上,我觉得这是Java中最值得期待的特性。SimpleDateFormat与其他格式化类已经饱受批评,出现了线程安全、重量级、序列化成本高等问题。这可能是Java团队的一个机会,可以彻底消灭这些问题,以一个新的开始为Java创建优秀的API。
这个改进来的有些晚,但却是最值得期待的。Java Date API太丑陋,并且复杂,诸如joda-time之类的方式提供了更为强大的日期处理手段。但标准API是非常受欢迎的,因为诸如Hibernate与 JPA等框架可以使用标准来进行数据交换。一个常见的问题是当应用分层时,不兼容的技术会导致问题变得复杂。举个例子,标准的JSF日期转换器与 joda-time API就不兼容,你需要编写自己的转换器,但即便如此,primefaces日期部件也无法兼容于joda-time,因为他们的实现方式依赖于 java.util.Date对象。
发布新版的time-api.jar如何,它可以反映出Java 8的情况?0.6.3版(其项目主页上的最新版)依然使用老的包名,如果这样做,那么已经提供了支持的软件(比如说DataNucleus JDO/JPA)就可以在Java 8发布前更新了。
我也认为发布新的二进制jar包更合适一点,但通过Ant脚本我可以轻松地从源代码进行构建。
Java 8计划在今年10月份正式发布。