第二部分Calendar原理和思想

来源:互联网 发布:污网络用语是啥意思 编辑:程序博客网 时间:2024/05/17 04:42

第二部分 Calendar的原理和思想
我们使用Calendar,无非是操作Calendar的“年、月、日、星期、时、分、秒”这些字段。下面,我们对这些字段的的来源、定义以及计算方法进行学习。
1. Calendar 各个字段值的来源
我们使用Calendar,无非是使用“年、月、日、星期、时、分、秒”等信息。那么它是如何做到的呢? 本质上,Calendar就是保存了一个时间。如下定义:

1
2
3
// time 是当前时间,单位是毫秒。
// 它是当前时间距离“January 1, 1970, 0:00:00 GMT”的差值。
protectedlongtime;

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数组中。定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 保存这17个字段的数组
protectedint fields[];
// 数组的定义函数
protectedCalendar(TimeZone zone, Locale aLocale)
{
 // 初始化“fields数组”
 fields = newint[FIELD_COUNT];
 isSet = newboolean[FIELD_COUNT];
 stamp = newint[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”字段的值。它的定义如下:

1
2
3
4
5
6
publicintget(intfield) {
 // 计算各个字段的值
 complete();
 // 返回field字段的值
 returninternalGet(field);
}

说明:get(int field)的代码很简单。先通过 complete() 计算各个字段的值,然后在通过 internalGet(field) 返回“field字段的值”。
complete() 的作用就是计算Calendar各个字段的值。它定义在Calendar.java中,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
protectedvoidcomplete()
{
 if(!isTimeSet)
 updateTime();
 if(!areFieldsSet || !areAllFieldsSet) {
 computeFields();// fills in unset fields
 areAllFieldsSet = areFieldsSet = true;
 }
}
privatevoidupdateTime() {
 computeTime();
 isTimeSet = true;
}
updateTime() 调用到的 computeTime() 定义在 Calendar.java的实现类中。下面,我列出GregorianCalendar.java中computeTime()的实现:
protectedvoidcomputeTime() {
 // 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 = newint[FIELD_COUNT];
 }
 for(intfield = 0; field < FIELD_COUNT; field++) {
  intvalue = internalGet(field);
  if(isExternallySet(field)) {
  // Quick validation for any out of range values
  if(value < getMinimum(field) || value > getMaximum(field)) {
   thrownewIllegalArgumentException(getFieldName(field));
  }
  }
  originalFields[field] = value;
 }
 }
 
 // Let the super class determine which calendar fields to be
 // used to calculate the time.
 intfieldMask = 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.
 intyear = isSet(YEAR) ? internalGet(YEAR) : EPOCH_YEAR;
 
 intera = internalGetEra();
 if(era == BCE) {
 year = 1- year;
 }elseif(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.)
 thrownewIllegalArgumentException("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.
 longtimeOfDay = 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.
 longfixedDate = timeOfDay / ONE_DAY;
 timeOfDay %= ONE_DAY;
 while(timeOfDay < 0) {
 timeOfDay += ONE_DAY;
 --fixedDate;
 }
 
 // Calculate the fixed date since January 1, 1 (Gregorian).
 calculateFixedDate: {
 longgfd, jfd;
 if(year > gregorianCutoverYear && year > gregorianCutoverYearJulian) {
  gfd = fixedDate + getFixedDate(gcal, year, fieldMask);
  if(gfd >= gregorianCutoverDate) {
  fixedDate = gfd;
  breakcalculateFixedDate;
  }
  jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask);
 }elseif(year < gregorianCutoverYear && year < gregorianCutoverYearJulian) {
  jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask);
  if(jfd < gregorianCutoverDate) {
  fixedDate = jfd;
  breakcalculateFixedDate;
  }
  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()) {
   thrownewIllegalArgumentException("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.
 longmillis = (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 = newint[2];
 }
 inttzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
 if(tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
 if(zoneinstanceofZoneInfo) {
  ((ZoneInfo)zone).getOffsetsByWall(millis, zoneOffsets);
 }else{
  intgmtOffset = 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;
 
 intmask = computeFields(fieldMask | getSetStateFields(), tzMask);
 
 if(!isLenient()) {
 for(intfield = 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);
  thrownewIllegalArgumentException(getFieldName(field));
  }
 }
 }
 setFieldsNormalized(mask);
}

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

1
2
3
protectedfinalint internalGet(intfield) {
 returnfields[field];
}

从中,我们就看出,get(int field) 最终是通过 internalGet(int field)来返回值的。
而 internalGet(int field) ,实际上返回的是field数组中的第field个元素。这就正好和Calendar的17个元素所对应了!
总之,我们需要了解的就是:Calendar就是以一个time(毫秒)为基数,而计算出“年月日时分秒”等,从而方便我们对“年月日时分秒”等进行操作。下面,介绍以下Calendar提供的相关操作函数。


原创粉丝点击