Java 本身的日期类在 JDK1.0 版本之后就再也没有更新过,同时还存在着一些众所周知的问题(例如 1 月从 0 开始,导致了很多月份差一的漏洞)。一份新的 Java 规范请求(JSR,Java Specification Request)已经被提交,目的就是要解决上述问题,此版本的类库仍处在 Alpha 版本。在其稳定之前,很多开发者还是会使用 Joda Time 类库,该类库与 JSR-310的参考实现类似(但不完全相同)。
Date4j 为在 Java 中处理日期提供了一套新的解决方案,但与 Joda Time 所关注的范围完全不同。对比如下:
Joda Time | Date4j |
拥有的类的数量: 140+ | 拥有的类的数量< 10 |
包含可变和不可变类 | 仅包含不可变类 |
强调速度和功能 | 强调简单和有效 |
支持格里高里历(Gregorian)、 科普特语日历(Coptic)、 伊斯兰教历(Islamic)、佛历(Buddhist)等等 | 只提供对格里高里历的支持 |
可以完全取代 JDK 日期类 | 和 JDK 日期类配合使用 |
精确到毫秒级操作 | 支持到纳秒(十亿分之一秒)级操作 |
修复了天“溢出”的问题 | 天“溢出”的问题可配置 |
针对的是通常意义的日期维护 | 适用于通过数据库来维护的日期 |
采用 Apache 2.0 授权许可 | 采用 BSD 授权许可 |
虽然乍一看 Date4j 只具备了 Joda 中一部分的特性,但它有两个主要的特点是 Joda 所不具备的。
首先,Date4j 的开发者宣称类库不应莫名其妙地将日期截断。Joda 只支持毫秒级的精度而且在将来可能也不会改善。一些数据库也已经有了更好的解决方案。比如流行的 PostgreSQL 数据库对时间戳精度就已经支持到微秒级(百万分之一秒)。Date4j 可在处理日期时对精度毫无损伤。
第二个特征是日期“溢出”的问题,例如向某个日期增加一段时间后,日期落在下月的情况。最简单的例子就是在 3 月 31 日增加一个月的计算:
DateTime dt = new DateTime ("2011-03-31"); DateTime result = dt.plusMonths (1);
System.out.println (result.toString ());
当使用 Joda Time 时,会输出 4 月 30 日,但这也许并不是你想要的结果。
鉴于这种不确定性,Date4j 为您提供了4种选择:
1. | 第一天 |
2. | 最后一天(与 Joda Time 相同) |
3. | 日期顺延 |
4. | 抛出异常 |
以下通过举例的实例来说明,用 Date4j 替换 Joda 后的输出差异:
DateTime dt1 = new DateTime ("2011-03-31"); DateTime result1 = dt1.plus (0,1,0,0,0,0,DayOverflow.FirstDay);
System.out.println (result1.toString ());
//会输出 2011-05-01 (5月的第一天)
DateTime dt2 = new DateTime ("2011-03-31");
DateTime result2 = dt2.plus (0,1,0,0,0,0,DayOverflow.LastDay);
System.out.println (result2.toString ());
//会输出 2011-04-30(4月 30 日,与 Joda 输出结果相同)
DateTime dt3= new DateTime ("2011-03-31");
DateTime result3 = dt3.plus (0,1,0,0,0,0,DayOverflow.Abort);
System.out.println (result3.toString ());
//抛出运行时异常(Runtime Exception)
下面的代码展示了 DayOverflow.Spillover 中的选项:
//Joda Time 代码 DateTime dt = new DateTime ("2010-12-31");
DateTime result = dt.plusMonths (2);
System.out.println (result.toString ());
//输出 2011-02-28(2月的最后一天)
//Date4j 代码
DateTime dt1 = new DateTime ("2010-12-31");
DateTime result1 = dt1.plus (0,2,0,0,0,0,DayOverflow.FirstDay);
System.out.println (result1.toString ());
//输出 2011-03-01(3月的第一天)
//Date4j 代码
DateTime dt2 = new DateTime ("2010-12-31");
DateTime result2 = dt2.plus (0,2,0,0,0,0,DayOverflow.LastDay);
System.out.println (result2.toString ());
//输出 2011-02-28 (2月 28 日,与 Joda 输出相同)
//Date4j 代码
DateTime dt3= new DateTime ("2010-12-31");
DateTime result3 = dt3.plus (0,2,0,0,0,0,DayOverflow.Spillover);
System.out.println (result3.toString ());
//输出 2011-03-02(3月第二天)
如果应用对精度要求较高并且在处理数据库日期时不想对精度有所损失,或者在处理跨月份日期时想有更灵活的方案供选择,Date4j 会是一个不错的选择。现在,源代码已可直接下载。
查看英文原文:Date4j - A Minimalistic Library for Handling Dates