Java日历 – 设置day_of_week后日期无法预测

我在JUnit测试中有以下代码,上周似乎工作失败了本周:

Calendar cal = Calendar.getInstance(); cal.set(2011, Calendar.JULY, 12); cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); // push the date to 15 System.out.println(cal.get(Calendar.DATE)); 

正如您可能从我的评论中推断出的那样,因为12日是星期二,所以在将DAY_OF_WEEK设置为星期五后,我预计日期为15。 但是,打印的值为22,导致测试失败。

但是,如果我按如下方式更改代码,并添加一个额外的调用来获取:

 Calendar cal = Calendar.getInstance(); cal.set(2011, Calendar.JULY, 12); System.out.println(cal.get(Calendar.DATE)); cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); // push the date to 15 System.out.println(cal.get(Calendar.DATE)); 

我得到了我期望的输出,12和15。

有人可以解释一下发生了什么,为什么这个测试上周有效?

首先要明白的是,Month + Day + DayOfWeek对日历没有任何意义。 日历将根据日期计算日期的真实值

年+月+日

要么

年+月+周W_K_MONTH + DAY_OF_WEEK

(或其他一些组合,如年份+一年中的某一天等)所以Date + DayOfWeek本身并不意味着太多。

要理解的第二件事是,当您设置Java日历时,它实际上不会重新计算绝对时间或更新相关字段,直到强制计算的操作发生。

第一次设置后,日历处于冲突状态。 月份和日期说它是7月12日,但是“月中的一周”和“星期几”仍然表示它是今天,无论今天是什么。 然后,您将周日设置为星期五。 所以今天的月份和日期说的是7月12日,但是“一个月的一周”和“一周的一天”字段表示它是’这个’周的星期五。

日历的规则说最近设置的字段在出现冲突时“获胜”,因此本周五的星期和星期几组合用于计算其他字段。

在中间插入一个“修复”它,因为它强制将日历的整个内部状态重新计算到7月12日星期二,然后再设置为星期五,因此没有内部冲突。 通过在您将星期几设置为星期五之前的重新计算,将“月中的一周”设置为包含7月12日的那一周。

编辑:很抱歉在两天后进行更改,在旧浏览器标签中注意到这一点,并且我认为我会扩展为未来googlers的希望帮助:

Jon在评论中工作的原因是他住在伦敦。 他的电脑认为星期一星期开始。 所以当被问到’这个’周的星期五时,7月17日星期日被问到时,它仍然在7月15日回复。 我提出这个问题是因为在不同的Locales中一周的第一天不同只是尝试在日历中使用WEEK_OF字段的另一种方式。

有错误4655637 (看起来类似于您的问题)。 我在最新的JDK6(Windows)下检查了该代码,在这两种情况下我都有15 。 顺便说一句:我建议明确地使用GregorianCalendar类,除非你想要别的东西(取决于你的语言环境)。

编辑: 官方文档 :

以下是日历字段的默认组合。 将使用由最近设置的单个字段确定的最新组合。

对于日期字段:

  YEAR + MONTH + DAY_OF_MONTH YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK YEAR + DAY_OF_YEAR YEAR + DAY_OF_WEEK + WEEK_OF_YEAR 

对于一天中的时间字段:

  HOUR_OF_DAY AM_PM + HOUR 

除了@Affe的明确答案外,以下组合似乎也有效 (截至@ GrzegorzSzpetkowski的错误报告链接)

日历需要以下字段组合来确定日期。

  MONTH + DAY_OF_MONTH MONTH + WEEK_OF_MONTH + DAY_OF_WEEK MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK DAY_OF_YEAR DAY_OF_WEEK + WEEK_OF_YEAR 

设置DAY_OF_WEEK时,日历需要设置周字段(WEEK_OF_MONTH,DAY_OF_WEEK_IN_MONTH或WEEK_OF_YEAR)。 因此,请避免在未设置其中一个周字段的情况下设置DAY_OF_WEEK。