Calendar.set用法的深入探讨

一般来说,用Calendar做日期处理的时候,都习惯于使用add方法:

Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.SECOND, 1);

 

最近代码Review的时候看到有人用set方法来做日期的处理:

Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + month);

 

于是第一时间跳出来,义正词严的告诉他写错了,可惜马上就被BS回来了,因为见下面的测试代码:

  Calendar cal = Calendar.getInstance();
  cal.set(2009, 02, 15);
  Date testDate = cal.getTime();
  System.out.println(testDate);
  
  Calendar calendar = Calendar.getInstance();
  calendar.setTime(testDate);
  calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1);
  Date testDate2 = calendar.getTime();
  System.out.println(testDate2);

输出结果:

Sun Mar 15 11:21:18 CST 2009
Wed Apr 15 11:21:18 CST 2009

也就是说,set方法也可以用于字段操作!!

 

好在我是个勇于承认错误的人,在道歉后,得到了对方的原谅,但是心中一直无法接受这个事实,JDK也太土了吧,设计这么两个方法难道就没有任何区别吗?那还要add方法干吗?

 

带着上面的问题,今天又开始了代码Review,越想越想不通,没道理啊,算了,查查JDK文档吧,下面是JDK中Calendar类的注释:

 


 

可以使用三种方法更改日历字段:set()add()roll()

 

set(f, value) 将日历字段 f 更改为 value。此外,它设置了一个内部成员变量,以指示日历字段 f 已经被更改。尽管日历字段 f 是立即更改的,但是直到下次调用 get()getTime()getTimeInMillis()add()roll() 时才会重新计算日历的时间值(以毫秒为单位)。因此,多次调用 set() 不会触发多次不必要的计算。使用 set() 更改日历字段的结果是,其他日历字段也可能发生更改,这取决于日历字段、日历字段值和日历系统。此外,在重新计算日历字段之后,get(f) 没必要通过调用 set 方法返回 value 集合。具体细节是通过具体的日历类确定的。

示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 set(Calendar.MONTH, Calendar.SEPTEMBER) 将该日期设置为 1999 年 9 月 31 日。如果随后调用 getTime(),那么这是解析 1999 年 10 月 1 日的一个暂时内部表示。但是,在调用 getTime() 之前调用 set(Calendar.DAY_OF_MONTH, 30) 会将该日期设置为 1999 年 9 月 30 日,因为在调用 set() 之后没有发生重新计算。

add(f, delta)delta 添加到 f 字段中。这等同于调用 set(f, get(f) + delta),但要带以下两个调整:

Add 规则 1。调用后 f 字段的值减去调用前 f 字段的值等于 delta,以字段 f 中发生的任何溢出为模。溢出发生在字段值超出其范围时,结果,下一个更大的字段会递增或递减,并将字段值调整回其范围内。

Add 规则 2。如果期望某一个更小的字段是不变的,但让它等于以前的值是不可能的,因为在字段 f 发生更改之后,或者在出现其他约束之后,比如时区偏移量发生更改,它的最大值和最小值也在发生更改,然后它的值被调整为尽量接近于所期望的值。更小的字段表示一个更小的时间单元。HOUR 是一个比 DAY_OF_MONTH 小的字段。对于不期望是不变字段的更小字段,无需进行任何调整。日历系统会确定期望不变的那些字段。

此外,与 set() 不同,add() 强迫日历系统立即重新计算日历的毫秒数和所有字段。

示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 add(Calendar.MONTH, 13) 将日历设置为 2000 年 9 月 30 日。Add 规则 1MONTH 字段设置为 September,因为向 August 添加 13 个月得出的就是下一年的 September。因为在 GregorianCalendar 中,DAY_OF_MONTH 不可能是 9 月 31 日,所以 add 规则 2DAY_OF_MONTH 设置为 30,即最可能的值。尽管它是一个更小的字段,但不能根据规则 2 调整 DAY_OF_WEEK,因为在 GregorianCalendar 中的月份发生变化时,该值也需要发生变化。

roll(f, delta)delta 添加到 f 字段中,但不更改更大的字段。这等同于调用 add(f, delta),但要带以下调整:

Roll 规则。在完成调用后,更大的字段无变化。更大的字段表示一个更大的时间单元。DAY_OF_MONTH 是一个比 HOUR 大的字段。

示例:请参阅 GregorianCalendar.roll(int, int)

使用模型。为了帮助理解 add()roll() 的行为,假定有一个用户界面组件,它带有用于月、日、年和底层 GregorianCalendar 的递增或递减按钮。如果从界面上读取的日期为 1999 年 1 月 31 日,并且用户按下月份的递增按钮,那么应该得到什么?如果底层实现使用 set(),那么可以将该日期读为 1999 年 3 月 3 日。更好的结果是 1999 年 2 月 28 日。此外,如果用户再次按下月份的递增按钮,那么该日期应该读为 1999 年 3 月 31 日,而不是 1999 年 3 月 28 日。通过保存原始日期并使用 add()roll(),根据是否会影响更大的字段,用户界面可以像大多数用户所期望的那样运行。


 

仔细研究上述注释,终于发现了一丝蛛丝马迹,add方法跟set还是有区别的,应该是在自然日的处理上有所不同,于是有了下面的代码:

  Calendar cal = Calendar.getInstance();
  cal.set(2009, 02, 31);
  Date testDate = cal.getTime();
  System.out.println(testDate);
  

  Calendar cal1 = Calendar.getInstance();
  cal1.setTime(testDate);
  cal1.add(Calendar.MONTH, 1);
  Date testDate1 = cal1.getTime();
  System.out.println(testDate1);

输出如下:

Tue Mar 31 11:27:41 CST 2009
Thu Apr 30 11:27:41 CST 2009

 

OK,结论出来了,add方法会处理自然月的增减来处理日期部分,set方法却只是当成30天来处理。事实证明原来的感觉是对的,希望以后少碰到类似错误!!

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Calendar类的set()方法用于设置Calendar对象的年月日等信息。例如,通过设置Calendar对象的DATE字段可以将日期设置为指定的值,如本月3号:calendar.set(Calendar.DATE, 3)。如果想将日期设置为本月最后一天,可以使用getActualMaximum()方法获取本月的最大日期值,并将其设置为DATE字段的值:calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE))。同样,如果想将日期设置为本月的第一天,可以使用getActualMinimum()方法获取本月的最小日期值,并将其设置为DATE字段的值:calendar.set(Calendar.DATE, calendar.getActualMinimum(Calendar.DATE))。 另外,通过get()方法结合Calendar.DAY_OF_WEEK参数,可以获取Calendar对象所代表的日期对应的星期几。需要注意的是,Calendar类中的月份是从0开始计数的,即0代表一月,11代表十二月。 以下是一个示例代码,展示了如何使用Calendar类的set()方法和get()方法进行日期的设置和获取: ```java // 创建一个Calendar对象 Calendar calendar = Calendar.getInstance(); // 设置年份为2022 calendar.set(Calendar.YEAR, 2022); // 设置月份为十二月(注意:月份从0开始计数) calendar.set(Calendar.MONTH, 11); // 设置日期为20号 calendar.set(Calendar.DATE, 20); // 获取设置后的年份、月份和日期 int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH); int date = calendar.get(Calendar.DATE); // 获取日期对应的星期几(注意:星期天为1,星期六为7) int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); // 输出结果 System.out.println("年份:" + year); System.out.println("月份:" + (month + 1)); System.out.println("日期:" + date); System.out.println("星期:" + dayOfWeek); ``` 以上示例代码演示了使用set()方法设置年份、月份和日期,并使用get()方法获取日期对应的星期几。需要注意的是,月份需要加1才能得到正确的值。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Calendar类的使用](https://blog.csdn.net/zeng_z/article/details/120199878)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [calendar.java](https://download.csdn.net/download/dreggler/16262363)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值