Joda-Time使用一览
在Java公共库中,有没有那个类能比日期(Date)、时间(Time)更不好用?
没有;有没有哪个类像Date类中有各种莫名其妙的构造方法,然后再给上面加上deprecated注解?
有没有哪个类在初始化过程中就充满陷阱,初始化的结果根本不符合预期?
可以说最违背软件工程学、颠覆你认知的就是JDK中的日期和时间操作了。
当线上问题、程序bug最终是因为某个时间初始化造成行为不一致时,你总是会在心里暗暗骂一句:wtf;然后再追问一下,这个初始化为什么这么不符合常理?充满怨念的改完bug,下次还有可能会掉入到另外的陷阱中。
You can't escape time. Why not make it easy?
没有在哪个程序中能避免使用时间,并且在金融、保险行业中时间的使用时极其重要的部分,我们需要能健壮的初始化、操作时间的库,那么Joda-Time,面向Java平台的易于使用的开源DateTime库,是你最为正确的选择,其目的在于替换掉Java中的日期时间操作。我们下面来学习下JodaTime库的使用。
目前Joda-Time的最新版本为2.2,jar包大小仅为560K,最重要的是对其它包没有依赖,可以直接导入Joda-Time包进行使用。
在学习Joda-Time之前,我们先来熟悉一下Joda-Time引入的几个概念:
Instant:
Internally, the class holds one piece of data, the instant as milliseconds from the Java epoch of 1970-01-01T00:00:00Z.
Instant表示时间线上的某个精确时刻,使用epoch开始计算的毫秒表示。这个是时间的基础,也是Joda-Time库能与JDK date和Calendar兼容的原因。该Instant表示从标准时间点1970-01-01T00:00:00Z开始计算的毫秒数,如果先于这个时间点结果会是个负数。可以这么理解Instant,从初始点的时间线上截取一个点,该点与初始点的距离就是一个Instant,单位是millisecond。
Partial:
A Partial instance can be used to hold any combination of fields. The instance does not contain a time zone, so any datetime is local.
Partial是指时间的片段,和Instant不同,Partial可以表示时间线上的多个点。Instant初始化为时间线上的点后,含义非常精确;而Partial则定义了一个时间线上概念,在时间线上有多个Instant满足Partial的要求。如将Partial设定为5月第一个周一的任意时刻,则是指每年的5月第一个周一当天的时间都满足Partial的要求;同理,Partial为13:31 pm也对应与任意年任意天的13:31 pm,并且每天只能生效一次。
熟悉Quartz-Scheduler、或者linux上crontab就能比较容易的理解Instant和Partial的区别:前者是时间点,后者是重复的时间点,条件比前者更为宽松,可以用于创建重复出现的日期。
下面这三个Interval、Duration、Period都有表示时间间隔的意思,他们之间还是有差异性的:
Interval:
A time interval represents a period of time between two instants. Intervals are inclusive of the start instant and exclusive of the end. The end instant is always greater than or equal to the start instant.
Interval表示的时间间隔是半封闭的(左闭右开),能够判断时间间隔,重要的是能够获得[开始时间,结束时间),包括时区信息;如果你设定时间跨度后,需要在连续时间中知晓开始和结束时间,请用Interval。
Duration:
A duration is defined by a fixed number of milliseconds. There is no concept of fields, such as days or seconds, as these fields can vary in length. A duration may be converted to a Period to obtain field values. This conversion will typically cause a loss of precision however
Duration表示的是绝对的时间间隔,以毫秒为单位。将时间间隔转化为标准表示形式(秒、分、时)等,可以进行数学转换。在时间线上切两个点,这两个点之间的差值用毫秒表示就是Duration;这个时间跨度只有这个差值毫秒信息,并没有开始、结束的概念,如果不关心其它信息的话可以使用Duration。
Period:
A time period is divided into a number of fields, such as hours and seconds. Which fields are supported is defined by the PeriodType class. The default is the standard period type, which supports years, months, weeks, days, hours, minutes, seconds and millis.
Period表示的信息和Duration信息相同,也是时间间隔,没有开始和结束的概念;但是和Duration不同的是,Period表示的是人们熟悉的概念:年、月、周、时、分、秒。和Duration使用方法相同,如果你关心检索单个字段的功能,就可以使用Period。Duration和Period的选择使用可以简单归结为前者关注处理方便、后者关注检索方便,同时两者都不关心开始和结束时间点。
Chronology
Chronology provides access to the individual date time fields for a chronological calendar system.
Chronology提供年表日历系统,实际上是实现了一种执行日历算法的框架,目前Joda-Time支持的年表系统包括:ISO8601、Buddhist、Coptic、Ethiopic、Gregorian、GregorianJulian、Islamic、Julian,其中ISO8601是默认使用的年表系统。
注意:上面的几个概念都是线程安全(thread-safe)、不可变的(Immutable),在多线程中使用都没有问题。
我们看下这个图,包括前面提到的几个概念和在Joda-Time中使用的几个类的对应关系:
如果理解了这些概念,再加上这幅图,Joda-Time的使用就不是问题了。其实上面这些只是解决了Joda-Time的入门概念问题,离真正的使用还有距离,可以参考这两个内容:
http://joda-time.sourceforge.net/index.html,官网永远是你的首选。
http://www.ibm.com/developerworks/cn/java/j-jodatime.html
我看了之后,感觉Joda-Time的使用也不能写的比上面的更好,大家可以直接看上面的内容学习一下使用。如果在Joda-Time的概念有有疑问的话,本文的前半部分就是你需要的内容。
我写几个使用说明,或者说吐槽点吧,大家看过就行:
1:使用直接、简单:
如果我写下
Date today = new Date(2013,3,17);
我指的就是3013年3月17日,为什么我在初始化是还要计算2013-1900=113来初始化日期?这个113是什么含义?既不简单也不直接,这个就是很大的问题楽。
2:Joda-Time使用广泛的很大原因是其链式编程的成功使用,在方法名上直接使用minus、plus、add、withXXX、toXXX等,包含的信息量比直接使用运算符加减操作要直接许多。
3:容易扩展和替换:和JDK的时间系统无缝切换使用,这个对于从JDK切换到Joda-Time是个福音,对于其它框架的时间支持也是很重要的一个方面,如Hibernate、JSP、i18n等,具体扩展项目可见:http://joda-time.sourceforge.net/related.html
4:文档的完整和成熟也是其易于入门的原因
日期处理,推荐使用Joda-Time。