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