?? gregoriancalendar.java
字號:
} else { // We handle most fields here. The algorithm is to add a computed amount // of millis to the current millis. The only wrinkle is with DST -- if // the result of the add operation is to move from DST to Standard, or vice // versa, we need to adjust by an hour forward or back, respectively. // Otherwise you get weird effects in which the hour seems to shift when // you add to the DAY_OF_MONTH field, for instance. // We only adjust the DST for fields larger than an hour. For fields // smaller than an hour, we cannot adjust for DST without causing problems. // for instance, if you add one hour to April 5, 1998, 1:00 AM, in PST, // the time becomes "2:00 AM PDT" (an illegal value), but then the adjustment // sees the change and compensates by subtracting an hour. As a result the // time doesn't advance at all. long delta = amount; boolean adjustDST = true; switch (field) { case WEEK_OF_YEAR: case WEEK_OF_MONTH: case DAY_OF_WEEK_IN_MONTH: delta *= 7 * 24 * 60 * 60 * 1000; // 7 days break; case AM_PM: delta *= 12 * 60 * 60 * 1000; // 12 hrs break; case DATE: // synonym of DAY_OF_MONTH case DAY_OF_YEAR: case DAY_OF_WEEK: delta *= 24 * 60 * 60 * 1000; // 1 day break; case HOUR_OF_DAY: case HOUR: delta *= 60 * 60 * 1000; // 1 hour adjustDST = false; break; case MINUTE: delta *= 60 * 1000; // 1 minute adjustDST = false; break; case SECOND: delta *= 1000; // 1 second adjustDST = false; break; case MILLISECOND: adjustDST = false; break; case ZONE_OFFSET: case DST_OFFSET: default: throw new IllegalArgumentException(); } // Save the current DST state. long dst = 0; if (adjustDST) { dst = internalGet(DST_OFFSET); } setTimeInMillis(time + delta); // Automatically computes fields if necessary if (adjustDST) { // Now do the DST adjustment alluded to above. // Only call setTimeInMillis if necessary, because it's an expensive call. dst -= internalGet(DST_OFFSET); if (dst != 0) { setTimeInMillis(time + dst); } } } } /** * Adds or subtracts (up/down) a single unit of time on the given time * field without changing larger fields. * <p> * <em>Example</em>: Consider a <code>GregorianCalendar</code> * originally set to December 31, 1999. Calling <code>roll(Calendar.MONTH, true)</code> * sets the calendar to January 31, 1999. The <code>Year</code> field is unchanged * because it is a larger field than <code>MONTH</code>.</p> * @param up indicates if the value of the specified time field is to be * rolled up or rolled down. Use true if rolling up, false otherwise. * @exception IllegalArgumentException if an unknown field value is given. * @see GregorianCalendar#add * @see GregorianCalendar#set */ public void roll(int field, boolean up) { roll(field, up ? +1 : -1); } /** * Add to field a signed amount without changing larger fields. * A negative roll amount means to subtract from field without changing * larger fields. * <p> * <em>Example</em>: Consider a <code>GregorianCalendar</code> * originally set to August 31, 1999. Calling <code>roll(Calendar.MONTH, * 8)</code> sets the calendar to April 30, <strong>1999</strong>. Using a * <code>GregorianCalendar</code>, the <code>DAY_OF_MONTH</code> field cannot * be 31 in the month April. <code>DAY_OF_MONTH</code> is set to the closest possible * value, 30. The <code>YEAR</code> field maintains the value of 1999 because it * is a larger field than <code>MONTH</code>. * <p> * <em>Example</em>: Consider a <code>GregorianCalendar</code> * originally set to Sunday June 6, 1999. Calling * <code>roll(Calendar.WEEK_OF_MONTH, -1)</code> sets the calendar to * Tuesday June 1, 1999, whereas calling * <code>add(Calendar.WEEK_OF_MONTH, -1)</code> sets the calendar to * Sunday May 30, 1999. This is because the roll rule imposes an * additional constraint: The <code>MONTH</code> must not change when the * <code>WEEK_OF_MONTH</code> is rolled. Taken together with add rule 1, * the resultant date must be between Tuesday June 1 and Saturday June * 5. According to add rule 2, the <code>DAY_OF_WEEK</code>, an invariant * when changing the <code>WEEK_OF_MONTH</code>, is set to Tuesday, the * closest possible value to Sunday (where Sunday is the first day of the * week).</p> * @param field the time field. * @param amount the signed amount to add to <code>field</code>. * @since 1.2 * @see GregorianCalendar#add * @see GregorianCalendar#set */ public void roll(int field, int amount) { if (amount == 0) { return; // Nothing to do } int min = 0, max = 0, gap; if (field >= 0 && field < FIELD_COUNT) { complete(); min = getMinimum(field); max = getMaximum(field); } switch (field) { case ERA: case YEAR: case AM_PM: case MINUTE: case SECOND: case MILLISECOND: // These fields are handled simply, since they have fixed minima // and maxima. The field DAY_OF_MONTH is almost as simple. Other // fields are complicated, since the range within they must roll // varies depending on the date. break; case HOUR: case HOUR_OF_DAY: // Rolling the hour is difficult on the ONSET and CEASE days of // daylight savings. For example, if the change occurs at // 2 AM, we have the following progression: // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std // To get around this problem we don't use fields; we manipulate // the time in millis directly. { // Assume min == 0 in calculations below Date start = getTime(); int oldHour = internalGet(field); int newHour = (oldHour + amount) % (max + 1); if (newHour < 0) { newHour += max + 1; } setTime(new Date(start.getTime() + ONE_HOUR * (newHour - oldHour))); return; } case MONTH: // Rolling the month involves both pinning the final value to [0, 11] // and adjusting the DAY_OF_MONTH if necessary. We only adjust the // DAY_OF_MONTH if, after updating the MONTH field, it is illegal. // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>. { int mon = (internalGet(MONTH) + amount) % 12; if (mon < 0) { mon += 12; } set(MONTH, mon); // Keep the day of month in range. We don't want to spill over // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 -> // mar3. // NOTE: We could optimize this later by checking for dom <= 28 // first. Do this if there appears to be a need. [LIU] int monthLen = monthLength(mon); int dom = internalGet(DAY_OF_MONTH); if (dom > monthLen) { set(DAY_OF_MONTH, monthLen); } return; } case WEEK_OF_YEAR: { // Unlike WEEK_OF_MONTH, WEEK_OF_YEAR never shifts the day of the // week. Also, rolling the week of the year can have seemingly // strange effects simply because the year of the week of year // may be different from the calendar year. For example, the // date Dec 28, 1997 is the first day of week 1 of 1998 (if // weeks start on Sunday and the minimal days in first week is // <= 3). int woy = internalGet(WEEK_OF_YEAR); // Get the ISO year, which matches the week of year. This // may be one year before or after the calendar year. int isoYear = internalGet(YEAR); int isoDoy = internalGet(DAY_OF_YEAR); if (internalGet(MONTH) == Calendar.JANUARY) { if (woy >= 52) { --isoYear; isoDoy += yearLength(isoYear); } } else { if (woy == 1) { isoDoy -= yearLength(isoYear); ++isoYear; } } woy += amount; // Do fast checks to avoid unnecessary computation: if (woy < 1 || woy > 52) { // Determine the last week of the ISO year. // First, we calculate the relative fractional days of the // last week of the year. (This doesn't include days in // the year before or after the calendar year.) int lastDoy = yearLength(isoYear); int normalizedDayOfWeek = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek(); if (normalizedDayOfWeek < 0) { normalizedDayOfWeek += 7; } int lastRelDow = (lastDoy - isoDoy + normalizedDayOfWeek) % 7; if (lastRelDow < 0) { lastRelDow += 7; } // Next, calculate the minimal last week of year. // Now this value is just the total number of weeks in the // year all of which have 7 days a week. Need to check the // first and the last week of the year, which would have // days fewer than 7. int lastWoy; lastDoy -= (lastRelDow+1); lastWoy = lastDoy / 7; // If the relative fraction of the first week of the year // is more than MinimalDaysInFirstWeek, add 1 to the last // week // of the year. if ((lastDoy - (lastWoy*7)) >= getMinimalDaysInFirstWeek()) { lastWoy++; } // If the relative fraction of the last week of the year // is more than MinimalDaysInFirstWeek, add 1 to the last // week of the year. if ((6 - lastRelDow) < getMinimalDaysInFirstWeek()) { lastWoy++; } woy = ((woy + lastWoy - 1) % lastWoy) + 1; } set(WEEK_OF_YEAR, woy); set(YEAR, isoYear); return; } case WEEK_OF_MONTH: { // This is tricky, because during the roll we may have to shift // to a different day of the week. For example: // s m t w r f s // 1 2 3 4 5 // 6 7 8 9 10 11 12 // When rolling from the 6th or 7th back one week, we go to the // 1st (assuming that the first partial week counts). The same // thing happens at the end of the month. // The other tricky thing is that we have to figure out whether // the first partial week actually counts or not, based on the // minimal first days in the week. And we have to use the // correct first day of the week to delineate the week // boundaries. // Here's our algorithm. First, we find the real boundaries of // the month. Then we discard the first partial week if it // doesn't count in this locale. Then we fill in the ends with // phantom days, so that the first partial week and the last // partial week are full weeks. We then have a nice square // block of weeks. We do the usual rolling within this block, // as is done elsewhere in this method. If we wind up on one of // the phantom days that we added, we recognize this and pin to // the first or the last day of the month. Easy, eh? // Normalize the DAY_OF_WEEK so that 0 is the first day of the week // in this locale. We have dow in 0..6. int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek(); if (dow < 0) { dow += 7; } // Find the day of the week (normalized for locale) for the first // of the month. int fdm = (dow - internalGet(DAY_OF_MONTH) + 1) % 7; if (fdm < 0) {
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -