好用java庫(一):java date/time api:joda-time


基於java的項目,最大的一個好處是有很多開源,優秀的第三方jar包拿過來直接使用,但是引用第三方包時一定要小心審核,確認包的作者或組織的權威性,以免未知的第三方包對項目的性能,安全和正確性的影響。作為一個java coder,有些包你不得不了解下,它們真的可以幫你在項目中節省很多時間去寫自己的utils包,況且成熟的社區維護的第三方工具包比自己動手寫的專業多了。所以我想寫個系列性的文章,介紹下平時在用,而且值得推薦給大家的一些好的第三方jar包。

時間,日期的處理在應用項目中是經常要用到的一塊,如果你還是個稍微追求感覺的程序員,應該早就受不了jdk中java.util.Date這個類,莫名奇妙的構造方法,再加上一堆的deprecated方法,讓你覺得為何不把這個類給取消算了。舉個例子,現在實例化一個日期對象(2013-1-6):

1 Date today = new Date(2013,1,6);

不好意思,其實返回的日期是:3913-2-6,有病吧......

我們也經常要用到一些時間變換處理的地方,如返回這個月的最后一個星期二,當月的最后一天,每個月的第一天的凌晨三點進行批處理任務,這個時候我們一般會用jdk中的Calendar 。雖然Calendar的出現改善了一些情況,但是為了處理一些時間變換時寫出各種DAY_OF_WEEK,DAY_OF_MONTH,MONTH....和add,set,roll之間的組合調用時,這種不直觀的api仍然使我們容易犯糊塗。

 Joda-Time 是一個非常優秀的時間日期處理工具,正是它的強大易用性,作者好像成了JSR310的Leader,下面我們就來大概介紹一下里面強大的api。

實例化日期/時間對象及操作對象

初始化:

DateTime dt = new DateTime(2013,1,7,12,23,58,874);

 

改變對象:

dt = dt.withDayOfMonth(20);//設置成20號
dt = dt.minusDays(2);//前面兩天
dt = dt.plusWeeks(1);//后面一個星期

 

注意:「DateTime」對象是不可變對象(Immutable),所以每次操作改變對象時實質上是返回的一個全新對象。

字段屬性:

DateTime dt2 = new DateTime();
Property prop = dt.dayOfMonth();//得到字段屬性
int i = prop.getDifference(dt2);//兩個日期之間相差幾天

 

格式化(format)和解析(parse)

「格式化和解析日期/時間」這種需求在時間處理中大家都會碰到,在標准jdk中使用的是SimpleDateFormat,但是大家都知道這個玩藝用起來真是愛恨交加。首先SimpleDateFormat的實例化是重量級的,這就會促使我們用單例模式使用它,可是這個類又是「線程不安全的」,所以要synchronized調用。

joda-time對這類需求支持很強大:

DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS");
String s = fmt.print(dt);
dt = fmt.parseDateTime("2013-01-07 22:39:13 782");

 

如果你不想每次格式化輸出的時候都要去關聯一個DateTimeFormatter,可以直接這樣:

DateTime dt = new DateTime();
String str = dt.toString("yyyy-MM-dd HH:mm:ss SSS");

 

這里面的原理其實就是在調用toString的時候把傳進去的format string參數生成一個DateTimeFormatter,然后再格式化輸出。你可能考慮擔心每次都會實例化DateTimeFormatter對象會影響性能,其實內部已經做了緩存了。

private static final Map<String, DateTimeFormatter> cPatternedCache = new HashMap<String, DateTimeFormatter>(7);

 如果你以為joda-time只有這些對相對jdk改善的功能,那么太小看它了,且看里面一些jdk沒有的高級api功能。

Interval/Duration/Period

 它們三個之間很有相似性,都有“時間間隔”的意思,我們先看一段代碼,再講下它們之間的差異性。

        DateTime start = new DateTime(2011,12,2,15,33);//開始時間
        DateTime end = new DateTime(2013,3,1,12,34);//結束時間
        Interval inteval = new Interval(start,end);
        Duration duration = new Duration(start,end);
        Period period = inteval.toPeriod();
        
        boolean between = inteval.contains(new DateTime(2013,2,1,12,34));//判斷指定時間在這段時間間隔里: True
        long millSeconds = duration.getMillis();//得到兩個時間相差的豪秒數:39301260000
        long millSeconds1 = period.getMillis();//得到兩個時間相差的豪秒數:0

 

上面的三個返回結果中,第一個好理解,「2013-2-1 12:34」是中「2011-12-2 15:33」和「2013-3-1 12:34」之間的一個時間。第二個返回這兩個時間相差的毫秒數(39301260000),也比較好理解。可是第三個結果返回 0 ,就比較暈了,其實Interval,Duration,Period都有「時間間隔」的意思在里面,但是這里定義三個類肯定是有三種語義在里面的,要理解這三者的區別,只要搞清楚這三個不同的類里包含的「信息」就知道差異了。

  • Interval   : 它包含了「開始時間」和「結束時間」信息,包括各自的時區。所以可以根據Interval對象得到「開始時間」和「結束時間」這兩個信息。
  • Duration : 它沒有任何「開始時間」和「結束時間」的概念,只是保存了一個具體的時間間隔,也就是一個以毫秒為單元的整數。
  • Period     : 它和「Duration」差不多意思,也是沒有包含「開始時間」和「結束時間」的信息,但是它和「Duration」不同的是,它所指的時間間隔不是以毫秒為單元,而是「年」,「月」,「天」,「小時」,「分」,「秒」,「毫秒」為相對單元。舉個例子:「Duration」就像是“我比你大30天”的語義,而「Period」就是“我大你一個月”。它們兩種語義是不同的,“大你30天”是個絕對值,而“大你一個月”就是相對的,因為「一個月」可能是30天,也可能是31天,也可能會碰到2月分的28天,所以說是相對的。

再看上面代碼中的輸出結果,就知道為什么duration和period調用getMillis會出現不同的結果了:duration.getMillis返回的是絕對相差毫秒數,而period.getMillis返回0是因為「開始時間」和「結束時間」間隔的相對時間是:1年2個月3個星期5天21個小時1分鍾。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM