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