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.DurationField;
021: import org.joda.time.field.FieldUtils;
022: import org.joda.time.field.ImpreciseDateTimeField;
023:
024: /**
025: * Provides time calculations for the week of the weekyear component of time.
026: *
027: * @author Guy Allard
028: * @author Stephen Colebourne
029: * @author Brian S O'Neill
030: * @since 1.1, refactored from GJWeekyearDateTimeField
031: */
032: final class BasicWeekyearDateTimeField extends ImpreciseDateTimeField {
033:
034: private static final long serialVersionUID = 6215066916806820644L;
035:
036: private static final long WEEK_53 = (53L - 1)
037: * DateTimeConstants.MILLIS_PER_WEEK;
038:
039: private final BasicChronology iChronology;
040:
041: /**
042: * Restricted constructor
043: */
044: BasicWeekyearDateTimeField(BasicChronology chronology) {
045: super (DateTimeFieldType.weekyear(), chronology
046: .getAverageMillisPerYear());
047: iChronology = chronology;
048: }
049:
050: public boolean isLenient() {
051: return false;
052: }
053:
054: /**
055: * Get the Year of a week based year component of the specified time instant.
056: *
057: * @see org.joda.time.DateTimeField#get
058: * @param instant the time instant in millis to query.
059: * @return the year extracted from the input.
060: */
061: public int get(long instant) {
062: return iChronology.getWeekyear(instant);
063: }
064:
065: /**
066: * Add the specified years to the specified time instant.
067: *
068: * @see org.joda.time.DateTimeField#add
069: * @param instant the time instant in millis to update.
070: * @param years the years to add (can be negative).
071: * @return the updated time instant.
072: */
073: public long add(long instant, int years) {
074: if (years == 0) {
075: return instant;
076: }
077: return set(instant, get(instant) + years);
078: }
079:
080: public long add(long instant, long value) {
081: return add(instant, FieldUtils.safeToInt(value));
082: }
083:
084: /**
085: * Add to the year component of the specified time instant
086: * wrapping around within that component if necessary.
087: *
088: * @see org.joda.time.DateTimeField#addWrapField
089: * @param instant the time instant in millis to update.
090: * @param years the years to add (can be negative).
091: * @return the updated time instant.
092: */
093: public long addWrapField(long instant, int years) {
094: return add(instant, years);
095: }
096:
097: public long getDifferenceAsLong(long minuendInstant,
098: long subtrahendInstant) {
099: if (minuendInstant < subtrahendInstant) {
100: return -getDifference(subtrahendInstant, minuendInstant);
101: }
102:
103: int minuendWeekyear = get(minuendInstant);
104: int subtrahendWeekyear = get(subtrahendInstant);
105:
106: long minuendRem = remainder(minuendInstant);
107: long subtrahendRem = remainder(subtrahendInstant);
108:
109: // Balance leap weekyear differences on remainders.
110: if (subtrahendRem >= WEEK_53
111: && iChronology.getWeeksInYear(minuendWeekyear) <= 52) {
112: subtrahendRem -= DateTimeConstants.MILLIS_PER_WEEK;
113: }
114:
115: int difference = minuendWeekyear - subtrahendWeekyear;
116: if (minuendRem < subtrahendRem) {
117: difference--;
118: }
119: return difference;
120: }
121:
122: /**
123: * Set the Year of a week based year component of the specified time instant.
124: *
125: * @see org.joda.time.DateTimeField#set
126: * @param instant the time instant in millis to update.
127: * @param year the year (-9999,9999) to set the date to.
128: * @return the updated DateTime.
129: * @throws IllegalArgumentException if year is invalid.
130: */
131: public long set(long instant, int year) {
132: FieldUtils.verifyValueBounds(this , Math.abs(year), iChronology
133: .getMinYear(), iChronology.getMaxYear());
134: //
135: // Do nothing if no real change is requested.
136: //
137: int this Weekyear = get(instant);
138: if (this Weekyear == year) {
139: return instant;
140: }
141: //
142: // Calculate the DayOfWeek (to be preserved).
143: //
144: int this Dow = iChronology.getDayOfWeek(instant);
145: //
146: // Calculate the maximum weeks in the target year.
147: //
148: int weeksInFromYear = iChronology.getWeeksInYear(this Weekyear);
149: int weeksInToYear = iChronology.getWeeksInYear(year);
150: int maxOutWeeks = (weeksInToYear < weeksInFromYear) ? weeksInToYear
151: : weeksInFromYear;
152: //
153: // Get the current week of the year. This will be preserved in
154: // the output unless it is greater than the maximum possible
155: // for the target weekyear. In that case it is adjusted
156: // to the maximum possible.
157: //
158: int setToWeek = iChronology.getWeekOfWeekyear(instant);
159: if (setToWeek > maxOutWeeks) {
160: setToWeek = maxOutWeeks;
161: }
162: //
163: // Get a wroking copy of the current date-time.
164: // This can be a convenience for debugging.
165: //
166: long workInstant = instant; // Get a copy
167: //
168: // Attempt to get close to the proper weekyear.
169: // Note - we cannot currently call ourself, so we just call
170: // set for the year. This at least gets us close.
171: //
172: workInstant = iChronology.setYear(workInstant, year);
173: //
174: // Calculate the weekyear number for the get close to value
175: // (which might not be equal to the year just set).
176: //
177: int workWoyYear = get(workInstant);
178:
179: //
180: // At most we are off by one year, which can be "fixed" by
181: // adding/subtracting a week.
182: //
183: if (workWoyYear < year) {
184: workInstant += DateTimeConstants.MILLIS_PER_WEEK;
185: } else if (workWoyYear > year) {
186: workInstant -= DateTimeConstants.MILLIS_PER_WEEK;
187: }
188: //
189: // Set the proper week in the current weekyear.
190: //
191:
192: // BEGIN: possible set WeekOfWeekyear logic.
193: int currentWoyWeek = iChronology.getWeekOfWeekyear(workInstant);
194: // No range check required (we already know it is OK).
195: workInstant = workInstant + (setToWeek - currentWoyWeek)
196: * (long) DateTimeConstants.MILLIS_PER_WEEK;
197: // END: possible set WeekOfWeekyear logic.
198:
199: //
200: // Reset DayOfWeek to previous value.
201: //
202: // Note: This works fine, but it ideally shouldn't invoke other
203: // fields from within a field.
204: workInstant = iChronology.dayOfWeek().set(workInstant, this Dow);
205: //
206: // Return result.
207: //
208: return workInstant;
209: }
210:
211: public DurationField getRangeDurationField() {
212: return null;
213: }
214:
215: public boolean isLeap(long instant) {
216: return iChronology.getWeeksInYear(iChronology
217: .getWeekyear(instant)) > 52;
218: }
219:
220: public int getLeapAmount(long instant) {
221: return iChronology.getWeeksInYear(iChronology
222: .getWeekyear(instant)) - 52;
223: }
224:
225: public DurationField getLeapDurationField() {
226: return iChronology.weeks();
227: }
228:
229: public int getMinimumValue() {
230: return iChronology.getMinYear();
231: }
232:
233: public int getMaximumValue() {
234: return iChronology.getMaxYear();
235: }
236:
237: public long roundFloor(long instant) {
238: // Note: This works fine, but it ideally shouldn't invoke other
239: // fields from within a field.
240: instant = iChronology.weekOfWeekyear().roundFloor(instant);
241: int wow = iChronology.getWeekOfWeekyear(instant);
242: if (wow > 1) {
243: instant -= ((long) DateTimeConstants.MILLIS_PER_WEEK)
244: * (wow - 1);
245: }
246: return instant;
247: }
248:
249: public long remainder(long instant) {
250: return instant - roundFloor(instant);
251: }
252:
253: /**
254: * Serialization singleton
255: */
256: private Object readResolve() {
257: return iChronology.weekyear();
258: }
259: }
|