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.field;
17
18 import org.joda.time.DateTimeField;
19 import org.joda.time.DateTimeFieldType;
20 import org.joda.time.DurationField;
21
22 /**
23 * Divides a DateTimeField such that the retrieved values are reduced by a
24 * fixed divisor. The field's unit duration is scaled accordingly, but the
25 * range duration is unchanged.
26 * <p>
27 * DividedDateTimeField is thread-safe and immutable.
28 *
29 * @see RemainderDateTimeField
30 *
31 * @author Stephen Colebourne
32 * @author Brian S O'Neill
33 * @since 1.0
34 */
35 public class DividedDateTimeField extends DecoratedDateTimeField {
36
37 @SuppressWarnings("unused")
38 private static final long serialVersionUID = 8318475124230605365L;
39
40 // Shared with RemainderDateTimeField.
41 final int iDivisor;
42 final DurationField iDurationField;
43 final DurationField iRangeDurationField;
44
45 private final int iMin;
46 private final int iMax;
47
48 /**
49 * Constructor.
50 *
51 * @param field the field to wrap, like "year()".
52 * @param type the field type this field will actually use
53 * @param divisor divisor, such as 100 years in a century
54 * @throws IllegalArgumentException if divisor is less than two
55 */
56 public DividedDateTimeField(DateTimeField field,
57 DateTimeFieldType type, int divisor) {
58 this(field, field.getRangeDurationField(), type, divisor);
59 }
60
61 /**
62 * Constructor.
63 *
64 * @param field the field to wrap, like "year()".
65 * @param rangeField the range field, null to derive
66 * @param type the field type this field will actually use
67 * @param divisor divisor, such as 100 years in a century
68 * @throws IllegalArgumentException if divisor is less than two
69 */
70 public DividedDateTimeField(DateTimeField field, DurationField rangeField,
71 DateTimeFieldType type, int divisor) {
72 super(field, type);
73 if (divisor < 2) {
74 throw new IllegalArgumentException("The divisor must be at least 2");
75 }
76 DurationField unitField = field.getDurationField();
77 if (unitField == null) {
78 iDurationField = null;
79 } else {
80 iDurationField = new ScaledDurationField(
81 unitField, type.getDurationType(), divisor);
82 }
83 iRangeDurationField = rangeField;
84 iDivisor = divisor;
85 int i = field.getMinimumValue();
86 int min = (i >= 0) ? i / divisor : ((i + 1) / divisor - 1);
87 int j = field.getMaximumValue();
88 int max = (j >= 0) ? j / divisor : ((j + 1) / divisor - 1);
89 iMin = min;
90 iMax = max;
91 }
92
93 /**
94 * Construct a DividedDateTimeField that compliments the given
95 * RemainderDateTimeField.
96 *
97 * @param remainderField complimentary remainder field, like "yearOfCentury()".
98 * @param type the field type this field will actually use
99 */
100 public DividedDateTimeField(RemainderDateTimeField remainderField, DateTimeFieldType type) {
101 this(remainderField, null, type);
102 }
103
104 /**
105 * Construct a DividedDateTimeField that compliments the given
106 * RemainderDateTimeField.
107 *
108 * @param remainderField complimentary remainder field, like "yearOfCentury()".
109 * @param rangeField the range field, null to derive
110 * @param type the field type this field will actually use
111 */
112 public DividedDateTimeField(RemainderDateTimeField remainderField, DurationField rangeField, DateTimeFieldType type) {
113 super(remainderField.getWrappedField(), type);
114 int divisor = iDivisor = remainderField.iDivisor;
115 iDurationField = remainderField.iRangeField;
116 iRangeDurationField = rangeField;
117 DateTimeField field = getWrappedField();
118 int i = field.getMinimumValue();
119 int min = (i >= 0) ? i / divisor : ((i + 1) / divisor - 1);
120 int j = field.getMaximumValue();
121 int max = (j >= 0) ? j / divisor : ((j + 1) / divisor - 1);
122 iMin = min;
123 iMax = max;
124 }
125
126 @Override
127 public DurationField getRangeDurationField() {
128 if (iRangeDurationField != null) {
129 return iRangeDurationField;
130 }
131 return super.getRangeDurationField();
132 }
133
134 /**
135 * Get the amount of scaled units from the specified time instant.
136 *
137 * @param instant the time instant in millis to query.
138 * @return the amount of scaled units extracted from the input.
139 */
140 public int get(long instant) {
141 int value = getWrappedField().get(instant);
142 if (value >= 0) {
143 return value / iDivisor;
144 } else {
145 return ((value + 1) / iDivisor) - 1;
146 }
147 }
148
149 /**
150 * Add the specified amount of scaled units to the specified time
151 * instant. The amount added may be negative.
152 *
153 * @param instant the time instant in millis to update.
154 * @param amount the amount of scaled units to add (can be negative).
155 * @return the updated time instant.
156 */
157 public long add(long instant, int amount) {
158 return getWrappedField().add(instant, amount * iDivisor);
159 }
160
161 /**
162 * Add the specified amount of scaled units to the specified time
163 * instant. The amount added may be negative.
164 *
165 * @param instant the time instant in millis to update.
166 * @param amount the amount of scaled units to add (can be negative).
167 * @return the updated time instant.
168 */
169 public long add(long instant, long amount) {
170 return getWrappedField().add(instant, amount * iDivisor);
171 }
172
173 /**
174 * Add to the scaled component of the specified time instant,
175 * wrapping around within that component if necessary.
176 *
177 * @param instant the time instant in millis to update.
178 * @param amount the amount of scaled units to add (can be negative).
179 * @return the updated time instant.
180 */
181 public long addWrapField(long instant, int amount) {
182 return set(instant, FieldUtils.getWrappedValue(get(instant), amount, iMin, iMax));
183 }
184
185 public int getDifference(long minuendInstant, long subtrahendInstant) {
186 return getWrappedField().getDifference(minuendInstant, subtrahendInstant) / iDivisor;
187 }
188
189 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
190 return getWrappedField().getDifferenceAsLong(minuendInstant, subtrahendInstant) / iDivisor;
191 }
192
193 /**
194 * Set the specified amount of scaled units to the specified time instant.
195 *
196 * @param instant the time instant in millis to update.
197 * @param value value of scaled units to set.
198 * @return the updated time instant.
199 * @throws IllegalArgumentException if value is too large or too small.
200 */
201 public long set(long instant, int value) {
202 FieldUtils.verifyValueBounds(this, value, iMin, iMax);
203 int remainder = getRemainder(getWrappedField().get(instant));
204 return getWrappedField().set(instant, value * iDivisor + remainder);
205 }
206
207 /**
208 * Returns a scaled version of the wrapped field's unit duration field.
209 */
210 public DurationField getDurationField() {
211 return iDurationField;
212 }
213
214 /**
215 * Get the minimum value for the field.
216 *
217 * @return the minimum value
218 */
219 public int getMinimumValue() {
220 return iMin;
221 }
222
223 /**
224 * Get the maximum value for the field.
225 *
226 * @return the maximum value
227 */
228 public int getMaximumValue() {
229 return iMax;
230 }
231
232 public long roundFloor(long instant) {
233 DateTimeField field = getWrappedField();
234 return field.roundFloor(field.set(instant, get(instant) * iDivisor));
235 }
236
237 public long remainder(long instant) {
238 return set(instant, get(getWrappedField().remainder(instant)));
239 }
240
241 /**
242 * Returns the divisor applied, in the field's units.
243 *
244 * @return the divisor
245 */
246 public int getDivisor() {
247 return iDivisor;
248 }
249
250 private int getRemainder(int value) {
251 if (value >= 0) {
252 return value % iDivisor;
253 } else {
254 return (iDivisor - 1) + ((value + 1) % iDivisor);
255 }
256 }
257
258 }
259