加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java中的Calendar日历API用法完全解析

发布时间:2020-05-23 21:00:39 所属栏目:Java 来源:互联网
导读:第一部分Calendar介绍Calendar定义:publicabstractclassCalendarimplementsSerializable,Cloneable,ComparableCalendar{}

第一部分 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(),fieldMask);
 } else if (year < gregorianCutoverYear && year < gregorianCutoverYearJulian) {
  jfd = fixedDate + getFixedDate(getJulianCalendarSystem(),fieldMask);
  if (jfd < gregorianCutoverDate) {
  fixedDate = jfd;
  break calculateFixedDate;
  }
  gfd = fixedDate + getFixedDate(gcal,fieldMask);
 } else {
  gfd = fixedDate + getFixedDate(gcal,fieldMask);
  jfd = fixedDate + getFixedDate(getJulianCalendarSystem(),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,fields,fields.length);
  throw new IllegalArgumentException(getFieldName(field));
  }
 }
 }
 setFieldsNormalized(mask);
}

下面,我们看看internalGet(field)的定义。如下:

protected final int internalGet(int field) {
 return fields[field];
}

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读