2010-05-31 21 views
14

我剛剛遇到了一個GregorianCalendar類的奇怪行爲,而且我在想我是否真的做得不好。GregorianCalendar的奇怪行爲

只有當初始化日期的月份的actualMaximum大於我要設置日曆的月份時,纔會附加此選項。

下面是示例代碼:

// today is 2010/05/31 
    GregorianCalendar cal = new GregorianCalendar(); 

    cal.set(Calendar.YEAR, 2010); 
    cal.set(Calendar.MONTH, 1); // FEBRUARY 

    cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); 
    cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY)); 
    cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE)); 
    cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND)); 
    cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND)); 

    return cal.getTime(); // => 2010/03/03, wtf 

我知道這個問題的事實,日曆初始化日期是31天月(5月),這與設置爲二月月份混亂造成的(28天)。修復很簡單(只需在設置年月之前將day_of_month設置爲1),但我想知道這是否真的是想要的行爲。有什麼想法嗎 ?

回答

14

它正在獲取當前日期/時間的實際最大值。可能有31天,比2月28日多3天,因此將轉移到3月3日。

你需要獲得後打電話到Calendar#clear() /創建它:

GregorianCalendar cal = new GregorianCalendar(); 
cal.clear(); 
// ... 

這導致:

Sun Feb 28 23:59:59 GMT-04:00 2010 

(這是正確的,因爲按我的時區)

正如一說的答案,java.util.CalendarDate是史詩般的失敗。進行密集的日期/時間操作時,請考慮JodaTime

+0

這是有效的。謝謝!! – 2015-04-01 09:39:58

2

是的,這是它是如何工作的。如果您從具有確切日期的GregorianCalendar開始,並且通過使其不一致而對其進行修改,則不應相信您獲得的結果。

根據該文件有關getActualMaximum(..)它指出:

例如,如果此實例的日期是2004年2月1日,DAY_OF_MONTH字段的實際最大值是29,因爲2004年是閏年,並且如果此實例的日期是2005年2月1日,則爲28.

因此,它應該可以工作,但必須用一致的值來提供它。 2010年2月31日是不正確的,應用依賴日期值的東西(如getActualMaximum)無法正常工作。它應該如何解決它本身?通過決定那個月是錯的?或者那一天是錯誤的?

順便說一句,大家都一直規定使用JodaTime .. :)

+1

如果您將月份設置爲2月份,則正常API會認識到該日無效並需要進行調整。但'java.util.Calendar'是很多東西,但不是一個普通的API。請點擊此處查看第一條評論,瞭解JodaTime如何操作:http://blog.smart-java.nl/blog/index.php/2010/01/20/java-util-calendar-getactualmaximum-returns-strange-results/ – Yishai 2010-05-31 16:02:58

1

也許setLenient(boolean lenient)將整理出來給你。我運行下面的代碼時遇到異常。

如果不是,喬達是一個更好的答案。

import java.util.Calendar; 

public class CalTest 
{ 
    public static void main(String[] args) 
    { 
     // today is 2010/05/31 
     Calendar cal = Calendar.getInstance(); 
     cal.setLenient(false); 

     cal.set(Calendar.YEAR, 2010); 
     cal.set(Calendar.MONTH, 1); // FEBRUARY 

     cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); 
     cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY)); 
     cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE)); 
     cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND)); 
     cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND)); 

     System.out.println(cal.getTime()); 
    } 
} 
+0

不幸的是,即使是嚴格的日曆也是有用的。它會在'getTime'處引發異常,但沒有其他。 http://blog.smart-java.nl/blog/index.php/2010/01/20/java-util-calendar-getactualmaximum-returns-strange-results/ – Yishai 2010-05-31 16:05:55

+0

同意 - 這就是我所觀察到的。 – duffymo 2010-05-31 16:07:18

2

我確定它不是想要的行爲。我也同樣確信,沒有人真的認爲這個用例在他們上課的時候就通過了。事實是Calendar對於內部狀態以及它如何管理所有設置方法中的所有潛在轉換都有很大的問題。

如果你不能在你的項目中使用JodaTime或JSR-310,那麼在使用Calendar類時,單元測試將會很沉重。正如你在這種情況下可以看到的一樣,日曆代碼的行爲有所不同,具體取決於你運行代碼的月份的哪一天(或哪一天)。

0

原因應該是,MONTH具有類似枚舉的邏輯結構。您可以輕鬆地填寫和讀取數組/列表。由於國際化,它必須是可以枚舉的(間接訪問)。 DAY只是一個可直接訪問的Integer。這是不同的。

0

在您的示例中,日曆從2010年5月31日開始。當您將月份設置爲2月份時,日期更改爲2010年2月31日,並將其規格化爲2010年3月3日,因此cal.getActualMaximum(Calendar.DAY_OF_MONTH)將在3月份返回31。

Calendar c = Calendar.getInstance(); 
c.set(Calendar.YEAR, 2010); 
c.set(Calendar.MONTH, Calendar.MAY); 
c.set(Calendar.DAY_OF_MONTH, 31); 
System.out.println(c.getTime()); 
c.set(Calendar.MONTH, Calendar.FEBRUARY); 
System.out.println(c.getTime()); 

輸出:

Mon May 31 20:20:25 GMT+03:00 2010 
Wed Mar 03 20:20:25 GMT+03:00 2010 

要解決你的代碼,你可以添加cal.clear();或在設置月份前設置第1..28天