計算
Java
日期--學習怎樣創建和使用日期
概要
????
不管你是處理財務交易還是計劃著下一步的行動,你都要知道怎樣在
Java
中建立,使用和顯示日期。這需要你簡單的查閱一下相應類的
API
參考:一個日期可以創建
3
個相關類的對象。這篇文章告訴你你想要知道的內容。(
3,000
字)
????Java
統計從
1970
年
1
月
1
日起的毫秒的數量表示日期。也就是說,例如,
1970
年
1
月
2
日,是在
1
月
1
日后的
86
,
400
,
000
毫秒。同樣的,
1969
年
12
月
31
日是在
1970
年
1
月
1
日前
86
,
400
,
000
毫秒。
Java
的
Date
類使用
long
類型紀錄這些毫秒值
.
因為
long
是有符號整數,所以日期可以在
1970
年
1
月
1
日之前,也可以在這之后。
Long
類型表示的最大正值和最大負值可以輕松的表示
290
,
000
,
000
年的時間,這適合大多數人的時間要求。
Date?
類
???Date
類可以在
java.util
包中找到,用一個
long
類型的值表示一個指定的時刻。它的一個有用的構造函數是
Date(),
它創建一個表示創建時刻的對象。
getTime()
方法返回
Date
對象的
long
值。在下面的程序中,我使用
Date()
構造函數創建一個表示程序運行時刻的對象,并且利用
getTime()
方法找到這個日期代表的毫秒數量:
1.???
?
2.???
import
?java.util.*;
3.???
?
4.???
?
5.???
public
?class?Now?{
6.???
???public?static?void?main(String[]?args)?{
7.???
??????Date?now?=?new?Date();
8.???
??????long?nowLong?=?now.getTime();
9.???
??????System.out.println("Value?is?"?+?nowLong);
10.??
???}
11.??
}
當我運行這個程序后,我得到
972,568,255,150.
快速確認一下這個數字,起碼在一個合理的范圍:它不到
31
年,這個數值相對
1970
年
1
月
1
日到我寫這篇文章的時間來說,是合理的。計算機是這個毫秒值表示時間,人們可不愿意說
"?
我將在
996,321,998,34
見到你。
"
幸運的是,
Java
提供了一個轉換
Date
對象到字符串的途徑,表示成傳統的形式。我們在下一節討論
DateFormat
類,它直觀的建立日期字符串。
?
DateFormat
類
?DateFormat
類的一個目標是建立一個人們能夠識別的字符串。然而,因為語言的差別,不是所有的人希望看到嚴格的相同格式的日期。法國人更喜歡看到
"25?decembre?2000,",
但是美國人習慣看到
"December?25,2000."
所以一個
DateFormat
的實例創建以后,這個對象包含了日期的顯示格式的信息。如果使用用戶電腦區域設置缺省的格式,你可以象下面那樣,創建
DateFormat
對象,使用
getDateInstance()
方法:
1.???
?
2.???
DateFormat
?df?=?DateFormat.getDateInstance();???
DateFormat
類在
java.text
包中可以找到。
轉換成字符串
你可以使用
format()
方法轉換
Date
對象為一個字符串。下面的示例程序說明了這個問題:
1.???
?
2.???
import
?java.util.*;
3.???
import
?java.text.*;
4.???
?
5.???
public
?class?NowString?{
6.???
???public?static?void?main(String[]?args)?{
7.???
??????Date?now?=?new?Date();
8.???
??????DateFormat?df?=?DateFormat.getDateInstance();
9.???
??????String?s?=?df.format(now);
10.??
??????System.out.println("Today?is?"?+?s);
11.??
???}
12.??
}??
在上面的代碼中,展示了沒有參數,使用缺省格式的
getDateInstance()
方法。
Java
還提供了幾個選擇日期格式,你可以通過使用重載的
getDateInstance(int?style)
獲得。出于方便的原因,
DateFormat
提供了幾種預置的常量,你可以使用這些常量參數。下面是幾個
SHORT,?MEDIUM,?LONG,?
和
FULL
類型的示例:
1.???
?
2.???
import
?java.util.*;
3.???
import
?java.text.*;
4.???
?
5.???
public
?class?StyleDemo?{
6.???
???public?static?void?main(String[]?args)?{
7.???
??????Date?now?=?new?Date();
8.???
?
9.???
??????DateFormat?df?=??DateFormat.getDateInstance();
10.??
??????DateFormat?df1?=?DateFormat.getDateInstance(DateFormat.SHORT);
11.??
??????DateFormat?df2?=?DateFormat.getDateInstance(DateFormat.MEDIUM);
12.??
??????DateFormat?df3?=?DateFormat.getDateInstance(DateFormat.LONG);
13.??
??????DateFormat?df4?=?DateFormat.getDateInstance(DateFormat.FULL);?
14.??
??????String?s?=??df.format(now);
15.??
??????String?s1?=?df1.format(now);
16.??
??????String?s2?=?df2.format(now);
17.??
??????String?s3?=?df3.format(now);
18.??
??????String?s4?=?df4.format(now);
19.??
?
20.??
??????System.out.println("(Default)?Today?is?"?+?s);
21.??
??????System.out.println("(SHORT)???Today?is?"?+?s1);
22.??
??????System.out.println("(MEDIUM)??Today?is?"?+?s2);
23.??
??????System.out.println("(LONG)????Today?is?"?+?s3);
24.??
??????System.out.println("(FULL)????Today?is?"?+?s4);
25.??
???}
26.??
}
程序輸出如下:
(Default)?Today?is?Nov?8,?2000
(SHORT)???Today?is?11/8/00
(MEDIUM)??Today?is?Nov?8,?2000
(LONG)????Today?is?November?8,?2000
(FULL)????Today?is?Wednesday,?November?8,?2000
同樣的程序,在我的電腦上使用缺省設置運行后,改變區域設置為瑞典,輸出如下:
(Default)?Today?is?2000-nov-08
(SHORT)???Today?is?2000-11-08
(MEDIUM)??Today?is?2000-nov-08
(LONG)????Today?is?den?8?november?2000
(FULL)????Today?is?den?8?november?2000?????
??
從這里,你能看到,瑞典的月份不是大寫的(雖然
November
還是
november
)
.
還有,
LONG
和
FULL
版本在瑞典語中是一樣的,但是美國英語卻不同。另外,有趣的是,瑞典語單詞的星期三
,onsdag
,沒有包含在
FULL
日期里,英語卻包括。
?
注意你能夠使用
getDateInstance()
方法改變
DateFormat
實例的語種;但是,在上面的例子中,是通過改變
Windows98
的控制面板的區域設置做到的。不同的地方的區域設置不同,結果就不同,這樣有好處,也有不足,
Java
程序員應該了解這些。一個好處是
Java
程序員可以只寫一行代碼就可以顯示日期,而且世界不同地區的電腦運行同樣的程序會有不用的日期格式。
?
但是這也是一個缺點,當程序員希望顯示同一種格式的時
--
這也有可取之處,舉例來說,在程序中混合輸出文本和日期,如果文本是英文,我們就不希望日期格式是其他的格式,象德文或是西班牙文。如果程序員依靠日期格式編程,日期格式將根據運行程序所在電腦的區域設置不用而不同。
?
解析字符串
?
通過
parse()
方法,
DateFormat
能夠以一個字符串創立一個
Date
對象。這個方法能拋出
ParseException
異常,所以你必須使用適當的異常處理技術。下面的例子程序通過字符串創建
Date
對象:
1.???
?
2.???
import
?java.util.*;
3.???
import
?java.text.*;
4.???
?
5.???
public
?class?ParseExample?{
6.???
???public?static?void?main(String[]?args)?{
7.???
??????String?ds?=?"November?1,?2000";
8.???
??????DateFormat?df?=?DateFormat.getDateInstance();
9.???
??????try?{
10.??
?????????Date?d?=?df.parse(ds);
11.??
??????}
12.??
??????catch(ParseException?e)?{
13.??
?????????System.out.println("Unable?to?parse?"?+?ds);
14.??
??????}
15.??
???}
16.??
}
在創建一個任意的日期時
parse()
方法很有用。我將通過另一種方法創建一個任意得日期。同時,你將看到怎樣進行基本日期計算,例如計算
90
天后的另一天。你可以使用
GregorianCalendar
類來完成這個任務。
?
GregorianCalendar
類
?
創建一個代表任意日期的一個途徑使用
GregorianCalendar
類的構造函數,它包含在
java.util
包中:
1.???
?
2.???
GregorianCalendar
(int?year,?int?month,?int?date)?
注意月份的表示,一月是
0
,二月是
1
,以此類推,是
12
月是
11
。因為大多數人習慣于使用單詞而不是使用數字來表示月份,這樣程序也許更易讀,父類
Calendar
使用常量來表示月份:
JANUARY,?FEBRUARY,
等等。所以,創建
Wilbur?
和
?Orville
制造第一架動力飛機的日期(
December?17,?1903
),你可以使用:
1.???
?
2.???
GregorianCalendar
?firstFlight?=?new?GregorianCalendar(1903,?Calendar.DECEMBER,?17);?
出于清楚的考慮,你應該使用前面的形式。但是,你也應該學習怎樣閱讀下面的短格式。下面的例子同樣表示
December?17,1903
(記住,在短格式中,
11
表示
December
)
1.???
?
2.???
GregorianCalendar
?firstFlight?=?new?GregorianCalendar(1903,?11,?17);???
在上一節中,你學習了轉換
Date
對象到字符串。這里,你可以做同樣的事情;但是首先,你需要將
GregorianCalendar
對象轉換到
Date
。要做到這一點,你可以使用
getTime()
方法,從它得父類
Calendar
繼承而來。
GetTime()
方法返回
GregorianCalendar
相應的
Date
對象。你能夠創建
GregorianCalendar
對象,轉換到
Date
對象,得到和輸出相應的字符串這樣一個過程。下面是例子:
?
1.???
?
2.???
import
?java.util.*;
3.???
import
?java.text.*;
4.???
?
5.???
public
?class?Flight?{
6.???
?
7.???
???public?static?void?main(String[]?args)?{
8.???
??????GregorianCalendar?firstFlight?=?new?GregorianCalendar(1903,?Calendar.DECEMBER,?17);????
9.???
??????Date?d?=?firstFlight.getTime();
10.??
??????DateFormat?df?=?DateFormat.getDateInstance();
11.??
??????String?s?=?df.format(d);
12.??
??????System.out.println("First?flight?was?"?+?s);
13.??
???}
14.??
}
有時候創建一個代表當前時刻的
GregorianCalendar
類的實例是很有用的。你可以簡單的使用沒有參數的
GregorianCalendar
構造函數,象這樣:
1.???
?
2.???
GregorianCalendar
?thisday?=?new?GregorianCalendar();
一個輸出今天日期的例子程序,使用
GregorianCalendar
對象:
1.???
?
2.???
import
?java.util.*;
3.???
import
?java.text.*;
4.???
?
5.???
class
?Today?{
6.???
???public?static?void?main(String[]?args)?{
7.???
??????GregorianCalendar?thisday?=?new?GregorianCalendar();?
8.???
??????Date?d?=?thisday.getTime();
9.???
??????DateFormat?df?=?DateFormat.getDateInstance();
10.??
??????String?s?=?df.format(d);
11.??
??????System.out.println("Today?is?"?+?s);
12.??
???}
13.??
}
注意到,
Date()
構造函數和
GregorianCalendar()
構造函數很類似:都創建一個對象,條件簡單,代表今天。
日期處理
GregorianCalendar
類提供處理日期的方法。一個有用的方法是
add().
使用
add()
方法,你能夠增加象年,月數,天數到日期對象中。要使用
add()
方法,你必須提供要增加的字段,要增加的數量。一些有用的字段是
DATE,?MONTH,?YEAR,?
和
?WEEK_OF_YEAR
。下面的程序使用
add()
方法計算未來
80
天的一個日期。在
Jules
的
<
環球
80
天
>
是一個重要的數字,使用這個程序可以計算
Phileas?Fogg
從出發的那一天
1872
年
10
月
2
日后
80
天的日期:
1.???
?
2.???
import
?java.util.*;
3.???
import
?java.text.*;
4.???
?
5.???
public
?class?World?{
6.???
???public?static?void?main(String[]?args)?{
7.???
??????GregorianCalendar?worldTour?=?new?GregorianCalendar(1872,?Calendar.OCTOBER,?2);
8.???
??????worldTour.add(GregorianCalendar.DATE,?80);
9.???
??????Date?d?=?worldTour.getTime();
10.??
??????DateFormat?df?=?DateFormat.getDateInstance();
11.??
??????String?s?=?df.format(d);
12.??
??????System.out.println("80?day?trip?will?end?"?+?s);
13.??
???}
14.??
}
這個例子是想象的,但在一個日期上增加天數是一個普遍的操作:影碟可以租
3
天,圖書館可以借書
21
天,商店經常需要將購買的物品在
30
天內賣出。下面的程序演示了使用年計算:
1.???
?
2.???
import
?java.util.*;
3.???
import
?java.text.*;
4.???
?
5.???
public
?class?Mortgage?{
6.???
???public?static?void?main(String[]?args)?{
7.???
??????GregorianCalendar?mortgage?=?new?GregorianCalendar(1997,?Calendar.MAY,?18);
8.???
??????mortgage.add(Calendar.YEAR,?15);
9.???
??????Date?d?=?mortgage.getTime();
10.??
??????DateFormat?df?=?DateFormat.getDateInstance();
11.??
??????String?s?=?df.format(d);
12.??
??????System.out.println("15?year?mortgage?amortized?on?"?+?s);????}
13.??
}
????add()
一個重要的副作用是它改變的原來的日期。有時候,擁有原始日期和修改后的日期很重要。不幸的是,你不能簡單的創建一個
GregorianCalendar
對象,設置它和原來的相等(
equal
)。原因是兩個變量指向同一個
Date()
對象地址。如果
Date
對象改變,兩個變量就指向改變后的日期對象。代替這種做法,應該創建一個新對象。下面的程序示范了這種做法:
1.???
?
2.???
import
?java.util.*;
3.???
import
?java.text.*;
4.???
?
5.???
public
?class?ThreeDates?{
6.???
???public?static?void?main(String[]?args)?{
7.???
??????GregorianCalendar?gc1?=?new?GregorianCalendar(2000,?Calendar.JANUARY,?1);
8.???
??????GregorianCalendar?gc2?=?gc1;
9.???
??????GregorianCalendar?gc3?=?new?GregorianCalendar(2000,?Calendar.JANUARY,?1);
10.??
??????//Three?dates?all?equal?to?January?1,?2000
11.??
?
12.??
??????gc1.add(Calendar.YEAR,?1);
13.??
??????file://gc1?and?gc2?are?changed
14.??
?
15.??
??????DateFormat?df?=?DateFormat.getDateInstance();
16.??
?
17.??
??????Date?d1?=?gc1.getTime();
18.??
??????Date?d2?=?gc2.getTime();
19.??
??????Date?d3?=?gc3.getTime();
20.??
?
21.??
??????String?s1?=?df.format(d1);
22.??
??????String?s2?=?df.format(d2);
23.??
??????String?s3?=?df.format(d3);
24.??
?
25.??
??????System.out.println("gc1?is?"?+?s1);
26.??
??????System.out.println("gc2?is?"?+?s2);
27.??
??????System.out.println("gc3?is?"?+?s3);
28.??
???}
29.??
}
????
程序運行后,
gc1
和
gc2
被變成
2001
年(因為兩個對象指向同一個
Date
,而
Date
已經被改變了)。對象
gc3
指向一個單獨的
Date
,它沒有被改變。
計算復習日期
在這節,你將看到一個依據現實世界的例子。這個詳細的程序計算過去一個具體的日期。例如,你閱讀這篇文章,你想要記住一個印象深刻的知識點。如果你沒有照片一樣的記憶力,你就要定期的復習這些新資料,這將幫助你記住它。關于復習系統,
Kurt?Hanks?
和
?Gerreld?L.?Pulsipher
在他們的
<?Five?Secrets?to?Personal?Productivity
個人能力的
5
個秘密
>
中有討論,建議看過第一眼后馬上回顧一下,然后是
1
天后,
1
個星期后,
1
個月后,
3
個月后,
1
年后。我的這篇文章,你要馬上回顧一下,從現在算起,再就是明天,然后是
1
個星期,
1
個月,
3
個月,
1
年后。我們的程序將計算這些日期。
這個程序非常有用的,它將是
PIM(Personal?Information?Manager
個人信息管理器
)
的一個組成部分,并將確定復習時間。在下面的程序中,
getDates()
方法對一個返回日期數組(復習日期)的電子軟件很有用。另外,你可以返回單獨的一個日期,使用
getFirstDay(),getOneDay(),getOneWeek(),getOnMonth()
和
getOneYear().
當時間范圍超出這個
PIM
的
ReviewDates
的計算范圍時
ReviewDates
類演示了怎樣計算時間段。現在,你可以容易的修改它用來處理你需要的時間段,象圖書館借書,錄影帶租賃和抵押計算。首先,
ReviewDates
類顯示在下面:
1.???
?
2.???
import
?java.util.*;
3.???
import
?java.text.*;
4.???
?
5.???
public
?class?ReviewDates?{
6.???
???private?GregorianCalendar?firstDay,?oneDay,?oneWeek,?oneMonth,?oneQuarter,?oneYear;
7.???
???final?int?dateArraySize?=?6;
8.???
?
9.???
???ReviewDates(GregorianCalendar?gcDate)?{
10.??
??????int?year?=?gcDate.get(GregorianCalendar.YEAR);
11.??
??????int?month?=?gcDate.get(GregorianCalendar.MONTH);
12.??
??????int?date?=?gcDate.get(GregorianCalendar.DATE);
13.??
?
14.??
??????firstDay?=?new?GregorianCalendar(year,?month,?date);
15.??
??????oneDay?=?new?GregorianCalendar(year,?month,?date);
16.??
??????oneWeek?=?new?GregorianCalendar(year,?month,?date);
17.??
??????oneMonth?=?new?GregorianCalendar(year,?month,?date);
18.??
??????oneQuarter?=?new?GregorianCalendar(year,?month,?date);
19.??
??????oneYear?=?new?GregorianCalendar(year,?month,?date);
20.??
?
21.??
??????oneDay.add(GregorianCalendar.DATE,?1);
22.??
??????oneWeek.add(GregorianCalendar.DATE,?7);
23.??
??????oneMonth.add(GregorianCalendar.MONTH,?1);
24.??
??????oneQuarter.add(GregorianCalendar.MONTH,?3);
25.??
??????oneYear.add(GregorianCalendar.YEAR,?1);
26.??
???}
27.??
?
28.??
???ReviewDates()?{
29.??
??????this(new?GregorianCalendar());
30.??
???}
31.??
?
32.??
???public?void?listDates()?{
33.??
??????DateFormat?df?=?DateFormat.getDateInstance(DateFormat.LONG);?
34.??
??????Date?startDate?=?firstDay.getTime();
35.??
??????Date?date1?=?oneDay.getTime();
36.??
??????Date?date2?=?oneWeek.getTime();
37.??
??????Date?date3?=?oneMonth.getTime();
38.??
??????Date?date4?=?oneQuarter.getTime();
39.??
??????Date?date5?=?oneYear.getTime();
40.??
?
41.??
??????String?ss?=??df.format(startDate);
42.??
??????String?ss1?=?df.format(date1);
43.??
??????String?ss2?=?df.format(date2);
44.??
??????String?ss3?=?df.format(date3);
45.??
??????String?ss4?=?df.format(date4);
46.??
??????String?ss5?=?df.format(date5);
47.??
?
48.??
??????System.out.println("Start?date?is?"?+?ss);
49.??
??????System.out.println("Following?review?dates?are:");
50.??
??????System.out.println(ss1);
51.??
??????System.out.println(ss2);
52.??
??????System.out.println(ss3);
53.??
??????System.out.println(ss4);
54.??
??????System.out.println(ss5);
55.??
??????System.out.println();
56.??
???}
57.??
?
58.??
???public?GregorianCalendar[]?getDates()?{
59.??
??????GregorianCalendar[]?memoryDates?=?new?GregorianCalendar[dateArraySize];
60.??
??????memoryDates[0]?=?firstDay;
61.??
??????memoryDates[1]?=?oneDay;
62.??
??????memoryDates[2]?=?oneWeek;
63.??
??????memoryDates[3]?=?oneMonth;
64.??
??????memoryDates[4]?=?oneQuarter;
65.??
??????memoryDates[5]?=?oneYear;
66.??
??????return?memoryDates;
67.??
???}
68.??
?
69.??
???public?GregorianCalendar?getFirstDay()?{
70.??
??????return?this.firstDay;
71.??
???}
72.??
?
73.??
???public?GregorianCalendar?getOneDay()?{
74.??
??????return?this.oneDay;
75.??
???}
76.??
?
77.??
???public?GregorianCalendar?getOneWeek()?{
78.??
??????return?this.oneWeek;
79.??
???}
80.??
?
81.??
???public?GregorianCalendar?getOneMonth()?{
82.??
??????return?this.oneMonth;
83.??
???}
84.??
?
85.??
???public?GregorianCalendar?getOneQuarter()?{
86.??
??????return?this.oneQuarter;
87.??
???}
88.??
?
89.??
???public?GregorianCalendar?getOneYear()?{
90.??
??????return?this.oneYear;
91.??
???}
92.??
}?
下面是使用
ReviewDates
類列出復習日期的例子程序:
1.???
?
2.???
import
?java.util.*;
3.???
?
4.???
public
?class?ShowDates?{
5.???
???public?static?void?main(String[]?args)?{
6.???
??????ReviewDates?rd?=?new?ReviewDates();
7.???
??????rd.listDates();
8.???
?
9.???
??????GregorianCalendar?gc?=?new?GregorianCalendar(2001,?Calendar.JANUARY,?15);
10.??
??????ReviewDates?jan15?=?new?ReviewDates(gc);
11.??
??????jan15.listDates();
12.??
???}
13.??
}
?
總結
?
這篇文章介紹了關于日期處理的
3
個重要的類:
Date,DateFormat,GregorianCalendar.
這些類讓你創建日期,轉換成字符串,和計算日期基本元素。處理
Java
中的日期問題,這篇文章只是冰山一角??墒牵以谶@里介紹的類和方法不僅僅是你學習高級技術的跳板,這些類和方法本身就可以處理很多通常的日期相關的任務
關于作者
?Robert?Nielsen
是
SCJP
。他擁有碩士學位,專攻計算機教育,并且在計算機領域執教多年。他也在各樣的雜志上發表過很多計算機相關的文章。
關于譯者
Cocia?Lin(cocia@163.com)
是程序員。他擁有學士學位,現在專攻
Java
相關技術,剛剛開始在計算機領域折騰。