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 */
016package org.joda.time.field;
017
018import org.joda.time.DateTimeField;
019import org.joda.time.DateTimeFieldType;
020import 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 */
030public 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}