1 /*
2 * Copyright 2001-2011 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.base;
17
18 import java.io.Serializable;
19 import java.util.Locale;
20
21 import org.joda.time.Chronology;
22 import org.joda.time.DateTimeField;
23 import org.joda.time.DateTimeUtils;
24 import org.joda.time.ReadablePartial;
25 import org.joda.time.convert.ConverterManager;
26 import org.joda.time.convert.PartialConverter;
27 import org.joda.time.format.DateTimeFormat;
28 import org.joda.time.format.DateTimeFormatter;
29
30 /**
31 * BasePartial is an abstract implementation of ReadablePartial that stores
32 * data in array and <code>Chronology</code> fields.
33 * <p>
34 * This class should generally not be used directly by API users.
35 * The {@link org.joda.time.ReadablePartial} interface should be used when different
36 * kinds of partial objects are to be referenced.
37 * <p>
38 * BasePartial subclasses may be mutable and not thread-safe.
39 *
40 * @author Stephen Colebourne
41 * @since 1.0
42 */
43 public abstract class BasePartial
44 extends AbstractPartial
45 implements ReadablePartial, Serializable {
46
47 /** Serialization version */
48 private static final long serialVersionUID = 2353678632973660L;
49
50 /** The chronology in use */
51 private final Chronology iChronology;
52 /** The values of each field in this partial */
53 private final int[] iValues;
54
55 //-----------------------------------------------------------------------
56 /**
57 * Constructs a partial with the current time, using ISOChronology in
58 * the default zone to extract the fields.
59 * <p>
60 * The constructor uses the default time zone, resulting in the local time
61 * being initialised. Once the constructor is complete, all further calculations
62 * are performed without reference to a timezone (by switching to UTC).
63 */
64 protected BasePartial() {
65 this(DateTimeUtils.currentTimeMillis(), null);
66 }
67
68 /**
69 * Constructs a partial with the current time, using the specified chronology
70 * and zone to extract the fields.
71 * <p>
72 * The constructor uses the time zone of the chronology specified.
73 * Once the constructor is complete, all further calculations are performed
74 * without reference to a timezone (by switching to UTC).
75 *
76 * @param chronology the chronology, null means ISOChronology in the default zone
77 */
78 protected BasePartial(Chronology chronology) {
79 this(DateTimeUtils.currentTimeMillis(), chronology);
80 }
81
82 /**
83 * Constructs a partial extracting the partial fields from the specified
84 * milliseconds using the ISOChronology in the default zone.
85 * <p>
86 * The constructor uses the default time zone, resulting in the local time
87 * being initialised. Once the constructor is complete, all further calculations
88 * are performed without reference to a timezone (by switching to UTC).
89 *
90 * @param instant the milliseconds from 1970-01-01T00:00:00Z
91 */
92 protected BasePartial(long instant) {
93 this(instant, null);
94 }
95
96 /**
97 * Constructs a partial extracting the partial fields from the specified
98 * milliseconds using the chronology provided.
99 * <p>
100 * The constructor uses the time zone of the chronology specified.
101 * Once the constructor is complete, all further calculations are performed
102 * without reference to a timezone (by switching to UTC).
103 *
104 * @param instant the milliseconds from 1970-01-01T00:00:00Z
105 * @param chronology the chronology, null means ISOChronology in the default zone
106 */
107 protected BasePartial(long instant, Chronology chronology) {
108 super();
109 chronology = DateTimeUtils.getChronology(chronology);
110 iChronology = chronology.withUTC();
111 iValues = chronology.get(this, instant);
112 }
113
114 /**
115 * Constructs a partial from an Object that represents a time, using the
116 * specified chronology.
117 * <p>
118 * The recognised object types are defined in
119 * {@link org.joda.time.convert.ConverterManager ConverterManager} and
120 * include ReadableInstant, String, Calendar and Date.
121 * <p>
122 * The constructor uses the time zone of the chronology specified.
123 * Once the constructor is complete, all further calculations are performed
124 * without reference to a timezone (by switching to UTC).
125 *
126 * @param instant the datetime object
127 * @param chronology the chronology, null means use converter
128 * @throws IllegalArgumentException if the date is invalid
129 */
130 protected BasePartial(Object instant, Chronology chronology) {
131 super();
132 PartialConverter converter = ConverterManager.getInstance().getPartialConverter(instant);
133 chronology = converter.getChronology(instant, chronology);
134 chronology = DateTimeUtils.getChronology(chronology);
135 iChronology = chronology.withUTC();
136 iValues = converter.getPartialValues(this, instant, chronology);
137 }
138
139 /**
140 * Constructs a partial from an Object that represents a time, using the
141 * specified chronology.
142 * <p>
143 * The recognised object types are defined in
144 * {@link org.joda.time.convert.ConverterManager ConverterManager} and
145 * include ReadableInstant, String, Calendar and Date.
146 * <p>
147 * The constructor uses the time zone of the chronology specified.
148 * Once the constructor is complete, all further calculations are performed
149 * without reference to a timezone (by switching to UTC).
150 *
151 * @param instant the datetime object
152 * @param chronology the chronology, null means use converter
153 * @param parser if converting from a String, the given parser is preferred
154 * @throws IllegalArgumentException if the date is invalid
155 * @since 1.3
156 */
157 protected BasePartial(Object instant, Chronology chronology, DateTimeFormatter parser) {
158 super();
159 PartialConverter converter = ConverterManager.getInstance().getPartialConverter(instant);
160 chronology = converter.getChronology(instant, chronology);
161 chronology = DateTimeUtils.getChronology(chronology);
162 iChronology = chronology.withUTC();
163 iValues = converter.getPartialValues(this, instant, chronology, parser);
164 }
165
166 /**
167 * Constructs a partial with specified time field values and chronology.
168 * <p>
169 * The constructor uses the time zone of the chronology specified.
170 * Once the constructor is complete, all further calculations are performed
171 * without reference to a timezone (by switching to UTC).
172 * <p>
173 * The array of values is assigned (not cloned) to the new instance.
174 *
175 * @param values the new set of values
176 * @param chronology the chronology, null means ISOChronology in the default zone
177 * @throws IllegalArgumentException if the values are invalid
178 */
179 protected BasePartial(int[] values, Chronology chronology) {
180 super();
181 chronology = DateTimeUtils.getChronology(chronology);
182 iChronology = chronology.withUTC();
183 chronology.validate(this, values);
184 iValues = values;
185 }
186
187 /**
188 * Private constructor to be used by subclasses only which performs no validation.
189 * <p>
190 * Data is assigned (not cloned) to the new instance.
191 *
192 * @param base the base partial
193 * @param values the new set of values, not cloned, null means use base
194 */
195 protected BasePartial(BasePartial base, int[] values) {
196 super();
197 iChronology = base.iChronology;
198 iValues = values;
199 }
200
201 /**
202 * Private constructor to be used by subclasses only which performs no validation.
203 * <p>
204 * Data is assigned (not cloned) to the new instance.
205 * This should not be used by mutable subclasses.
206 *
207 * @param base the base partial
208 * @param chrono the chronology to use, null means use base
209 */
210 protected BasePartial(BasePartial base, Chronology chrono) {
211 super();
212 iChronology = chrono.withUTC();
213 iValues = base.iValues;
214 }
215
216 //-----------------------------------------------------------------------
217 /**
218 * Gets the value of the field at the specifed index.
219 *
220 * @param index the index
221 * @return the value
222 * @throws IndexOutOfBoundsException if the index is invalid
223 */
224 public int getValue(int index) {
225 return iValues[index];
226 }
227
228 /**
229 * Gets an array of the value of each of the fields that this partial supports.
230 * <p>
231 * The fields are returned largest to smallest, for example Hour, Minute, Second.
232 * Each value corresponds to the same array index as <code>getFields()</code>
233 *
234 * @return the current values of each field (cloned), largest to smallest
235 */
236 public int[] getValues() {
237 return (int[]) iValues.clone();
238 }
239
240 /**
241 * Gets the chronology of the partial which is never null.
242 * <p>
243 * The {@link Chronology} is the calculation engine behind the partial and
244 * provides conversion and validation of the fields in a particular calendar system.
245 *
246 * @return the chronology, never null
247 */
248 public Chronology getChronology() {
249 return iChronology;
250 }
251
252 //-----------------------------------------------------------------------
253 /**
254 * Sets the value of the field at the specified index.
255 * <p>
256 * In version 2.0 and later, this method copies the array into the original.
257 * This is because the instance variable has been changed to be final to satisfy the Java Memory Model.
258 * This only impacts subclasses that are mutable.
259 *
260 * @param index the index
261 * @param value the value to set
262 * @throws IndexOutOfBoundsException if the index is invalid
263 */
264 protected void setValue(int index, int value) {
265 DateTimeField field = getField(index);
266 int[] values = field.set(this, index, iValues, value);
267 System.arraycopy(values, 0, iValues, 0, iValues.length);
268 }
269
270 /**
271 * Sets the values of all fields.
272 * <p>
273 * In version 2.0 and later, this method copies the array into the original.
274 * This is because the instance variable has been changed to be final to satisfy the Java Memory Model.
275 * This only impacts subclasses that are mutable.
276 *
277 * @param values the array of values
278 */
279 protected void setValues(int[] values) {
280 getChronology().validate(this, values);
281 System.arraycopy(values, 0, iValues, 0, iValues.length);
282 }
283
284 //-----------------------------------------------------------------------
285 /**
286 * Output the date using the specified format pattern.
287 *
288 * @param pattern the pattern specification, null means use <code>toString</code>
289 * @see org.joda.time.format.DateTimeFormat
290 */
291 public String toString(String pattern) {
292 if (pattern == null) {
293 return toString();
294 }
295 return DateTimeFormat.forPattern(pattern).print(this);
296 }
297
298 /**
299 * Output the date using the specified format pattern.
300 *
301 * @param pattern the pattern specification, null means use <code>toString</code>
302 * @param locale Locale to use, null means default
303 * @see org.joda.time.format.DateTimeFormat
304 */
305 public String toString(String pattern, Locale locale) throws IllegalArgumentException {
306 if (pattern == null) {
307 return toString();
308 }
309 return DateTimeFormat.forPattern(pattern).withLocale(locale).print(this);
310 }
311
312 }