View Javadoc

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 }