第二部分Calendar原理和思想
第二部分 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原理和思想的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 日常生活小技巧 -- markdown编
- 下一篇: UNIX再学习 -- 函数 fork 和