?????除了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í)!