001 /*
002 * Copyright 2001-2005 Stephen Colebourne
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.joda.time.field;
017
018 import org.joda.time.DateTimeField;
019 import org.joda.time.DateTimeFieldType;
020 import org.joda.time.DurationField;
021
022 /**
023 * Divides a DateTimeField such that the retrieved values are reduced by a
024 * fixed divisor. The field's unit duration is scaled accordingly, but the
025 * range duration is unchanged.
026 * <p>
027 * DividedDateTimeField is thread-safe and immutable.
028 *
029 * @see RemainderDateTimeField
030 *
031 * @author Stephen Colebourne
032 * @author Brian S O'Neill
033 * @since 1.0
034 */
035 public class DividedDateTimeField extends DecoratedDateTimeField {
036
037 private static final long serialVersionUID = 8318475124230605365L;
038
039 // Shared with RemainderDateTimeField.
040 final int iDivisor;
041 final DurationField iDurationField;
042
043 private final int iMin;
044 private final int iMax;
045
046 /**
047 * Constructor.
048 *
049 * @param field the field to wrap, like "year()".
050 * @param type the field type this field will actually use
051 * @param divisor divisor, such as 100 years in a century
052 * @throws IllegalArgumentException if divisor is less than two
053 */
054 public DividedDateTimeField(DateTimeField field,
055 DateTimeFieldType type, int divisor) {
056 super(field, type);
057
058 if (divisor < 2) {
059 throw new IllegalArgumentException("The divisor must be at least 2");
060 }
061
062 DurationField unitField = field.getDurationField();
063 if (unitField == null) {
064 iDurationField = null;
065 } else {
066 iDurationField = new ScaledDurationField(
067 unitField, type.getDurationType(), divisor);
068 }
069
070 iDivisor = divisor;
071
072 int i = field.getMinimumValue();
073 int min = (i >= 0) ? i / divisor : ((i + 1) / divisor - 1);
074
075 int j = field.getMaximumValue();
076 int max = (j >= 0) ? j / divisor : ((j + 1) / divisor - 1);
077
078 iMin = min;
079 iMax = max;
080 }
081
082 /**
083 * Construct a DividedDateTimeField that compliments the given
084 * RemainderDateTimeField.
085 *
086 * @param remainderField complimentary remainder field, like "yearOfCentury()".
087 * @param type the field type this field will actually use
088 */
089 public DividedDateTimeField(RemainderDateTimeField remainderField, DateTimeFieldType type) {
090 super(remainderField.getWrappedField(), type);
091 int divisor = iDivisor = remainderField.iDivisor;
092 iDurationField = remainderField.iRangeField;
093
094 DateTimeField field = getWrappedField();
095 int i = field.getMinimumValue();
096 int min = (i >= 0) ? i / divisor : ((i + 1) / divisor - 1);
097
098 int j = field.getMaximumValue();
099 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 }