2月java.util.Calendar的一个奇怪的行为

我遇到了来自java.util.Calendar的奇怪行为:

import static org.junit.Assert.*; import org.junit.Test; import java.util.Calendar; public class Tester1 { @Test public void test_monthOfDate() { assertEquals(1, monthOfDate(2013, 1, 30)); // OK assertEquals(1, monthOfDate(2013, 1, 31)); // OK // Start of February assertEquals(2, monthOfDate(2013, 2, 1)); // FAIL assertEquals(2, monthOfDate(2013, 2, 28)); // FAIL // to the end of it // and after that it is okay also assertEquals(3, monthOfDate(2013, 3, 1)); // OK } public int monthOfDate(int year, int month, int day) { Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, year); cal.set(Calendar.MONTH, month - 1); // just a simple get! but seems it is very important cal.get(Calendar.MONTH); // cal.set(Calendar.DAY_OF_MONTH, day); return cal.get(Calendar.MONTH) + 1; } } 

我想知道为什么会发生这种情况?

问题是你将在2013年1月30日开始使用日历。

然后你将年份设置为2013年 – 这不是问题。

然后,您将月份设置为1(即2月)。 你期望在这里发生什么? 实际发生的是它会记住它需要将月份设置为1,但不会重新计算实际时间值。 根据文档 (emphsis mine), 在您的调用中重新计算时间值:

set(f,value)将日历字段f更改为值。 此外,它设置内部成员变量以指示日历字段f已更改。 虽然日历字段f立即更改,但在下一次调用get(),getTime(),getTimeInMillis(),add()或roll()之前,不会重新计算日历的时间值(以毫秒为单位)。 因此,对set()的多次调用不会触发多次不必要的计算。 作为使用set()更改日历字段的结果,其他日历字段也可能会更改,具体取决于日历字段,日历字段值和日历系统。 此外,在重新计算日历字段之后,get(f)不一定返回通过调用set方法设置的值。 具体情况由具体的日历类决定。

当您尝试将“1月30日”更改为“2月30日”并强制进行计算时, 实际发生的情况是您最终将于3月2日在我的框中结束 – 但实际情况可能会有所不同。

最好的修复方法是:

  • 使用Calendar.set(year, month, date)来避免此排序问题
  • 首先使用Joda Time作为更合理的API。