【java笔记】Calendar类的陷阱

===============================================

 2021/3/30_第1次修改                       ccb_warlock

 

===============================================

最近和同事讨论了日历类Calendar的问题,按照公司项目的代码定义,是获取当前服务器时间的上个月,但是调试下来获取的月份依旧是当前月。

我粗看代码,虽然写的累赘,但是好像没问题。由于刚学java没多久,于是查了资料后发现代码写的确实有问题,而这个问题只有指定的日期才能复现,为了方便今天赶紧记录下来。

 

陷阱1:月份设置

刚开始接触Calendar时,以为月份的设置和年份一样,假设我想设置该日历为2020年2月15号,初学者自然以为是按下面设置:

Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2020);
cal.set(Calendar.MONTH, 2); // 重点在这行
cal.set(Calendar.DAY_OF_MONTH, 15);

 

但是cal这个日历对象调用getTime方法后就会发现,cal的日期被设置成了2020年3月15号,而不是预想的2020年2月15号。

原因就出在Calendar类对月份的定义上。

Date类(java.util),从1月到12月,月份值从1开始对应。即1月的月份值为1、2月的月份值为2、12月的月份值为12。

Calendar类(java.util)作为取当前时间的推荐方案,从1月到12月,月份值却是从0开始对应。即1月的月份值为0、2月的月份值为1、12月的月份值为11。

 

所以根据假设的要求,想设置该日历为2020年2月15号,应该将Calendar.MONTH设置为1,如下:

Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2020);
cal.set(Calendar.MONTH, 1); // 2月的月份值为1
cal.set(Calendar.DAY_OF_MONTH, 15);

 

陷阱2:日期的设置

下面是简化后的代码:

private String getLastMonth(){
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) - 1);
    return sdf.format(cal.getTime());
}

 

乍一看好像没什么问题,cal取的是当前服务器的时间,而设置月份是原来的月份减1。

 

调试结果得到的还是这个月。

 

接着将函数拷贝一份将日期也加上,如下:

private String getLastMonthDay(){
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) - 1);
    return sdf.format(cal.getTime());
}

 

看到这个结果恍然大悟,服务器当前时间为2021年3月31号,然而上个月却没有31号,所以按照Calendar类的定义,日期就顺延到了2021年3月3号,然而getLastMonth方法定义的日期格式只保留了年、月,所以截取后就还是2021年3月,而不是预想的2021年2月了。

 

同理上面这种写法在大多数日期并不会触发bug,然而到了某些日期(3.30,3.31,5.31,7.31,10.31,12.31)时调用就会复现该问题。

 

 

 

posted @ 2021-03-31 00:10  粽先生  阅读(127)  评论(0编辑  收藏  举报