1 /*
2 * Copyright 2001-2013 Stephen Colebourne
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.joda.time.chrono;
17
18 import org.joda.time.DateTimeConstants;
19 import org.joda.time.DateTimeFieldType;
20 import org.joda.time.DurationField;
21 import org.joda.time.field.FieldUtils;
22 import org.joda.time.field.ImpreciseDateTimeField;
23
24 /**
25 * Provides time calculations for the week of the weekyear component of time.
26 *
27 * @author Guy Allard
28 * @author Stephen Colebourne
29 * @author Brian S O'Neill
30 * @since 1.1, refactored from GJWeekyearDateTimeField
31 */
32 final class BasicWeekyearDateTimeField extends ImpreciseDateTimeField {
33
34 @SuppressWarnings("unused")
35 private static final long serialVersionUID = 6215066916806820644L;
36
37 private static final long WEEK_53 = (53L - 1) * DateTimeConstants.MILLIS_PER_WEEK;
38
39 private final BasicChronology iChronology;
40
41 /**
42 * Restricted constructor
43 */
44 BasicWeekyearDateTimeField(BasicChronology chronology) {
45 super(DateTimeFieldType.weekyear(), chronology.getAverageMillisPerYear());
46 iChronology = chronology;
47 }
48
49 public boolean isLenient() {
50 return false;
51 }
52
53 /**
54 * Get the Year of a week based year component of the specified time instant.
55 *
56 * @see org.joda.time.DateTimeField#get
57 * @param instant the time instant in millis to query.
58 * @return the year extracted from the input.
59 */
60 public int get(long instant) {
61 return iChronology.getWeekyear(instant);
62 }
63
64 /**
65 * Add the specified years to the specified time instant.
66 *
67 * @see org.joda.time.DateTimeField#add
68 * @param instant the time instant in millis to update.
69 * @param years the years to add (can be negative).
70 * @return the updated time instant.
71 */
72 public long add(long instant, int years) {
73 if (years == 0) {
74 return instant;
75 }
76 return set(instant, get(instant) + years);
77 }
78
79 public long add(long instant, long value) {
80 return add(instant, FieldUtils.safeToInt(value));
81 }
82
83 /**
84 * Add to the year component of the specified time instant
85 * wrapping around within that component if necessary.
86 *
87 * @see org.joda.time.DateTimeField#addWrapField
88 * @param instant the time instant in millis to update.
89 * @param years the years to add (can be negative).
90 * @return the updated time instant.
91 */
92 public long addWrapField(long instant, int years) {
93 return add(instant, years);
94 }
95
96 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
97 if (minuendInstant < subtrahendInstant) {
98 return -getDifference(subtrahendInstant, minuendInstant);
99 }
100
101 int minuendWeekyear = get(minuendInstant);
102 int subtrahendWeekyear = get(subtrahendInstant);
103
104 long minuendRem = remainder(minuendInstant);
105 long subtrahendRem = remainder(subtrahendInstant);
106
107 // Balance leap weekyear differences on remainders.
108 if (subtrahendRem >= WEEK_53 && iChronology.getWeeksInYear(minuendWeekyear) <= 52) {
109 subtrahendRem -= DateTimeConstants.MILLIS_PER_WEEK;
110 }
111
112 int difference = minuendWeekyear - subtrahendWeekyear;
113 if (minuendRem < subtrahendRem) {
114 difference--;
115 }
116 return difference;
117 }
118
119 /**
120 * Set the Year of a week based year component of the specified time instant.
121 *
122 * @see org.joda.time.DateTimeField#set
123 * @param instant the time instant in millis to update.
124 * @param year the year (-9999,9999) to set the date to.
125 * @return the updated DateTime.
126 * @throws IllegalArgumentException if year is invalid.
127 */
128 public long set(long instant, int year) {
129 FieldUtils.verifyValueBounds(this, Math.abs(year),
130 iChronology.getMinYear(), iChronology.getMaxYear());
131 //
132 // Do nothing if no real change is requested.
133 //
134 int thisWeekyear = get( instant );
135 if ( thisWeekyear == year ) {
136 return instant;
137 }
138 //
139 // Calculate the DayOfWeek (to be preserved).
140 //
141 int thisDow = iChronology.getDayOfWeek(instant);
142 //
143 // Calculate the maximum weeks in the target year.
144 //
145 int weeksInFromYear = iChronology.getWeeksInYear( thisWeekyear );
146 int weeksInToYear = iChronology.getWeeksInYear( year );
147 int maxOutWeeks = (weeksInToYear < weeksInFromYear) ?
148 weeksInToYear : weeksInFromYear;
149 //
150 // Get the current week of the year. This will be preserved in
151 // the output unless it is greater than the maximum possible
152 // for the target weekyear. In that case it is adjusted
153 // to the maximum possible.
154 //
155 int setToWeek = iChronology.getWeekOfWeekyear(instant);
156 if ( setToWeek > maxOutWeeks ) {
157 setToWeek = maxOutWeeks;
158 }
159 //
160 // Get a wroking copy of the current date-time.
161 // This can be a convenience for debugging.
162 //
163 long workInstant = instant; // Get a copy
164 //
165 // Attempt to get close to the proper weekyear.
166 // Note - we cannot currently call ourself, so we just call
167 // set for the year. This at least gets us close.
168 //
169 workInstant = iChronology.setYear( workInstant, year );
170 //
171 // Calculate the weekyear number for the get close to value
172 // (which might not be equal to the year just set).
173 //
174 int workWoyYear = get( workInstant );
175
176 //
177 // At most we are off by one year, which can be "fixed" by
178 // adding/subtracting a week.
179 //
180 if ( workWoyYear < year ) {
181 workInstant += DateTimeConstants.MILLIS_PER_WEEK;
182 } else if ( workWoyYear > year ) {
183 workInstant -= DateTimeConstants.MILLIS_PER_WEEK;
184 }
185 //
186 // Set the proper week in the current weekyear.
187 //
188
189 // BEGIN: possible set WeekOfWeekyear logic.
190 int currentWoyWeek = iChronology.getWeekOfWeekyear(workInstant);
191 // No range check required (we already know it is OK).
192 workInstant = workInstant + (setToWeek - currentWoyWeek)
193 * (long)DateTimeConstants.MILLIS_PER_WEEK;
194 // END: possible set WeekOfWeekyear logic.
195
196 //
197 // Reset DayOfWeek to previous value.
198 //
199 // Note: This works fine, but it ideally shouldn't invoke other
200 // fields from within a field.
201 workInstant = iChronology.dayOfWeek().set( workInstant, thisDow );
202 //
203 // Return result.
204 //
205 return workInstant;
206 }
207
208 public DurationField getRangeDurationField() {
209 return null;
210 }
211
212 public boolean isLeap(long instant) {
213 return iChronology.getWeeksInYear(iChronology.getWeekyear(instant)) > 52;
214 }
215
216 public int getLeapAmount(long instant) {
217 return iChronology.getWeeksInYear(iChronology.getWeekyear(instant)) - 52;
218 }
219
220 public DurationField getLeapDurationField() {
221 return iChronology.weeks();
222 }
223
224 public int getMinimumValue() {
225 return iChronology.getMinYear();
226 }
227
228 public int getMaximumValue() {
229 return iChronology.getMaxYear();
230 }
231
232 public long roundFloor(long instant) {
233 // Note: This works fine, but it ideally shouldn't invoke other
234 // fields from within a field.
235 instant = iChronology.weekOfWeekyear().roundFloor(instant);
236 int wow = iChronology.getWeekOfWeekyear(instant);
237 if (wow > 1) {
238 instant -= ((long) DateTimeConstants.MILLIS_PER_WEEK) * (wow - 1);
239 }
240 return instant;
241 }
242
243 public long remainder(long instant) {
244 return instant - roundFloor(instant);
245 }
246
247 /**
248 * Serialization singleton
249 */
250 private Object readResolve() {
251 return iChronology.weekyear();
252 }
253 }
254