Calendar在有31号的月份时注意事项

先说下我的情况:

    /**
     * 获得某一年某一季度的最后一天日期
     * @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();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山鬼谣me

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值