通过jdk文档讲解Calendar类

本文通过jdk中文文档 来详细解读Calendar

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

1、定义:

Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEARMONTHDAY_OF_MONTHHOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距即格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000,格里高利历)的偏移量。

2、实例化:

与其他语言环境敏感类一样,Calendar 提供了一个类方法 getInstance,以获得此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar对象,其日历字段已由当前日期和时间初始化。

其实,getInstance方法得到的是Calendar的一个子类,GregorianCalendar(格里高利日历)。示例1:

 //1、 getInstance() 创建的事例, 其实是Calendar 的子类GreogrianCalendar
    public static void testgetInstance(){
        Calendar cal = Calendar.getInstance();
        if(cal instanceof GregorianCalendar){
            System.out.println(" 返回的是具体的格里高利日历");
            System.out.println("cal:"+cal);
        }
        /*  结果:
            cal:java.util.GregorianCalendar[time=1520574737770,areFieldsSet=true,
            areAllFieldsSet=true,lenient=true,
            zone=sun.util.calendar.ZoneInfo[
            id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],
            firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2018,MONTH=2,WEEK_OF_YEAR=10,
            WEEK_OF_MONTH=2,DAY_OF_MONTH=9,DAY_OF_YEAR=68,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=2,
            AM_PM=1,HOUR=1,HOUR_OF_DAY=13,MINUTE=52,SECOND=17,MILLISECOND=770,ZONE_OFFSET=28800000,
            DST_OFFSET=0]
         */

从结果可以看出,返回的是GregorianCalendar, 并且有详细的日历描述: 所有的字段均可以在源代码中或者文档中有解释。

3、宽松性(默认)

Calendar 有两种解释日历字段的模式,即 lenient (宽松)和 non-lenient(非宽松)。当 Calendar 处于 lenient 模式时,它可接受比它所生成的日历字段范围更大范围内的值。当 Calendar 重新计算日历字段值,以便由 get() 返回这些值时,所有日历字段都被标准化。例如,lenient 模式下的 GregorianCalendar 将 MONTH == JANUARYDAY_OF_MONTH == 32 解释为 February 1。

解释:宽松状态可以接受超过日历字段范围的值。 根据规定,日历合法值,一月份31天, 二月28,或29天。。。  如果设置日历 1月32号, 显然是不合理的,但是,在宽松条件下并且重新计算日历后,日历会吧超过的一天顺势往后推。变成2月1号。但在  non-lenient(非宽松)的状态下,就会报错,抛出异常 示例2:

 public static void testLenient(){
        Calendar cal3 = Calendar.getInstance();
        cal3.set(2000,0,32);    //2000-1-32    0是一月份,  月份从0开始,  源码定义了每个月份对应常量值。
        Date date = cal3.getTime();              // 重新计算
        System.out.println(date);

        //结果: Tue Feb 01 10:40:45 CST 2000    2000-2-1
    }
public static void testLenient2(){
        Calendar cal3 = Calendar.getInstance();
        cal3.setLenient(false);             //  设置为非宽松状态
        cal3.set(2000,0,32);
        System.out.println(cal3.getTime());

        //结果: Exception in thread "main" java.lang.IllegalArgumentException: DAY_OF_MONTH  ,非法字段
    }

这里还涉及到另外一个关键点:重新计算。

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

set(f, value) 将日历字段 f 更改为 value。此外,它设置了一个内部成员变量,以指示日历字段 f 已经被更改。尽管日历字段 f 是立即更改的,但是直到下次调用 get()getTime()getTimeInMillis()add() 或 roll() 时才会重新计算日历的时间值(以毫秒为单位)。因此,多次调用 set() 不会触发多次不必要的计算。

解释:  重新计算指的是: 计算整个日历,年月日,时分秒,每月的第几天,每年的第几天,每月的第几星期。。。。。这里的多次set()不会触发多次不必要的计算 应该理解为:不管set调用多少次,都不会触发整个日历的计算。 示例3:

public static void testSet() {
        Calendar cal = Calendar.getInstance();
        cal.set(1999, 7, 31, 0, 0, 0); // 1999-8-31
        cal.set(Calendar.MONTH, Calendar.SEPTEMBER);                            // 1999-9-31
        cal.set(Calendar.DAY_OF_MONTH, 30);                                    // 1999-9-30
        System.out.println("getTime:"+cal.getTime());

        //结果: Thu Sep 30 00:00:00 CST 1999    1999-9-30

    }

中途重新计算 示例4:

public static void testSet() {
        Calendar cal = Calendar.getInstance();
        cal.set(1999, 7, 31, 0, 0, 0); // 1999-8-31
        cal.set(Calendar.MONTH, Calendar.SEPTEMBER);                            // 1999-9-31

        System.out.println("getTime1:"+cal.getTime());      //Fri Oct 01 00:00:00 CST 1999

        cal.set(Calendar.DAY_OF_MONTH, 30);                                    // 1999-10-30
        System.out.println("getTime2:"+cal.getTime());

        //结果: :Sat Oct 30 00:00:00 CST 1999    1999-10-30

    }

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

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

理解:对于不合理的值,日历会自动计算,使其变成合理的日历格式。 如: 2000-7-32 ----> 2000-8-1.。 

另外, delta的值可以是负数,正数。 

如, 假设日历:2000-7-20,  add(Calendar.DAY_OF_MONTH,-10)后得,200-7-10

add(Calendar.DAY_OF_MONTH,-20) =2000-7-0,显然不合理,自动计算 得:2000-6-30 .

add(Calendar.DAY_OF_MONTH,12) = 2000-7-32, 显然不合理,自动计算 得:2000-8-1


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

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

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


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

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

理解: 比被修改的字段大的那个字段,不会发生改变。比如:修改的是DAY_OF_MONTH,那么MONTH不会改变。 不过,在0,30,31, 等敏感的地方,要注意了。

如: 2000-8-20

roll(Calendar.DAY_OF_MONTH,-20) , 得到 2000-8-31

roll(Calendar.DAY_OF_MONTH,12) ,得到 2000-8-01

从上面两个例子可以总结出

负数(减去),计算时,可以认为按照逆时针方向计算,来获取最接近的值。

所以在八月份的天数计算:0(20-20)的逆时针最接近值是31.

正数(加上),计算时,可以认为按照顺时针方向计算,来获取最接近的值。

所以在八月份的天数计算:32(20+12)的顺时针最接近值是01.


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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值