深入理解Java常用類-----時間日期

?????除了String這個類在日常的項目中比較常用之外,有關時間和日期的操作也是經常遇到的,本篇就講詳細介紹下Java API中對時間和日期的支持。其實在Java 8之前時間日期的API并不是很好用,以至于人們在項目中大多使用的是一個第三方庫 Joda-Time,當然Java 8 吸收了該庫的大部分優點,改進了相關API,現在的時間日期處理接口相對以前來說是好用很多,本篇也將學習下這個優秀的第三方庫。下面是本篇主要涉及內容:

  • 古老的Date類
  • 處理年月日的年歷類Calendar
  • 格式化字符串和日期對象的DateFormat格式轉換類
  • 好用的SimpleDateFormat實現類
  • Joda-Time庫

一、古老的Date類
?????Date這個類自jdk1.0開始就被設計出來, 從它的源代碼中我們也是可以看出來,Date類曾經扮演過很重要的角色,jdk早期的版本中有關日期和時間的操作幾乎都是由Date類完成的,下面我們一起看看它的源碼:

private transient long fastTime;

首先Date中有封裝一個long類型的變量,這個變量是整個時間日期操作的對象,也就是我們使用該變量代表時間和日期。下面說明它是如何表示時間和日期的。所有計算機中的時間都是用一個整數表示的,該整數的值代表的是距離格林尼治標準時間(1970年1月1日0時0分0秒)的毫秒數,也就是說fastTime值為1000的時候代表時間為1970年1月1日0時0分1秒。至于為什么是這個時間,由于種種歷史原因大家也可以去了解下,此處不再贅述。

由于該類中大部分方法都被注解了@Deprecated,已經不再推薦使用了,所以接下來我們主要還是看看其中還保留著的方法。只剩下兩個構造方法:

public Date(long date) {
        fastTime = date;
    }
public Date() {
        this(System.currentTimeMillis());
    }

只推薦使用上述兩個構造方法來構造我們的Date對象,一個是默認無參構造器(內部調用本地函數獲取系統當前時間計算與標準時間的毫秒差值),另一個則需要手動傳入一個毫秒值構造Date對象。

剩下的則主要是一些獲取和設置fastTime的函數,以及比較日期大小的函數,其他的都被注解了,至于上述這些函數,代碼相對簡單此處不再贅述。

二、處理年月日的年歷類Calendar
?????以前我們是可以使用Date來處理日期年月日的,但是由于該類不支持國際化等原因,現在其中大部分方法被注解,不再推薦使用,現在的Date類更像是代表著某一個時刻的對象,而處理年月日的這種轉換則完全交給了Calendar類處理。所以Calendar目前是日期時間處理中的核心類,接下來我們看看其中源碼:

//和Date一樣封裝了毫秒屬性
protected long  time;
protected int           fields[];
//封裝了十七個靜態常量
public final static int ERA = 0;
public final static int YEAR = 1;
public final static int MONTH = 2;
public final static int WEEK_OF_YEAR = 3;
.........
public final static int DST_OFFSET = 16;

在Calendar的內部封裝了17個靜態常量,這些常量將會作為索引用來檢索fields屬性,例如:fields[YEAR]將返回當前毫秒值對應的日期時間的年份部分,fields[MONTH]將返回的是月份部分的值等等。至于這些值是哪里來的,等我們介紹到后續源碼的時候再說明,此處只需要理解這些常量的作用即可。

該類是抽象類,我們使用工廠方法獲取該類實例:

public static Calendar getInstance()
{
    return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}

public static Calendar getInstance(TimeZone zone)
{
    return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}

public static Calendar getInstance(Locale aLocale)
{
    return createCalendar(TimeZone.getDefault(), aLocale);
}

public static Calendar getInstance(TimeZone zone,
                                       Locale aLocale)
{
    return createCalendar(zone, aLocale);
}

主要有四個方法用于創建Calendar實例,其實內部調用的是同一的方法只是傳入的參數的值不同。創建一個Calend 實例需要兩個參數,一個是TimeZone時區,另一個是Locale語言國家。因為每個國家或地區他們表示時間的形式是不一樣的,所以我們需要通過這兩個參數確定具體需要使用的格式,當然是以本地時間作為fastTime的值的,如果我們沒有指定時區和國家語言,那么將會默認使用本機系統信息。接下來我們看如何通過獲取到Calendar實例完成對日期時間進行計算。

我們有獲取和設置內部代表毫秒的time屬性:

public final Date getTime() {
    return new Date(getTimeInMillis());
}
public void setTimeInMillis(long millis){}

也有獲取上述介紹的17中屬性的方法:

public int get(int field)
{
    complete();
    return internalGet(field);
}

其中complete方法就是調用了本地函數完成對fields屬性中沒有值的元素賦值。 調用internalGet方法其實就是調用的fields[field],為我們返回指定屬性的結果值。我們可以看個例子:

public static void main(String[] args){
    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.get(Calendar.YEAR));
    System.out.println(calendar.get(Calendar.MONTH));
    System.out.println(calendar.get(Calendar.AM_PM))
}

結果如下:


這里寫圖片描述

上述代碼運行在不同的時候的結果都是不一樣的,寫作時的時間:2017/5/29 14:02。需要注意一點的是,month屬性是從0開始的,也就是0表示一月,4表示5月,星期也是一樣。此外,上述中的AM_PM表示的是上下午的概念,上午為0,下午為1。

除了獲取有關日期時間的信息,我們也是有可以用來設置他們的方法的:

//為指定屬性設置值
public void set(int field, int value)
//設置年月日等,很多重載
public final void set(int year, int month, int date)
......
//清空所有該Calendar實例的屬性值
public final void clear()

除此之外,還有一些通過計算來設置Calendar屬性的方法:

//為指定屬性添加值
abstract public void add(int field, int amount);

例如:

public static void main(String[] args){
     Calendar calendar = Calendar.getInstance();
     System.out.print(calendar.get(Calendar.YEAR));
     calendar.add(Calendar.YEAR,10);
     System.out.print(calendar.get(Calendar.YEAR));
}

改程序將輸出:2017 2027。還有一個roll方法也很有意思:

abstract public void roll(int field, boolean up);
//重載
public void roll(int field, int amount)
{
     while (amount > 0) {
         roll(field, true);
         amount--;
     }
     while (amount < 0) {
         roll(field, false);
         amount++;
     }
}

我們需要記住的是,roll方法完成的工作是和add一樣的,只是add方法處理了越界的特殊情況(越界會向上進一位),而roll方法會重新回到初始值再加。例如:

public static void main(String[] args){
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.MONTH, 11);//十二月
    System.out.println(calendar.getTime());

    //calendar.add(Calendar.MONTH,5);
    //calendar.roll(Calendar.MONTH,5);

    System.out.println(calendar.getTime());
}

上述程序我們設置Calendar日期為2017/12,針對上述兩種方式add和roll,輸出結果如下:

這里寫圖片描述

這里寫圖片描述

對于12月,add方法加5之后,month為5月但是已經是2018年,而roll則沒有向上進位,這就是區別,實際使用的時候還需加以區分。當然,如果你對某個屬性的范圍不是很明確,可以使用下面兩個方法獲?。?/p>

abstract public int getMinimum(int field);

abstract public int getMaximum(int field);

還有一些有關比較的函數,和Date是類似的:

public boolean equals(Object obj)
public int compareTo(Calendar anotherCalendar)
public boolean after(Object when)
public boolean before(Object when)

三、DateFormat處理格式轉換
?????DateFormat是一個抽象類,該類主要用于實現Date對象和字符串之間相互轉換, 涉及到兩個轉換的方法:

//將Date類型轉換為String類型
public final String format(Date date)
//將String類型轉換Date類型
public Date parse(String source)

除此之外,DateFormat還提供了四個靜態常量,代表著四種不同的風格。不同的風格輸出信息的內容詳盡程度不同,默認的風格是MEDIUM。(折中)

    public static final int FULL = 0;
    public static final int LONG = 1;

    public static final int MEDIUM = 2;

    public static final int SHORT = 3;

    public static final int DEFAULT = MEDIUM;

該類是抽象類,一樣需要使用靜態工廠獲取實例對象。

public final static DateFormat getTimeInstance()
public final static DateFormat getTimeInstance(int style)
public final static DateFormat getTimeInstance(int style,Locale aLocale)

public final static DateFormat getDateInstance()
public final static DateFormat getDateInstance(int style)
public final static DateFormat getDateInstance(int style,Locale aLocale)

public final static DateFormat getDateTimeInstance()
public final static DateFormat getDateTimeInstance(int dateStyle,int timeStyle)
public final static DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)

很明顯,有三種不同的方式來獲取DateFormat實例,每種方式有三個重載,getDateInstance用來處理日期,getTimeInstance用來處理時間,getDateTimeInstance既可以處理日期,也可以處理時間。我們通過一個例子看看他們之間的區別:

public static void main(String[] args) {
                                                                                      Calendar c = Calendar.getInstance();         System.out.println(DateFormat.getDateInstance().format(c.getTime()));
        System.out.println(DateFormat.getTimeInstance().format(c.getTime()));
        System.out.println(DateFormat.getDateTimeInstance().format(c.getTime()));
}

輸出結果:

2017-5-29
17:18:26
2017-5-29 17:18:26

很顯然,三者之間的區別也是不言而喻。對于他們另外兩個重載來說,一個重載提供修改輸出風格,另一個提供修改locale。無論是上述的哪一種工廠方法,在他們內部都調用的是同一個函數

private static DateFormat get(int timeStyle, int dateStyle,int flags, Locale loc)

四個參數,所有我們在調用工廠方法的時候沒有提供的參數值都會使用默認值。至于該方法具體是如何實現創建一個實例返回的我們就暫時不深究了。至于其他的一些方法,我們將在其子類SimpleDateFormat中學習。

四、優秀的實現類SimpleDateFormat
?????SimpleDateFormat是DateFormat的一個優秀的實現類,它增強了一個重要的性質。它允許自定義格式輸出模板。構造SimpleDateFormat實例的時候,可以傳入一個pattern作為輸出模板??磦€例子:

    public static void main(String[] args) {
        Calendar c = Calendar.getInstance();
        SimpleDateFormat sm = new SimpleDateFormat("yyyy年MM月dd日 E HH時mm分ss秒");
        System.out.println(sm.format(c.getTime()));
    }

輸出結果:

2017年05月29日 星期一 20時25分31秒

上述的代碼中,字符串yyyy年MM月dd日 E HH時mm分ss秒就是一個模板pattern,其中:

  • yyyy表示使用四位數字輸出年份
  • MM表示使用兩位數字表示月份
  • dd表示使用兩位數字表示日
  • E表示星期幾
  • HH表示使用兩位數字表示小時(24以內)
  • mm和ss分別表示分鐘和秒數

其中需要注意一點的是,m這個字母大寫狀態被用作表示月份,小寫狀態被用作表示分鐘,不能混用二者。除了可以使用HH表示小時以外,hh也可以表示小時,只是它是12的(上午和下午)。當然我們也可以逆向操作:

    public static void main(String[] args) throws ParseException {
        Calendar c = Calendar.getInstance();
        SimpleDateFormat sm = new SimpleDateFormat("yyyy年MM月dd日 E HH時mm分ss秒");
        String s = "2016年11月11日 星期五 00時00分00秒";
        System.out.println(sm.parse(s));
    }

輸出結果:

Fri Nov 11 00:00:00 CST 2016

五、開源第三方庫Joda-Time
?????Joda-Time庫中的內容還是很多的,我們簡單了解下基本的使用即可,至于深入學習該庫,大家可以自行嘗試,此處限于篇幅,不再贅述。在該庫中DateTime相當于jdk中Calendar,主要完成對日期年月日的計算操作。首先我們通過簡單易理解的方式創建DateTime的實例對象:

//2017-05-29 21:40
DateTime dt = new DateTime(2017,5,29,21,40);

//2017-05-29 21:40 50秒
DateTime dt2 = new DateTime(2017,5,29,21,40,50);

創建一個日期實例比Calendar中為每個屬性set值方便多了。在該庫中獲取日期的操作被分解了,不像Calendar中共享一個int數組。

DateTime dt = new DateTime(2017,5,29,21,40);
System.out.println("year: "+dt.getYear());
System.out.println("month: "+dt.getMonthOfYear());
System.out.println("day: "+dt.getDayOfMonth());
System.out.println("hour: "+dt.getHourOfDay());
System.out.println("minute: "+dt.getMinuteOfHour());
System.out.println("second: "+dt.getSecondOfMinute());
System.out.println("millisecond: " +dt.getMillisOfSecond());
System.out.println("day_of_week: " +dt.getDayOfWeek());

我們也可以直接使用DateTime的tostring方法來實現將日期轉換成指定pattern的字符串,例如:

DateTime dt = new DateTime(2017,5,29,21,40);
System.out.println(dt.toString("yyyy-MM-dd HH:mm:ss"));

上述代碼將會把日期類型按照指定的模板輸出,該Joda-Time庫中內容很多,此處就簡單介紹到這, 感興趣的同學可以自行研究,該庫的核心優勢就在于它將很多復雜的操作分解為單個簡單操作,這也是我們程序設計中核心的思維方式。

有關Java中日期和時間的內容本篇已經簡單介紹完了,有理解不到之處,望大家指出,相互學習!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,436評論 2 378

推薦閱讀更多精彩內容