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 | } |