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