001: /*
002: * Copyright 2001-2005 Stephen Colebourne
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.joda.time.chrono;
017:
018: import org.joda.time.DateTimeConstants;
019: import org.joda.time.DateTimeFieldType;
020: import org.joda.time.DateTimeUtils;
021: import org.joda.time.DurationField;
022: import org.joda.time.ReadablePartial;
023: import org.joda.time.field.FieldUtils;
024: import org.joda.time.field.ImpreciseDateTimeField;
025:
026: /**
027: * Provides time calculations for the month of the year component of time.
028: *
029: * @author Guy Allard
030: * @author Stephen Colebourne
031: * @author Brian S O'Neill
032: * @since 1.2, refactored from GJMonthOfYearDateTimeField
033: */
034: class BasicMonthOfYearDateTimeField extends ImpreciseDateTimeField {
035:
036: /** Serialization version */
037: private static final long serialVersionUID = -8258715387168736L;
038:
039: private static final int MIN = DateTimeConstants.JANUARY;
040:
041: private final BasicChronology iChronology;
042: private final int iMax;
043: private final int iLeapMonth;
044:
045: /**
046: * Restricted constructor.
047: *
048: * @param leapMonth the month of year that leaps
049: */
050: BasicMonthOfYearDateTimeField(BasicChronology chronology,
051: int leapMonth) {
052: super (DateTimeFieldType.monthOfYear(), chronology
053: .getAverageMillisPerMonth());
054: iChronology = chronology;
055: iMax = iChronology.getMaxMonth();
056: iLeapMonth = leapMonth;
057: }
058:
059: //-----------------------------------------------------------------------
060: public boolean isLenient() {
061: return false;
062: }
063:
064: //-----------------------------------------------------------------------
065: /**
066: * Get the Month component of the specified time instant.
067: *
068: * @see org.joda.time.DateTimeField#get(long)
069: * @see org.joda.time.ReadableDateTime#getMonthOfYear()
070: * @param instant the time instant in millis to query.
071: * @return the month extracted from the input.
072: */
073: public int get(long instant) {
074: return iChronology.getMonthOfYear(instant);
075: }
076:
077: //-----------------------------------------------------------------------
078: /**
079: * Add the specified month to the specified time instant.
080: * The amount added may be negative.<p>
081: * If the new month has less total days than the specified
082: * day of the month, this value is coerced to the nearest
083: * sane value. e.g.<p>
084: * 07-31 - (1 month) = 06-30<p>
085: * 03-31 - (1 month) = 02-28 or 02-29 depending<p>
086: *
087: * @see org.joda.time.DateTimeField#add
088: * @see org.joda.time.ReadWritableDateTime#addMonths(int)
089: * @param instant the time instant in millis to update.
090: * @param months the months to add (can be negative).
091: * @return the updated time instant.
092: */
093: public long add(long instant, int months) {
094: if (months == 0) {
095: return instant; // the easy case
096: }
097: //
098: // Save time part first.
099: //
100: long timePart = iChronology.getMillisOfDay(instant);
101: //
102: //
103: // Get this year and month.
104: //
105: int this Year = iChronology.getYear(instant);
106: int this Month = iChronology.getMonthOfYear(instant, this Year);
107: // ----------------------------------------------------------
108: //
109: // Do not refactor without careful consideration.
110: // Order of calculation is important.
111: //
112: int yearToUse;
113: // Initially, monthToUse is zero-based
114: int monthToUse = this Month - 1 + months;
115: if (monthToUse >= 0) {
116: yearToUse = this Year + (monthToUse / iMax);
117: monthToUse = (monthToUse % iMax) + 1;
118: } else {
119: yearToUse = this Year + (monthToUse / iMax) - 1;
120: monthToUse = Math.abs(monthToUse);
121: int remMonthToUse = monthToUse % iMax;
122: // Take care of the boundary condition
123: if (remMonthToUse == 0) {
124: remMonthToUse = iMax;
125: }
126: monthToUse = iMax - remMonthToUse + 1;
127: // Take care of the boundary condition
128: if (monthToUse == 1) {
129: yearToUse += 1;
130: }
131: }
132: // End of do not refactor.
133: // ----------------------------------------------------------
134:
135: //
136: // Quietly force DOM to nearest sane value.
137: //
138: int dayToUse = iChronology.getDayOfMonth(instant, this Year,
139: this Month);
140: int maxDay = iChronology.getDaysInYearMonth(yearToUse,
141: monthToUse);
142: if (dayToUse > maxDay) {
143: dayToUse = maxDay;
144: }
145: //
146: // get proper date part, and return result
147: //
148: long datePart = iChronology.getYearMonthDayMillis(yearToUse,
149: monthToUse, dayToUse);
150: return datePart + timePart;
151: }
152:
153: //-----------------------------------------------------------------------
154: public long add(long instant, long months) {
155: int i_months = (int) months;
156: if (i_months == months) {
157: return add(instant, i_months);
158: }
159:
160: // Copied from add(long, int) and modified slightly:
161:
162: long timePart = iChronology.getMillisOfDay(instant);
163:
164: int this Year = iChronology.getYear(instant);
165: int this Month = iChronology.getMonthOfYear(instant, this Year);
166:
167: long yearToUse;
168: long monthToUse = this Month - 1 + months;
169: if (monthToUse >= 0) {
170: yearToUse = this Year + (monthToUse / iMax);
171: monthToUse = (monthToUse % iMax) + 1;
172: } else {
173: yearToUse = this Year + (monthToUse / iMax) - 1;
174: monthToUse = Math.abs(monthToUse);
175: int remMonthToUse = (int) (monthToUse % iMax);
176: if (remMonthToUse == 0) {
177: remMonthToUse = iMax;
178: }
179: monthToUse = iMax - remMonthToUse + 1;
180: if (monthToUse == 1) {
181: yearToUse += 1;
182: }
183: }
184:
185: if (yearToUse < iChronology.getMinYear()
186: || yearToUse > iChronology.getMaxYear()) {
187:
188: throw new IllegalArgumentException(
189: "Magnitude of add amount is too large: " + months);
190: }
191:
192: int i_yearToUse = (int) yearToUse;
193: int i_monthToUse = (int) monthToUse;
194:
195: int dayToUse = iChronology.getDayOfMonth(instant, this Year,
196: this Month);
197: int maxDay = iChronology.getDaysInYearMonth(i_yearToUse,
198: i_monthToUse);
199: if (dayToUse > maxDay) {
200: dayToUse = maxDay;
201: }
202:
203: long datePart = iChronology.getYearMonthDayMillis(i_yearToUse,
204: i_monthToUse, dayToUse);
205: return datePart + timePart;
206: }
207:
208: //-----------------------------------------------------------------------
209: public int[] add(ReadablePartial partial, int fieldIndex,
210: int[] values, int valueToAdd) {
211: // overridden as superclass algorithm can't handle
212: // 2004-02-29 + 48 months -> 2008-02-29 type dates
213: if (valueToAdd == 0) {
214: return values;
215: }
216: if (DateTimeUtils.isContiguous(partial)) {
217: long instant = 0L;
218: for (int i = 0, isize = partial.size(); i < isize; i++) {
219: instant = partial.getFieldType(i).getField(iChronology)
220: .set(instant, values[i]);
221: }
222: instant = add(instant, valueToAdd);
223: return iChronology.get(partial, instant);
224: } else {
225: return super .add(partial, fieldIndex, values, valueToAdd);
226: }
227: }
228:
229: //-----------------------------------------------------------------------
230: /**
231: * Add to the Month component of the specified time instant
232: * wrapping around within that component if necessary.
233: *
234: * @see org.joda.time.DateTimeField#addWrapField
235: * @param instant the time instant in millis to update.
236: * @param months the months to add (can be negative).
237: * @return the updated time instant.
238: */
239: public long addWrapField(long instant, int months) {
240: return set(instant, FieldUtils.getWrappedValue(get(instant),
241: months, MIN, iMax));
242: }
243:
244: //-----------------------------------------------------------------------
245: public long getDifferenceAsLong(long minuendInstant,
246: long subtrahendInstant) {
247: if (minuendInstant < subtrahendInstant) {
248: return -getDifference(subtrahendInstant, minuendInstant);
249: }
250:
251: int minuendYear = iChronology.getYear(minuendInstant);
252: int minuendMonth = iChronology.getMonthOfYear(minuendInstant,
253: minuendYear);
254: int subtrahendYear = iChronology.getYear(subtrahendInstant);
255: int subtrahendMonth = iChronology.getMonthOfYear(
256: subtrahendInstant, subtrahendYear);
257:
258: long difference = (minuendYear - subtrahendYear)
259: * ((long) iMax) + minuendMonth - subtrahendMonth;
260:
261: // Before adjusting for remainder, account for special case of add
262: // where the day-of-month is forced to the nearest sane value.
263: int minuendDom = iChronology.getDayOfMonth(minuendInstant,
264: minuendYear, minuendMonth);
265: if (minuendDom == iChronology.getDaysInYearMonth(minuendYear,
266: minuendMonth)) {
267: // Last day of the minuend month...
268: int subtrahendDom = iChronology.getDayOfMonth(
269: subtrahendInstant, subtrahendYear, subtrahendMonth);
270: if (subtrahendDom > minuendDom) {
271: // ...and day of subtrahend month is larger.
272: // Note: This works fine, but it ideally shouldn't invoke other
273: // fields from within a field.
274: subtrahendInstant = iChronology.dayOfMonth().set(
275: subtrahendInstant, minuendDom);
276: }
277: }
278:
279: // Inlined remainder method to avoid duplicate calls.
280: long minuendRem = minuendInstant
281: - iChronology.getYearMonthMillis(minuendYear,
282: minuendMonth);
283: long subtrahendRem = subtrahendInstant
284: - iChronology.getYearMonthMillis(subtrahendYear,
285: subtrahendMonth);
286:
287: if (minuendRem < subtrahendRem) {
288: difference--;
289: }
290:
291: return difference;
292: }
293:
294: //-----------------------------------------------------------------------
295: /**
296: * Set the Month component of the specified time instant.<p>
297: * If the new month has less total days than the specified
298: * day of the month, this value is coerced to the nearest
299: * sane value. e.g.<p>
300: * 07-31 to month 6 = 06-30<p>
301: * 03-31 to month 2 = 02-28 or 02-29 depending<p>
302: *
303: * @param instant the time instant in millis to update.
304: * @param month the month (1,12) to update the time to.
305: * @return the updated time instant.
306: * @throws IllegalArgumentException if month is invalid
307: */
308: public long set(long instant, int month) {
309: FieldUtils.verifyValueBounds(this , month, MIN, iMax);
310: //
311: int this Year = iChronology.getYear(instant);
312: //
313: int this Dom = iChronology.getDayOfMonth(instant, this Year);
314: int maxDom = iChronology.getDaysInYearMonth(this Year, month);
315: if (this Dom > maxDom) {
316: // Quietly force DOM to nearest sane value.
317: this Dom = maxDom;
318: }
319: // Return newly calculated millis value
320: return iChronology.getYearMonthDayMillis(this Year, month,
321: this Dom)
322: + iChronology.getMillisOfDay(instant);
323: }
324:
325: //-----------------------------------------------------------------------
326: public DurationField getRangeDurationField() {
327: return iChronology.years();
328: }
329:
330: //-----------------------------------------------------------------------
331: public boolean isLeap(long instant) {
332: int this Year = iChronology.getYear(instant);
333: if (iChronology.isLeapYear(this Year)) {
334: return (iChronology.getMonthOfYear(instant, this Year) == iLeapMonth);
335: }
336: return false;
337: }
338:
339: //-----------------------------------------------------------------------
340: public int getLeapAmount(long instant) {
341: return isLeap(instant) ? 1 : 0;
342: }
343:
344: //-----------------------------------------------------------------------
345: public DurationField getLeapDurationField() {
346: return iChronology.days();
347: }
348:
349: //-----------------------------------------------------------------------
350: public int getMinimumValue() {
351: return MIN;
352: }
353:
354: //-----------------------------------------------------------------------
355: public int getMaximumValue() {
356: return iMax;
357: }
358:
359: //-----------------------------------------------------------------------
360: public long roundFloor(long instant) {
361: int year = iChronology.getYear(instant);
362: int month = iChronology.getMonthOfYear(instant, year);
363: return iChronology.getYearMonthMillis(year, month);
364: }
365:
366: //-----------------------------------------------------------------------
367: public long remainder(long instant) {
368: return instant - roundFloor(instant);
369: }
370:
371: //-----------------------------------------------------------------------
372: /**
373: * Serialization singleton
374: */
375: private Object readResolve() {
376: return iChronology.monthOfYear();
377: }
378: }
|