最近,手頭有不少項(xiàng)目,其中就有一個(gè)類似公文流轉(zhuǎn)的項(xiàng)目,其中有一個(gè)模塊是任務(wù)的制定,而在任務(wù)的類型中有一個(gè)循環(huán)任務(wù),就是用戶輸入任務(wù)的開始日期、結(jié)束日期,還需要輸入一個(gè)周期數(shù),比如每周的星期幾、每月的多少號等,這里面就牽扯到JAVA日期時(shí)間的計(jì)算,從網(wǎng)上收集了一下,整理下來,供大家參考^_^
Java 語言的Calendar,GregorianCalendar (日歷),Date(日期), 和DateFormat(日期格式)組成了Java標(biāo)準(zhǔn)的一個(gè)基本但是非常重要的部分. 日期是商業(yè)邏輯計(jì)算一個(gè)關(guān)鍵的部分. 所有的開發(fā)者都應(yīng)該能夠計(jì)算未來的日期, 定制日期的顯示格式, 并將文本數(shù)據(jù)解析成日期對象。學(xué)習(xí)日期, 日期格式, 日期的解析和日期的計(jì)算。
我們將討論下面的類:
1、 具體類(和抽象類相對)java.util.Date
2、 抽象類java.text.DateFormat 和它的一個(gè)具體子類,java.text.SimpleDateFormat
3、 抽象類java.util.Calendar 和它的一個(gè)具體子類,java.util.GregorianCalendar 具體類可以被實(shí)例化, 但是抽象類卻不能. 你首先必須實(shí)現(xiàn)抽象類的一個(gè)具體子類.
1. java.util.Date及其格式化
Date 類從Java 開發(fā)包(JDK) 1.0 就開始進(jìn)化, 當(dāng)時(shí)它只包含了幾個(gè)取得或者設(shè)置一個(gè)日期數(shù)據(jù)的各個(gè)部分的方法, 比如說月, 日, 和年. 這些方法現(xiàn)在遭到了批評并且已經(jīng)被轉(zhuǎn)移到了Calendar類里去了, 我們將在本文中進(jìn)一步討論它. 這種改進(jìn)旨在更好的處理日期數(shù)據(jù)的國際化格式. 就象在JDK 1.1中一樣, Date 類實(shí)際上只是一個(gè)包裹類, 它包含的是一個(gè)長整型數(shù)據(jù), 表示的是從GMT(格林尼治標(biāo)準(zhǔn)時(shí)間)1970年, 1 月 1日00:00:00這一刻之前或者是之后經(jīng)歷的毫秒數(shù).
1.1. 創(chuàng)建java.util.DateJava
統(tǒng)計(jì)從1970年1月1日起的毫秒的數(shù)量表示日期。也就是說,例如,1970年1月2日,是在1月1日后的86,400,000毫秒。同樣的,1969年12月31日是在1970年1月1日前86,400,000毫秒。Java的Date類使用long類型紀(jì)錄這些毫秒值.因?yàn)閘ong是有符號整數(shù),所以日期可以在1970年1月1日之前,也可以在這之后。Long類型表示的最大正值和最大負(fù)值可以輕松的表示290,000,000年的時(shí)間,這適合大多數(shù)人的時(shí)間要求。讓我們看一個(gè)使用系統(tǒng)的當(dāng)前日期和時(shí)間創(chuàng)建一個(gè)日期對象并返回一個(gè)長整數(shù)的簡單例子. 這個(gè)時(shí)間通常被稱為Java 虛擬機(jī)(JVM)主機(jī)環(huán)境的系統(tǒng)時(shí)間.
import java.util.Date;
public class DateExample1
{
public static void main(String] args)
{
// Get the system date/time Date date = new Date(); // 打印出具體的年,月,日,小時(shí),分鐘,秒鐘以及時(shí)區(qū)
System.out.println(date.getTime());
}
}
在星期六, 2001年9月29日, 下午大約是6:50的樣子, 上面的例子在系統(tǒng)輸出設(shè)備上顯示的結(jié)果是 1001803809710. 在這個(gè)例子中,值得注意的是我們使用了Date 構(gòu)造函數(shù)創(chuàng)建一個(gè)日期對象, 這個(gè)構(gòu)造函數(shù)沒有接受任何參數(shù). 而這個(gè)構(gòu)造函數(shù)在內(nèi)部使用了System.currentTimeMillis() 方法來從系統(tǒng)獲取日期.
//1年前日期
java.util.Date myDate=new java.util.Date();
long myTime=(myDate.getTime()/1000)-60*60*24*365;
myDate.setTime(myTime*1000);
String mDate=formatter.format(myDate);//明天日期
myDate=new java.util.Date();
myTime=(myDate.getTime()/1000)+60*60*24;
myDate.setTime(myTime*1000);
mDate=formatter.format(myDate);
//兩個(gè)時(shí)間之間的天數(shù)
SimpleDateFormat myFormatter = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date= myFormatter.parse("2003-05-1");
java.util.Date mydate= myFormatter.parse("1899-12-30");
long day=(date.getTime()-mydate.getTime())/(24*60*60*1000);
//加半小時(shí)SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
java.util.Date date1 = format.parse("2002-02-28 23:16:00");
long Time=(date1.getTime()/1000)+60*30;
date1.setTime(Time*1000);String mydate1=formatter.format(date1);
//年月周求日期
SimpleDateFormat formatter2 = new SimpleDateFormat("yyyy-MM F E");
java.util.Date date2= formatter2.parse("2003-05 5 星期五");
SimpleDateFormat formatter3 = new SimpleDateFormat("yyyy-MM-dd");
String mydate2=formatter3.format(date2);
//求是星期幾
mydate= myFormatter.parse("2001-1-1");
SimpleDateFormat formatter4 = new SimpleDateFormat("E");
String mydate3=formatter4.format(mydate);
1.2. Date格式化
能以一種用戶明白的格式來顯示這個(gè)日期呢? 在這里類java.text.SimpleDateFormat 和它的抽象基類 java.text.DateFormat。那么, 現(xiàn)在我們已經(jīng)知道了如何獲取從1970年1月1日開始經(jīng)歷的毫秒數(shù)了. 我們?nèi)绾尾舊ormat 就派得上用場了.
// 我們能不能用下面的代碼構(gòu)件出 2001/8/8 8:8
import java.io.*;
import java.util.*;
public class WhatIsDate
{
public static void main(String[] args)
{
Date date = new Date(2001, 8, 8, 8, 8, 8);
System.out.println(date);
}
}
Java 的編譯器竟然報(bào)如下信息 (Sun JDK1.3, Windows 2000 中文下)
注意:
WhatIsDate.java 使用或覆蓋一個(gè)不鼓勵使用的API。
注意:
使用-deprecation重新編譯,以得到詳細(xì)信息。!那么 Date 對象究竟是為了滿足哪個(gè)需求呢?看來它不是用來實(shí)現(xiàn)基于年/月/日小時(shí):分鐘 的時(shí)間表述。我們查看 Java 的文檔,我們看到有 getTime() 方法,它返回的竟然是一個(gè) long 值。文檔進(jìn)一步又告訴我們這個(gè)值代表了當(dāng)前系統(tǒng)的時(shí)間離1970/1/1 0:0 的毫秒差,而且是在 GMT 時(shí)區(qū)下(也被稱為 EPOC)。如果我們指定的時(shí)間是在此之前的,那它將返回一個(gè)負(fù)數(shù)值。這個(gè)發(fā)現(xiàn)讓我們對 Date 對象有了一個(gè)全新的認(rèn)識-Date 存放的是與 EPOC 的偏差值。換而言之我們也可通過 long 類型來表示時(shí)間?
對了,這個(gè)猜想是得到了 Java 的支持:
// 第二種獲得當(dāng)前時(shí)間的方法
long dateInMilliSeconds = System.currentTimeMillis();// 這時(shí)候打印出的只是一串?dāng)?shù)字而已
System.out.println(dateInMilliSeconds);
對程序執(zhí)行效率敏感的程序員可以發(fā)現(xiàn)這個(gè)方法只是生成一個(gè) Java 的原始類型 (primitive type) long, 不需要實(shí)例化一個(gè)對象。因此如果我們對時(shí)間的處理只是在內(nèi)部進(jìn)行時(shí),可以用 long 來代替 Date 對象。最典型的應(yīng)用就是在一段代碼開始和結(jié)束時(shí),分別獲得系統(tǒng)當(dāng)前的時(shí)間,然后計(jì)算出代碼執(zhí)行所需的時(shí)間(微秒級)。
long start = System.currentTimeMillis();
// 代碼段
System.out.println("需要 "+(System.currentTimeMillis()-start)+" 微秒");
那么當(dāng)我們要把這個(gè) long 值已更為友好的表現(xiàn)形式顯示處理的時(shí)候,我們可以用它來構(gòu)造 Date 對象:Date date = new Date(dateInMilliSeconds);System.out.println(date);我們看到了在 Java 中對時(shí)間最為基本的表示,有通過對EPOC 的偏差值進(jìn)行處理。Date 對象是對它的一個(gè)對象的封裝。我們同時(shí)也看到了,在現(xiàn)時(shí)世界中我們對時(shí)間的描述通常是通過"某年某月某日某時(shí)某分"來定義的。Date 的顯示(實(shí)際上是 toString() 方法)描述了這些信息,但 Java 并不建議我們用這種方式直接來構(gòu)件 Date 對象。因此我們需要找出哪個(gè)對象可以實(shí)現(xiàn)這個(gè)需求。這就是我們下面就要講述的 Calendar 對象的功能。在我們進(jìn)一步研究 Calendar 之前,請記住 Date 只是一個(gè)對 long 值(基于 GMT 時(shí)區(qū))的對象封裝。它所表現(xiàn)出來的年/月/日小時(shí):分鐘時(shí)區(qū)的時(shí)間表述,只是它的 toString() 方法所提供的。千萬不要為這個(gè)假象所迷惑。假如我們希望定制日期數(shù)據(jù)的格式, 比方星期六-9月-29日-2001年. 下面的例子展示了如何完成這個(gè)工作:
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateExample2
{
public static void main(String[] args)
{
SimpleDateFormat bartDateFormat = new SimpleDateFormat("EEEE-MMMM-dd-yyyy");
Date date = new Date();
System.out.println(bartDateFormat.format(date));
}
}
只要通過向SimpleDateFormat 的構(gòu)造函數(shù)傳遞格式字符串"EEE-MMMM-dd-yyyy", 我們就能夠指明自己想要的格式. 你應(yīng)該可以看見, 格式字符串中的ASCII 字符告訴格式化函數(shù)下面顯示日期數(shù)據(jù)的哪一個(gè)部分. EEEE是星期, MMMM是月, dd是日, yyyy是年. 字符的個(gè)數(shù)決定了日期是如何格式化的.傳遞"EE-MM-dd-yy"會顯示 Sat-09-29-01. 請察看Sun 公司的Web 站點(diǎn)獲取日期格式化選項(xiàng)的完整的指示. 1.3. 文本數(shù)據(jù)解析成日期對象 假設(shè)我們有一個(gè)文本字符串包含了一個(gè)格式化了的日期對象, 而我們希望解析這個(gè)字符串并從文本日期數(shù)據(jù)創(chuàng)建一個(gè)日期對象. 我們將再次以格式化字符串"MM-dd-yyyy" 調(diào)用SimpleDateFormat類, 但是這一次, 我們使用格式化解析而不是生成一個(gè)文本日期數(shù)據(jù). 我們的例子, 顯示在下面, 將解析文本字符串"9-29-2001"并創(chuàng)建一個(gè)值為001736000000 的日期對象. 通過parse()方法,DateFormat能夠以一個(gè)字符串創(chuàng)立一個(gè)Date對象。這個(gè)方法能拋出ParseException異常,所以你必須使用適當(dāng)?shù)漠惓L幚砑夹g(shù)。例子程序:
import java.text.SimpleDateFormat; import java.util.Date; public class DateExample3
{
public static void main(String[] args)
{
// Create a date formatter that can parse dates of
// the form MM-dd-yyyy. SimpleDateFormat bartDateFormat = new SimpleDateFormat("MM-dd-yyyy");
// Create a string containing a text date to be parsed. String dateStringToParse = "9-29-2001";
try
{
// Parse the text version of the date.
// We have to perform the parse method in a
// try-catch construct in case dateStringToParse
// does not contain a date in the format we are expecting. Date date = bartDateFormat.parse(dateStringToParse);
// Now send the parsed date as a long value
// to the system output. System.out.println(date.getTime()); }catch (Exception ex)
{
System.out.println(ex.getMessage());
}
}
}
1.4. 使用標(biāo)準(zhǔn)的日期格式化過程
既然我們已經(jīng)可以生成和解析定制的日期格式了, 讓我們來看一看如何使用內(nèi)建的格式化過程. 方法 DateFormat.getDateTimeInstance() 讓我們得以用幾種不同的方法獲得標(biāo)準(zhǔn)的日期格式化過程. 在下面的例子中, 我們獲取了四個(gè)內(nèi)建的日期格式化過程. 它們包括一個(gè)短的, 中等的, 長的, 和完整的日期格式.
import java.text.DateFormat;
import java.util.Date;
public class DateExample4
{
public static void main(String[] args)
{ Date date = new Date();
DateFormat shortDateFormat = DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT);
DateFormat mediumDateFormat = DateFormat.getDateTimeInstance( DateFormat.MEDIUM, DateFormat.MEDIUM);
DateFormat longDateFormat = DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG);
DateFormat fullDateFormat = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.FULL);
System.out.println(shortDateFormat.format(date)); System.out.println(mediumDateFormat.format(date));
System.out.println(longDateFormat.format(date)); System.out.println(fullDateFormat.format(date));
}
}
注意我們在對 getDateTimeInstance的每次調(diào)用中都傳遞了兩個(gè)值. 第一個(gè)參數(shù)是日期風(fēng)格, 而第二個(gè)參數(shù)是時(shí)間風(fēng)格. 它們都是基本數(shù)據(jù)類型int(整型). 考慮到可讀性, 我們使用了DateFormat 類提供的常量: SHORT, MEDIUM, LONG, 和 FULL. 要知道獲取時(shí)間和日期格式化過程的更多的方法和選項(xiàng), 請看Sun 公司W(wǎng)eb 站點(diǎn)上的解釋. 運(yùn)行我們的例子程序的時(shí)候, 它將向標(biāo)準(zhǔn)輸出設(shè)備輸出下面的內(nèi)容: 9/29/01 8:44 PM Sep 29, 2001 8:44:45 PM September 29, 2001 8:44:45 PM EDT Saturday, September 29, 2001 8:44:45 PM EDT 2. Calendar 日歷類 首先請記住 Calendar 只是一個(gè)抽象類, 也就是說你無法直接獲得它的一個(gè)實(shí)例,換而言之你可以提供一個(gè)自己開發(fā)的 Calendar 對象。那究竟什么是一個(gè) Calendar 呢?中文的翻譯就是日歷,那我們立刻可以想到我們生活中有陽(公)歷、陰(農(nóng))歷之分。它們的區(qū)別在哪呢?比如有:月份的定義 - 陽`(公)歷 一年12 個(gè)月,每個(gè)月的天數(shù)各不同;陰(農(nóng))歷,每個(gè)月固定28天,每周的第一天 - 陽(公)歷星期日是第一天;陰(農(nóng))歷,星期一是第一天實(shí)際上,在歷史上有著許多種紀(jì)元的方法。它們的差異實(shí)在太大了,比如說一個(gè)人的生日是"八月八日" 那么一種可能是陽(公)歷的八月八日,但也可以是陰(農(nóng))歷的日期。所以為了計(jì)時(shí)的統(tǒng)一,必需指定一個(gè)日歷的選擇。那現(xiàn)在最為普及和通用的日歷就是 "Gregorian Calendar"。也就是我們在講述年份時(shí)常用 "公元幾幾年"。Calendar 抽象類定義了足夠的方法,讓我們能夠表述日歷的規(guī)則。Java 本身提供了對 "Gregorian Calendar" 規(guī)則的實(shí)現(xiàn)。我們從 Calendar.getInstance() 中所獲得的實(shí)例就是一個(gè) "GreogrianCalendar" 對象(與您通過 new GregorianCalendar() 獲得的結(jié)果一致)。下面的代碼可以證明這一點(diǎn):
import java.io.*;
import java.util.*;
public class WhatIsCalendar
{
public static void main(String[] args)
{
Calendar calendar = Calendar.getInstance();
if (calendar instanceof GregorianCalendar)
System.out.println("It is an instance of GregorianCalendar");
}
}
Calendar 在 Java 中是一個(gè)抽象類(Abstract Class),GregorianCalendar 是它的一個(gè)具體實(shí)現(xiàn)。Calendar 與 Date 的轉(zhuǎn)換非常簡單: Calendar calendar = Calendar.getInstance(); // 從一個(gè) Calendar 對象中獲取 Date 對象 Date date = calendar.getTime(); // 將 Date 對象反應(yīng)到一個(gè) Calendar 對象中, // Calendar/GregorianCalendar 沒有構(gòu)造函數(shù)可以接受 Date 對象 // 所以我們必需先獲得一個(gè)實(shí)例,然后設(shè)置 Date 對象 calendar.setTime(date);Calendar 對象在使用時(shí),有一些值得注意的事項(xiàng):
1. Calendar 的 set() 方法
set(int field, int value) - 是用來設(shè)置"年/月/日/小時(shí)/分鐘/秒/微秒"等值field 的定義在 Calendar 中set(int year, int month, int day, int hour, int minute, int second) 但沒有set(int year, int month, int day, int hour, int minute, int second, int millisecond) 前面 set(int,int,int,int,int,int) 方法不會自動將 MilliSecond 清為 0。另外,月份的起始值為0而不是1,所以要設(shè)置八月時(shí),我們用7而不是8。calendar.set(Calendar.MONTH, 7);我們通常需要在程序邏輯中將它清為 0,否則可能會出現(xiàn)下面的情況:
import java.io.*;
import java.util.*;
public class WhatIsCalendarWrite
{
public static void main(String[] args) throws Exception{
ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("calendar.out"));
Calendar cal1 = Calendar.getInstance();