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     * Generic offset adjusting datetime field.
024     * <p>
025     * OffsetDateTimeField is thread-safe and immutable.
026     * 
027     * @author Brian S O'Neill
028     * @since 1.0
029     */
030    public class OffsetDateTimeField extends DecoratedDateTimeField {
031        private static final long serialVersionUID = 3145790132623583142L;
032    
033        private final int iOffset;
034    
035        private final int iMin;
036        private final int iMax;
037    
038        /**
039         * Constructor.
040         * 
041         * @param field  the field to wrap, like "year()".
042         * @param offset  offset to add to field values
043         * @throws IllegalArgumentException if offset is zero
044         */
045        public OffsetDateTimeField(DateTimeField field, int offset) {
046            this(field, (field == null ? null : field.getType()), offset, Integer.MIN_VALUE, Integer.MAX_VALUE);
047        }
048    
049        /**
050         * Constructor.
051         * 
052         * @param field  the field to wrap, like "year()".
053         * @param type  the field type this field actually uses
054         * @param offset  offset to add to field values
055         * @throws IllegalArgumentException if offset is zero
056         */
057        public OffsetDateTimeField(DateTimeField field, DateTimeFieldType type, int offset) {
058            this(field, type, offset, Integer.MIN_VALUE, Integer.MAX_VALUE);
059        }
060    
061        /**
062         * Constructor.
063         * 
064         * @param field  the field to wrap, like "year()".
065         * @param type  the field type this field actually uses
066         * @param offset  offset to add to field values
067         * @param minValue  minimum allowed value
068         * @param maxValue  maximum allowed value
069         * @throws IllegalArgumentException if offset is zero
070         */
071        public OffsetDateTimeField(DateTimeField field, DateTimeFieldType type, int offset,
072                                   int minValue, int maxValue) {
073            super(field, type);
074                    
075            if (offset == 0) {
076                throw new IllegalArgumentException("The offset cannot be zero");
077            }
078    
079            iOffset = offset;
080    
081            if (minValue < (field.getMinimumValue() + offset)) {
082                iMin = field.getMinimumValue() + offset;
083            } else {
084                iMin = minValue;
085            }
086            if (maxValue > (field.getMaximumValue() + offset)) {
087                iMax = field.getMaximumValue() + offset;
088            } else {
089                iMax = maxValue;
090            }
091        }
092    
093        /**
094         * Get the amount of offset units from the specified time instant.
095         * 
096         * @param instant  the time instant in millis to query.
097         * @return the amount of units extracted from the input.
098         */
099        public int get(long instant) {
100            return super.get(instant) + iOffset;
101        }
102    
103        /**
104         * Add the specified amount of offset units to the specified time
105         * instant. The amount added may be negative.
106         * 
107         * @param instant  the time instant in millis to update.
108         * @param amount  the amount of units to add (can be negative).
109         * @return the updated time instant.
110         */
111        public long add(long instant, int amount) {
112            instant = super.add(instant, amount);
113            FieldUtils.verifyValueBounds(this, get(instant), iMin, iMax);
114            return instant;
115        }
116    
117        /**
118         * Add the specified amount of offset units to the specified time
119         * instant. The amount added may be negative.
120         * 
121         * @param instant  the time instant in millis to update.
122         * @param amount  the amount of units to add (can be negative).
123         * @return the updated time instant.
124         */
125        public long add(long instant, long amount) {
126            instant = super.add(instant, amount);
127            FieldUtils.verifyValueBounds(this, get(instant), iMin, iMax);
128            return instant;
129        }
130    
131        /**
132         * Add to the offset component of the specified time instant,
133         * wrapping around within that component if necessary.
134         * 
135         * @param instant  the time instant in millis to update.
136         * @param amount  the amount of units to add (can be negative).
137         * @return the updated time instant.
138         */
139        public long addWrapField(long instant, int amount) {
140            return set(instant, FieldUtils.getWrappedValue(get(instant), amount, iMin, iMax));
141        }
142    
143        /**
144         * Set the specified amount of offset units to the specified time instant.
145         * 
146         * @param instant  the time instant in millis to update.
147         * @param value  value of units to set.
148         * @return the updated time instant.
149         * @throws IllegalArgumentException if value is too large or too small.
150         */
151        public long set(long instant, int value) {
152            FieldUtils.verifyValueBounds(this, value, iMin, iMax);
153            return super.set(instant, value - iOffset);
154        }
155    
156        public boolean isLeap(long instant) {
157            return getWrappedField().isLeap(instant);
158        }
159    
160        public int getLeapAmount(long instant) {
161            return getWrappedField().getLeapAmount(instant);
162        }
163    
164        public DurationField getLeapDurationField() {
165            return getWrappedField().getLeapDurationField();
166        }
167    
168        /**
169         * Get the minimum value for the field.
170         * 
171         * @return the minimum value
172         */
173        public int getMinimumValue() {
174            return iMin;
175        }
176    
177        /**
178         * Get the maximum value for the field.
179         * 
180         * @return the maximum value
181         */
182        public int getMaximumValue() {
183            return iMax;
184        }
185        
186        public long roundFloor(long instant) {
187            return getWrappedField().roundFloor(instant);
188        }
189    
190        public long roundCeiling(long instant) {
191            return getWrappedField().roundCeiling(instant);
192        }
193    
194        public long roundHalfFloor(long instant) {
195            return getWrappedField().roundHalfFloor(instant);
196        }
197    
198        public long roundHalfCeiling(long instant) {
199            return getWrappedField().roundHalfCeiling(instant);
200        }
201    
202        public long roundHalfEven(long instant) {
203            return getWrappedField().roundHalfEven(instant);
204        }
205    
206        public long remainder(long instant) {
207            return getWrappedField().remainder(instant);
208        }
209    
210        /**
211         * Returns the offset added to the field values.
212         * 
213         * @return the offset
214         */
215        public int getOffset() {
216            return iOffset;
217        }
218    }