第二部分Calendar原理和思想
第二部分 Calendar的原理和思想
我們使用Calendar,無(wú)非是操作Calendar的“年、月、日、星期、時(shí)、分、秒”這些字段。下面,我們對(duì)這些字段的的來(lái)源、定義以及計(jì)算方法進(jìn)行學(xué)習(xí)。
1. Calendar 各個(gè)字段值的來(lái)源
我們使用Calendar,無(wú)非是使用“年、月、日、星期、時(shí)、分、秒”等信息。那么它是如何做到的呢? 本質(zhì)上,Calendar就是保存了一個(gè)時(shí)間。如下定義:
| 1 2 3 | // time 是當(dāng)前時(shí)間,單位是毫秒。 // 它是當(dāng)前時(shí)間距離“January 1, 1970, 0:00:00 GMT”的差值。 protectedlong time; |
Calendar就是根據(jù) time 計(jì)算出 “Calendar的年、月、日、星期、時(shí)、分、秒”等等信息。
2. Calendar 各個(gè)字段的定義和初始化
Calendar 的“年、月、日、星期、時(shí)、分、秒”這些信息,一共是17個(gè)字段。
我們使用Calendar,無(wú)非是就是使用這17個(gè)字段。它們的定義如下:
(字段0) public final static int ERA = 0;
說(shuō)明:紀(jì)元。
取值:只能為0 或 1。0表示BC(“before Christ”,即公元前),1表示AD(拉丁語(yǔ)“Anno Domini”,即公元)。
(字段1) public final static int YEAR = 1;
說(shuō)明:年。
(字段2) public final static int MONTH = 2;
說(shuō)明:月
取值:可以為,JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, UNDECIMBER。
???? 其中第一個(gè)月是 JANUARY,它為 0。
(字段3) public final static int WEEK_OF_YEAR = 3;
說(shuō)明:當(dāng)前日期在本年中對(duì)應(yīng)第幾個(gè)星期。一年中第一個(gè)星期的值為 1。
(字段4) public final static int WEEK_OF_MONTH = 4;
說(shuō)明:當(dāng)前日期在本月中對(duì)應(yīng)第幾個(gè)星期。一個(gè)月中第一個(gè)星期的值為 1。
(字段5) public final static int DATE = 5;
說(shuō)明:日。一個(gè)月中第一天的值為 1。
(字段5) public final static int DAY_OF_MONTH = 5;
說(shuō)明:同“DATE”,表示“日”。
(字段6) public final static int DAY_OF_YEAR = 6;
說(shuō)明:當(dāng)前日期在本年中對(duì)應(yīng)第幾天。一年中第一天的值為 1。
(字段7) public final static int DAY_OF_WEEK = 7;
說(shuō)明:星期幾。
取值:可以為,SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY 和 SATURDAY。
???? 其中,SUNDAY為1,MONDAY為2,依次類推。
(字段8) public final static int DAY_OF_WEEK_IN_MONTH = 8;
說(shuō)明:當(dāng)前月中的第幾個(gè)星期。
取值:DAY_OF_MONTH 1 到 7 總是對(duì)應(yīng)于 DAY_OF_WEEK_IN_MONTH 1;8 到 14 總是對(duì)應(yīng)于 DAY_OF_WEEK_IN_MONTH 2,依此類推。
(字段9) public final static int AM_PM = 9;
說(shuō)明:上午 還是 下午
取值:可以是AM 或 PM。AM為0,表示上午;PM為1,表示下午。
(字段10) public final static int HOUR = 10;
說(shuō)明:指示一天中的第幾小時(shí)。
???? HOUR 用于 12 小時(shí)制時(shí)鐘 (0 - 11)。中午和午夜用 0 表示,不用 12 表示。
(字段11) public final static int HOUR_OF_DAY = 11;
說(shuō)明:指示一天中的第幾小時(shí)。
???? HOUR_OF_DAY 用于 24 小時(shí)制時(shí)鐘。例如,在 10:04:15.250 PM 這一時(shí)刻,HOUR_OF_DAY 為 22。
(字段12) public final static int MINUTE = 12;
說(shuō)明:一小時(shí)中的第幾分鐘。
例如,在 10:04:15.250 PM這一時(shí)刻,MINUTE 為 4。
(字段13) public final static int SECOND = 13;
說(shuō)明:一分鐘中的第幾秒。
例如,在 10:04:15.250 PM 這一時(shí)刻,SECOND 為 15。
(字段14) public final static int MILLISECOND = 14;
說(shuō)明:一秒中的第幾毫秒。
例如,在 10:04:15.250 PM 這一時(shí)刻,MILLISECOND 為 250。
(字段15) public final static int ZONE_OFFSET = 15;
說(shuō)明:毫秒為單位指示距 GMT 的大致偏移量。
(字段16) public final static int DST_OFFSET = 16;
說(shuō)明:毫秒為單位指示夏令時(shí)的偏移量。
public final static int FIELD_COUNT = 17;
這17個(gè)字段是保存在int數(shù)組中。定義如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | // 保存這17個(gè)字段的數(shù)組 protectedint? fields[]; // 數(shù)組的定義函數(shù) protectedCalendar(TimeZone zone, Locale aLocale) { ?// 初始化“fields數(shù)組” ?fields = newint[FIELD_COUNT]; ?isSet = newboolean[FIELD_COUNT]; ?stamp = newint[FIELD_COUNT]; ?this.zone = zone; ?setWeekCountData(aLocale); } |
protected Calendar(TimeZone zone, Locale aLocale) 這是Calendar的構(gòu)造函數(shù)。它會(huì)被它的子類的構(gòu)造函數(shù)調(diào)用到,從而新建“保存Calendar的17個(gè)字段數(shù)據(jù)”的數(shù)組。
3. Calendar 各個(gè)字段值的計(jì)算
下面以get(int field)為例,簡(jiǎn)要的說(shuō)明Calendar的17個(gè)字段的計(jì)算和操作。 get(int field)是獲取“field”字段的值。它的定義如下:
| 1 2 3 4 5 6 | publicint get(intfield) { ?// 計(jì)算各個(gè)字段的值 ?complete(); ?// 返回field字段的值 ?returninternalGet(field); } |
說(shuō)明:get(int field)的代碼很簡(jiǎn)單。先通過(guò) complete() 計(jì)算各個(gè)字段的值,然后在通過(guò) internalGet(field) 返回“field字段的值”。
complete() 的作用就是計(jì)算Calendar各個(gè)字段的值。它定義在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() 調(diào)用到的 computeTime() 定義在 Calendar.java的實(shí)現(xiàn)類中。下面,我列出GregorianCalendar.java中computeTime()的實(shí)現(xiàn): 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) 最終是通過(guò) internalGet(int field)來(lái)返回值的。
而 internalGet(int field) ,實(shí)際上返回的是field數(shù)組中的第field個(gè)元素。這就正好和Calendar的17個(gè)元素所對(duì)應(yīng)了!
總之,我們需要了解的就是:Calendar就是以一個(gè)time(毫秒)為基數(shù),而計(jì)算出“年月日時(shí)分秒”等,從而方便我們對(duì)“年月日時(shí)分秒”等進(jìn)行操作。下面,介紹以下Calendar提供的相關(guān)操作函數(shù)。
總結(jié)
以上是生活随笔為你收集整理的第二部分Calendar原理和思想的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 日常生活小技巧 -- markdown编
- 下一篇: UNIX再学习 -- 函数 fork 和