第一部分 Calendar介紹java
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {}
Calendar 能夠看做是一個抽象類。
它的實現,採用了設計模式中的工廠方法。表如今:當咱們獲取Calendar實例時,Calendar會根據傳入的參數來返回相應的Calendar對象。獲取Calendar實例,有如下兩種方式:
(1) 當咱們經過 Calendar.getInstance() 獲取日曆時,默認的是返回的一個GregorianCalendar對象。
GregorianCalendar是Calendar的一個實現類,它提供了世界上大多數國家/地區使用的標準日曆系統。
(2) 當咱們經過 Calendar.getInstance(TimeZone timezone, Locale locale) 或 Calendar.getInstance(TimeZone timezone) 或 Calendar.getInstance(Locale locale)獲取日曆時,是返回「對應時區(zone) 或 地區(local)等所使用的日曆」。設計模式
例如,如果日本,則返回JapaneseImperialCalendar對象。
參考以下代碼:數組
public static Calendar getInstance() { // 調用createCalendar()建立日曆 Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault()); cal.sharedZone = true; return cal; } public static Calendar getInstance(TimeZone zone) { // 調用createCalendar()建立日曆 return createCalendar(zone, Locale.getDefault()); } public static Calendar getInstance(Locale aLocale) { // 調用createCalendar()建立日曆 Calendar cal = createCalendar(TimeZone.getDefaultRef(), aLocale); cal.sharedZone = true; return cal; } public static Calendar getInstance(TimeZone zone, Locale aLocale) { // 調用createCalendar()建立日曆 return createCalendar(zone, aLocale); } private static Calendar createCalendar(TimeZone zone, Locale aLocale) { // (01) 若地區是「th」,則返回BuddhistCalendar對象 // (02) 若地區是「JP」,則返回JapaneseImperialCalendar對象 if ("th".equals(aLocale.getLanguage()) && ("TH".equals(aLocale.getCountry()))) { return new sun.util.BuddhistCalendar(zone, aLocale); } else if ("JP".equals(aLocale.getVariant()) && "JP".equals(aLocale.getCountry()) && "ja".equals(aLocale.getLanguage())) { return new JapaneseImperialCalendar(zone, aLocale); } // (03) 不然,返回GregorianCalendar對象 return new GregorianCalendar(zone, aLocale); }
當咱們獲取Calendar實例以後,就能夠經過Calendar提供的一些列方法來操做日曆。app
第二部分 Calendar的原理和思想less
咱們使用Calendar,無非是操做Calendar的「年、月、日、星期、時、分、秒」這些字段。下面,咱們對這些字段的的來源、定義以及計算方法進行學習。dom
1. Calendar 各個字段值的來源函數
咱們使用Calendar,無非是使用「年、月、日、星期、時、分、秒」等信息。那麼它是如何作到的呢? 本質上,Calendar就是保存了一個時間。以下定義:學習
// time 是當前時間,單位是毫秒。 // 它是當前時間距離「January 1, 1970, 0:00:00 GMT」的差值。 protected long time;
Calendar就是根據 time 計算出 「Calendar的年、月、日、星期、時、分、秒」等等信息。測試
2. Calendar 各個字段的定義和初始化ui
Calendar 的「年、月、日、星期、時、分、秒」這些信息,一共是17個字段。
咱們使用Calendar,無非是就是使用這17個字段。它們的定義以下:
(字段0) public final static int ERA = 0;
說明:紀元。
取值:只能爲0 或 1。0表示BC(「before Christ」,即公元前),1表示AD(拉丁語「Anno Domini」,即公元)。
(字段1) public final static int YEAR = 1;
說明:年。
(字段2) public final static int MONTH = 2;
說明:月
取值:能夠爲,JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, UNDECIMBER。
其中第一個月是 JANUARY,它爲 0。
(字段3) public final static int WEEK_OF_YEAR = 3;
說明:當前日期在本年中對應第幾個星期。一年中第一個星期的值爲 1。
(字段4) public final static int WEEK_OF_MONTH = 4;
說明:當前日期在本月中對應第幾個星期。一個月中第一個星期的值爲 1。
(字段5) public final static int DATE = 5;
說明:日。一個月中第一天的值爲 1。
(字段5) public final static int DAY_OF_MONTH = 5;
說明:同「DATE」,表示「日」。
(字段6) public final static int DAY_OF_YEAR = 6;
說明:當前日期在本年中對應第幾天。一年中第一天的值爲 1。
(字段7) public final static int DAY_OF_WEEK = 7;
說明:星期幾。
取值:能夠爲,SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY 和 SATURDAY。
其中,SUNDAY爲1,MONDAY爲2,依次類推。
(字段8) public final static int DAY_OF_WEEK_IN_MONTH = 8;
說明:當前月中的第幾個星期。
取值:DAY_OF_MONTH 1 到 7 老是對應於 DAY_OF_WEEK_IN_MONTH 1;8 到 14 老是對應於 DAY_OF_WEEK_IN_MONTH 2,依此類推。
(字段9) public final static int AM_PM = 9;
說明:上午 仍是 下午
取值:能夠是AM 或 PM。AM爲0,表示上午;PM爲1,表示下午。
(字段10) public final static int HOUR = 10;
說明:指示一天中的第幾小時。
HOUR 用於 12 小時制時鐘 (0 - 11)。中午和午夜用 0 表示,不用 12 表示。
(字段11) public final static int HOUR_OF_DAY = 11;
說明:指示一天中的第幾小時。
HOUR_OF_DAY 用於 24 小時制時鐘。例如,在 10:04:15.250 PM 這一時刻,HOUR_OF_DAY 爲 22。
(字段12) public final static int MINUTE = 12;
說明:一小時中的第幾分鐘。
例如,在 10:04:15.250 PM這一時刻,MINUTE 爲 4。
(字段13) public final static int SECOND = 13;
說明:一分鐘中的第幾秒。
例如,在 10:04:15.250 PM 這一時刻,SECOND 爲 15。
(字段14) public final static int MILLISECOND = 14;
說明:一秒中的第幾毫秒。
例如,在 10:04:15.250 PM 這一時刻,MILLISECOND 爲 250。
(字段15) public final static int ZONE_OFFSET = 15;
說明:毫秒爲單位指示距 GMT 的大體偏移量。
(字段16) public final static int DST_OFFSET = 16;
說明:毫秒爲單位指示夏令時的偏移量。
public final static int FIELD_COUNT = 17;
這17個字段是保存在int數組中。定義以下:
// 保存這17個字段的數組 protected int fields[]; // 數組的定義函數 protected Calendar(TimeZone zone, Locale aLocale) { // 初始化「fields數組」 fields = new int[FIELD_COUNT]; isSet = new boolean[FIELD_COUNT]; stamp = new int[FIELD_COUNT]; this.zone = zone; setWeekCountData(aLocale); }
protected Calendar(TimeZone zone, Locale aLocale) 這是Calendar的構造函數。它會被它的子類的構造函數調用到,從而新建「保存Calendar的17個字段數據」的數組。
3. Calendar 各個字段值的計算
下面以get(int field)爲例,簡要的說明Calendar的17個字段的計算和操做。 get(int field)是獲取「field」字段的值。
它的定義以下:
public int get(int field) { // 計算各個字段的值 complete(); // 返回field字段的值 return internalGet(field); }
說明:get(int field)的代碼很簡單。先經過 complete() 計算各個字段的值,而後在經過 internalGet(field) 返回「field字段的值」。
complete() 的做用就是計算Calendar各個字段的值。它定義在Calendar.java中,代碼以下:
protected void complete() { if (!isTimeSet) updateTime(); if (!areFieldsSet || !areAllFieldsSet) { computeFields(); // fills in unset fields areAllFieldsSet = areFieldsSet = true; } } private void updateTime() { computeTime(); isTimeSet = true; } updateTime() 調用到的 computeTime() 定義在 Calendar.java的實現類中。下面,我列出GregorianCalendar.java中computeTime()的實現: protected void computeTime() { // In non-lenient mode, perform brief checking of calendar // fields which have been set externally. Through this // checking, the field values are stored in originalFields[] // to see if any of them are normalized later. if (!isLenient()) { if (originalFields == null) { originalFields = new int[FIELD_COUNT]; } for (int field = 0; field < FIELD_COUNT; field++) { int value = internalGet(field); if (isExternallySet(field)) { // Quick validation for any out of range values if (value < getMinimum(field) || value > getMaximum(field)) { throw new IllegalArgumentException(getFieldName(field)); } } originalFields[field] = value; } } // Let the super class determine which calendar fields to be // used to calculate the time. int fieldMask = selectFields(); // The year defaults to the epoch start. We don't check // fieldMask for YEAR because YEAR is a mandatory field to // determine the date. int year = isSet(YEAR) ? internalGet(YEAR) : EPOCH_YEAR; int era = internalGetEra(); if (era == BCE) { year = 1 - year; } else if (era != CE) { // Even in lenient mode we disallow ERA values other than CE & BCE. // (The same normalization rule as add()/roll() could be // applied here in lenient mode. But this checking is kept // unchanged for compatibility as of 1.5.) throw new IllegalArgumentException("Invalid era"); } // If year is 0 or negative, we need to set the ERA value later. if (year <= 0 && !isSet(ERA)) { fieldMask |= ERA_MASK; setFieldsComputed(ERA_MASK); } // Calculate the time of day. We rely on the convention that // an UNSET field has 0. long timeOfDay = 0; if (isFieldSet(fieldMask, HOUR_OF_DAY)) { timeOfDay += (long) internalGet(HOUR_OF_DAY); } else { timeOfDay += internalGet(HOUR); // The default value of AM_PM is 0 which designates AM. if (isFieldSet(fieldMask, AM_PM)) { timeOfDay += 12 * internalGet(AM_PM); } } timeOfDay *= 60; timeOfDay += internalGet(MINUTE); timeOfDay *= 60; timeOfDay += internalGet(SECOND); timeOfDay *= 1000; timeOfDay += internalGet(MILLISECOND); // Convert the time of day to the number of days and the // millisecond offset from midnight. long fixedDate = timeOfDay / ONE_DAY; timeOfDay %= ONE_DAY; while (timeOfDay < 0) { timeOfDay += ONE_DAY; --fixedDate; } // Calculate the fixed date since January 1, 1 (Gregorian). calculateFixedDate: { long gfd, jfd; if (year > gregorianCutoverYear && year > gregorianCutoverYearJulian) { gfd = fixedDate + getFixedDate(gcal, year, fieldMask); if (gfd >= gregorianCutoverDate) { fixedDate = gfd; break calculateFixedDate; } jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask); } else if (year < gregorianCutoverYear && year < gregorianCutoverYearJulian) { jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask); if (jfd < gregorianCutoverDate) { fixedDate = jfd; break calculateFixedDate; } gfd = fixedDate + getFixedDate(gcal, year, fieldMask); } else { gfd = fixedDate + getFixedDate(gcal, year, fieldMask); jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask); } // Now we have to determine which calendar date it is. if (gfd >= gregorianCutoverDate) { if (jfd >= gregorianCutoverDate) { fixedDate = gfd; } else { // The date is in an "overlapping" period. No way // to disambiguate it. Determine it using the // previous date calculation. if (calsys == gcal || calsys == null) { fixedDate = gfd; } else { fixedDate = jfd; } } } else { if (jfd < gregorianCutoverDate) { fixedDate = jfd; } else { // The date is in a "missing" period. if (!isLenient()) { throw new IllegalArgumentException("the specified date doesn't exist"); } // Take the Julian date for compatibility, which // will produce a Gregorian date. fixedDate = jfd; } } } // millis represents local wall-clock time in milliseconds. long millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay; // Compute the time zone offset and DST offset. There are two potential // ambiguities here. We'll assume a 2:00 am (wall time) switchover time // for discussion purposes here. // 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am // can be in standard or in DST depending. However, 2:00 am is an invalid // representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST). // We assume standard time. // 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am // can be in standard or DST. Both are valid representations (the rep // jumps from 1:59:59 DST to 1:00:00 Std). // Again, we assume standard time. // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET // or DST_OFFSET fields; then we use those fields. TimeZone zone = getZone(); if (zoneOffsets == null) { zoneOffsets = new int[2]; } int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK); if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) { if (zone instanceof ZoneInfo) { ((ZoneInfo)zone).getOffsetsByWall(millis, zoneOffsets); } else { int gmtOffset = isFieldSet(fieldMask, ZONE_OFFSET) ? internalGet(ZONE_OFFSET) : zone.getRawOffset(); zone.getOffsets(millis - gmtOffset, zoneOffsets); } } if (tzMask != 0) { if (isFieldSet(tzMask, ZONE_OFFSET)) { zoneOffsets[0] = internalGet(ZONE_OFFSET); } if (isFieldSet(tzMask, DST_OFFSET)) { zoneOffsets[1] = internalGet(DST_OFFSET); } } // Adjust the time zone offset values to get the UTC time. millis -= zoneOffsets[0] + zoneOffsets[1]; // Set this calendar's time in milliseconds time = millis; int mask = computeFields(fieldMask | getSetStateFields(), tzMask); if (!isLenient()) { for (int field = 0; field < FIELD_COUNT; field++) { if (!isExternallySet(field)) { continue; } if (originalFields[field] != internalGet(field)) { // Restore the original field values System.arraycopy(originalFields, 0, fields, 0, fields.length); throw new IllegalArgumentException(getFieldName(field)); } } } setFieldsNormalized(mask); }
下面,咱們看看internalGet(field)的定義。以下:
protected final int internalGet(int field) { return fields[field]; }
從中,咱們就看出,get(int field) 最終是經過 internalGet(int field)來返回值的。
而 internalGet(int field) ,實際上返回的是field數組中的第field個元素。這就正好和Calendar的17個元素所對應了!
總之,咱們須要瞭解的就是:Calendar就是以一個time(毫秒)爲基數,而計算出「年月日時分秒」等,從而方便咱們對「年月日時分秒」等進行操做。下面,介紹如下Calendar提供的相關操做函數。
第三部分 Calendar函數接口
1. Calendar的17個字段的公共接口
Calendar的這17個字段,都支持下面的公共函數接口。 這些公共接口的使用示例,請參考CalendarTest.java 示例中的 testAllCalendarSections() 函數。
(1) getMaximum(int field)
做用:獲取「字段的最大值」。注意「對比它和 getActualMaximum() 的區別」。 示例:以「MONTH」字段來講。使用方法爲:
// 獲取Calendar實例 Calendar cal = Calendar.getInstance(); // 獲取MONTH的最大值 int max = cal.getMaximum(Calendar.MONTH);
若要獲取其它字段的最大值,只須要將示例中的MONTH相應的替換成其它字段名便可。
(2) getActualMaximum(int field)
做用:獲取「當前日期下,該字段的最大值」。 示例:以「MONTH」字段來講。使用方法爲:
// 獲取Calendar實例 Calendar cal = Calendar.getInstance(); // 獲取當前MONTH的最大值 int max = cal.getActualMaximum(Calendar.MONTH);
若要獲取其它字段的最大值,只須要將示例中的MONTH相應的替換成其它字段名便可。
注意:對比getActualMaximum() 和 getMaximum() 的區別。參考下面的對比示例,
A、 getMaximum() 獲取的「字段最大值」,是指在綜合全部的日期,在全部這些日期中得出的「字段最大值」。
例如,getMaximum(Calendar.DATE)的目的是「獲取‘日的最大值'」。綜合全部的日期,得出一個月最多有31天。所以,getMaximum(Calendar.DATE)的返回值是「31」!
B、 getActualMaximum() 獲取的「當前日期時,該字段的最大值」。
例如,當日期爲2013-09-01時,getActualMaximum(Calendar.DATE)是獲取「日的最大值」是「30」。當前日期是9月份,而9月只有30天。所以,getActualMaximum(Calendar.DATE)的返回值是「30」!
(3) getMinimum(int field)
做用:獲取「字段的最小值」。注意「對比它和 getActualMinimum() 的區別」。 示例:以「MONTH」字段來講。使用方法爲:
// 獲取Calendar實例 Calendar cal = Calendar.getInstance(); // 獲取MONTH的最小值 int min = cal.getMinimum(Calendar.MONTH);
(4) getActualMinimum(int field)
做用:獲取「當前日期下,該字段的最小值」。 示例:以「MONTH」字段來講。使用方法爲: // 獲取Calendar實例 Calendar cal = Calendar.getInstance(); // 獲取MONTH的最小值 int min = cal.getMinimum(Calendar.MONTH);
若要獲取其它字段的最小值,只須要將示例中的MONTH相應的替換成其它字段名便可。
注意:在Java默認的Calendar中,雖然 getMinimum() 和 getActualMinimum() 的含義不一樣;可是,它們的返回值是同樣的。由於Calendar的默認是返回GregorianCalendar對象,而在GregorianCalendar.java中,getMinimum() 和 getActualMinimum() 返回值同樣。
(5) get(int field)
做用:獲取「字段的當前值」。獲取field字段的當前值。 示例:以「MONTH」字段來講。「獲取MONTH的當前值」的方法爲:
// 獲取Calendar實例 Calendar cal = Calendar.getInstance(); // 獲取「cal日曆」的當前MONTH值 int MONTH = cal.get(Calendar.MONTH);
若要獲取其它字段的當前值,只須要將示例中的MONTH相應的替換成其它字段名便可。
(6) set(int field, int value)
做用:設置「字段的當前值」。設置field字段的當前值爲value 示例:以「MONTH」字段來講。「設置MONTH的當前值」的方法爲:
// 獲取Calendar實例 Calendar cal = Calendar.getInstance(); // 設置「cal日曆」的當前MONTH值爲 1988年 cal.set(Calendar.MONTH, 1988);
說明:
A、1988 是想要設置的MONTH的當前值。這個設置值必須是整數。
B、若要設置其它字段的當前值,只須要將示例中的MONTH相應的替換成其它字段名便可。
(7) add(int field, int value)
做用:給「字段的當前值」添加值。給field字段的當前值添加value。 示例:以「MONTH」字段來講。方法以下:
// 獲取Calendar實例,並設置日期爲「2013-09-01」 Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, 2013); cal.set(Calendar.MONTH, 8); cal.set(Calendar.DATE, 1); // 給「cal日曆」的當前MONTH值 「添加-10」 cal.add(Calendar.MONTH, -10);
說明:
A、 -10 是添加值。
添加值能夠爲正數,也能夠是負數。
正數表示將日期增長,負數表示將日期減小。
假設:如今cal的值是「2013-09-01」,如今咱們將MONTH字段值增長-10。獲得的結果是:「2012-10-01」。
爲何會這樣呢?「2013-09-01」增長-10,也就是將日期向前減小10個月;獲得的結果就是「2012-10-01」。
B、 Calendar的17個字段中:除了回滾Calendar.ZONE_OFFSET時,會拋出IllegalArgumentException異常;其它的字段都支持該操做。
C、 若要設置其它字段的當前值,只須要將示例中的MONTH相應的替換成其它字段名便可。
(8) roll(int field, int value)
做用:回滾「字段的當前值」 示例:以「MONTH」字段來講。「回滾MONTH的當前值」的方法爲:
// 獲取Calendar實例,並設置日期爲「2013-09-01」 Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, 2013); cal.set(Calendar.MONTH, 8); cal.set(Calendar.DATE, 1); // 將「cal日曆」的當前MONTH值 「向前滾動10」 cal.roll(Calendar.MONTH, -10);
說明:
A、 -10 是回滾值。
當回滾值是負數時,表示將當前字段向前滾;
當回滾值是正數時,表示將當前字段向後滾。
回滾Calendar中某一字段時,不更改更大的字段!
這是roll()與add()的根據區別!add()可能會更改更大字段,好比「使用add()修改‘MONTH'字段,可能會引發‘YEAR'字段的改變」;可是roll()不會更改更大的字段,例如「使用roll()修改‘MONTH'字段,不回引發‘YEAR'字段的改變。」
假設:如今cal的值是「2013-09-01」,如今咱們將MONTH字段值增長-10。獲得的結果是:「2013-10-01」。
爲何會這樣呢?這就是由於「回滾」就是「在最小值和最大值之間來回滾動」。本例中,MONTH是9月,前回滾10,獲得的值是10月,可是roll()不會改變「比MONTH」更大的字段,因此YEAR字段不會改變。因此結果是「2013-10-01」。
B、 Calendar的17個字段中:除了回滾Calendar.ZONE_OFFSET時,會拋出IllegalArgumentException異常;其它的字段都支持該操做。
C、 若要設置其它字段的當前值,只須要將示例中的MONTH相應的替換成其它字段名便可。
(9) clear(int field)
做用:清空「字段的當前值」。所謂清空,其實是將「field」的值設置爲0;若field最小值爲1,則設置爲1。 示例:以「MONTH」字段來講。「清空MONTH」的方法爲:
// 獲取Calendar實例,並設置日期爲「9月」 Calendar cal = Calendar.getInstance(); cal.set(Calendar.MONTH, 9); // 清空MONTH cal.clear(Calendar.MONTH);
若要清空其它字段,只須要將示例中的MONTH相應的替換成其它字段名便可。
(10) isSet(int field)
做用:判斷「字段field」是否被設置。若調用clear()清空以後,則field變爲「沒有設置狀態」。 示例:以「MONTH」字段來講。「判斷MONTH是否被設置」的方法爲:
// 獲取Calendar實例 Calendar cal = Calendar.getInstance(); // 判斷MONTH是否被設置 boolean bset = cal.isSet(Calendar.MONTH);
若要判斷其它字段,只須要將示例中的MONTH相應的替換成其它字段名便可。
2. Calendar的其它函數
(1) 日期比較函數
Calendar的比較函數,主要有如下幾個:
// 比較「當前Calendar對象」和「calendar」 的日期、時區等內容是否相等。 boolean equals(Object object) // 當前Calendar對象 是否 早於calendar boolean before(Object calendar) // 當前Calendar對象 是否 晚於calendar boolean after(Object calendar) // 比較「當前Calendar對象」和「calendar」。 // 若 早於 「calendar」 則,返回-1 // 若 相等, 則,返回0 // 若 晚於 「calendar」 則,返回1 int compareTo(Calendar anotherCalendar)
這些函數的使用示例,請參考CalendarTest.java示例中的 testComparatorAPIs() 函數。
示例:假設cal1 和 cal2 都是Calendar的兩個對象。
// 它們的使用方法以下 boolean isEqual = cal1.equals(cal2); boolean isBefore = cal1.before(cal2); boolean isAfter = cal1.after(cal2); int icompare = cal1.compareTo(cal2);
(2) 「寬容」函數
// 設置「Calendar的寬容度」 void setLenient(boolean value) // 獲取「Calendar的寬容度」 boolean isLenient()
這些函數的使用示例,請參考CalendarTest.java示例中的 testLenientAPIs() 函數。
說明:
Calendar 有兩種解釋日曆字段的模式,即 lenient 和 non-lenient。
A、 當 Calendar 處於 lenient 模式時,它可接受比它所生成的日曆字段範圍更大範圍內的值。當 Calendar 從新計算日曆字段值,以便由 get() 返回這些值時,全部日曆字段都被標準化。
例如,lenient 模式下的 GregorianCalendar 將 MONTH == JANUARY、DAY_OF_MONTH == 32 解釋爲 February 1。
B、 當 Calendar 處於 non-lenient 模式時,若是其日曆字段中存在任何不一致性,它都會拋出一個異常。
例如,GregorianCalendar 老是在 1 與月份的長度之間生成 DAY_OF_MONTH 值。若是已經設置了任何超出範圍的字段值,那麼在計算時間或日曆字段值時,處於 non-lenient 模式下的 GregorianCalendar 會拋出一個異常。
注意:在(02)步驟中的異常,在使用set()時不會拋出,而須要在使用get()、getTimeInMillis()、getTime()、add() 和 roll() 等函數中才拋出。由於set()只是設置了一個修改標誌,而get()等方法纔會引發時間的從新計算,此時纔會拋出異常!
(3) "年月日(時分秒)"、Date、TimeZone、MilliSecond函數
// 設置「年月日」 final void set(int year, int month, int day) // 設置「年月日時分」 final void set(int year, int month, int day, int hourOfDay, int minute, int second) // 設置「年月日時分秒」 final void set(int year, int month, int day, int hourOfDay, int minute) // 獲取Calendar對應的日期 final Date getTime() // 設置Calendar爲date final void setTime(Date date) // 獲取Calendar對應的時區 TimeZone getTimeZone() // 設置Calendar對應的時區 void setTimeZone(TimeZone timezone) // 獲取Calendar對應的milliscondes值,就是「Calendar當前日期」距離「1970-01-01 0:00:00 GMT」的毫秒數 long getTimeInMillis() // 設置Calendar對應的milliscondes值 void setTimeInMillis(long milliseconds)
這些函數的使用示例,請參考CalendarTest.java示例中的 testTimeAPIs() 函數。
(4) 其它操做
// 克隆Calendar Object clone() // 獲取「每週的第一天是星期幾」。例如,在美國,這一天是 SUNDAY,而在法國,這一天是 MONDAY。 int getFirstDayOfWeek() // 設置「每週的第一天是星期幾」。例如,在美國,這一天是 SUNDAY,而在法國,這一天是 MONDAY。 void setFirstDayOfWeek(int value) // 獲取一年中第一個星期所需的最少天數,例如,若是定義第一個星期包含一年第一個月的第一天,則此方法將返回 1。若是最少天數必須是一整個星期,則此方法將返回 7。 int getMinimalDaysInFirstWeek() // 設置一年中第一個星期所需的最少天數,例如,若是定義第一個星期包含一年第一個月的第一天,則使用值 1 調用此方法。若是最少天數必須是一整個星期,則使用值 7 調用此方法。 void setMinimalDaysInFirstWeek(int value)
這些函數的使用示例,請參考CalendarTest.java示例中的 testOtherAPIs() 函數。
第四部分 Calendar使用示例
下面,咱們經過示例學習使用Calendar的API。CalendarTest.java的源碼以下:
import java.util.Date; import java.util.Calendar; import java.util.TimeZone; import java.util.Random; public class CalendarTest { public static void main(String[] args) { // 測試Calendar的「17個字段的公共函數接口」 testAllCalendarSections() ; // 測試Calendar的「比較接口」 testComparatorAPIs() ; // 測試Calendar的「比較接口」 testLenientAPIs() ; // 測試Calendar的Date、TimeZone、MilliSecond等相關函數 testTimeAPIs() ; // 測試Calendar的clone(),getFirstDayOfWeek()等接口 testOtherAPIs() ; } /** * 測試「Calendar的字段」 * * @param cal -- Calendar對象 * @param field -- 要測試的「Calendar字段」。能夠爲如下值: * Calendar.YEAR, Calendar.MONTH, Calendar.DATE, ... 等等 * @param title -- 標題 */ private static void testSection(Calendar cal, int field, String title) { final Random random = new Random(); final Date date = cal.getTime(); final int min = cal.getMinimum(field); // 獲取"字段最小值" final int max = cal.getMaximum(field); // 獲取「字段最大值」 final int actualMin = cal.getActualMinimum(field); // 獲取"當前日期下,該字段最小值" final int actualMax = cal.getActualMaximum(field); // 獲取「當前日期下,該字段的最大值」 // 獲取「字段的當前值」 final int ori = cal.get(field); // 設置「字段的當前值」, 並獲取「設置以後的值」 final int r1 = random.nextInt(max); cal.set(field, r1); final int set = cal.get(field); try { // 回滾「字段的當前值」:在「字段最小值」和「字段最大值」之間回滾。 // 「回滾值」能夠爲正,也能夠爲負。 cal.roll(field, -max); } catch (IllegalArgumentException e) { // 當field == Calendar.ZONE_OFFSET時,會拋出該異常! e.printStackTrace(); } final int roll = cal.get(field); // 獲取一個隨機值 final int sign = ( random.nextInt(2) == 1) ? 1 : -1; final int r2 = sign * random.nextInt(max); try { // 增長「字段的當前值」 ,並獲取「新的當前字段值」 // add的「參數值」能夠爲正,也能夠爲負。 cal.add(field, r2); } catch (IllegalArgumentException e) { // 當field == Calendar.ZONE_OFFSET時,會拋出該異常! e.printStackTrace(); } final int add = cal.get(field); // 打印字段信息 System.out.printf("%s:\n\trange is [%d - %d] actualRange is [%d - %d]. original=%d, set(%d)=%d, roll(%d)=%d, add(%d)=%d\n", title, min, max, actualMin, actualMax, ori, r1, set, -max, roll, r2, add); } /** * 測試Calendar的「17個字段的公共函數接口」 */ private static void testAllCalendarSections() { // 00. ERA 字段 testSection(Calendar.getInstance(), Calendar.ERA, "Calendar.ERA"); // 01. YEAR 字段 testSection(Calendar.getInstance(), Calendar.YEAR, "Calendar.YEAR"); // 02. MONTH 字段 testSection(Calendar.getInstance(), Calendar.MONTH, "Calendar.MONTH"); // 03. WEEK_OF_YEAR 字段 testSection(Calendar.getInstance(), Calendar.WEEK_OF_YEAR, "Calendar.WEEK_OF_YEAR"); // 04. WEEK_OF_MONTH 字段 testSection(Calendar.getInstance(), Calendar.WEEK_OF_MONTH, "Calendar.WEEK_OF_MONTH"); // 05. DATE 字段 testSection(Calendar.getInstance(), Calendar.DATE, "Calendar.DATE"); // 06. DAY_OF_MONTH 字段 testSection(Calendar.getInstance(), Calendar.DAY_OF_MONTH, "Calendar.DAY_OF_MONTH"); // 07. DAY_OF_YEAR 字段 testSection(Calendar.getInstance(), Calendar.DAY_OF_YEAR, "Calendar.DAY_OF_YEAR"); // 08. DAY_OF_WEEK 字段 testSection(Calendar.getInstance(), Calendar.DAY_OF_WEEK, "Calendar.DAY_OF_WEEK"); // 09. DAY_OF_WEEK_IN_MONTH 字段 testSection(Calendar.getInstance(), Calendar.DAY_OF_WEEK_IN_MONTH, "Calendar.DAY_OF_WEEK_IN_MONTH"); // 10. AM_PM 字段 testSection(Calendar.getInstance(), Calendar.AM_PM, "Calendar.AM_PM"); // 11. HOUR 字段 testSection(Calendar.getInstance(), Calendar.HOUR, "Calendar.HOUR"); // 12. HOUR_OF_DAY 字段 testSection(Calendar.getInstance(), Calendar.HOUR_OF_DAY, "Calendar.HOUR_OF_DAY"); // 13. MINUTE 字段 testSection(Calendar.getInstance(), Calendar.MINUTE, "Calendar.MINUTE"); // 14. SECOND 字段 testSection(Calendar.getInstance(), Calendar.SECOND, "Calendar.SECOND"); // 15. MILLISECOND 字段 testSection(Calendar.getInstance(), Calendar.MILLISECOND, "Calendar.MILLISECOND"); // 16. ZONE_OFFSET 字段 testSection(Calendar.getInstance(), Calendar.ZONE_OFFSET, "Calendar.ZONE_OFFSET"); } /** * 測試Calendar的「比較接口」 */ private static void testComparatorAPIs() { // 新建cal1 ,且時間爲1988年 Calendar cal1 = Calendar.getInstance(); cal1.set(Calendar.YEAR, 1988); // 新建cal2 ,且時間爲2000年 Calendar cal2 = Calendar.getInstance(); cal2.set(Calendar.YEAR, 2000); // 新建cal3, 爲cal1的克隆對象 Calendar cal3 = (Calendar)cal1.clone(); // equals 判斷 cal1和cal2的「時間、時區等」內容是否相等 boolean isEqual12 = cal1.equals(cal2); // equals 判斷 cal1和cal3的「時間、時區等」內容是否相等 boolean isEqual13 = cal1.equals(cal3); // cal1是否比cal2早 boolean isBefore = cal1.before(cal2); // cal1是否比cal2晚 boolean isAfter = cal1.after(cal2); // 比較cal1和cal2 // (01) 若cal1 早於 cal2,返回-1 // (02) 若cal1 等於 cal2,返回0 // (03) 若cal1 晚於 cal2,返回1 int icompare = cal1.compareTo(cal2); System.out.printf("\ntestComparatorAPIs: isEuqal12=%s, isEqual13=%s, isBefore=%s, isAfter=%s, icompare=%s\n", isEqual12, isEqual13, isBefore, isAfter, icompare); } /** * 測試Calendar的「比較接口」 */ private static void testLenientAPIs() { Calendar cal = Calendar.getInstance(); // 獲取默認的「寬容度」。返回true boolean oriLenient = cal.isLenient(); // MONTH值只能是「0-11」,這裏越界。可是因爲當前cal是寬容的,因此不會拋出異常 cal.set(Calendar.MONTH, 50); // 設置「寬容度」爲false。 cal.setLenient(false); // 獲取設置後的「寬容度」 boolean curLenient = cal.isLenient(); try { // MONTH值只能是「0-11」,這裏越界。並且當前cal是不寬容的,因此會產生異常。 // 可是,異常到下次計算日期時纔會拋出。即,set()中不回拋出異常,而要等到get()中才會拋出異常 cal.set(Calendar.MONTH, 50); // 此時,對cal進行讀取。讀取會致使從新計算cal的值,因此此時拋出異常! int m2 = cal.get(Calendar.MONTH); } catch (IllegalArgumentException e) { e.printStackTrace(); } System.out.printf("\ntestLenientAPIs: oriLenient=%s, curLenient=%s\n", oriLenient, curLenient); } /** * 測試Calendar的Date、TimeZone、MilliSecond等相關函數 */ private static void testTimeAPIs() { Calendar cal = Calendar.getInstance(); // 設置cal的時區爲「GMT+8」 cal.setTimeZone(TimeZone.getTimeZone("GMT+8")); // 獲取當前的cal時區 TimeZone timezone = cal.getTimeZone(); // 設置 milliseconds cal.setTimeInMillis(1279419645742l); // 獲取 milliseconds long millis = cal.getTimeInMillis(); // 設置 milliseconds以後,時間也改變了。 // 獲取cal對應的日期 Date date = cal.getTime(); // 設置時間爲「1988-08-08」 cal.set(1988, 08, 08); // 設置時間爲「1999-09-09 09:09」 cal.set(1999, 09, 09, 9, 9); // 設置時間爲「2000-10-10 10:10:10」 cal.set(2000, 10, 10, 10, 10, 10); System.out.printf("\ntestTimeAPIs: date=%s, timezone=%s, millis=%s\n", date, timezone, millis); } /** * 測試Calendar的clone(),getFirstDayOfWeek()等接口 */ private static void testOtherAPIs() { Calendar cal = Calendar.getInstance(); // 克隆cal Calendar clone = (Calendar)cal.clone(); // 設置 爲 2013-01-10。 // 注:2013-01-01 爲「星期二」,2013-01-06爲「星期天」, clone.set(Calendar.YEAR, 2013); clone.set(Calendar.MONTH, 0); clone.set(Calendar.DATE, 10); // 設置「本年的第一個星期最少包含1天」。 // 則2013-01-10屬於第2個星期 clone.setMinimalDaysInFirstWeek(1); int m1 = clone.getMinimalDaysInFirstWeek(); int index1 = clone.get(Calendar.WEEK_OF_YEAR); // 設置「本年的第一個星期最少包含7天」。 // 則2013-01-10屬於第1個星期 clone.setMinimalDaysInFirstWeek(7); int m2 = clone.getMinimalDaysInFirstWeek(); int index2 = clone.get(Calendar.WEEK_OF_YEAR); // 設置「每週的第一天是星期幾」。 clone.setFirstDayOfWeek(Calendar.WEDNESDAY); // 獲取「每週的第一天是星期幾」。 int firstdayOfWeek = clone.getFirstDayOfWeek(); System.out.printf("\ntestOtherAPIs: firstdayOfWeek=%s, [minimalDay, WeekOfYear]={(%s, %s), (%s, %s)} %s\n", firstdayOfWeek, m1, index1, m2, index2, clone.getTime()); } }
第五部分 自定義的Calendar接口示例
這些接口在寫日曆程序時可能會用到。
源代碼以下(CalendarSelfDefineTest.java):
import java.util.Calendar; /** * 根據Calendar的API封裝的一些經常使用函數 */ public class CalendarSelfDefineTest { public static void main(String[] args) { Calendar cal = Calendar.getInstance(); // 設置日期爲「2013-09-18」 cal.set(2013, Calendar.SEPTEMBER, 18); // 獲取「年」 System.out.printf("year: %s\n", getYear(cal) ); // 獲取「月」 System.out.printf("month: %s\n", getMonth(cal) ); // 獲取「上月」 System.out.printf("previcou month: %s\n", getLastMonth(cal) ); // 獲取「下月」 System.out.printf("next month: %s\n", getNextMonth(cal) ); // 獲取「日」 System.out.printf("day: %s\n", getDay(cal) ); // 獲取Cal對應星期幾 System.out.printf("weekday: %s\n", getWeekDay(cal) ); // 本月天數 System.out.printf("Current Month days: %s\n", getMonthDays(cal) ); // 上月天數 System.out.printf("Previcous Month days: %s\n", getPrevMonthDays(cal) ); // 下月天數 System.out.printf("Next Month days: %s\n", getNextMonthDays(cal) ); // 獲取當月第一天的星期幾 System.out.printf("First day' weekday : %s\n", getFirstDayWeekday(cal) ); // 獲取當前月最後一天的星期幾 System.out.printf("Last day' weekday : %s\n", getLastDayWeekday(cal) ); // 獲取上月最後一天的星期幾 System.out.printf("PrevMonth Last day' weekday: %s\n", getLastDayWeekdayOfPrevMonth(cal) ); // 獲取下月第一天的星期幾 System.out.printf("NextMonth First day' weekday: %s\n", getFirstDayWeekdayOfNextMonth(cal) ); } /** * 獲取「年」 * * @return 例如,2013-09-18,則返回2013 */ public static int getYear(Calendar cal) { return cal.get(Calendar.YEAR); } /** * 獲取「月」 * * @return 返回值能夠爲如下值: * JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, UNDECIMBER。 * 其中第一個月是 JANUARY,它爲 0。 * * 例如,2013-09-18,則返回8 */ public static int getMonth(Calendar cal) { return cal.get(Calendar.MONTH); } /** * 獲取「上一個月」 * * @return 返回值能夠爲如下值: * JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, UNDECIMBER。 * 其中第一個月是 JANUARY,它爲 0。 * * 例如,2012-01-12的上一個月是「11」(即DECEMBER)。 */ public static int getLastMonth(Calendar cal) { return (cal.get(Calendar.MONTH) + 11) % 12; } /** * 獲取「下一個月」 * * @return 返回值能夠爲如下值: * JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, UNDECIMBER。 * 其中第一個月是 JANUARY,它爲 0。 * * 例如,2013-12-12的下一個月是「1」(即DECEMBER)。 */ public static int getNextMonth(Calendar cal) { return (cal.get(Calendar.MONTH) + 13) % 12; } /** * 獲取「日」 * * @return 例如,2013-09-18,則返回18 * */ public static int getDay(Calendar cal) { return cal.get(Calendar.DATE); } /** * 獲取「本月的天數」 * * @return 例如,2013-09-18,則返回30 * */ public static int getMonthDays(Calendar cal) { return cal.getActualMaximum(Calendar.DATE); } /** * 獲取「上一個月的天數」 * * @return 例如,2013-01-18,則返回31 (由於2012-12有31天) * */ public static int getPrevMonthDays(Calendar cal) { Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。後面對tmpCal操做,就不會改變cal tmpCal.add(Calendar.MONTH, -1); // 設爲「上一個月」 return tmpCal.getActualMaximum(Calendar.DATE); } /** * 獲取「下一個月的天數」 * * @return 例如,2013-12-18,則返回31 (由於2014-01有31天) * */ public static int getNextMonthDays(Calendar cal) { Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。後面對tmpCal操做,就不會改變cal tmpCal.add(Calendar.MONTH, 1); // 設爲「下一個月」 return tmpCal.getActualMaximum(Calendar.DATE); } /** * 獲取Cal對應星期幾 * * @return 返回「星期幾」,能夠爲如下值: * SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY 和 SATURDAY。 * SUNDAY爲1,MONDAY爲2,依次類推。 * 例如,2013-09-18(星期三),則返回4 */ public static int getWeekDay(Calendar cal) { return cal.get(Calendar.DAY_OF_WEEK); } /** * 獲取當月第一天對應星期幾 * * @return SUNDAY爲1,MONDAY爲2,依次類推。 */ public static int getFirstDayWeekday(Calendar cal) { Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。後面對tmpCal操做,就不會改變cal tmpCal.set(Calendar.DATE, 1); // 把日期設置爲當月第一天 return tmpCal.get(Calendar.DAY_OF_WEEK); } /** * 獲取當前月最後一天對應星期幾 * * @return SUNDAY爲1,MONDAY爲2,依次類推。 */ public static int getLastDayWeekday(Calendar cal) { Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。後面對tmpCal操做,就不會改變cal tmpCal.set(Calendar.DATE, 1); // 把日期設置爲當月第一天 tmpCal.roll(Calendar.DATE, -1); // 把日期設置爲當月最後一天 return tmpCal.get(Calendar.DAY_OF_WEEK); } /** * 獲取上月最後一天的星期幾 * * @return SUNDAY爲1,MONDAY爲2,依次類推。 */ public static int getLastDayWeekdayOfPrevMonth(Calendar cal) { Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。後面對tmpCal操做,就不會改變cal tmpCal.set(Calendar.DATE, 1); // 把日期設置爲當月第一天 tmpCal.add(Calendar.DATE, -1); // 把日期設置爲上一個月最後一天 return tmpCal.get(Calendar.DAY_OF_WEEK); } /** * 獲取下月第一天的星期偏移 * * @return SUNDAY爲1,MONDAY爲2,依次類推。 */ public static int getFirstDayWeekdayOfNextMonth(Calendar cal) { Calendar tmpCal = (Calendar)cal.clone(); // 克隆cal。後面對tmpCal操做,就不會改變cal tmpCal.add(Calendar.MONTH, 1); // 設爲「下一個月」 tmpCal.set(Calendar.DATE, 1); // 設爲「第一天」 return tmpCal.get(Calendar.DAY_OF_WEEK); } }