最近在写周期性闹钟的功能,需要对时间进行处理,频繁使用Calendar,Calendar 的使用过程中又有一些陷阱。这里对Calendar进行总结一下。

Calendar 可以看作是一个抽象类。
它的实现,采用了设计模式中的工厂方法。表现在:当我们获取Calendar实例时,Calendar会根据传入的参数来返回相应的Calendar对象。获取Calendar实例,有以下两种方式:
(1) 当我们通过 Calendar.getInstance() 获取日历时,默认的是返回的一个GregorianCalendar对象。
GregorianCalendar是Calendar的一个实现类,它提供了世界上大多数国家/地区使用的标准日历系统。
(2) 当我们通过 Calendar.getInstance(TimeZone timezone, Locale locale) 或 Calendar.getInstance(TimeZone timezone) 或 Calendar.getInstance(Locale locale)获取日历时,是返回“对应时区(zone) 或 地区(local)等所使用的日历”。
Calendar的使用无非是对年月日时分秒等信息的操作,Calendar实际上是存了一个时间。
Calendar 各个字段的定义和初始化

Calendar 的“年、月、日、星期、时、分、秒”这些信息,一共是17个字段。
我们使用Calendar,无非是就是使用这17个字段。它们的定义如下:
(字段0) public final static int ERA = 0;
说明:纪元。
取值:只能为0 或 1。0表示BC(“before Christ”,即公元前),1表示AD(拉丁语“Anno Domini”,即公元)。(字段1) public final static int YEAR = 1;
说明:年。(字段2) public final static int MONTH = 2;
说明:月
取值:可以为,JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, UNDECIMBER。其中第一个月是 JANUARY,它为 0。(字段3) public final static int WEEK_OF_YEAR = 3;
(字段4) public final static int WEEK_OF_MONTH = 4;
说明:当前日期在本月中对应第几个星期。一个月中第一个星期的值为 1。(字段5) public final static int DATE = 5;
说明:日。一个月中第一天的值为 1。(字段5) public final static int DAY_OF_MONTH = 5;
说明:同“DATE”,表示“日”。(字段6) public final static int DAY_OF_YEAR = 6;
说明:当前日期在本年中对应第几天。一年中第一天的值为 1。(字段7) public final static int DAY_OF_WEEK = 7;
说明:星期几。
取值:可以为,SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY 和 SATURDAY。其中,SUNDAY为1,MONDAY为2,依次类推。(字段8) public final static int DAY_OF_WEEK_IN_MONTH = 8;
说明:当前月中的第几个星期。(字段9) public final static int AM_PM = 9;
说明:上午 还是 下午
取值:可以是AM 或 PM。AM为0,表示上午;PM为1,表示下午。(字段10) public final static int HOUR = 10;
说明:指示一天中的第几小时。
HOUR 用于 12 小时制时钟 (0 - 11)。中午和午夜用 0 表示,不用 12 表示。(字段11) public final static int HOUR_OF_DAY = 11;
说明:指示一天中的第几小时。
HOUR_OF_DAY 用于 24 小时制时钟。例如,在 10:04:15.250 PM 这一时刻,HOUR_OF_DAY 为 22。(字段12) public final static int MINUTE = 12;
说明:一小时中的第几分钟。
例如,在 10:04:15.250 PM这一时刻,MINUTE 为 4。(字段13) public final static int SECOND = 13;
说明:一分钟中的第几秒。
例如,在 10:04:15.250 PM 这一时刻,SECOND 为 15。(字段14) public final static int MILLISECOND = 14;
说明:一秒中的第几毫秒。
例如,在 10:04:15.250 PM 这一时刻,MILLISECOND 为 250。(字段15) public final static int ZONE_OFFSET = 15;
说明:毫秒为单位指示距 GMT 的大致偏移量。(字段16) public final static int DST_OFFSET = 16;
说明:毫秒为单位指示夏令时的偏移量。public final static int FIELD_COUNT = 17;
这17个字段是保存在int数组中。定义如下:// 保存这17个字段的数组
protected int  fields[];
// 数组的定义函数
protected Calendar(TimeZone zone, Locale aLocale)
{// 初始化“fields数组”fields = new int[FIELD_COUNT];isSet = new boolean[FIELD_COUNT];stamp = new int[FIELD_COUNT];this.zone = zone;setWeekCountData(aLocale);
}protected Calendar(TimeZone zone, Locale aLocale) 这是Calendar的构造函数。
它会被它的子类的构造函数调用到,从而新建“保存Calendar的17个字段数据”的数组。

问题:
这里有点不懂的是:public final static int WEEK_OF_MONTH = 4 和 public final static int DAY_OF_WEEK_IN_MONTH = 8 这两个字段的区别目前还不知道。
已解决:
public final static int WEEK_OF_MONTH = 4:这个属性是指一个月中从星期天开始计算第一个星期,比如2018年8月的第一天是周三,那么8月的第一个星期的范围是1-4号,也就是周三到周六。
public final static int DAY_OF_WEEK_IN_MONTH = 8: 这个属性不会随着某个月的第一天的星期而改变。DAY_OF_WEEK_IN_MONTH = 1时对弈的是这个月的1-7号,2对应的是8-14号,以此类推。


Calendar的17个字段的公共接口
Calendar的这17个字段,都支持下面的公共函数接口。 这些公共接口的使用示例,请参考CalendarTest.java 示例中的 testAllCalendarSections() 函数。
(1) getMaximum(int field)
作用:获取“字段的最大值”。注意“对比它和 getActualMaximum() 的区别”。 示例:以“MONTH”字段来说。使用方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 获取MONTH的最大值
int max = cal.getMaximum(Calendar.MONTH);
若要获取其它字段的最大值,只需要将示例中的MONTH相应的替换成其它字段名即可。

(2) getActualMaximum(int field)
作用:获取“当前日期下,该字段的最大值”。 示例:以“MONTH”字段来说。使用方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 获取MONTH的最大值
int max = cal.getMaximum(Calendar.MONTH);
若要获取其它字段的最大值,只需要将示例中的MONTH相应的替换成其它字段名即可。

注意:对比getActualMaximum() 和 getMaximum() 的区别。参考下面的对比示例,
A、 getMaximum() 获取的“字段最大值”,是指在综合所有的日期,在所有这些日期中得出的“字段最大值”。
例如,getMaximum(Calendar.DATE)的目的是“获取‘日的最大值’”。综合所有的日期,得出一个月最多有31天。因此,getMaximum(Calendar.DATE)的返回值是“31”!
B、 getActualMaximum() 获取的“当前日期时,该字段的最大值”。
例如,当日期为2013-09-01时,getActualMaximum(Calendar.DATE)是获取“日的最大值”是“30”。当前日期是9月份,而9月只有30天。因此,getActualMaximum(Calendar.DATE)的返回值是“30”!
(3) getMinimum(int field)
作用:获取“字段的最小值”。注意“对比它和 getActualMinimum() 的区别”。 示例:以“MONTH”字段来说。使用方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 获取MONTH的最小值
int min = cal.getMinimum(Calendar.MONTH);
若要获取其它字段的最小值,只需要将示例中的MONTH相应的替换成其它字段名即可。

(4) getActualMinimum(int field)

作用:获取“当前日期下,该字段的最小值”。 示例:以“MONTH”字段来说。使用方法为:
// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 获取MONTH的最小值
int min = cal.getMinimum(Calendar.MONTH);
若要获取其它字段的最小值,只需要将示例中的MONTH相应的替换成其它字段名即可。

注意:在Java默认的Calendar中,虽然 getMinimum() 和 getActualMinimum() 的含义不同;但是,它们的返回值是一样的。因为Calendar的默认是返回GregorianCalendar对象,而在GregorianCalendar.java中,getMinimum() 和 getActualMinimum() 返回值一样。
(5) get(int field)
作用:获取“字段的当前值”。获取field字段的当前值。 示例:以“MONTH”字段来说。“获取MONTH的当前值”的方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 获取“cal日历”的当前MONTH值
int MONTH = cal.get(Calendar.MONTH);
若要获取其它字段的当前值,只需要将示例中的MONTH相应的替换成其它字段名即可。

(6) set(int field, int value)
作用:设置“字段的当前值”。设置field字段的当前值为value 示例:以“MONTH”字段来说。“设置MONTH的当前值”的方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 设置“cal日历”的当前MONTH值为 1988年
cal.set(Calendar.MONTH, 1988);

说明:
A、1988 是想要设置的MONTH的当前值。这个设置值必须是整数。
B、若要设置其它字段的当前值,只需要将示例中的MONTH相应的替换成其它字段名即可。
(7) add(int field, int value)
作用:给“字段的当前值”添加值。给field字段的当前值添加value。 示例:以“MONTH”字段来说。方法如下:

// 获取Calendar实例,并设置日期为“2013-09-01”
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2013);
cal.set(Calendar.MONTH, 8);
cal.set(Calendar.DATE, 1);
// 给“cal日历”的当前MONTH值 “添加-10”
cal.add(Calendar.MONTH, -10);

说明:
A、 -10 是添加值。
添加值可以为正数,也可以是负数。
正数表示将日期增加,负数表示将日期减少。
假设:现在cal的值是“2013-09-01”,现在我们将MONTH字段值增加-10。得到的结果是:“2012-10-01”。
为什么会这样呢?“2013-09-01”增加-10,也就是将日期向前减少10个月;得到的结果就是“2012-10-01”。
B、 Calendar的17个字段中:除了回滚Calendar.ZONE_OFFSET时,会抛出IllegalArgumentException异常;其它的字段都支持该操作。
C、 若要设置其它字段的当前值,只需要将示例中的MONTH相应的替换成其它字段名即可。
(8) roll(int field, int value)
作用:回滚“字段的当前值” 示例:以“MONTH”字段来说。“回滚MONTH的当前值”的方法为:

// 获取Calendar实例,并设置日期为“2013-09-01”
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2013);
cal.set(Calendar.MONTH, 8);
cal.set(Calendar.DATE, 1);
// 将“cal日历”的当前MONTH值 “向前滚动10”
cal.roll(Calendar.MONTH, -10);

说明:
A、 -10 是回滚值。
当回滚值是负数时,表示将当前字段向前滚;
当回滚值是正数时,表示将当前字段向后滚。
回滚Calendar中某一字段时,不更改更大的字段!
这是roll()与add()的根据区别!add()可能会更改更大字段,比如“使用add()修改‘MONTH’字段,可能会引起‘YEAR’字段的改变”;但是roll()不会更改更大的字段,例如“使用roll()修改‘MONTH’字段,不回引起‘YEAR’字段的改变。”
假设:现在cal的值是“2013-09-01”,现在我们将MONTH字段值增加-10。得到的结果是:“2013-10-01”。
为什么会这样呢?这就是因为“回滚”就是“在最小值和最大值之间来回滚动”。本例中,MONTH是9月,前回滚10,得到的值是10月,但是roll()不会改变“比MONTH”更大的字段,所以YEAR字段不会改变。所以结果是“2013-10-01”。
B、 Calendar的17个字段中:除了回滚Calendar.ZONE_OFFSET时,会抛出IllegalArgumentException异常;其它的字段都支持该操作。
C、 若要设置其它字段的当前值,只需要将示例中的MONTH相应的替换成其它字段名即可。
(9) clear(int field)
作用:清空“字段的当前值”。所谓清空,实际上是将“field”的值设置为0;若field最小值为1,则设置为1。 示例:以“MONTH”字段来说。“清空MONTH”的方法为:

// 获取Calendar实例,并设置日期为“9月”
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MONTH, 9);
// 清空MONTH
cal.clear(Calendar.MONTH);
若要清空其它字段,只需要将示例中的MONTH相应的替换成其它字段名即可。

(10) isSet(int field)
作用:判断“字段field”是否被设置。若调用clear()清空之后,则field变为“没有设置状态”。 示例:以“MONTH”字段来说。“判断MONTH是否被设置”的方法为:

// 获取Calendar实例
Calendar cal = Calendar.getInstance();
// 判断MONTH是否被设置
boolean bset = cal.isSet(Calendar.MONTH);

若要判断其它字段,只需要将示例中的MONTH相应的替换成其它字段名即可。

  1. Calendar的其它函数
    (1) 日期比较函数
    Calendar的比较函数,主要有以下几个:
// 比较“当前Calendar对象”和“calendar” 的日期、时区等内容是否相等。
boolean equals(Object object)
// 当前Calendar对象 是否 早于calendar
boolean before(Object calendar)
// 当前Calendar对象 是否 晚于calendar
boolean after(Object calendar)
// 比较“当前Calendar对象”和“calendar”。
// 若 早于 “calendar” 则,返回-1
// 若 相等, 则,返回0
// 若 晚于 “calendar” 则,返回1
int compareTo(Calendar anotherCalendar) 

这些函数的使用示例,请参考CalendarTest.java示例中的 testComparatorAPIs() 函数。
示例:假设cal1 和 cal2 都是Calendar的两个对象。

// 设置“Calendar的宽容度”
void setLenient(boolean value)
// 获取“Calendar的宽容度”
boolean isLenient()

(2) “宽容”函数

// 设置“Calendar的宽容度”
void setLenient(boolean value)
// 获取“Calendar的宽容度”
boolean isLenient()

这些函数的使用示例,请参考CalendarTest.java示例中的 testLenientAPIs() 函数。
说明:
Calendar 有两种解释日历字段的模式,即 lenient 和 non-lenient。
A、 当 Calendar 处于 lenient 模式时,它可接受比它所生成的日历字段范围更大范围内的值。当 Calendar 重新计算日历字段值,以便由 get() 返回这些值时,所有日历字段都被标准化。
例如,lenient 模式下的 GregorianCalendar 将 MONTH == JANUARY、DAY_OF_MONTH == 32 解释为 February 1。
B、 当 Calendar 处于 non-lenient 模式时,如果其日历字段中存在任何不一致性,它都会抛出一个异常。
例如,GregorianCalendar 总是在 1 与月份的长度之间生成 DAY_OF_MONTH 值。如果已经设置了任何超出范围的字段值,那么在计算时间或日历字段值时,处于 non-lenient 模式下的 GregorianCalendar 会抛出一个异常。
注意:在(02)步骤中的异常,在使用set()时不会抛出,而需要在使用get()、getTimeInMillis()、getTime()、add() 和 roll() 等函数中才抛出。因为set()只是设置了一个修改标志,而get()等方法才会引起时间的重新计算,此时才会抛出异常!


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 规则 1 将 MONTH 字段设置为 September,因为向 August 添加 13 个月得出的就是下一年的 September。因为在 GregorianCalendar 中,DAY_OF_MONTH 不可能是 9 月 31 日,所以 add 规则 2 将 DAY_OF_MONTH 设置为 30,即最可能的值。尽管它是一个更小的字段,但不能根据规则 2 调整 DAY_OF_WEEK,因为在 GregorianCalendar 中的月份发生变化时,该值也需要发生变化。

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

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

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

Calendar的基本使用和属性说明相关推荐

  1. 【Java6】Date类/Calendar类,System类/Math类,包装类,集合,泛型,内部类

    文章目录 1.Date类:getTime(),SimpleDateFormat 2.Calendar类:只有子类对象才能向上转型 3.System类:System.exit(0) 4.Math类:ce ...

  2. java calendar getactualmaximum_Java Calendar

    1 什么是Calendar? Calendar(读音:[ˈkælɪndə(r)] )是java util包下的一个工具类,提供了不同日期格式的处理. Calendar是一个抽象类,不能用构造器来创建, ...

  3. python中calendar模块_python calendar模块

    calendar模块,即日历模块,提供了对日期的一些操作方法,和生成日历的方法. calendar模块中提供了三大类: 一.calendar.Calendar(firstweekday=0) 该类提供 ...

  4. 设计模式 笔记4 | 简单工厂模式 在源码中的应用 | Calendar 日历 | 源码浅析 | 使用总结 | 建造者模式

    文章目录 一.Calendar 日历类 1.1 内部属性 1.2 设置时间属性值 1.3 获取时间属性 1.4 使用 Calander 计算时间 二.Calender 类中的设计模式 2.1 简单工厂 ...

  5. Java中的日期类Calendar的常用方法

     Calendar类可以使用set方法设置时间,get方法获取时间.可以用于 获取设置 年,月,日,星期 一.实例化Calendar  使用Calendar的静态方法getInstance()实例化对 ...

  6. python基础语法合集-Python基础语法合集.zip

    [实例简介]精心整理的Python基础语法合集,变量,循环,输入输出等等都有,主要是知道概念和怎么用的 如果打开文件后有文字变成符号的,先把字体改为宋体就正常了 [实例截图] [核心代码] 目录 了解 ...

  7. ASP.NET中的Theme和Skin

    Theme是允许你定义页面和控件外观的许多属性的集合, 应用这些属性可以在web application中的页面里, 或者是整个web application, 或者是一个服务器上跨越多个web ap ...

  8. VS.NET2005中的WEBPART初步(一)

    VS.NET2005正式发行,给一帆的影响是什么呢?最大的收获就是可以做真正的.NET穷人了,一帆天生的就不是读书的料,可是又偏偏赶上了电脑的新潮...         VS.NET2005中的WEB ...

  9. vs2005之主题与皮肤的学习

    什么是皮肤?皮肤是应用到一个控件上的样式信息,在vs2005中它的内容保存在.skin文件中,可以通过对皮肤用SkinID命名,然后控件在使用皮肤时,设置它的SkinID属性来达到控制控件的样式的目的 ...

最新文章

  1. Develop chrome extension study
  2. C++ 注册表取值 按行读取txt文件 时间差天数 格林威治时间转标准时间
  3. LogManager分析
  4. sql子查询示例_学习SQL:SQL查询示例
  5. 思科智能交换机受多个严重漏洞影响
  6. 线性支持向量机与软间隔最大化
  7. 录ppt的时候录光标_光标的使用.ppt
  8. 三菱伺服自动调谐_三菱伺服参数设置调试软件MR Configurator2 Ver 1.70Y
  9. 基于PCL的QT开发(两个月内更新完)
  10. 写论文引用参考文献详细教程
  11. STN( Spatial Transformer Network)
  12. canoe模拟发有checksum报文_CANoe 入门 Step by step系列(三)简单例子的剖析
  13. 开题报告:基于java医院在线预约挂号系统 毕业设计论文开题报告模板
  14. 施工员报考建筑八大员报考提高工程建筑施工人员安全生产意识
  15. Spring Boot实践 | 利用Spring Security快速搞定权限控制
  16. mysql bigint 长度最大多少位_bigint 有多少位
  17. 【数据分析 R语言实战】学习笔记 第六章 参数估计与R实现(上)
  18. “Derivative of state ‘1‘ in block ‘X/Y/Integrator‘ at time 0.55 is not finite“类问题解决办法
  19. react中的this.state与this.setState的区别
  20. MySQL数据库30条规范解读

热门文章

  1. 酒精测试仪方案开发PCBA
  2. 南方科技大学计算机系图灵班,【独家】上了大学就自由了,老师管得少! 这所高校:喔?是吗?...
  3. PCDATA和CDATA的区别究竟是什么呢?
  4. JAVA基础编程——IO编程
  5. GateWay入门Demo
  6. 全网最新最细最全最牛的jmeter接口测试,性能测试,没有之一,如有雷同纯属抄袭。
  7. prctl()和pthread_setname_np()函数-设置线程名称
  8. 逻辑回归原理及推导过程
  9. vue中获取/操作组件中的dom元素
  10. C++实现输出一个集合的全部子集