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  * Generic offset adjusting datetime field.
24  * <p>
25  * OffsetDateTimeField is thread-safe and immutable.
26  * 
27  * @author Brian S O'Neill
28  * @since 1.0
29  */

30 public class OffsetDateTimeField extends DecoratedDateTimeField {
31     @SuppressWarnings("unused")
32     private static final long serialVersionUID = 3145790132623583142L;
33
34     private final int iOffset;
35
36     private final int iMin;
37     private final int iMax;
38
39     /**
40      * Constructor.
41      * 
42      * @param field  the field to wrap, like "year()".
43      * @param offset  offset to add to field values
44      * @throws IllegalArgumentException if offset is zero
45      */

46     public OffsetDateTimeField(DateTimeField field, int offset) {
47         this(field, (field == null ? null : field.getType()), offset, Integer.MIN_VALUE, Integer.MAX_VALUE);
48     }
49
50     /**
51      * Constructor.
52      * 
53      * @param field  the field to wrap, like "year()".
54      * @param type  the field type this field actually uses
55      * @param offset  offset to add to field values
56      * @throws IllegalArgumentException if offset is zero
57      */

58     public OffsetDateTimeField(DateTimeField field, DateTimeFieldType type, int offset) {
59         this(field, type, offset, Integer.MIN_VALUE, Integer.MAX_VALUE);
60     }
61
62     /**
63      * Constructor.
64      * 
65      * @param field  the field to wrap, like "year()".
66      * @param type  the field type this field actually uses
67      * @param offset  offset to add to field values
68      * @param minValue  minimum allowed value
69      * @param maxValue  maximum allowed value
70      * @throws IllegalArgumentException if offset is zero
71      */

72     public OffsetDateTimeField(DateTimeField field, DateTimeFieldType type, int offset,
73                                int minValue, int maxValue) {
74         super(field, type);
75                 
76         if (offset == 0) {
77             throw new IllegalArgumentException("The offset cannot be zero");
78         }
79
80         iOffset = offset;
81
82         if (minValue < (field.getMinimumValue() + offset)) {
83             iMin = field.getMinimumValue() + offset;
84         } else {
85             iMin = minValue;
86         }
87         if (maxValue > (field.getMaximumValue() + offset)) {
88             iMax = field.getMaximumValue() + offset;
89         } else {
90             iMax = maxValue;
91         }
92     }
93
94     /**
95      * Get the amount of offset units from the specified time instant.
96      * 
97      * @param instant  the time instant in millis to query.
98      * @return the amount of units extracted from the input.
99      */

100     public int get(long instant) {
101         return super.get(instant) + iOffset;
102     }
103
104     /**
105      * Add the specified amount of offset units to the specified time
106      * instant. The amount added may be negative.
107      * 
108      * @param instant  the time instant in millis to update.
109      * @param amount  the amount of units to add (can be negative).
110      * @return the updated time instant.
111      */

112     public long add(long instant, int amount) {
113         instant = super.add(instant, amount);
114         FieldUtils.verifyValueBounds(this, get(instant), iMin, iMax);
115         return instant;
116     }
117
118     /**
119      * Add the specified amount of offset units to the specified time
120      * instant. The amount added may be negative.
121      * 
122      * @param instant  the time instant in millis to update.
123      * @param amount  the amount of units to add (can be negative).
124      * @return the updated time instant.
125      */

126     public long add(long instant, long amount) {
127         instant = super.add(instant, amount);
128         FieldUtils.verifyValueBounds(this, get(instant), iMin, iMax);
129         return instant;
130     }
131
132     /**
133      * Add to the offset component of the specified time instant,
134      * wrapping around within that component if necessary.
135      * 
136      * @param instant  the time instant in millis to update.
137      * @param amount  the amount of units to add (can be negative).
138      * @return the updated time instant.
139      */

140     public long addWrapField(long instant, int amount) {
141         return set(instant, FieldUtils.getWrappedValue(get(instant), amount, iMin, iMax));
142     }
143
144     /**
145      * Set the specified amount of offset units to the specified time instant.
146      * 
147      * @param instant  the time instant in millis to update.
148      * @param value  value of units to set.
149      * @return the updated time instant.
150      * @throws IllegalArgumentException if value is too large or too small.
151      */

152     public long set(long instant, int value) {
153         FieldUtils.verifyValueBounds(this, value, iMin, iMax);
154         return super.set(instant, value - iOffset);
155     }
156
157     public boolean isLeap(long instant) {
158         return getWrappedField().isLeap(instant);
159     }
160
161     public int getLeapAmount(long instant) {
162         return getWrappedField().getLeapAmount(instant);
163     }
164
165     public DurationField getLeapDurationField() {
166         return getWrappedField().getLeapDurationField();
167     }
168
169     /**
170      * Get the minimum value for the field.
171      * 
172      * @return the minimum value
173      */

174     public int getMinimumValue() {
175         return iMin;
176     }
177
178     /**
179      * Get the maximum value for the field.
180      * 
181      * @return the maximum value
182      */

183     public int getMaximumValue() {
184         return iMax;
185     }
186     
187     public long roundFloor(long instant) {
188         return getWrappedField().roundFloor(instant);
189     }
190
191     public long roundCeiling(long instant) {
192         return getWrappedField().roundCeiling(instant);
193     }
194
195     public long roundHalfFloor(long instant) {
196         return getWrappedField().roundHalfFloor(instant);
197     }
198
199     public long roundHalfCeiling(long instant) {
200         return getWrappedField().roundHalfCeiling(instant);
201     }
202
203     public long roundHalfEven(long instant) {
204         return getWrappedField().roundHalfEven(instant);
205     }
206
207     public long remainder(long instant) {
208         return getWrappedField().remainder(instant);
209     }
210
211     /**
212      * Returns the offset added to the field values.
213      * 
214      * @return the offset
215      */

216     public int getOffset() {
217         return iOffset;
218     }
219 }
220