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