先说下我的情况:
/**
* 获得某一年某一季度的最后一天日期
* @param year
* @param quarter
* 该方法和lastDayOfQuarter方法功能一样,只不过后者假设在10月31号执行代码时,会出现异常,日期不对。
* @return
* @author yutao
* @date 2016年10月13日下午1:24:06
*/
public static Date lastDayOfQuarter(Integer year, Integer quarter) {
if (quarter < 1 || quarter > 4) {
return null;
}
Calendar cal = Calendar.getInstance();
if (year != null && year > 0) {
cal.set(Calendar.YEAR, year);
}
if (quarter == 1) {
cal.set(Calendar.MONTH, 2);
} else if (quarter == 2) {
cal.set(Calendar.MONTH, 5);
} else if (quarter == 3) {
cal.set(Calendar.MONTH, 8);
} else {
cal.set(Calendar.MONTH, 11);
}
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 000);
return cal.getTime();
}
假设现在我们有这么一个main方法:
public static void main(String[] args) {
System.out.println(DateUtil.dateToString(lastDayOfQuarter(2016,1), "yyyy-MM-dd"));
System.out.println(DateUtil.dateToString(lastDayOfQuarter(2016,2), "yyyy-MM-dd"));
System.out.println(DateUtil.dateToString(lastDayOfQuarter(2016,3), "yyyy-MM-dd"));
System.out.println(DateUtil.dateToString(lastDayOfQuarter(2016,4), "yyyy-MM-dd"));
}
正常情况下,它应该打印出:
2016-03-31
2016-06-30
2016-09-30
2016-12-31
但如果是在31号去执行这段代码,就变成了:
2016-03-31
2016-07-01
2016-10-01
2016-12-31
我试过,只要是31号,执行该代码,就出现这个问题。
已经一系列调试,得出结论:首先你要知道set()方法是有延迟,就是当你写这么一段代码时
`cal.set(Calendar.MONTH, 5);` 它是没有立即生效的。
当调用get()、getTime()、getTimeInMillis()、add() 或 roll()方法时,
才会重新计算日历的时间值(以毫秒为单位)。
所以当我们在31号调用`lastDayOfQuarter(2016,2), "yyyy-MM-dd")`
走到这句时:
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
虽然它走了
else if (quarter == 2) {
cal.set(Calendar.MONTH, 5);
}
也就是说,此处已经把MONTH设置为5(也就是6月),但是由于此时没有刷新calender,
`Calendar.DAY_OF_MONTH`它获取到的是今天(10月31号)的日期,DAY为31号。
也就是说Calendar里面目前被set成了6月31号。
又由于使用的是set方法,它会自动调整日期,变成7月1号。
所以getActualMaximum获得的最大值为31。
看了下文档该方法解释:
`返回指定日历字段可能拥有的最大值。`
(我猜测,由于今天是31号,所以Calendar中DAY_OF_MONTH保存的依然也是31号。
也就是说它里面目前被set成了6月31号,即7月1号。当调用:
`cal.getActualMaximum(Calendar.DAY_OF_MONTH))`的时候,
获取到的是7月份的最大值。后来我做了测试验证了我的猜测是对的)
如果使用的是cal.add()的方法,则月份不会变,而是调整日期。
也就是变成6月30号。所以一般我们都使用set()。
解决办法上面问题也很简单,在调用`getActualMaximum`方法之前,把`DAY_OF_MONTH`,
设置为1号(反正别为31号,考虑二月份的问题,最后还是设置为1号)。
cal.set(Calendar.DAY_OF_MONTH, 1);
获取某个最后一天还可以有另一种写法:
上面的代码关键的地方是使用`getActualMaximum(Calendar.DAY_OF_MONTH)`来获取值。
我们也可以现获取后一个月的1号日期,再使用add的方法减一天就可以啦。
/**
* 获得某一年某一季度的最后一天日期
* @param year
* @param quarter
* @return
* @author yutao
* @date 2016年10月13日下午1:24:06
*/
public static Date lastDayOfQuarterByAdd(Integer year, Integer quarter) {
if (quarter < 1 || quarter > 4) {
return null;
}
Calendar cal = Calendar.getInstance();
if (year != null && year > 0) {
cal.set(Calendar.YEAR, year);
}
if (quarter == 1) {
cal.set(Calendar.MONTH, 3);
} else if (quarter == 2) {
cal.set(Calendar.MONTH, 6);
} else if (quarter == 3) {
cal.set(Calendar.MONTH, 9);
} else {
cal.set(Calendar.MONTH, 0);
}
// cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
cal.set(Calendar.DAY_OF_MONTH, 1);//设置该月1号
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 000);
cal.add(Calendar.DATE, -1);//使用add减一天
return cal.getTime();
}