第 12 章 新的日期和时间API
本章内容
- 为什么在Java 8中需要引入新的日期和时间库
- 同时为人和机器表示日期和时间
- 定义时间的度量
- 操纵、格式化以及解析日期
- 处理不同的时区和历法
Java API提供了很多有用的组件,能帮助你构建复杂的应用。不过,Java API也不总是完美的。相信大多数有经验的程序员都会赞同Java 8之前的库对日期和时间的支持就非常不理想。然而,你也不用太担心:Java 8中引入全新的日期和时间API就是要解决这一问题。
在Java 1.0中,对日期和时间的支持只能依赖java.util.Date类。正如类名所表达的,这个类无法表示日期,只能以毫秒的精度表示时间。更糟糕的是它的易用性,由于某些原因未知的设计决策,这个类的易用性被深深地损害了,比如:年份的起始选择是1900年,月份的起始从0开始。这意味着,如果你想要用Date表示Java 9的发布日期,即2017年9月21日,需要创建下面这样的Date实例:
Date date = new Date(117, 8, 21);
它的打印输出效果为:
Thu Sep 21 00:00:00 CET 2017
看起来不那么直观,不是吗?此外,Date类的toString方法返回的字符串也容易误导人。以我们的例子而言,它的返回值中甚至还包含了JVM的默认时区CET,即中欧时间(Central Europe Time)。但这并不表示Date类在任何方面支持时区。
随着Java 1.0退出历史舞台,Date类的种种问题和限制几乎一扫而光,但很明显,这些历史旧账如果不牺牲前向兼容性是无法解决的。所以,在Java 1.1中,Date类中的很多方法被废弃了,取而代之的是java.util.Calendar类。很不幸,Calendar类也有类似的问题和设计缺陷,导致使用这些方法写出的代码非常容易出错。比如,月份依旧是从0开始计算(不过,至少Calendar类去掉了由1900年开始计算年份这一设计)。更糟的是,同时存在Date和Calendar这两个类,也增加了程序员的困惑。到底该使用哪一个类呢?此外,有的特性只在某一个类中提供,比如用于以语言无关方式格式化和解析日期或时间的DateFormat方法就只在Date类里有。
DateFormat方法也有它自己的问题。比如,它不是线程安全的。这意味着两个线程如果尝试使用同一个formatter解析日期,你可能会得到无法预期的结果。
最后,Date和Calendar类都是可以变的。能把2017年9月21日修改成10月25日意味着什么呢?这种设计会将你拖入维护的噩梦,接下来在第18章所讨论的函数式编程中,你会了解到更多的细节。
所有这些缺陷和不一致导致用户们转投第三方的日期和时间库,比如Joda-Time。为了解决这些问题,Oracle决定在原生的Java API中提供高质量的日期和时间支持。所以,你会看到Java 8在java.time包中整合了很多Joda-Time的特性。
本章会探索新的日期和时间API所提供的新特性。我们从最基本的用例入手,比如创建同时适合人与机器的日期和时间,逐渐转入到日期和时间API更高级的一些应用,比如操纵、解析、打印输出日期–时间对象,使用不同的时区和年历。
