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.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 private static final long serialVersionUID = 8318475124230605365L;
38
39 // Shared with RemainderDateTimeField.
40 final int iDivisor;
41 final DurationField iDurationField;
42
43 private final int iMin;
44 private final int iMax;
45
46 /**
47 * Constructor.
48 *
49 * @param field the field to wrap, like "year()".
50 * @param type the field type this field will actually use
51 * @param divisor divisor, such as 100 years in a century
52 * @throws IllegalArgumentException if divisor is less than two
53 */
54 public DividedDateTimeField(DateTimeField field,
55 DateTimeFieldType type, int divisor) {
56 super(field, type);
57
58 if (divisor < 2) {
59 throw new IllegalArgumentException("The divisor must be at least 2");
60 }
61
62 DurationField unitField = field.getDurationField();
63 if (unitField == null) {
64 iDurationField = null;
65 } else {
66 iDurationField = new ScaledDurationField(
67 unitField, type.getDurationType(), divisor);
68 }
69
70 iDivisor = divisor;
71
72 int i = field.getMinimumValue();
73 int min = (i >= 0) ? i / divisor : ((i + 1) / divisor - 1);
74
75 int j = field.getMaximumValue();
76 int max = (j >= 0) ? j / divisor : ((j + 1) / divisor - 1);
77
78 iMin = min;
79 iMax = max;
80 }
81
82 /**
83 * Construct a DividedDateTimeField that compliments the given
84 * RemainderDateTimeField.
85 *
86 * @param remainderField complimentary remainder field, like "yearOfCentury()".
87 * @param type the field type this field will actually use
88 */
89 public DividedDateTimeField(RemainderDateTimeField remainderField, DateTimeFieldType type) {
90 super(remainderField.getWrappedField(), type);
91 int divisor = iDivisor = remainderField.iDivisor;
92 iDurationField = remainderField.iRangeField;
93
94 DateTimeField field = getWrappedField();
95 int i = field.getMinimumValue();
96 int min = (i >= 0) ? i / divisor : ((i + 1) / divisor - 1);
97
98 int j = field.getMaximumValue();
99 int max = (j >= 0) ? j / divisor : ((j + 1) / divisor - 1);
100
101 iMin = min;
102 iMax = max;
103 }
104
105 /**
106 * Get the amount of scaled units from the specified time instant.
107 *
108 * @param instant the time instant in millis to query.
109 * @return the amount of scaled units extracted from the input.
110 */
111 public int get(long instant) {
112 int value = getWrappedField().get(instant);
113 if (value >= 0) {
114 return value / iDivisor;
115 } else {
116 return ((value + 1) / iDivisor) - 1;
117 }
118 }
119
120 /**
121 * Add the specified amount of scaled units to the specified time
122 * instant. The amount added may be negative.
123 *
124 * @param instant the time instant in millis to update.
125 * @param amount the amount of scaled units to add (can be negative).
126 * @return the updated time instant.
127 */
128 public long add(long instant, int amount) {
129 return getWrappedField().add(instant, amount * iDivisor);
130 }
131
132 /**
133 * Add the specified amount of scaled units to the specified time
134 * instant. The amount added may be negative.
135 *
136 * @param instant the time instant in millis to update.
137 * @param amount the amount of scaled units to add (can be negative).
138 * @return the updated time instant.
139 */
140 public long add(long instant, long amount) {
141 return getWrappedField().add(instant, amount * iDivisor);
142 }
143
144 /**
145 * Add to the scaled component of the specified time instant,
146 * wrapping around within that component if necessary.
147 *
148 * @param instant the time instant in millis to update.
149 * @param amount the amount of scaled units to add (can be negative).
150 * @return the updated time instant.
151 */
152 public long addWrapField(long instant, int amount) {
153 return set(instant, FieldUtils.getWrappedValue(get(instant), amount, iMin, iMax));
154 }
155
156 public int getDifference(long minuendInstant, long subtrahendInstant) {
157 return getWrappedField().getDifference(minuendInstant, subtrahendInstant) / iDivisor;
158 }
159
160 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
161 return getWrappedField().getDifferenceAsLong(minuendInstant, subtrahendInstant) / iDivisor;
162 }
163
164 /**
165 * Set the specified amount of scaled units to the specified time instant.
166 *
167 * @param instant the time instant in millis to update.
168 * @param value value of scaled units to set.
169 * @return the updated time instant.
170 * @throws IllegalArgumentException if value is too large or too small.
171 */
172 public long set(long instant, int value) {
173 FieldUtils.verifyValueBounds(this, value, iMin, iMax);
174 int remainder = getRemainder(getWrappedField().get(instant));
175 return getWrappedField().set(instant, value * iDivisor + remainder);
176 }
177
178 /**
179 * Returns a scaled version of the wrapped field's unit duration field.
180 */
181 public DurationField getDurationField() {
182 return iDurationField;
183 }
184
185 /**
186 * Get the minimum value for the field.
187 *
188 * @return the minimum value
189 */
190 public int getMinimumValue() {
191 return iMin;
192 }
193
194 /**
195 * Get the maximum value for the field.
196 *
197 * @return the maximum value
198 */
199 public int getMaximumValue() {
200 return iMax;
201 }
202
203 public long roundFloor(long instant) {
204 DateTimeField field = getWrappedField();
205 return field.roundFloor(field.set(instant, get(instant) * iDivisor));
206 }
207
208 public long remainder(long instant) {
209 return set(instant, get(getWrappedField().remainder(instant)));
210 }
211
212 /**
213 * Returns the divisor applied, in the field's units.
214 *
215 * @return the divisor
216 */
217 public int getDivisor() {
218 return iDivisor;
219 }
220
221 private int getRemainder(int value) {
222 if (value >= 0) {
223 return value % iDivisor;
224 } else {
225 return (iDivisor - 1) + ((value + 1) % iDivisor);
226 }
227 }
228
229 }