JAVA Calendar方法詳解

 究竟什麼是一個 Calendar 呢?中文的翻譯就是日曆,那咱們馬上能夠想到咱們生活中有陽(公)歷、陰(農)歷之分。它們的區別在哪呢?java

    好比有:數據庫

     月份的定義 - 陽`(公)歷 一年12 個月,每月的天數各不一樣;陰(農)歷,每月固定28天編程

    每週的第一天 - 陽(公)歷星期日是第一天;陰(農)歷,星期一是第一天函數

    實際上,在歷史上有着許多種紀元的方法。它們的差別實在太大了,好比說一我的的生日是\"八月八日\" 那麼一種多是陽(公)歷的八月八日,但也能夠是陰(農)歷的日期。因此爲了計時的統一,必需指定一個日曆的選擇。那如今最爲普及和通用的日曆就是 \"Gregorian Calendar\"。也就是咱們在講述年份時經常使用 \"公元几几年\"。Calendar 抽象類定義了足夠的方法,讓咱們可以表述日曆的規則。Java 自己提供了對 \"Gregorian Calendar\" 規則的實現。咱們從 Calendar.getInstance() 中所得到的實例就是一個 \"GreogrianCalendar\" 對象(與您經過 new GregorianCalendar() 得到的結果一致)。性能

    下面的代碼能夠證實這一點:學習

    import java.io.*;     import java.util.*;翻譯

    public class WhatIsCalendar    orm

{對象

    public static void main(String[] args) {內存

    Calendar calendar = Calendar.getInstance();

    if (calendar instanceof GregorianCalendar)

    System.out.println(\"It is an instance of GregorianCalendar\"t;

    }  

   }

    Calendar 在 Java 中是一個抽象類(Abstract Class),GregorianCalendar 是它的一個具體實現。

    咱們也能夠本身的 Calendar 實現類,而後將它做爲 Calendar 對象返回(面向對象的特性)。在 IBM alphaWorks 上,IBM 的開發人員實現了多種日曆(http://www.alphaworks.ibm.com/tech/calendars)。一樣在 Internet 上,也有對中國農曆的實現。本文對如何擴展 Calendar 不做討論,你們能夠經過察看上述 Calendar 的源碼來學習。

    Calendar 與 Date 的轉換很是簡單:

    Calendar calendar = Calendar.getInstance();     // 從一個 Calendar 對象中獲取 Date 對象

    Date date = calendar.getTime();     // 將 Date 對象反應到一個 Calendar 對象中,

    // Calendar/GregorianCalendar 沒有構造函數能夠接受 Date 對象     // 因此咱們必需先得到一個實例,而後設置 Date 對象

    calendar.setTime(date);

    Calendar 對象在使用時,有一些值得注意的事項:

    1. Calendar 的 set() 方法

    set(int field, int value) - 是用來設置\"年/月/日/小時/分鐘/秒/微秒\"等值

    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,因此要設置八月時,咱們用7而不是8。

    calendar.set(Calendar.MONTH, 7);

    咱們一般須要在程序邏輯中將它清爲 0,不然可能會出現下面的狀況:

    import java.io.*;     import java.util.*;

    public class WhatIsCalendarWrite    

{

    public static void main(String[] args) throws Exception{[Page]    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(\"calendar.out\"t);

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 7, 1, 0, 0, 0);

    out.writeObject(cal1);

    Calendar cal2 = Calendar.getInstance();

    cal2.set(2000, 7, 1, 0, 0, 0);

    cal2.set(Calendar.MILLISECOND, 0);

    out.writeObject(cal2);     out.close();

    }

    }

    咱們將 Calendar 保存到文件中

    import java.io.*;

    import java.util.*;

    public class WhatIsCalendarRead

    {

    public static void main(String[] args) throws Exception{

    ObjectInputStream in =     new ObjectInputStream(     new FileInputStream(\"calendar.out\"t);

    Calendar cal2 = (Calendar)in.readObject();

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 7, 1, 0, 0, 0);  

   if (cal1.equals(cal2))     System.out.println(\"Equals\"t;

    else     System.out.println(\"NotEqual\"t;

    System.out.println(\"Old calendar \"+cal2.getTime().getTime());

    System.out.println(\"New calendar \"+cal1.getTime().getTime());

    cal1.set(Calendar.MILLISECOND, 0);

    cal2 = (Calendar)in.readObject();

    if (cal1.equals(cal2))     System.out.println(\"Equals\"t;

    else     System.out.println(\"NotEqual\"t;     System.out.println(\"Processed Old calendar \"+cal2.getTime().getTime());

    System.out.println(\"Processed New calendar \"+cal1.getTime().getTime());     }     }

    而後再另一個程序中取回來(模擬對數據庫的存儲),可是執行的結果是:

    NotEqual     Old calendar 965113200422 <------------ 最後三位的MilliSecond與當前時間有關     New calendar 965113200059 <-----------/     Equals     Processed Old calendar 965113200000     Processed New calendar 965113200000

    另外咱們要注意的一點是,Calendar 爲了性能緣由對 set() 方法採起延緩計算的方法。在 JavaDoc 中有下面的例子來講明這個問題:

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 7, 31, 0, 0 , 0); //2000-8-31

    cal1.set(Calendar.MONTH, Calendar.SEPTEMBER); //應該是 2000-9-31,也就是 2000-10-1

    cal1.set(Calendar.DAY_OF_MONTH, 30); //若是 Calendar 轉化到 2000-10-1,那麼如今的結果就該是 2000-10-30

    System.out.println(cal1.getTime()); //輸出的是2000-9-30,說明 Calendar 不是立刻就刷新其內部的記錄

 2. Calendar 對象的容錯性,Lenient 設置     咱們知道特定的月份有不一樣的日期,當一個用戶給出錯誤的日期時,Calendar 如何處理的呢?

 

    import java.io.*;

    import java.util.*;

    public class WhatIsCalendar

    {

    public static void main(String[] args) throws Exception{

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 1, 32, 0, 0, 0);

    System.out.println(cal1.getTime());

    cal1.setLenient(false);

[Page]     cal1.set(2000, 1, 32, 0, 0, 0);  

   System.out.println(cal1.getTime());

    }

    }

    它的執行結果是:

    Tue Feb 01 00:00:00 PST 2000

    Exception in thread \"main\" java.lang.IllegalArgumentException

    at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:1368)

    at java.util.Calendar.updateTime(Calendar.java:1508)

    at java.util.Calendar.getTimeInMillis(Calendar.java:890)

    at java.util.Calendar.getTime(Calendar.java:871)

    at WhatIsCalendar.main(WhatIsCalendar.java:12)  

   當咱們設置該 Calendar 爲 Lenient false 時,它會依據特定的月份檢查出錯誤的賦值。

    3. 不穩定的 Calendar

    咱們知道 Calendar 是能夠被 serialize 的,可是咱們要注意下面的問題

    import java.io.*;

    import java.util.*;

    public class UnstableCalendar implements Serializable     {

    public static void main(String[] args) throws Exception{  

   Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 7, 1, 0, 0 , 0);  

   cal1.set(Calendar.MILLISECOND, 0);

    ObjectOutputStream out =     new ObjectOutputStream(     new FileOutputStream(\"newCalendar.out\"t);

    out.writeObject(cal1);

    out.close();

    ObjectInputStream in =     new ObjectInputStream(     new FileInputStream(\"newCalendar.out\"t);

    Calendar cal2 = (Calendar)in.readObject();

    cal2.set(Calendar.MILLISECOND, 0);

    System.out.println(cal2.getTime());

    }

    }

    運行的結果居然是: Thu Jan 01 00:00:00 PST 1970

    它被複原到 EPOC 的起始點,咱們稱該 Calendar 是處於不穩定狀態。這個問題的根本緣由是 Java 在 serialize GregorianCalendar 時沒有保存全部的信息,因此當它被恢復到內存中,又缺乏足夠的信息時,Calendar 會被恢復到 EPOCH 的起始值。Calendar 對象由兩部分構成:字段和相對於 EPOC 的微秒時間差。字段信息是由微秒時間差計算出的,而 set() 方法不會強制 Calendar 從新計算字段。這樣字段值就不對了。

    下面的代碼能夠解決這個問題:

    import java.io.*;

    import java.util.*;

    public class StableCalendar implements Serializable     {

    public static void main(String[] args) throws Exception{

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 7, 1, 0, 0 , 0);

    cal1.set(Calendar.MILLISECOND, 0);

    ObjectOutputStream out =     new ObjectOutputStream(     new FileOutputStream(\"newCalendar.out\"t);

    out.writeObject(cal1);

    out.close();

    ObjectInputStream in =     new ObjectInputStream(     new FileInputStream(\"newCalendar.out\"t);

    Calendar cal2 = (Calendar)in.readObject();

    cal2.get(Calendar.MILLISECOND); //先調用 get(),強制 Calendar 刷新

    cal2.set(Calendar.MILLISECOND, 0);//再設值     System.out.println(cal2.getTime());     }     }

    運行的結果是: Tue Aug 01 00:00:00 PDT 2000

    這個問題主要會影響到在 EJB 編程中,參數對象中包含 Calendar 時。通過 Serialize/Deserialize 後,直接操做 Calendar 會產生不穩定的狀況。

4. add() 與 roll() 的區別

    add() 的功能很是強大,add 能夠對 Calendar 的字段進行計算。若是須要減去值,那麼使用負數值就能夠了,如 add(field, -value)。

    add() 有兩條規則:

    當被修改的字段超出它能夠的範圍時,那麼比它大的字段會自動修正。如:

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 8, 31, 0, 0 , 0); //2000-8-31

    cal1.add(Calendar.MONTH, 1); //2000-9-31 => 2000-10-1,對嗎?

    System.out.println(cal1.getTime()); //結果是 2000-9-30

    另外一個規則是,若是比它小的字段是不可變的(由 Calendar 的實現類決定),那麼該小字段會修正到變化最小的值。

    以上面的例子,9-31 就會變成 9-30,由於變化最小。

    Roll() 的規則只有一條:

    當被修改的字段超出它能夠的範圍時,那麼比它大的字段不會被修正。如:

    Calendar cal1 = Calendar.getInstance();

    cal1.set(1999, 5, 6, 0, 0, 0); //1999-6-6, 週日

    cal1.roll(Calendar.WEEK_OF_MONTH, -1); //1999-6-1, 週二

    cal1.set(1999, 5, 6, 0, 0, 0); //1999-6-6, 週日

    cal1.add(Calendar.WEEK_OF_MONTH, -1); //1999-5-30, 週日

    WEEK_OF_MONTH 比 MONTH 字段小,因此 roll 不能修正 MONTH 字段。

    Date類介紹

    Data和Calendar類:     1、建立一個日期對象r

    讓咱們看一個使用系統的當前日期和時間建立一個日期對象並返回一個長整數的簡單例子. 這個時間一般被稱爲Java 虛擬機(JVM)主機環境的系統時間.

    import java.util.Date;

    public class DateExample1

{

     public static void main(String[] args)

{     // Get the system date/time

    Date date = new Date();

    System.out.println(date.getTime());

    }

    }

    在星期六, 2001年9月29日, 下午大約是6:50的樣子, 上面的例子在系統輸出設備上     顯示的結果是 1001803809710. 在這個例子中,值得注意的是咱們使用了Date 構造

    函數建立一個日期對象, 這個構造函數沒有接受任何參數. 而這個構造函數在內部     使用了System.currentTimeMillis() 方法來從系統獲取日期.若是用

    System.out.println(new Date());

    則輸出形式爲:Tue Nov 08 14:28:07 CST 2005

    那麼, 如今咱們已經知道了如何獲取從1970年1月1日開始經歷的毫秒數了. 咱們如     何才能以一種用戶明白的格式來顯示這個日期呢? 在這裏類java.text.     SimpleDateFormat 和它的抽象基類 java.text.DateFormat 就派得上用場了.

    2、日期數據的定製格式

    假如咱們但願定製日期數據的格式, 比方星期六-9月-29日-2001年. 下面的例子展     示瞭如何完成這個工做:

    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 的構造函數傳遞格式字符串\"EEE-MMMM-dd-yyyy\",     咱們就可以指明本身想要的格式. 你應該能夠看見, 格式字符串中的ASCII 字符     告訴格式化函數下面顯示日期數據的哪個部分. EEEE是星期, MMMM是月, dd是日[Page]     , yyyy是年. 字符的個數決定了日期是如何格式化的.傳遞\"EE-MM-dd-yy\"會顯示     Sat-09-29-01. 請察看Sun 公司的Web 站點獲取日期格式化選項的完整的指示.

    3、將文本數據解析成日期對象r

    假設咱們有一個文本字符串包含了一個格式化了的日期對象, 而咱們但願解析這個     字符串並從文本日期數據建立一個日期對象. 咱們將再次以格式化字符串     \"MM-dd-yyyy\" 調用SimpleDateFormat類, 可是這一次, 咱們使用格式化解析而不     是生成一個文本日期數據. 咱們的例子, 顯示在下面, 將解析文本字符串     \"9-29-2001\"並建立一個值爲001736000000 的日期對象.

    例子程序:

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

5、使用標準的日期格式化過程

    既然咱們已經能夠生成和解析定製的日期格式了, 讓咱們來看一看如何使用內建的     格式化過程. 方法 DateFormat.getDateTimeInstance() 讓咱們得以用幾種不一樣的     方法得到標準的日期格式化過程. 在下面的例子中, 咱們獲取了四個內建的日期格     式化過程. 它們包括一個短的, 中等的, 長的, 和完整的日期格式.

    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的每次調用中都傳遞了兩個值. 第一個參數     是日期風格, 而第二個參數是時間風格. 它們都是基本數據類型int(整型). 考慮     到可讀性, 咱們使用了DateFormat 類提供的常量: SHORT, MEDIUM, LONG, 和 [Page]     FULL. 要知道獲取時間和日期格式化過程的更多的方法和選項, 請看Sun 公司Web     站點上的解釋.

    運行咱們的例子程序的時候, 它將向標準輸出設備輸出下面的內容:     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

相關文章
相關標籤/搜索