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 java.util.Locale; |
19 | |
20 | import org.joda.time.DateTimeField; |
21 | import org.joda.time.DateTimeFieldType; |
22 | import org.joda.time.DurationField; |
23 | import org.joda.time.IllegalFieldValueException; |
24 | import org.joda.time.ReadablePartial; |
25 | |
26 | /** |
27 | * BaseDateTimeField provides the common behaviour for DateTimeField |
28 | * implementations. |
29 | * <p> |
30 | * This class should generally not be used directly by API users. The |
31 | * DateTimeField class should be used when different kinds of DateTimeField |
32 | * objects are to be referenced. |
33 | * <p> |
34 | * BaseDateTimeField is thread-safe and immutable, and its subclasses must |
35 | * be as well. |
36 | * |
37 | * @author Brian S O'Neill |
38 | * @since 1.0 |
39 | * @see DecoratedDateTimeField |
40 | */ |
41 | public abstract class BaseDateTimeField extends DateTimeField { |
42 | |
43 | /** The field type. */ |
44 | private final DateTimeFieldType iType; |
45 | |
46 | /** |
47 | * Constructor. |
48 | */ |
49 | protected BaseDateTimeField(DateTimeFieldType type) { |
50 | super(); |
51 | if (type == null) { |
52 | throw new IllegalArgumentException("The type must not be null"); |
53 | } |
54 | iType = type; |
55 | } |
56 | |
57 | public final DateTimeFieldType getType() { |
58 | return iType; |
59 | } |
60 | |
61 | public final String getName() { |
62 | return iType.getName(); |
63 | } |
64 | |
65 | /** |
66 | * @return true always |
67 | */ |
68 | public final boolean isSupported() { |
69 | return true; |
70 | } |
71 | |
72 | // Main access API |
73 | //------------------------------------------------------------------------ |
74 | /** |
75 | * Get the value of this field from the milliseconds. |
76 | * |
77 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to query |
78 | * @return the value of the field, in the units of the field |
79 | */ |
80 | public abstract int get(long instant); |
81 | |
82 | //----------------------------------------------------------------------- |
83 | /** |
84 | * Get the human-readable, text value of this field from the milliseconds. |
85 | * If the specified locale is null, the default locale is used. |
86 | * <p> |
87 | * The default implementation returns getAsText(get(instant), locale). |
88 | * |
89 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to query |
90 | * @param locale the locale to use for selecting a text symbol, null means default |
91 | * @return the text value of the field |
92 | */ |
93 | public String getAsText(long instant, Locale locale) { |
94 | return getAsText(get(instant), locale); |
95 | } |
96 | |
97 | /** |
98 | * Get the human-readable, text value of this field from the milliseconds. |
99 | * <p> |
100 | * The default implementation calls {@link #getAsText(long, Locale)}. |
101 | * |
102 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to query |
103 | * @return the text value of the field |
104 | */ |
105 | public final String getAsText(long instant) { |
106 | return getAsText(instant, null); |
107 | } |
108 | |
109 | /** |
110 | * Get the human-readable, text value of this field from a partial instant. |
111 | * If the specified locale is null, the default locale is used. |
112 | * <p> |
113 | * The default implementation returns getAsText(fieldValue, locale). |
114 | * |
115 | * @param partial the partial instant to query |
116 | * @param fieldValue the field value of this field, provided for performance |
117 | * @param locale the locale to use for selecting a text symbol, null for default |
118 | * @return the text value of the field |
119 | */ |
120 | public String getAsText(ReadablePartial partial, int fieldValue, Locale locale) { |
121 | return getAsText(fieldValue, locale); |
122 | } |
123 | |
124 | /** |
125 | * Get the human-readable, text value of this field from a partial instant. |
126 | * If the specified locale is null, the default locale is used. |
127 | * <p> |
128 | * The default implementation calls {@link ReadablePartial#get(DateTimeFieldType)} |
129 | * and {@link #getAsText(ReadablePartial, int, Locale)}. |
130 | * |
131 | * @param partial the partial instant to query |
132 | * @param locale the locale to use for selecting a text symbol, null for default |
133 | * @return the text value of the field |
134 | */ |
135 | public final String getAsText(ReadablePartial partial, Locale locale) { |
136 | return getAsText(partial, partial.get(getType()), locale); |
137 | } |
138 | |
139 | /** |
140 | * Get the human-readable, text value of this field from the field value. |
141 | * If the specified locale is null, the default locale is used. |
142 | * <p> |
143 | * The default implementation returns Integer.toString(get(instant)). |
144 | * <p> |
145 | * Note: subclasses that override this method should also override |
146 | * getMaximumTextLength. |
147 | * |
148 | * @param fieldValue the numeric value to convert to text |
149 | * @param locale the locale to use for selecting a text symbol, null for default |
150 | * @return the text value of the field |
151 | */ |
152 | public String getAsText(int fieldValue, Locale locale) { |
153 | return Integer.toString(fieldValue); |
154 | } |
155 | |
156 | //----------------------------------------------------------------------- |
157 | /** |
158 | * Get the human-readable, short text value of this field from the milliseconds. |
159 | * If the specified locale is null, the default locale is used. |
160 | * <p> |
161 | * The default implementation returns getAsShortText(get(instant), locale). |
162 | * |
163 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to query |
164 | * @param locale the locale to use for selecting a text symbol, null means default |
165 | * @return the text value of the field |
166 | */ |
167 | public String getAsShortText(long instant, Locale locale) { |
168 | return getAsShortText(get(instant), locale); |
169 | } |
170 | |
171 | /** |
172 | * Get the human-readable, short text value of this field from the milliseconds. |
173 | * <p> |
174 | * The default implementation calls {@link #getAsShortText(long, Locale)}. |
175 | * |
176 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to query |
177 | * @return the text value of the field |
178 | */ |
179 | public final String getAsShortText(long instant) { |
180 | return getAsShortText(instant, null); |
181 | } |
182 | |
183 | /** |
184 | * Get the human-readable, short text value of this field from a partial instant. |
185 | * If the specified locale is null, the default locale is used. |
186 | * <p> |
187 | * The default implementation returns getAsShortText(fieldValue, locale). |
188 | * |
189 | * @param partial the partial instant to query |
190 | * @param fieldValue the field value of this field, provided for performance |
191 | * @param locale the locale to use for selecting a text symbol, null for default |
192 | * @return the text value of the field |
193 | */ |
194 | public String getAsShortText(ReadablePartial partial, int fieldValue, Locale locale) { |
195 | return getAsShortText(fieldValue, locale); |
196 | } |
197 | |
198 | /** |
199 | * Get the human-readable, short text value of this field from a partial instant. |
200 | * If the specified locale is null, the default locale is used. |
201 | * <p> |
202 | * The default implementation calls {@link ReadablePartial#get(DateTimeFieldType)} |
203 | * and {@link #getAsText(ReadablePartial, int, Locale)}. |
204 | * |
205 | * @param partial the partial instant to query |
206 | * @param locale the locale to use for selecting a text symbol, null for default |
207 | * @return the text value of the field |
208 | */ |
209 | public final String getAsShortText(ReadablePartial partial, Locale locale) { |
210 | return getAsShortText(partial, partial.get(getType()), locale); |
211 | } |
212 | |
213 | /** |
214 | * Get the human-readable, short text value of this field from the field value. |
215 | * If the specified locale is null, the default locale is used. |
216 | * <p> |
217 | * The default implementation returns getAsText(fieldValue, locale). |
218 | * <p> |
219 | * Note: subclasses that override this method should also override |
220 | * getMaximumShortTextLength. |
221 | * |
222 | * @param fieldValue the numeric value to convert to text |
223 | * @param locale the locale to use for selecting a text symbol, null for default |
224 | * @return the text value of the field |
225 | */ |
226 | public String getAsShortText(int fieldValue, Locale locale) { |
227 | return getAsText(fieldValue, locale); |
228 | } |
229 | |
230 | //----------------------------------------------------------------------- |
231 | /** |
232 | * Adds a value (which may be negative) to the instant value, |
233 | * overflowing into larger fields if necessary. |
234 | * <p> |
235 | * The value will be added to this field. If the value is too large to be |
236 | * added solely to this field, larger fields will increase as required. |
237 | * Smaller fields should be unaffected, except where the result would be |
238 | * an invalid value for a smaller field. In this case the smaller field is |
239 | * adjusted to be in range. |
240 | * <p> |
241 | * For example, in the ISO chronology:<br> |
242 | * 2000-08-20 add six months is 2001-02-20<br> |
243 | * 2000-08-20 add twenty months is 2002-04-20<br> |
244 | * 2000-08-20 add minus nine months is 1999-11-20<br> |
245 | * 2001-01-31 add one month is 2001-02-28<br> |
246 | * 2001-01-31 add two months is 2001-03-31<br> |
247 | * |
248 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to add to |
249 | * @param value the value to add, in the units of the field |
250 | * @return the updated milliseconds |
251 | */ |
252 | public long add(long instant, int value) { |
253 | return getDurationField().add(instant, value); |
254 | } |
255 | |
256 | /** |
257 | * Adds a value (which may be negative) to the instant value, |
258 | * overflowing into larger fields if necessary. |
259 | * |
260 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to add to |
261 | * @param value the long value to add, in the units of the field |
262 | * @return the updated milliseconds |
263 | * @throws IllegalArgumentException if value is too large |
264 | * @see #add(long,int) |
265 | */ |
266 | public long add(long instant, long value) { |
267 | return getDurationField().add(instant, value); |
268 | } |
269 | |
270 | /** |
271 | * Adds a value (which may be negative) to the partial instant, |
272 | * throwing an exception if the maximum size of the instant is reached. |
273 | * <p> |
274 | * The value will be added to this field, overflowing into larger fields |
275 | * if necessary. Smaller fields should be unaffected, except where the |
276 | * result would be an invalid value for a smaller field. In this case the |
277 | * smaller field is adjusted to be in range. |
278 | * <p> |
279 | * Partial instants only contain some fields. This may result in a maximum |
280 | * possible value, such as TimeOfDay being limited to 23:59:59:999. If this |
281 | * limit is breached by the add an exception is thrown. |
282 | * <p> |
283 | * For example, in the ISO chronology:<br> |
284 | * 2000-08-20 add six months is 2000-02-20<br> |
285 | * 2000-08-20 add twenty months is 2000-04-20<br> |
286 | * 2000-08-20 add minus nine months is 2000-11-20<br> |
287 | * 2001-01-31 add one month is 2001-02-28<br> |
288 | * 2001-01-31 add two months is 2001-03-31<br> |
289 | * |
290 | * @param instant the partial instant |
291 | * @param fieldIndex the index of this field in the partial |
292 | * @param values the values of the partial instant which should be updated |
293 | * @param valueToAdd the value to add, in the units of the field |
294 | * @return the passed in values |
295 | * @throws IllegalArgumentException if the value is invalid or the maximum instant is reached |
296 | */ |
297 | public int[] add(ReadablePartial instant, int fieldIndex, int[] values, int valueToAdd) { |
298 | if (valueToAdd == 0) { |
299 | return values; |
300 | } |
301 | // there are more efficient algorithms than this (especially for time only fields) |
302 | // trouble is when dealing with days and months, so we use this technique of |
303 | // adding/removing one from the larger field at a time |
304 | DateTimeField nextField = null; |
305 | |
306 | while (valueToAdd > 0) { |
307 | int max = getMaximumValue(instant, values); |
308 | long proposed = values[fieldIndex] + valueToAdd; |
309 | if (proposed <= max) { |
310 | values[fieldIndex] = (int) proposed; |
311 | break; |
312 | } |
313 | if (nextField == null) { |
314 | if (fieldIndex == 0) { |
315 | throw new IllegalArgumentException("Maximum value exceeded for add"); |
316 | } |
317 | nextField = instant.getField(fieldIndex - 1); |
318 | // test only works if this field is UTC (ie. local) |
319 | if (getRangeDurationField().getType() != nextField.getDurationField().getType()) { |
320 | throw new IllegalArgumentException("Fields invalid for add"); |
321 | } |
322 | } |
323 | valueToAdd -= (max + 1) - values[fieldIndex]; // reduce the amount to add |
324 | values = nextField.add(instant, fieldIndex - 1, values, 1); // add 1 to next bigger field |
325 | values[fieldIndex] = getMinimumValue(instant, values); // reset this field to zero |
326 | } |
327 | while (valueToAdd < 0) { |
328 | int min = getMinimumValue(instant, values); |
329 | long proposed = values[fieldIndex] + valueToAdd; |
330 | if (proposed >= min) { |
331 | values[fieldIndex] = (int) proposed; |
332 | break; |
333 | } |
334 | if (nextField == null) { |
335 | if (fieldIndex == 0) { |
336 | throw new IllegalArgumentException("Maximum value exceeded for add"); |
337 | } |
338 | nextField = instant.getField(fieldIndex - 1); |
339 | if (getRangeDurationField().getType() != nextField.getDurationField().getType()) { |
340 | throw new IllegalArgumentException("Fields invalid for add"); |
341 | } |
342 | } |
343 | valueToAdd -= (min - 1) - values[fieldIndex]; // reduce the amount to add |
344 | values = nextField.add(instant, fieldIndex - 1, values, -1); // subtract 1 from next bigger field |
345 | values[fieldIndex] = getMaximumValue(instant, values); // reset this field to max value |
346 | } |
347 | |
348 | return set(instant, fieldIndex, values, values[fieldIndex]); // adjusts smaller fields |
349 | } |
350 | |
351 | /** |
352 | * Adds a value (which may be negative) to the partial instant, |
353 | * wrapping the whole partial if the maximum size of the partial is reached. |
354 | * <p> |
355 | * The value will be added to this field, overflowing into larger fields |
356 | * if necessary. Smaller fields should be unaffected, except where the |
357 | * result would be an invalid value for a smaller field. In this case the |
358 | * smaller field is adjusted to be in range. |
359 | * <p> |
360 | * Partial instants only contain some fields. This may result in a maximum |
361 | * possible value, such as TimeOfDay normally being limited to 23:59:59:999. |
362 | * If ths limit is reached by the addition, this method will wrap back to |
363 | * 00:00:00.000. In fact, you would generally only use this method for |
364 | * classes that have a limitation such as this. |
365 | * <p> |
366 | * For example, in the ISO chronology:<br> |
367 | * 10:20:30 add 20 minutes is 10:40:30<br> |
368 | * 10:20:30 add 45 minutes is 11:05:30<br> |
369 | * 10:20:30 add 16 hours is 02:20:30<br> |
370 | * |
371 | * @param instant the partial instant |
372 | * @param fieldIndex the index of this field in the partial |
373 | * @param values the values of the partial instant which should be updated |
374 | * @param valueToAdd the value to add, in the units of the field |
375 | * @return the passed in values |
376 | * @throws IllegalArgumentException if the value is invalid or the maximum instant is reached |
377 | */ |
378 | public int[] addWrapPartial(ReadablePartial instant, int fieldIndex, int[] values, int valueToAdd) { |
379 | if (valueToAdd == 0) { |
380 | return values; |
381 | } |
382 | // there are more efficient algorithms than this (especially for time only fields) |
383 | // trouble is when dealing with days and months, so we use this technique of |
384 | // adding/removing one from the larger field at a time |
385 | DateTimeField nextField = null; |
386 | |
387 | while (valueToAdd > 0) { |
388 | int max = getMaximumValue(instant, values); |
389 | long proposed = values[fieldIndex] + valueToAdd; |
390 | if (proposed <= max) { |
391 | values[fieldIndex] = (int) proposed; |
392 | break; |
393 | } |
394 | if (nextField == null) { |
395 | if (fieldIndex == 0) { |
396 | valueToAdd -= (max + 1) - values[fieldIndex]; |
397 | values[fieldIndex] = getMinimumValue(instant, values); |
398 | continue; |
399 | } |
400 | nextField = instant.getField(fieldIndex - 1); |
401 | // test only works if this field is UTC (ie. local) |
402 | if (getRangeDurationField().getType() != nextField.getDurationField().getType()) { |
403 | throw new IllegalArgumentException("Fields invalid for add"); |
404 | } |
405 | } |
406 | valueToAdd -= (max + 1) - values[fieldIndex]; // reduce the amount to add |
407 | values = nextField.addWrapPartial(instant, fieldIndex - 1, values, 1); // add 1 to next bigger field |
408 | values[fieldIndex] = getMinimumValue(instant, values); // reset this field to zero |
409 | } |
410 | while (valueToAdd < 0) { |
411 | int min = getMinimumValue(instant, values); |
412 | long proposed = values[fieldIndex] + valueToAdd; |
413 | if (proposed >= min) { |
414 | values[fieldIndex] = (int) proposed; |
415 | break; |
416 | } |
417 | if (nextField == null) { |
418 | if (fieldIndex == 0) { |
419 | valueToAdd -= (min - 1) - values[fieldIndex]; |
420 | values[fieldIndex] = getMaximumValue(instant, values); |
421 | continue; |
422 | } |
423 | nextField = instant.getField(fieldIndex - 1); |
424 | if (getRangeDurationField().getType() != nextField.getDurationField().getType()) { |
425 | throw new IllegalArgumentException("Fields invalid for add"); |
426 | } |
427 | } |
428 | valueToAdd -= (min - 1) - values[fieldIndex]; // reduce the amount to add |
429 | values = nextField.addWrapPartial(instant, fieldIndex - 1, values, -1); // subtract 1 from next bigger field |
430 | values[fieldIndex] = getMaximumValue(instant, values); // reset this field to max value |
431 | } |
432 | |
433 | return set(instant, fieldIndex, values, values[fieldIndex]); // adjusts smaller fields |
434 | } |
435 | |
436 | /** |
437 | * Adds a value (which may be negative) to the instant value, |
438 | * wrapping within this field. |
439 | * <p> |
440 | * The value will be added to this field. If the value is too large to be |
441 | * added solely to this field then it wraps. Larger fields are always |
442 | * unaffected. Smaller fields should be unaffected, except where the |
443 | * result would be an invalid value for a smaller field. In this case the |
444 | * smaller field is adjusted to be in range. |
445 | * <p> |
446 | * For example, in the ISO chronology:<br> |
447 | * 2000-08-20 addWrapField six months is 2000-02-20<br> |
448 | * 2000-08-20 addWrapField twenty months is 2000-04-20<br> |
449 | * 2000-08-20 addWrapField minus nine months is 2000-11-20<br> |
450 | * 2001-01-31 addWrapField one month is 2001-02-28<br> |
451 | * 2001-01-31 addWrapField two months is 2001-03-31<br> |
452 | * <p> |
453 | * The default implementation internally calls set. Subclasses are |
454 | * encouraged to provide a more efficient implementation. |
455 | * |
456 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to add to |
457 | * @param value the value to add, in the units of the field |
458 | * @return the updated milliseconds |
459 | */ |
460 | public long addWrapField(long instant, int value) { |
461 | int current = get(instant); |
462 | int wrapped = FieldUtils.getWrappedValue |
463 | (current, value, getMinimumValue(instant), getMaximumValue(instant)); |
464 | return set(instant, wrapped); |
465 | } |
466 | |
467 | /** |
468 | * Adds a value (which may be negative) to the partial instant, |
469 | * wrapping within this field. |
470 | * <p> |
471 | * The value will be added to this field. If the value is too large to be |
472 | * added solely to this field then it wraps. Larger fields are always |
473 | * unaffected. Smaller fields should be unaffected, except where the |
474 | * result would be an invalid value for a smaller field. In this case the |
475 | * smaller field is adjusted to be in range. |
476 | * <p> |
477 | * For example, in the ISO chronology:<br> |
478 | * 2000-08-20 addWrapField six months is 2000-02-20<br> |
479 | * 2000-08-20 addWrapField twenty months is 2000-04-20<br> |
480 | * 2000-08-20 addWrapField minus nine months is 2000-11-20<br> |
481 | * 2001-01-31 addWrapField one month is 2001-02-28<br> |
482 | * 2001-01-31 addWrapField two months is 2001-03-31<br> |
483 | * <p> |
484 | * The default implementation internally calls set. Subclasses are |
485 | * encouraged to provide a more efficient implementation. |
486 | * |
487 | * @param instant the partial instant |
488 | * @param fieldIndex the index of this field in the instant |
489 | * @param values the values of the partial instant which should be updated |
490 | * @param valueToAdd the value to add, in the units of the field |
491 | * @return the passed in values |
492 | * @throws IllegalArgumentException if the value is invalid |
493 | */ |
494 | public int[] addWrapField(ReadablePartial instant, int fieldIndex, int[] values, int valueToAdd) { |
495 | int current = values[fieldIndex]; |
496 | int wrapped = FieldUtils.getWrappedValue |
497 | (current, valueToAdd, getMinimumValue(instant), getMaximumValue(instant)); |
498 | return set(instant, fieldIndex, values, wrapped); // adjusts smaller fields |
499 | } |
500 | |
501 | //----------------------------------------------------------------------- |
502 | /** |
503 | * Computes the difference between two instants, as measured in the units |
504 | * of this field. Any fractional units are dropped from the result. Calling |
505 | * getDifference reverses the effect of calling add. In the following code: |
506 | * |
507 | * <pre> |
508 | * long instant = ... |
509 | * int v = ... |
510 | * int age = getDifference(add(instant, v), instant); |
511 | * </pre> |
512 | * |
513 | * The value 'age' is the same as the value 'v'. |
514 | * |
515 | * @param minuendInstant the milliseconds from 1970-01-01T00:00:00Z to |
516 | * subtract from |
517 | * @param subtrahendInstant the milliseconds from 1970-01-01T00:00:00Z to |
518 | * subtract off the minuend |
519 | * @return the difference in the units of this field |
520 | */ |
521 | public int getDifference(long minuendInstant, long subtrahendInstant) { |
522 | return getDurationField().getDifference(minuendInstant, subtrahendInstant); |
523 | } |
524 | |
525 | /** |
526 | * Computes the difference between two instants, as measured in the units |
527 | * of this field. Any fractional units are dropped from the result. Calling |
528 | * getDifference reverses the effect of calling add. In the following code: |
529 | * |
530 | * <pre> |
531 | * long instant = ... |
532 | * long v = ... |
533 | * long age = getDifferenceAsLong(add(instant, v), instant); |
534 | * </pre> |
535 | * |
536 | * The value 'age' is the same as the value 'v'. |
537 | * |
538 | * @param minuendInstant the milliseconds from 1970-01-01T00:00:00Z to |
539 | * subtract from |
540 | * @param subtrahendInstant the milliseconds from 1970-01-01T00:00:00Z to |
541 | * subtract off the minuend |
542 | * @return the difference in the units of this field |
543 | */ |
544 | public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) { |
545 | return getDurationField().getDifferenceAsLong(minuendInstant, subtrahendInstant); |
546 | } |
547 | |
548 | /** |
549 | * Sets a value in the milliseconds supplied. |
550 | * <p> |
551 | * The value of this field will be set. |
552 | * If the value is invalid, an exception if thrown. |
553 | * <p> |
554 | * If setting this field would make other fields invalid, then those fields |
555 | * may be changed. For example if the current date is the 31st January, and |
556 | * the month is set to February, the day would be invalid. Instead, the day |
557 | * would be changed to the closest value - the 28th/29th February as appropriate. |
558 | * |
559 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to set in |
560 | * @param value the value to set, in the units of the field |
561 | * @return the updated milliseconds |
562 | * @throws IllegalArgumentException if the value is invalid |
563 | */ |
564 | public abstract long set(long instant, int value); |
565 | |
566 | /** |
567 | * Sets a value using the specified partial instant. |
568 | * <p> |
569 | * The value of this field (specified by the index) will be set. |
570 | * If the value is invalid, an exception if thrown. |
571 | * <p> |
572 | * If setting this field would make other fields invalid, then those fields |
573 | * may be changed. For example if the current date is the 31st January, and |
574 | * the month is set to February, the day would be invalid. Instead, the day |
575 | * would be changed to the closest value - the 28th/29th February as appropriate. |
576 | * |
577 | * @param partial the partial instant |
578 | * @param fieldIndex the index of this field in the instant |
579 | * @param values the values to update |
580 | * @param newValue the value to set, in the units of the field |
581 | * @return the updated values |
582 | * @throws IllegalArgumentException if the value is invalid |
583 | */ |
584 | public int[] set(ReadablePartial partial, int fieldIndex, int[] values, int newValue) { |
585 | FieldUtils.verifyValueBounds(this, newValue, getMinimumValue(partial, values), getMaximumValue(partial, values)); |
586 | values[fieldIndex] = newValue; |
587 | |
588 | // may need to adjust smaller fields |
589 | for (int i = fieldIndex + 1; i < partial.size(); i++) { |
590 | DateTimeField field = partial.getField(i); |
591 | if (values[i] > field.getMaximumValue(partial, values)) { |
592 | values[i] = field.getMaximumValue(partial, values); |
593 | } |
594 | if (values[i] < field.getMinimumValue(partial, values)) { |
595 | values[i] = field.getMinimumValue(partial, values); |
596 | } |
597 | } |
598 | return values; |
599 | } |
600 | |
601 | /** |
602 | * Sets a value in the milliseconds supplied from a human-readable, text value. |
603 | * If the specified locale is null, the default locale is used. |
604 | * <p> |
605 | * This implementation uses <code>convertText(String, Locale)</code> and |
606 | * {@link #set(long, int)}. |
607 | * <p> |
608 | * Note: subclasses that override this method should also override |
609 | * getAsText. |
610 | * |
611 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to set in |
612 | * @param text the text value to set |
613 | * @param locale the locale to use for selecting a text symbol, null for default |
614 | * @return the updated milliseconds |
615 | * @throws IllegalArgumentException if the text value is invalid |
616 | */ |
617 | public long set(long instant, String text, Locale locale) { |
618 | int value = convertText(text, locale); |
619 | return set(instant, value); |
620 | } |
621 | |
622 | /** |
623 | * Sets a value in the milliseconds supplied from a human-readable, text value. |
624 | * <p> |
625 | * This implementation uses {@link #set(long, String, Locale)}. |
626 | * <p> |
627 | * Note: subclasses that override this method should also override getAsText. |
628 | * |
629 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to set in |
630 | * @param text the text value to set |
631 | * @return the updated milliseconds |
632 | * @throws IllegalArgumentException if the text value is invalid |
633 | */ |
634 | public final long set(long instant, String text) { |
635 | return set(instant, text, null); |
636 | } |
637 | |
638 | /** |
639 | * Sets a value in the milliseconds supplied from a human-readable, text value. |
640 | * If the specified locale is null, the default locale is used. |
641 | * <p> |
642 | * This implementation uses <code>convertText(String, Locale)</code> and |
643 | * {@link #set(ReadablePartial, int, int[], int)}. |
644 | * |
645 | * @param instant the partial instant |
646 | * @param fieldIndex the index of this field in the instant |
647 | * @param values the values of the partial instant which should be updated |
648 | * @param text the text value to set |
649 | * @param locale the locale to use for selecting a text symbol, null for default |
650 | * @return the passed in values |
651 | * @throws IllegalArgumentException if the text value is invalid |
652 | */ |
653 | public int[] set(ReadablePartial instant, int fieldIndex, int[] values, String text, Locale locale) { |
654 | int value = convertText(text, locale); |
655 | return set(instant, fieldIndex, values, value); |
656 | } |
657 | |
658 | /** |
659 | * Convert the specified text and locale into a value. |
660 | * |
661 | * @param text the text to convert |
662 | * @param locale the locale to convert using |
663 | * @return the value extracted from the text |
664 | * @throws IllegalArgumentException if the text is invalid |
665 | */ |
666 | protected int convertText(String text, Locale locale) { |
667 | try { |
668 | return Integer.parseInt(text); |
669 | } catch (NumberFormatException ex) { |
670 | throw new IllegalFieldValueException(getType(), text); |
671 | } |
672 | } |
673 | |
674 | // Extra information API |
675 | //------------------------------------------------------------------------ |
676 | /** |
677 | * Returns the duration per unit value of this field. For example, if this |
678 | * field represents "hour of day", then the unit duration is an hour. |
679 | * |
680 | * @return the duration of this field, or UnsupportedDurationField if field |
681 | * has no duration |
682 | */ |
683 | public abstract DurationField getDurationField(); |
684 | |
685 | /** |
686 | * Returns the range duration of this field. For example, if this field |
687 | * represents "hour of day", then the range duration is a day. |
688 | * |
689 | * @return the range duration of this field, or null if field has no range |
690 | */ |
691 | public abstract DurationField getRangeDurationField(); |
692 | |
693 | /** |
694 | * Returns whether this field is 'leap' for the specified instant. |
695 | * <p> |
696 | * For example, a leap year would return true, a non leap year would return |
697 | * false. |
698 | * <p> |
699 | * This implementation returns false. |
700 | * |
701 | * @return true if the field is 'leap' |
702 | */ |
703 | public boolean isLeap(long instant) { |
704 | return false; |
705 | } |
706 | |
707 | /** |
708 | * Gets the amount by which this field is 'leap' for the specified instant. |
709 | * <p> |
710 | * For example, a leap year would return one, a non leap year would return |
711 | * zero. |
712 | * <p> |
713 | * This implementation returns zero. |
714 | */ |
715 | public int getLeapAmount(long instant) { |
716 | return 0; |
717 | } |
718 | |
719 | /** |
720 | * If this field were to leap, then it would be in units described by the |
721 | * returned duration. If this field doesn't ever leap, null is returned. |
722 | * <p> |
723 | * This implementation returns null. |
724 | */ |
725 | public DurationField getLeapDurationField() { |
726 | return null; |
727 | } |
728 | |
729 | /** |
730 | * Get the minimum allowable value for this field. |
731 | * |
732 | * @return the minimum valid value for this field, in the units of the |
733 | * field |
734 | */ |
735 | public abstract int getMinimumValue(); |
736 | |
737 | /** |
738 | * Get the minimum value for this field evaluated at the specified time. |
739 | * <p> |
740 | * This implementation returns the same as {@link #getMinimumValue()}. |
741 | * |
742 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to query |
743 | * @return the minimum value for this field, in the units of the field |
744 | */ |
745 | public int getMinimumValue(long instant) { |
746 | return getMinimumValue(); |
747 | } |
748 | |
749 | /** |
750 | * Get the minimum value for this field evaluated at the specified instant. |
751 | * <p> |
752 | * This implementation returns the same as {@link #getMinimumValue()}. |
753 | * |
754 | * @param instant the partial instant to query |
755 | * @return the minimum value for this field, in the units of the field |
756 | */ |
757 | public int getMinimumValue(ReadablePartial instant) { |
758 | return getMinimumValue(); |
759 | } |
760 | |
761 | /** |
762 | * Get the minimum value for this field using the partial instant and |
763 | * the specified values. |
764 | * <p> |
765 | * This implementation returns the same as {@link #getMinimumValue(ReadablePartial)}. |
766 | * |
767 | * @param instant the partial instant to query |
768 | * @param values the values to use |
769 | * @return the minimum value for this field, in the units of the field |
770 | */ |
771 | public int getMinimumValue(ReadablePartial instant, int[] values) { |
772 | return getMinimumValue(instant); |
773 | } |
774 | |
775 | /** |
776 | * Get the maximum allowable value for this field. |
777 | * |
778 | * @return the maximum valid value for this field, in the units of the |
779 | * field |
780 | */ |
781 | public abstract int getMaximumValue(); |
782 | |
783 | /** |
784 | * Get the maximum value for this field evaluated at the specified time. |
785 | * <p> |
786 | * This implementation returns the same as {@link #getMaximumValue()}. |
787 | * |
788 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to query |
789 | * @return the maximum value for this field, in the units of the field |
790 | */ |
791 | public int getMaximumValue(long instant) { |
792 | return getMaximumValue(); |
793 | } |
794 | |
795 | /** |
796 | * Get the maximum value for this field evaluated at the specified instant. |
797 | * <p> |
798 | * This implementation returns the same as {@link #getMaximumValue()}. |
799 | * |
800 | * @param instant the partial instant to query |
801 | * @return the maximum value for this field, in the units of the field |
802 | */ |
803 | public int getMaximumValue(ReadablePartial instant) { |
804 | return getMaximumValue(); |
805 | } |
806 | |
807 | /** |
808 | * Get the maximum value for this field using the partial instant and |
809 | * the specified values. |
810 | * <p> |
811 | * This implementation returns the same as {@link #getMaximumValue(ReadablePartial)}. |
812 | * |
813 | * @param instant the partial instant to query |
814 | * @param values the values to use |
815 | * @return the maximum value for this field, in the units of the field |
816 | */ |
817 | public int getMaximumValue(ReadablePartial instant, int[] values) { |
818 | return getMaximumValue(instant); |
819 | } |
820 | |
821 | /** |
822 | * Get the maximum text value for this field. The default implementation |
823 | * returns the equivalent of Integer.toString(getMaximumValue()).length(). |
824 | * |
825 | * @param locale the locale to use for selecting a text symbol |
826 | * @return the maximum text length |
827 | */ |
828 | public int getMaximumTextLength(Locale locale) { |
829 | int max = getMaximumValue(); |
830 | if (max >= 0) { |
831 | if (max < 10) { |
832 | return 1; |
833 | } else if (max < 100) { |
834 | return 2; |
835 | } else if (max < 1000) { |
836 | return 3; |
837 | } |
838 | } |
839 | return Integer.toString(max).length(); |
840 | } |
841 | |
842 | /** |
843 | * Get the maximum short text value for this field. The default |
844 | * implementation returns getMaximumTextLength(). |
845 | * |
846 | * @param locale the locale to use for selecting a text symbol |
847 | * @return the maximum short text length |
848 | */ |
849 | public int getMaximumShortTextLength(Locale locale) { |
850 | return getMaximumTextLength(locale); |
851 | } |
852 | |
853 | // Calculation API |
854 | //------------------------------------------------------------------------ |
855 | /** |
856 | * Round to the lowest whole unit of this field. After rounding, the value |
857 | * of this field and all fields of a higher magnitude are retained. The |
858 | * fractional millis that cannot be expressed in whole increments of this |
859 | * field are set to minimum. |
860 | * <p> |
861 | * For example, a datetime of 2002-11-02T23:34:56.789, rounded to the |
862 | * lowest whole hour is 2002-11-02T23:00:00.000. |
863 | * |
864 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to round |
865 | * @return rounded milliseconds |
866 | */ |
867 | public abstract long roundFloor(long instant); |
868 | |
869 | /** |
870 | * Round to the highest whole unit of this field. The value of this field |
871 | * and all fields of a higher magnitude may be incremented in order to |
872 | * achieve this result. The fractional millis that cannot be expressed in |
873 | * whole increments of this field are set to minimum. |
874 | * <p> |
875 | * For example, a datetime of 2002-11-02T23:34:56.789, rounded to the |
876 | * highest whole hour is 2002-11-03T00:00:00.000. |
877 | * <p> |
878 | * The default implementation calls roundFloor, and if the instant is |
879 | * modified as a result, adds one field unit. Subclasses are encouraged to |
880 | * provide a more efficient implementation. |
881 | * |
882 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to round |
883 | * @return rounded milliseconds |
884 | */ |
885 | public long roundCeiling(long instant) { |
886 | long newInstant = roundFloor(instant); |
887 | if (newInstant != instant) { |
888 | instant = add(newInstant, 1); |
889 | } |
890 | return instant; |
891 | } |
892 | |
893 | /** |
894 | * Round to the nearest whole unit of this field. If the given millisecond |
895 | * value is closer to the floor or is exactly halfway, this function |
896 | * behaves like roundFloor. If the millisecond value is closer to the |
897 | * ceiling, this function behaves like roundCeiling. |
898 | * |
899 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to round |
900 | * @return rounded milliseconds |
901 | */ |
902 | public long roundHalfFloor(long instant) { |
903 | long floor = roundFloor(instant); |
904 | long ceiling = roundCeiling(instant); |
905 | |
906 | long diffFromFloor = instant - floor; |
907 | long diffToCeiling = ceiling - instant; |
908 | |
909 | if (diffFromFloor <= diffToCeiling) { |
910 | // Closer to the floor, or halfway - round floor |
911 | return floor; |
912 | } else { |
913 | return ceiling; |
914 | } |
915 | } |
916 | |
917 | /** |
918 | * Round to the nearest whole unit of this field. If the given millisecond |
919 | * value is closer to the floor, this function behaves like roundFloor. If |
920 | * the millisecond value is closer to the ceiling or is exactly halfway, |
921 | * this function behaves like roundCeiling. |
922 | * |
923 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to round |
924 | * @return rounded milliseconds |
925 | */ |
926 | public long roundHalfCeiling(long instant) { |
927 | long floor = roundFloor(instant); |
928 | long ceiling = roundCeiling(instant); |
929 | |
930 | long diffFromFloor = instant - floor; |
931 | long diffToCeiling = ceiling - instant; |
932 | |
933 | if (diffToCeiling <= diffFromFloor) { |
934 | // Closer to the ceiling, or halfway - round ceiling |
935 | return ceiling; |
936 | } else { |
937 | return floor; |
938 | } |
939 | } |
940 | |
941 | /** |
942 | * Round to the nearest whole unit of this field. If the given millisecond |
943 | * value is closer to the floor, this function behaves like roundFloor. If |
944 | * the millisecond value is closer to the ceiling, this function behaves |
945 | * like roundCeiling. |
946 | * <p> |
947 | * If the millisecond value is exactly halfway between the floor and |
948 | * ceiling, the ceiling is chosen over the floor only if it makes this |
949 | * field's value even. |
950 | * |
951 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to round |
952 | * @return rounded milliseconds |
953 | */ |
954 | public long roundHalfEven(long instant) { |
955 | long floor = roundFloor(instant); |
956 | long ceiling = roundCeiling(instant); |
957 | |
958 | long diffFromFloor = instant - floor; |
959 | long diffToCeiling = ceiling - instant; |
960 | |
961 | if (diffFromFloor < diffToCeiling) { |
962 | // Closer to the floor - round floor |
963 | return floor; |
964 | } else if (diffToCeiling < diffFromFloor) { |
965 | // Closer to the ceiling - round ceiling |
966 | return ceiling; |
967 | } else { |
968 | // Round to the instant that makes this field even. If both values |
969 | // make this field even (unlikely), favor the ceiling. |
970 | if ((get(ceiling) & 1) == 0) { |
971 | return ceiling; |
972 | } |
973 | return floor; |
974 | } |
975 | } |
976 | |
977 | /** |
978 | * Returns the fractional duration milliseconds of this field. In other |
979 | * words, calling remainder returns the duration that roundFloor would |
980 | * subtract. |
981 | * <p> |
982 | * For example, on a datetime of 2002-11-02T23:34:56.789, the remainder by |
983 | * hour is 34 minutes and 56.789 seconds. |
984 | * <p> |
985 | * The default implementation computes |
986 | * <code>instant - roundFloor(instant)</code>. Subclasses are encouraged to |
987 | * provide a more efficient implementation. |
988 | * |
989 | * @param instant the milliseconds from 1970-01-01T00:00:00Z to get the |
990 | * remainder |
991 | * @return remainder duration, in milliseconds |
992 | */ |
993 | public long remainder(long instant) { |
994 | return instant - roundFloor(instant); |
995 | } |
996 | |
997 | /** |
998 | * Get a suitable debug string. |
999 | * |
1000 | * @return debug string |
1001 | */ |
1002 | public String toString() { |
1003 | return "DateTimeField[" + getName() + ']'; |
1004 | } |
1005 | |
1006 | } |