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

1
2
3
// time 是当前时间,单位是毫秒。
// 它是当前时间距离“January 1, 1970, 0:00:00 GMT”的差值。
protectedlong 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数组中。定义如下:

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
publicint get(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
protectedvoid complete()
{
 if(!isTimeSet)
 updateTime();
 if(!areFieldsSet || !areAllFieldsSet) {
 computeFields();// fills in unset fields
 areAllFieldsSet = areFieldsSet = true;
 }
}
privatevoid updateTime() {
 computeTime();
 isTimeSet = true;
}
updateTime() 调用到的 computeTime() 定义在 Calendar.java的实现类中。下面,我列出GregorianCalendar.java中computeTime()的实现:
protectedvoid 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 = 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)) {
   thrownew IllegalArgumentException(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.)
 thrownew 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.
 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()) {
   thrownew 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.
 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(zone instanceofZoneInfo) {
  ((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);
  thrownew IllegalArgumentException(getFieldName(field));
  }
 }
 }
 setFieldsNormalized(mask);
}

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

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

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

第二部分Calendar原理和思想相关推荐

  1. 解密汽车全景行车安全系统的前世和今生——第二讲:原理讲解

    解密汽车全景行车安全系统的前世和今生--第二讲:原理讲解 来源:深圳市汽车电子行业协会 作者:姜卫忠 发布时间:2013-3-7  浏览(4648)次 解密汽车全景行车安全系统的前世和今生 第二讲:全 ...

  2. 16位汇编语言第二讲系统调用原理,以及各个寄存器详解

    16位汇编语言第二讲系统调用原理,以及各个寄存器详解 昨天已将简单的写了一下汇编代码,并且执行了第一个显示到屏幕的helloworld 问题? helloworld怎么显示出来了. 一丶显卡,显存的概 ...

  3. 《吕鑫:VC++6.0就业培训宝典之MFC视频教程》学习笔记 -- 第二章 MFC原理介绍

    第二章 MFC原理介绍 2.1 第一个Win32软件 2.2 Win32对话框程序开发 2.3 程序资源管理和Windows数据类型 2.4 Win32环境下的多对话框管理 2.5 初步学习MFC软件 ...

  4. 最小二乘和极大似然估计的原理,思想?相同点以及异同?

    简述最小二乘和极大似然估计的原理,思想?相同点以及异同? 最小二乘估计和极大似然估计 简述最小二乘和极大似然估计的原理,思想?相同点以及异同? 1.多元线性回归方程的矩阵表示 1.1 最小二乘估计的原 ...

  5. 动态规划系列——原理与思想

    一.适用范围 每种方法都有自身的局限性,动态规划法也不是万能的.动态规划适合求解多阶段(状态转换)决策问题的最优解,也可用于含有线性或非线性递推关系的最优解问题,但是这些问题都必须满足最优化原理和子问 ...

  6. 卡尔曼滤波的原理与思想

    卡尔曼滤波是什么 卡尔曼滤波适用于估计一个动态系统的最优状态.即便是观测到的系统状态参数含有噪声,观测值不准确,卡尔曼滤波也能够完成对状态真实值的最优估计.网上大多数的教程讲到卡尔曼的数学公式推导,会 ...

  7. 作为现代计算机理论的基础的,作为现代计算机理论基础的冯·诺依曼原理和思想是()。...

    任特在获征数中据资料的关胜方法取有,现代述事细描并详件的起因,想的及感过程方法的是,在管作中被研他们究者键实列出理工例要求发生的关. 在E中,计算机理共选个单元格定了. 属于是(折线图的,论基理和下列 ...

  8. 【软考中级信安】第二章--网络攻击原理与常用方法

    目录 1.网络攻击概述 1.1 网络攻击概述 1.2 网络攻击模型 1.3 网络攻击发展演变 2.网络攻击一般过程 3.网络攻击常见技术方法 3.1 端口扫描 3.2 口令破解 3.3 缓冲区溢出(大 ...

  9. 分布式事务之底层原理揭秘

    , hi 大家好,今天分享一这篇文章,让大家彻底了解分布式原理,这个是后台开发必须掌握技能. 刚性事务 柔性事务 本地事务 分布式事务 单阶段原子提交协议 两阶段提交协议 定义 原理 性能 恢复 缺陷 ...

最新文章

  1. python3 requests 错误EOF occurred in violation of protocol 解决方法
  2. usb 转 uart cp210x 驱动解析
  3. 下周见| 重量级演讲阵容首曝光DTCC 2020阿里云议题抢鲜看
  4. 使用JavaFX构建反应系统
  5. 让S3c2410里拥有HIVE注册表的 全部步骤
  6. 拓端tecdat|电力消耗模型构建、分析和预测
  7. 前端基础(二十一):移动端H5调用摄像头拍照旋转解决方案
  8. 惠普HP8570W minipcie无线网卡接口改装m.2网卡Intel9260AC
  9. OpenCV 两张大小不同图片的叠加
  10. python 文件夹_使用python进行文件夹对比
  11. 如何搭建一个站内搜索引擎(二) 第2章 概述
  12. 阿里巴巴sentinel限流
  13. Stata | 时间序列操作
  14. 茶叶分类--六大茶类
  15. Windows + VS Code搭建 Go 开发环境
  16. Android FTP功能开发基于swiftp
  17. 平安科技-前端面试1
  18. nvm介绍、nvm下载、安装与使用
  19. 【实验四 循环结构】7-2 sdut-C语言实验—两个数比较
  20. 西行漫记(5):关于故事的故事

热门文章

  1. MATLAB教程(1) MATLAB 基础知识(2)
  2. 服务器bios修改uefi,服务器 uefi bios设置
  3. 南工院linux考试题库,操作系统复习题..doc
  4. php获取信息,PHP文件信息获取函数
  5. pxc mysql mycat_Mycat+Pxc的配置
  6. java getipaddress_教你java用getAddress方法取得IP地址
  7. 服务端和客户端测试连通ip设置记录
  8. 服务器测试网址填写注意事项
  9. mysql添加用户及权限
  10. 解决[ERROR] [MY-013276] [Server] Failed to set datadir to ‘F:\Mysql\mysql-8.0.19-winx64\data\‘ (OS er