深入理解Java常用類-----時(shí)間日期

?????除了String這個(gè)類在日常的項(xiàng)目中比較常用之外意系,有關(guān)時(shí)間和日期的操作也是經(jīng)常遇到的钾腺,本篇就講詳細(xì)介紹下Java API中對(duì)時(shí)間和日期的支持徙垫。其實(shí)在Java 8之前時(shí)間日期的API并不是很好用,以至于人們?cè)陧?xiàng)目中大多使用的是一個(gè)第三方庫(kù) Joda-Time放棒,當(dāng)然Java 8 吸收了該庫(kù)的大部分優(yōu)點(diǎn)姻报,改進(jìn)了相關(guān)API,現(xiàn)在的時(shí)間日期處理接口相對(duì)以前來(lái)說(shuō)是好用很多间螟,本篇也將學(xué)習(xí)下這個(gè)優(yōu)秀的第三方庫(kù)吴旋。下面是本篇主要涉及內(nèi)容:

  • 古老的Date類
  • 處理年月日的年歷類Calendar
  • 格式化字符串和日期對(duì)象的DateFormat格式轉(zhuǎn)換類
  • 好用的SimpleDateFormat實(shí)現(xiàn)類
  • Joda-Time庫(kù)

一、古老的Date類
?????Date這個(gè)類自jdk1.0開始就被設(shè)計(jì)出來(lái)厢破, 從它的源代碼中我們也是可以看出來(lái)荣瑟,Date類曾經(jīng)扮演過(guò)很重要的角色,jdk早期的版本中有關(guān)日期和時(shí)間的操作幾乎都是由Date類完成的摩泪,下面我們一起看看它的源碼:

private transient long fastTime;

首先Date中有封裝一個(gè)long類型的變量笆焰,這個(gè)變量是整個(gè)時(shí)間日期操作的對(duì)象,也就是我們使用該變量代表時(shí)間和日期见坑。下面說(shuō)明它是如何表示時(shí)間和日期的嚷掠。所有計(jì)算機(jī)中的時(shí)間都是用一個(gè)整數(shù)表示的,該整數(shù)的值代表的是距離格林尼治標(biāo)準(zhǔn)時(shí)間(1970年1月1日0時(shí)0分0秒)的毫秒數(shù)荞驴,也就是說(shuō)fastTime值為1000的時(shí)候代表時(shí)間為1970年1月1日0時(shí)0分1秒不皆。至于為什么是這個(gè)時(shí)間,由于種種歷史原因大家也可以去了解下熊楼,此處不再贅述霹娄。

由于該類中大部分方法都被注解了@Deprecated,已經(jīng)不再推薦使用了鲫骗,所以接下來(lái)我們主要還是看看其中還保留著的方法犬耻。只剩下兩個(gè)構(gòu)造方法:

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

只推薦使用上述兩個(gè)構(gòu)造方法來(lái)構(gòu)造我們的Date對(duì)象,一個(gè)是默認(rèn)無(wú)參構(gòu)造器(內(nèi)部調(diào)用本地函數(shù)獲取系統(tǒng)當(dāng)前時(shí)間計(jì)算與標(biāo)準(zhǔn)時(shí)間的毫秒差值)挎峦,另一個(gè)則需要手動(dòng)傳入一個(gè)毫秒值構(gòu)造Date對(duì)象香追。

剩下的則主要是一些獲取和設(shè)置fastTime的函數(shù),以及比較日期大小的函數(shù)坦胶,其他的都被注解了透典,至于上述這些函數(shù)晴楔,代碼相對(duì)簡(jiǎn)單此處不再贅述。

二峭咒、處理年月日的年歷類Calendar
?????以前我們是可以使用Date來(lái)處理日期年月日的税弃,但是由于該類不支持國(guó)際化等原因,現(xiàn)在其中大部分方法被注解凑队,不再推薦使用则果,現(xiàn)在的Date類更像是代表著某一個(gè)時(shí)刻的對(duì)象,而處理年月日的這種轉(zhuǎn)換則完全交給了Calendar類處理漩氨。所以Calendar目前是日期時(shí)間處理中的核心類西壮,接下來(lái)我們看看其中源碼:

//和Date一樣封裝了毫秒屬性
protected long  time;
protected int           fields[];
//封裝了十七個(gè)靜態(tài)常量
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的內(nèi)部封裝了17個(gè)靜態(tài)常量,這些常量將會(huì)作為索引用來(lái)檢索fields屬性叫惊,例如:fields[YEAR]將返回當(dāng)前毫秒值對(duì)應(yīng)的日期時(shí)間的年份部分款青,fields[MONTH]將返回的是月份部分的值等等。至于這些值是哪里來(lái)的霍狰,等我們介紹到后續(xù)源碼的時(shí)候再說(shuō)明抡草,此處只需要理解這些常量的作用即可。

該類是抽象類蔗坯,我們使用工廠方法獲取該類實(shí)例:

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);
}

主要有四個(gè)方法用于創(chuàng)建Calendar實(shí)例康震,其實(shí)內(nèi)部調(diào)用的是同一的方法只是傳入的參數(shù)的值不同。創(chuàng)建一個(gè)Calend 實(shí)例需要兩個(gè)參數(shù)宾濒,一個(gè)是TimeZone時(shí)區(qū)腿短,另一個(gè)是Locale語(yǔ)言國(guó)家。因?yàn)槊總€(gè)國(guó)家或地區(qū)他們表示時(shí)間的形式是不一樣的绘梦,所以我們需要通過(guò)這兩個(gè)參數(shù)確定具體需要使用的格式答姥,當(dāng)然是以本地時(shí)間作為fastTime的值的,如果我們沒(méi)有指定時(shí)區(qū)和國(guó)家語(yǔ)言谚咬,那么將會(huì)默認(rèn)使用本機(jī)系統(tǒng)信息。接下來(lái)我們看如何通過(guò)獲取到Calendar實(shí)例完成對(duì)日期時(shí)間進(jìn)行計(jì)算尚粘。

我們有獲取和設(shè)置內(nèi)部代表毫秒的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方法就是調(diào)用了本地函數(shù)完成對(duì)fields屬性中沒(méi)有值的元素賦值择卦。 調(diào)用internalGet方法其實(shí)就是調(diào)用的fields[field],為我們返回指定屬性的結(jié)果值郎嫁。我們可以看個(gè)例子:

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))
}

結(jié)果如下:


這里寫圖片描述

上述代碼運(yùn)行在不同的時(shí)候的結(jié)果都是不一樣的秉继,寫作時(shí)的時(shí)間:2017/5/29 14:02。需要注意一點(diǎn)的是泽铛,month屬性是從0開始的尚辑,也就是0表示一月,4表示5月盔腔,星期也是一樣杠茬。此外月褥,上述中的AM_PM表示的是上下午的概念,上午為0瓢喉,下午為1宁赤。

除了獲取有關(guān)日期時(shí)間的信息,我們也是有可以用來(lái)設(shè)置他們的方法的:

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

除此之外决左,還有一些通過(guò)計(jì)算來(lái)設(shè)置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));
}

改程序?qū)⑤敵觯?017 2027。還有一個(gè)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方法處理了越界的特殊情況(越界會(huì)向上進(jìn)一位),而roll方法會(huì)重新回到初始值再加坠狡。例如:

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());
}

上述程序我們?cè)O(shè)置Calendar日期為2017/12继找,針對(duì)上述兩種方式add和roll,輸出結(jié)果如下:

這里寫圖片描述

這里寫圖片描述

對(duì)于12月擦秽,add方法加5之后码荔,month為5月但是已經(jīng)是2018年,而roll則沒(méi)有向上進(jìn)位感挥,這就是區(qū)別缩搅,實(shí)際使用的時(shí)候還需加以區(qū)分。當(dāng)然触幼,如果你對(duì)某個(gè)屬性的范圍不是很明確硼瓣,可以使用下面兩個(gè)方法獲取:

abstract public int getMinimum(int field);

abstract public int getMaximum(int field);

還有一些有關(guān)比較的函數(shù)置谦,和Date是類似的:

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

三堂鲤、DateFormat處理格式轉(zhuǎn)換
?????DateFormat是一個(gè)抽象類,該類主要用于實(shí)現(xiàn)Date對(duì)象和字符串之間相互轉(zhuǎn)換媒峡, 涉及到兩個(gè)轉(zhuǎn)換的方法:

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

除此之外瘟栖,DateFormat還提供了四個(gè)靜態(tài)常量,代表著四種不同的風(fēng)格谅阿。不同的風(fēng)格輸出信息的內(nèi)容詳盡程度不同半哟,默認(rèn)的風(fēng)格是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;

該類是抽象類签餐,一樣需要使用靜態(tài)工廠獲取實(shí)例對(duì)象寓涨。

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)

很明顯,有三種不同的方式來(lái)獲取DateFormat實(shí)例氯檐,每種方式有三個(gè)重載戒良,getDateInstance用來(lái)處理日期,getTimeInstance用來(lái)處理時(shí)間冠摄,getDateTimeInstance既可以處理日期糯崎,也可以處理時(shí)間几缭。我們通過(guò)一個(gè)例子看看他們之間的區(qū)別:

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()));
}

輸出結(jié)果:

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

很顯然,三者之間的區(qū)別也是不言而喻拇颅。對(duì)于他們另外兩個(gè)重載來(lái)說(shuō)奏司,一個(gè)重載提供修改輸出風(fēng)格,另一個(gè)提供修改locale樟插。無(wú)論是上述的哪一種工廠方法韵洋,在他們內(nèi)部都調(diào)用的是同一個(gè)函數(shù)

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

四個(gè)參數(shù),所有我們?cè)谡{(diào)用工廠方法的時(shí)候沒(méi)有提供的參數(shù)值都會(huì)使用默認(rèn)值黄锤。至于該方法具體是如何實(shí)現(xiàn)創(chuàng)建一個(gè)實(shí)例返回的我們就暫時(shí)不深究了搪缨。至于其他的一些方法,我們將在其子類SimpleDateFormat中學(xué)習(xí)鸵熟。

四副编、優(yōu)秀的實(shí)現(xiàn)類SimpleDateFormat
?????SimpleDateFormat是DateFormat的一個(gè)優(yōu)秀的實(shí)現(xiàn)類,它增強(qiáng)了一個(gè)重要的性質(zhì)流强。它允許自定義格式輸出模板痹届。構(gòu)造SimpleDateFormat實(shí)例的時(shí)候,可以傳入一個(gè)pattern作為輸出模板打月《痈看個(gè)例子:

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

輸出結(jié)果:

2017年05月29日 星期一 20時(shí)25分31秒

上述的代碼中党窜,字符串yyyy年MM月dd日 E HH時(shí)mm分ss秒就是一個(gè)模板pattern拴袭,其中:

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

其中需要注意一點(diǎn)的是雀监,m這個(gè)字母大寫狀態(tài)被用作表示月份甫匹,小寫狀態(tài)被用作表示分鐘,不能混用二者也物。除了可以使用HH表示小時(shí)以外逛漫,hh也可以表示小時(shí)垛膝,只是它是12的(上午和下午)肺稀。當(dāng)然我們也可以逆向操作:

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

輸出結(jié)果:

Fri Nov 11 00:00:00 CST 2016

五第股、開源第三方庫(kù)Joda-Time
?????Joda-Time庫(kù)中的內(nèi)容還是很多的,我們簡(jiǎn)單了解下基本的使用即可话原,至于深入學(xué)習(xí)該庫(kù)炸茧,大家可以自行嘗試,此處限于篇幅稿静,不再贅述。在該庫(kù)中DateTime相當(dāng)于jdk中Calendar辕狰,主要完成對(duì)日期年月日的計(jì)算操作改备。首先我們通過(guò)簡(jiǎn)單易理解的方式創(chuàng)建DateTime的實(shí)例對(duì)象:

//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);

創(chuàng)建一個(gè)日期實(shí)例比Calendar中為每個(gè)屬性set值方便多了。在該庫(kù)中獲取日期的操作被分解了蔓倍,不像Calendar中共享一個(gè)int數(shù)組悬钳。

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方法來(lái)實(shí)現(xiàn)將日期轉(zhuǎn)換成指定pattern的字符串盐捷,例如:

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

上述代碼將會(huì)把日期類型按照指定的模板輸出,該Joda-Time庫(kù)中內(nèi)容很多默勾,此處就簡(jiǎn)單介紹到這碉渡, 感興趣的同學(xué)可以自行研究,該庫(kù)的核心優(yōu)勢(shì)就在于它將很多復(fù)雜的操作分解為單個(gè)簡(jiǎn)單操作母剥,這也是我們程序設(shè)計(jì)中核心的思維方式滞诺。

有關(guān)Java中日期和時(shí)間的內(nèi)容本篇已經(jīng)簡(jiǎn)單介紹完了,有理解不到之處环疼,望大家指出习霹,相互學(xué)習(xí)!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末炫隶,一起剝皮案震驚了整個(gè)濱河市淋叶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伪阶,老刑警劉巖煞檩,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異栅贴,居然都是意外死亡斟湃,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門筹误,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)桐早,“玉大人,你說(shuō)我怎么就攤上這事厨剪『逶停” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵祷膳,是天一觀的道長(zhǎng)陶衅。 經(jīng)常有香客問(wèn)我,道長(zhǎng)直晨,這世上最難降的妖魔是什么搀军? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮勇皇,結(jié)果婚禮上罩句,老公的妹妹穿的比我還像新娘。我一直安慰自己敛摘,他們只是感情好门烂,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般屯远。 火紅的嫁衣襯著肌膚如雪蔓姚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天慨丐,我揣著相機(jī)與錄音坡脐,去河邊找鬼。 笑死房揭,一個(gè)胖子當(dāng)著我的面吹牛备闲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播崩溪,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼浅役,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了伶唯?” 一聲冷哼從身側(cè)響起觉既,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎乳幸,沒(méi)想到半個(gè)月后瞪讼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粹断,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年符欠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓶埋。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡希柿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出养筒,到底是詐尸還是另有隱情曾撤,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布晕粪,位于F島的核電站挤悉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏巫湘。R本人自食惡果不足惜装悲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尚氛。 院中可真熱鬧诀诊,春花似錦、人聲如沸阅嘶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至奠涌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間磷杏,已是汗流浹背溜畅。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留极祸,地道東北人慈格。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像遥金,于是被迫代替她去往敵國(guó)和親浴捆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容