Java中的Calendar日历API用法完全解析
第一部分 calendar介绍
calendar 定义:
public abstract class calendar implements serializable, cloneable, comparable<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)等所使用的日历”。
例如,若是日本,则返回japaneseimperialcalendar对象。
参考如下代码:
public static calendar getinstance() { // 调用createcalendar()创建日历 calendar cal = createcalendar(timezone.getdefaultref(), locale.getdefault()); cal.sharedzone = true; return cal; } public static calendar getinstance(timezone zone) { // 调用createcalendar()创建日历 return createcalendar(zone, locale.getdefault()); } public static calendar getinstance(locale alocale) { // 调用createcalendar()创建日历 calendar cal = createcalendar(timezone.getdefaultref(), alocale); cal.sharedzone = true; return cal; } public static calendar getinstance(timezone zone, locale alocale) { // 调用createcalendar()创建日历 return createcalendar(zone, alocale); } private static calendar createcalendar(timezone zone, locale alocale) { // (01) 若地区是“th”,则返回buddhistcalendar对象 // (02) 若地区是“jp”,则返回japaneseimperialcalendar对象 if ("th".equals(alocale.getlanguage()) && ("th".equals(alocale.getcountry()))) { return new sun.util.buddhistcalendar(zone, alocale); } else if ("jp".equals(alocale.getvariant()) && "jp".equals(alocale.getcountry()) && "ja".equals(alocale.getlanguage())) { return new japaneseimperialcalendar(zone, alocale); } // (03) 否则,返回gregoriancalendar对象 return new gregoriancalendar(zone, alocale); }
当我们获取calendar实例之后,就可以通过calendar提供的一些列方法来操作日历。
第二部分 calendar的原理和思想
我们使用calendar,无非是操作calendar的“年、月、日、星期、时、分、秒”这些字段。下面,我们对这些字段的的来源、定义以及计算方法进行学习。
1. calendar 各个字段值的来源
我们使用calendar,无非是使用“年、月、日、星期、时、分、秒”等信息。那么它是如何做到的呢? 本质上,calendar就是保存了一个时间。如下定义:
// time 是当前时间,单位是毫秒。 // 它是当前时间距离“january 1, 1970, 0:00:00 gmt”的差值。 protected long time;
calendar就是根据 time 计算出 “calendar的年、月、日、星期、时、分、秒”等等信息。
2. 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;
说明:当前日期在本年中对应第几个星期。一年中第一个星期的值为 1。
(字段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;
说明:当前月中的第几个星期。
取值:day_of_month 1 到 7 总是对应于 day_of_week_in_month 1;8 到 14 总是对应于 day_of_week_in_month 2,依此类推。
(字段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个字段数据”的数组。
3. calendar 各个字段值的计算
下面以get(int field)为例,简要的说明calendar的17个字段的计算和操作。 get(int field)是获取“field”字段的值。它的定义如下:
public int get(int field) { // 计算各个字段的值 complete(); // 返回field字段的值 return internalget(field); }
说明:get(int field)的代码很简单。先通过 complete() 计算各个字段的值,然后在通过 internalget(field) 返回“field字段的值”。
complete() 的作用就是计算calendar各个字段的值。它定义在calendar.java中,代码如下:
protected void complete() { if (!istimeset) updatetime(); if (!arefieldsset || !areallfieldsset) { computefields(); // fills in unset fields areallfieldsset = arefieldsset = true; } } private void updatetime() { computetime(); istimeset = true; } updatetime() 调用到的 computetime() 定义在 calendar.java的实现类中。下面,我列出gregoriancalendar.java中computetime()的实现: protected void computetime() { // in non-lenient mode, perform brief checking of calendar // fields which have been set externally. through this // checking, the field values are stored in originalfields[] // to see if any of them are normalized later. if (!islenient()) { if (originalfields == null) { originalfields = new int[field_count]; } for (int field = 0; field < field_count; field++) { int value = internalget(field); if (isexternallyset(field)) { // quick validation for any out of range values if (value < getminimum(field) || value > getmaximum(field)) { throw new illegalargumentexception(getfieldname(field)); } } originalfields[field] = value; } } // let the super class determine which calendar fields to be // used to calculate the time. int fieldmask = selectfields(); // the year defaults to the epoch start. we don't check // fieldmask for year because year is a mandatory field to // determine the date. int year = isset(year) ? internalget(year) : epoch_year; int era = internalgetera(); if (era == bce) { year = 1 - year; } else if (era != ce) { // even in lenient mode we disallow era values other than ce & bce. // (the same normalization rule as add()/roll() could be // applied here in lenient mode. but this checking is kept // unchanged for compatibility as of 1.5.) throw new illegalargumentexception("invalid era"); } // if year is 0 or negative, we need to set the era value later. if (year <= 0 && !isset(era)) { fieldmask |= era_mask; setfieldscomputed(era_mask); } // calculate the time of day. we rely on the convention that // an unset field has 0. long timeofday = 0; if (isfieldset(fieldmask, hour_of_day)) { timeofday += (long) internalget(hour_of_day); } else { timeofday += internalget(hour); // the default value of am_pm is 0 which designates am. if (isfieldset(fieldmask, am_pm)) { timeofday += 12 * internalget(am_pm); } } timeofday *= 60; timeofday += internalget(minute); timeofday *= 60; timeofday += internalget(second); timeofday *= 1000; timeofday += internalget(millisecond); // convert the time of day to the number of days and the // millisecond offset from midnight. long fixeddate = timeofday / one_day; timeofday %= one_day; while (timeofday < 0) { timeofday += one_day; --fixeddate; } // calculate the fixed date since january 1, 1 (gregorian). calculatefixeddate: { long gfd, jfd; if (year > gregoriancutoveryear && year > gregoriancutoveryearjulian) { gfd = fixeddate + getfixeddate(gcal, year, fieldmask); if (gfd >= gregoriancutoverdate) { fixeddate = gfd; break calculatefixeddate; } jfd = fixeddate + getfixeddate(getjuliancalendarsystem(), year, fieldmask); } else if (year < gregoriancutoveryear && year < gregoriancutoveryearjulian) { jfd = fixeddate + getfixeddate(getjuliancalendarsystem(), year, fieldmask); if (jfd < gregoriancutoverdate) { fixeddate = jfd; break calculatefixeddate; } gfd = fixeddate + getfixeddate(gcal, year, fieldmask); } else { gfd = fixeddate + getfixeddate(gcal, year, fieldmask); jfd = fixeddate + getfixeddate(getjuliancalendarsystem(), year, fieldmask); } // now we have to determine which calendar date it is. if (gfd >= gregoriancutoverdate) { if (jfd >= gregoriancutoverdate) { fixeddate = gfd; } else { // the date is in an "overlapping" period. no way // to disambiguate it. determine it using the // previous date calculation. if (calsys == gcal || calsys == null) { fixeddate = gfd; } else { fixeddate = jfd; } } } else { if (jfd < gregoriancutoverdate) { fixeddate = jfd; } else { // the date is in a "missing" period. if (!islenient()) { throw new illegalargumentexception("the specified date doesn't exist"); } // take the julian date for compatibility, which // will produce a gregorian date. fixeddate = jfd; } } } // millis represents local wall-clock time in milliseconds. long millis = (fixeddate - epoch_offset) * one_day + timeofday; // compute the time zone offset and dst offset. there are two potential // ambiguities here. we'll assume a 2:00 am (wall time) switchover time // for discussion purposes here. // 1. the transition into dst. here, a designated time of 2:00 am - 2:59 am // can be in standard or in dst depending. however, 2:00 am is an invalid // representation (the representation jumps from 1:59:59 am std to 3:00:00 am dst). // we assume standard time. // 2. the transition out of dst. here, a designated time of 1:00 am - 1:59 am // can be in standard or dst. both are valid representations (the rep // jumps from 1:59:59 dst to 1:00:00 std). // again, we assume standard time. // we use the timezone object, unless the user has explicitly set the zone_offset // or dst_offset fields; then we use those fields. timezone zone = getzone(); if (zoneoffsets == null) { zoneoffsets = new int[2]; } int tzmask = fieldmask & (zone_offset_mask|dst_offset_mask); if (tzmask != (zone_offset_mask|dst_offset_mask)) { if (zone instanceof zoneinfo) { ((zoneinfo)zone).getoffsetsbywall(millis, zoneoffsets); } else { int gmtoffset = isfieldset(fieldmask, zone_offset) ? internalget(zone_offset) : zone.getrawoffset(); zone.getoffsets(millis - gmtoffset, zoneoffsets); } } if (tzmask != 0) { if (isfieldset(tzmask, zone_offset)) { zoneoffsets[0] = internalget(zone_offset); } if (isfieldset(tzmask, dst_offset)) { zoneoffsets[1] = internalget(dst_offset); } } // adjust the time zone offset values to get the utc time. millis -= zoneoffsets[0] + zoneoffsets[1]; // set this calendar's time in milliseconds time = millis; int mask = computefields(fieldmask | getsetstatefields(), tzmask); if (!islenient()) { for (int field = 0; field < field_count; field++) { if (!isexternallyset(field)) { continue; } if (originalfields[field] != internalget(field)) { // restore the original field values system.arraycopy(originalfields, 0, fields, 0, fields.length); throw new illegalargumentexception(getfieldname(field)); } } } setfieldsnormalized(mask); }
下面,我们看看internalget(field)的定义。如下:
protected final int internalget(int field) { return fields[field]; }
从中,我们就看出,get(int field) 最终是通过 internalget(int field)来返回值的。
而 internalget(int field) ,实际上返回的是field数组中的第field个元素。这就正好和calendar的17个元素所对应了!
总之,我们需要了解的就是:calendar就是以一个time(毫秒)为基数,而计算出“年月日时分秒”等,从而方便我们对“年月日时分秒”等进行操作。下面,介绍以下calendar提供的相关操作函数。
第三部分 calendar函数接口
1. 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.getactualmaximum(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相应的替换成其它字段名即可。
2. 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的两个对象。
// 它们的使用方法如下 boolean isequal = cal1.equals(cal2); boolean isbefore = cal1.before(cal2); boolean isafter = cal1.after(cal2); int icompare = cal1.compareto(cal2);
(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()等方法才会引起时间的重新计算,此时才会抛出异常!
(3) "年月日(时分秒)"、date、timezone、millisecond函数
// 设置“年月日” final void set(int year, int month, int day) // 设置“年月日时分” final void set(int year, int month, int day, int hourofday, int minute, int second) // 设置“年月日时分秒” final void set(int year, int month, int day, int hourofday, int minute) // 获取calendar对应的日期 final date gettime() // 设置calendar为date final void settime(date date) // 获取calendar对应的时区 timezone gettimezone() // 设置calendar对应的时区 void settimezone(timezone timezone) // 获取calendar对应的milliscondes值,就是“calendar当前日期”距离“1970-01-01 0:00:00 gmt”的毫秒数 long gettimeinmillis() // 设置calendar对应的milliscondes值 void settimeinmillis(long milliseconds)
这些函数的使用示例,请参考calendartest.java示例中的 testtimeapis() 函数。
(4) 其它操作
// 克隆calendar object clone() // 获取“每周的第一天是星期几”。例如,在美国,这一天是 sunday,而在法国,这一天是 monday。 int getfirstdayofweek() // 设置“每周的第一天是星期几”。例如,在美国,这一天是 sunday,而在法国,这一天是 monday。 void setfirstdayofweek(int value) // 获取一年中第一个星期所需的最少天数,例如,如果定义第一个星期包含一年第一个月的第一天,则此方法将返回 1。如果最少天数必须是一整个星期,则此方法将返回 7。 int getminimaldaysinfirstweek() // 设置一年中第一个星期所需的最少天数,例如,如果定义第一个星期包含一年第一个月的第一天,则使用值 1 调用此方法。如果最少天数必须是一整个星期,则使用值 7 调用此方法。 void setminimaldaysinfirstweek(int value)
这些函数的使用示例,请参考calendartest.java示例中的 testotherapis() 函数。
第四部分 calendar使用示例
下面,我们通过示例学习使用calendar的api。calendartest.java的源码如下:
import java.util.date; import java.util.calendar; import java.util.timezone; import java.util.random; public class calendartest { public static void main(string[] args) { // 测试calendar的“17个字段的公共函数接口” testallcalendarsections() ; // 测试calendar的“比较接口” testcomparatorapis() ; // 测试calendar的“比较接口” testlenientapis() ; // 测试calendar的date、timezone、millisecond等相关函数 testtimeapis() ; // 测试calendar的clone(),getfirstdayofweek()等接口 testotherapis() ; } /** * 测试“calendar的字段” * * @param cal -- calendar对象 * @param field -- 要测试的“calendar字段”。可以为以下值: * calendar.year, calendar.month, calendar.date, ... 等等 * @param title -- 标题 */ private static void testsection(calendar cal, int field, string title) { final random random = new random(); final date date = cal.gettime(); final int min = cal.getminimum(field); // 获取"字段最小值" final int max = cal.getmaximum(field); // 获取“字段最大值” final int actualmin = cal.getactualminimum(field); // 获取"当前日期下,该字段最小值" final int actualmax = cal.getactualmaximum(field); // 获取“当前日期下,该字段的最大值” // 获取“字段的当前值” final int ori = cal.get(field); // 设置“字段的当前值”, 并获取“设置之后的值” final int r1 = random.nextint(max); cal.set(field, r1); final int set = cal.get(field); try { // 回滚“字段的当前值”:在“字段最小值”和“字段最大值”之间回滚。 // “回滚值”可以为正,也可以为负。 cal.roll(field, -max); } catch (illegalargumentexception e) { // 当field == calendar.zone_offset时,会抛出该异常! e.printstacktrace(); } final int roll = cal.get(field); // 获取一个随机值 final int sign = ( random.nextint(2) == 1) ? 1 : -1; final int r2 = sign * random.nextint(max); try { // 增加“字段的当前值” ,并获取“新的当前字段值” // add的“参数值”可以为正,也可以为负。 cal.add(field, r2); } catch (illegalargumentexception e) { // 当field == calendar.zone_offset时,会抛出该异常! e.printstacktrace(); } final int add = cal.get(field); // 打印字段信息 system.out.printf("%s:\n\trange is [%d - %d] actualrange is [%d - %d]. original=%d, set(%d)=%d, roll(%d)=%d, add(%d)=%d\n", title, min, max, actualmin, actualmax, ori, r1, set, -max, roll, r2, add); } /** * 测试calendar的“17个字段的公共函数接口” */ private static void testallcalendarsections() { // 00. era 字段 testsection(calendar.getinstance(), calendar.era, "calendar.era"); // 01. year 字段 testsection(calendar.getinstance(), calendar.year, "calendar.year"); // 02. month 字段 testsection(calendar.getinstance(), calendar.month, "calendar.month"); // 03. week_of_year 字段 testsection(calendar.getinstance(), calendar.week_of_year, "calendar.week_of_year"); // 04. week_of_month 字段 testsection(calendar.getinstance(), calendar.week_of_month, "calendar.week_of_month"); // 05. date 字段 testsection(calendar.getinstance(), calendar.date, "calendar.date"); // 06. day_of_month 字段 testsection(calendar.getinstance(), calendar.day_of_month, "calendar.day_of_month"); // 07. day_of_year 字段 testsection(calendar.getinstance(), calendar.day_of_year, "calendar.day_of_year"); // 08. day_of_week 字段 testsection(calendar.getinstance(), calendar.day_of_week, "calendar.day_of_week"); // 09. day_of_week_in_month 字段 testsection(calendar.getinstance(), calendar.day_of_week_in_month, "calendar.day_of_week_in_month"); // 10. am_pm 字段 testsection(calendar.getinstance(), calendar.am_pm, "calendar.am_pm"); // 11. hour 字段 testsection(calendar.getinstance(), calendar.hour, "calendar.hour"); // 12. hour_of_day 字段 testsection(calendar.getinstance(), calendar.hour_of_day, "calendar.hour_of_day"); // 13. minute 字段 testsection(calendar.getinstance(), calendar.minute, "calendar.minute"); // 14. second 字段 testsection(calendar.getinstance(), calendar.second, "calendar.second"); // 15. millisecond 字段 testsection(calendar.getinstance(), calendar.millisecond, "calendar.millisecond"); // 16. zone_offset 字段 testsection(calendar.getinstance(), calendar.zone_offset, "calendar.zone_offset"); } /** * 测试calendar的“比较接口” */ private static void testcomparatorapis() { // 新建cal1 ,且时间为1988年 calendar cal1 = calendar.getinstance(); cal1.set(calendar.year, 1988); // 新建cal2 ,且时间为2000年 calendar cal2 = calendar.getinstance(); cal2.set(calendar.year, 2000); // 新建cal3, 为cal1的克隆对象 calendar cal3 = (calendar)cal1.clone(); // equals 判断 cal1和cal2的“时间、时区等”内容是否相等 boolean isequal12 = cal1.equals(cal2); // equals 判断 cal1和cal3的“时间、时区等”内容是否相等 boolean isequal13 = cal1.equals(cal3); // cal1是否比cal2早 boolean isbefore = cal1.before(cal2); // cal1是否比cal2晚 boolean isafter = cal1.after(cal2); // 比较cal1和cal2 // (01) 若cal1 早于 cal2,返回-1 // (02) 若cal1 等于 cal2,返回0 // (03) 若cal1 晚于 cal2,返回1 int icompare = cal1.compareto(cal2); system.out.printf("\ntestcomparatorapis: iseuqal12=%s, isequal13=%s, isbefore=%s, isafter=%s, icompare=%s\n", isequal12, isequal13, isbefore, isafter, icompare); } /** * 测试calendar的“比较接口” */ private static void testlenientapis() { calendar cal = calendar.getinstance(); // 获取默认的“宽容度”。返回true boolean orilenient = cal.islenient(); // month值只能是“0-11”,这里越界。但是由于当前cal是宽容的,所以不会抛出异常 cal.set(calendar.month, 50); // 设置“宽容度”为false。 cal.setlenient(false); // 获取设置后的“宽容度” boolean curlenient = cal.islenient(); try { // month值只能是“0-11”,这里越界。而且当前cal是不宽容的,所以会产生异常。 // 但是,异常到下次计算日期时才会抛出。即,set()中不回抛出异常,而要等到get()中才会抛出异常 cal.set(calendar.month, 50); // 此时,对cal进行读取。读取会导致重新计算cal的值,所以此时抛出异常! int m2 = cal.get(calendar.month); } catch (illegalargumentexception e) { e.printstacktrace(); } system.out.printf("\ntestlenientapis: orilenient=%s, curlenient=%s\n", orilenient, curlenient); } /** * 测试calendar的date、timezone、millisecond等相关函数 */ private static void testtimeapis() { calendar cal = calendar.getinstance(); // 设置cal的时区为“gmt+8” cal.settimezone(timezone.gettimezone("gmt+8")); // 获取当前的cal时区 timezone timezone = cal.gettimezone(); // 设置 milliseconds cal.settimeinmillis(1279419645742l); // 获取 milliseconds long millis = cal.gettimeinmillis(); // 设置 milliseconds之后,时间也改变了。 // 获取cal对应的日期 date date = cal.gettime(); // 设置时间为“1988-08-08” cal.set(1988, 08, 08); // 设置时间为“1999-09-09 09:09” cal.set(1999, 09, 09, 9, 9); // 设置时间为“2000-10-10 10:10:10” cal.set(2000, 10, 10, 10, 10, 10); system.out.printf("\ntesttimeapis: date=%s, timezone=%s, millis=%s\n", date, timezone, millis); } /** * 测试calendar的clone(),getfirstdayofweek()等接口 */ private static void testotherapis() { calendar cal = calendar.getinstance(); // 克隆cal calendar clone = (calendar)cal.clone(); // 设置 为 2013-01-10。 // 注:2013-01-01 为“星期二”,2013-01-06为“星期天”, clone.set(calendar.year, 2013); clone.set(calendar.month, 0); clone.set(calendar.date, 10); // 设置“本年的第一个星期最少包含1天”。 // 则2013-01-10属于第2个星期 clone.setminimaldaysinfirstweek(1); int m1 = clone.getminimaldaysinfirstweek(); int index1 = clone.get(calendar.week_of_year); // 设置“本年的第一个星期最少包含7天”。 // 则2013-01-10属于第1个星期 clone.setminimaldaysinfirstweek(7); int m2 = clone.getminimaldaysinfirstweek(); int index2 = clone.get(calendar.week_of_year); // 设置“每周的第一天是星期几”。 clone.setfirstdayofweek(calendar.wednesday); // 获取“每周的第一天是星期几”。 int firstdayofweek = clone.getfirstdayofweek(); system.out.printf("\ntestotherapis: firstdayofweek=%s, [minimalday, weekofyear]={(%s, %s), (%s, %s)} %s\n", firstdayofweek, m1, index1, m2, index2, clone.gettime()); } }
第五部分 自定义的calendar接口示例
这些接口在写日历程序时可能会用到。
源代码如下(calendarselfdefinetest.java):
import java.util.calendar; /** * 根据calendar的api封装的一些常用函数 */ public class calendarselfdefinetest { public static void main(string[] args) { calendar cal = calendar.getinstance(); // 设置日期为“2013-09-18” cal.set(2013, calendar.september, 18); // 获取“年” system.out.printf("year: %s\n", getyear(cal) ); // 获取“月” system.out.printf("month: %s\n", getmonth(cal) ); // 获取“上月” system.out.printf("previcou month: %s\n", getlastmonth(cal) ); // 获取“下月” system.out.printf("next month: %s\n", getnextmonth(cal) ); // 获取“日” system.out.printf("day: %s\n", getday(cal) ); // 获取cal对应星期几 system.out.printf("weekday: %s\n", getweekday(cal) ); // 本月天数 system.out.printf("current month days: %s\n", getmonthdays(cal) ); // 上月天数 system.out.printf("previcous month days: %s\n", getprevmonthdays(cal) ); // 下月天数 system.out.printf("next month days: %s\n", getnextmonthdays(cal) ); // 获取当月第一天的星期几 system.out.printf("first day' weekday : %s\n", getfirstdayweekday(cal) ); // 获取当前月最后一天的星期几 system.out.printf("last day' weekday : %s\n", getlastdayweekday(cal) ); // 获取上月最后一天的星期几 system.out.printf("prevmonth last day' weekday: %s\n", getlastdayweekdayofprevmonth(cal) ); // 获取下月第一天的星期几 system.out.printf("nextmonth first day' weekday: %s\n", getfirstdayweekdayofnextmonth(cal) ); } /** * 获取“年” * * @return 例如,2013-09-18,则返回2013 */ public static int getyear(calendar cal) { return cal.get(calendar.year); } /** * 获取“月” * * @return 返回值可以为以下值: * january, february, march, april, may, june, july, august, september, october, november, december, undecimber。 * 其中第一个月是 january,它为 0。 * * 例如,2013-09-18,则返回8 */ public static int getmonth(calendar cal) { return cal.get(calendar.month); } /** * 获取“上一个月” * * @return 返回值可以为以下值: * january, february, march, april, may, june, july, august, september, october, november, december, undecimber。 * 其中第一个月是 january,它为 0。 * * 例如,2012-01-12的上一个月是“11”(即december)。 */ public static int getlastmonth(calendar cal) { return (cal.get(calendar.month) + 11) % 12; } /** * 获取“下一个月” * * @return 返回值可以为以下值: * january, february, march, april, may, june, july, august, september, october, november, december, undecimber。 * 其中第一个月是 january,它为 0。 * * 例如,2013-12-12的下一个月是“1”(即december)。 */ public static int getnextmonth(calendar cal) { return (cal.get(calendar.month) + 13) % 12; } /** * 获取“日” * * @return 例如,2013-09-18,则返回18 * */ public static int getday(calendar cal) { return cal.get(calendar.date); } /** * 获取“本月的天数” * * @return 例如,2013-09-18,则返回30 * */ public static int getmonthdays(calendar cal) { return cal.getactualmaximum(calendar.date); } /** * 获取“上一个月的天数” * * @return 例如,2013-01-18,则返回31 (因为2012-12有31天) * */ public static int getprevmonthdays(calendar cal) { calendar tmpcal = (calendar)cal.clone(); // 克隆cal。后面对tmpcal操作,就不会改变cal tmpcal.add(calendar.month, -1); // 设为“上一个月” return tmpcal.getactualmaximum(calendar.date); } /** * 获取“下一个月的天数” * * @return 例如,2013-12-18,则返回31 (因为2014-01有31天) * */ public static int getnextmonthdays(calendar cal) { calendar tmpcal = (calendar)cal.clone(); // 克隆cal。后面对tmpcal操作,就不会改变cal tmpcal.add(calendar.month, 1); // 设为“下一个月” return tmpcal.getactualmaximum(calendar.date); } /** * 获取cal对应星期几 * * @return 返回“星期几”,可以为以下值: * sunday、monday、tuesday、wednesday、thursday、friday 和 saturday。 * sunday为1,monday为2,依次类推。 * 例如,2013-09-18(星期三),则返回4 */ public static int getweekday(calendar cal) { return cal.get(calendar.day_of_week); } /** * 获取当月第一天对应星期几 * * @return sunday为1,monday为2,依次类推。 */ public static int getfirstdayweekday(calendar cal) { calendar tmpcal = (calendar)cal.clone(); // 克隆cal。后面对tmpcal操作,就不会改变cal tmpcal.set(calendar.date, 1); // 把日期设置为当月第一天 return tmpcal.get(calendar.day_of_week); } /** * 获取当前月最后一天对应星期几 * * @return sunday为1,monday为2,依次类推。 */ public static int getlastdayweekday(calendar cal) { calendar tmpcal = (calendar)cal.clone(); // 克隆cal。后面对tmpcal操作,就不会改变cal tmpcal.set(calendar.date, 1); // 把日期设置为当月第一天 tmpcal.roll(calendar.date, -1); // 把日期设置为当月最后一天 return tmpcal.get(calendar.day_of_week); } /** * 获取上月最后一天的星期几 * * @return sunday为1,monday为2,依次类推。 */ public static int getlastdayweekdayofprevmonth(calendar cal) { calendar tmpcal = (calendar)cal.clone(); // 克隆cal。后面对tmpcal操作,就不会改变cal tmpcal.set(calendar.date, 1); // 把日期设置为当月第一天 tmpcal.add(calendar.date, -1); // 把日期设置为上一个月最后一天 return tmpcal.get(calendar.day_of_week); } /** * 获取下月第一天的星期偏移 * * @return sunday为1,monday为2,依次类推。 */ public static int getfirstdayweekdayofnextmonth(calendar cal) { calendar tmpcal = (calendar)cal.clone(); // 克隆cal。后面对tmpcal操作,就不会改变cal tmpcal.add(calendar.month, 1); // 设为“下一个月” tmpcal.set(calendar.date, 1); // 设为“第一天” return tmpcal.get(calendar.day_of_week); } }