相信使用过Java Date api的朋友或许都或多或少的感觉Java Date系列的api并不是十分好用。笔者在很多项目中不得不大量编写操作时间方面的类库来进行时间和日期的操作。前段时间“JSR-310 日期/时间api草案发布”刚刚发布,貌似给笔者这样的Java爱好者带来了曙光,下面让我们一起来看看JSR-310 Date与Time API能给我们带来什么?
近日JSR 310 Date与Time API的领导Stephen Colebourne发布了该提案(准备加入到Java语言中)的早期草案。 InfoQ有幸在QCon London上采访到了Stephen以深入了 解该项目。
InfoQ:我们为何需要一个新的Date与Time API?现有的怎么了?
Stephen:目前API(java.util.Date与java.util.Calendar)的一个主要问题在于他们是可变 的。换句话说,请看如下代码:
public class Employee { private final Date startDate; public Employee(Date date) { startDate = date; } public Date getDate() { return startDate; } }
即便将startDate标识为final,但返回的java.util.Date实例依然是可变的,这样在外面就可以通过调用setYear() 修改雇员的起始日期。此外,还有其他一些小问题,比如年份是从1900开始计算的而月份则从0开始,但关键问题还是可变性。这些问题没法修复。
InfoQ:在JSR 310中对应于java.util.Date的是什么?
Stephen:JSR 310实际上有两个日期概念。第一个是Instant, 它大致对应于java.util.Date类,因为它代表了一个确定的时间点,即相对于标准Java纪元(1970年1月1日)的偏移量;但与 java.util.Date类不同的是其精确到了纳秒级别。
第二个对应于人类自身的观念,比如LocalDate和LocalTime。 他们代表了一般的时区概念,要么是日期(不包含时间),要么是时间(不包含日期),类似于java.sql的表示方式。此外,还有一个MonthDay, 它可以存储某人的生日(不包含年份)。每个类都在内部存储正确的数据而不是像java.util.Date那样利用午夜12点来区分日期,利用 1970-01-01来表示时间。
InfoQ:你方才提到了时区这个令人苦恼的概念,在这方面,新的API有什么与众不同之处么?
Stephen:首先我们要区分时区(比如Europe/Paris与America/New_York)和距UTC的偏移量(比 如+01:00与-08:00)之间的差别。偏移量仅仅是UTC和本地时间之间的差值,而时区则是一个具名的规则集合,描述了偏移量该如何随着时间的变化 而变化。比如说,时区会描述一个特定的区域(如纽约)在给定的一个时刻具有某个偏移量,之后具有另一个偏移量(在本地时间线上创建一个间隙或是重叠,如春 秋夏时制的变换等)。
有3个级别的类支持这些概念。LocalDateTime无 需使用偏移量和时区就能表示时间。OffsetDateTime额 外地指定了偏移量而ZonedDateTime则 增加了时区规则。过去,很多应用都喜欢使用时区,但他们真正需要的其实只是偏移量而已(使用偏移量更简单、更快且不易出错)。XML Schema规范就是一个典型,它只支持偏移量而不支持时区。JSR 310可以明确表示出这些差别。
最后我想说的是,时区规则会随着时间的推移而不断发生变化。就在千禧年之前,一些国家将时区由国际日界线之后改为之前;此外,夏时制也在不断变化。 比如说,美国最近将夏时制的开始时间推后了,这样现在已经进入了美国的夏时制而尚未进入欧洲的夏时制。还有一些国家几乎每年都在变化,比如巴西。JSR 310 API支持时区的版本化,新版的时区数据可以替换掉旧版的。虽然这种替换取决于具体的实现,但组织可以通过将新数据追加到类路径之前以将新规则加到现有的 VM中,这样就无需更新整个JVM的安装文件了。
InfoQ:开始与结束时间之间的范围是如何界定的呢?
Stephen:可以使用Duration界 定任意两个Instant之间的范围。对于目前使用了开始与结束日期的代码来说,这是最接近的类比。
如前所述,现在有一些具体的概念来表示YearMonth和MonthDay, 在适当的时候应该使用这两个类。还有一个Period类 来表示任意的时间周期,如“两年、3个月、7天、4小时、50分钟”等。
InfoQ:那么其他日历如何呢?
Stephen:核心日历是ISOChronology, 默认情况下使用它来映射时间,就像目前Java API中的GregorianCalendar一样。然而,我们对其他一些年代也提供了支持,如CopticChronology和ThaiBuddhistChronology, 如果需要还可以支持更多。
InfoQ:上面一些概念已经出现在了JodaTime中, 那么JodaTime与JSR 310是什么关系呢?
Stephen:很多开发者已经开始使用JodaTime了,现在是时候改进Java基类了。最明显的变化就是包名(从 org.joda.time变为javax.time),但实际上还有一些细小的差别。
首先,很多Joda Time API都接受null值来表示0时间或是间隔。虽然这么做很诱人,但这会导致大量的小错误(在没有恰当地返回值的情况下)。JSR 310通过抛出null参数异常修复了这个问题。
其次,与计算机相关的时间(Instant)和与人类相关的时间(DateTime)之间的差别变得更明显了。我们使用父接口InstantProvider替 换掉之前的ReadableInstant以将任意时间转换为Instant。
第三,现在所有抛出的异常都是CalendricalExcpetion的 子类。虽然CalendricalExcpetion是一个RuntimeException,但客户端的库调用都可以捕获这个父类。
InfoQ:最后我想问的是,目前JSR 310的状态如何?
Stephen:JSR 310专家组维护着一个开放的邮件列表,3周前也已经发布了规 范的早期草案供大家审查。审查周期截止到3月28日;如果你有任何意见或建议,请直接发给dev@jsr-310.dev.java.net, 或是在Expert Draft Review wiki上留下你的意见。
查看英文原文:JSR 310 Date and Time API for Java