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 java.util.Locale;
019
020 import org.joda.time.DateTimeField;
021 import org.joda.time.DateTimeFieldType;
022 import org.joda.time.DurationField;
023 import org.joda.time.IllegalFieldValueException;
024 import org.joda.time.ReadablePartial;
025
026 /**
027 * BaseDateTimeField provides the common behaviour for DateTimeField
028 * implementations.
029 * <p>
030 * This class should generally not be used directly by API users. The
031 * DateTimeField class should be used when different kinds of DateTimeField
032 * objects are to be referenced.
033 * <p>
034 * BaseDateTimeField is thread-safe and immutable, and its subclasses must
035 * be as well.
036 *
037 * @author Brian S O'Neill
038 * @since 1.0
039 * @see DecoratedDateTimeField
040 */
041 public abstract class BaseDateTimeField extends DateTimeField {
042
043 /** The field type. */
044 private final DateTimeFieldType iType;
045
046 /**
047 * Constructor.
048 */
049 protected BaseDateTimeField(DateTimeFieldType type) {
050 super();
051 if (type == null) {
052 throw new IllegalArgumentException("The type must not be null");
053 }
054 iType = type;
055 }
056
057 public final DateTimeFieldType getType() {
058 return iType;
059 }
060
061 public final String getName() {
062 return iType.getName();
063 }
064
065 /**
066 * @return true always
067 */
068 public final boolean isSupported() {
069 return true;
070 }
071
072 // Main access API
073 //------------------------------------------------------------------------
074 /**
075 * Get the value of this field from the milliseconds.
076 *
077 * @param instant the milliseconds from 1970-01-01T00:00:00Z to query
078 * @return the value of the field, in the units of the field
079 */
080 public abstract int get(long instant);
081
082 //-----------------------------------------------------------------------
083 /**
084 * Get the human-readable, text value of this field from the milliseconds.
085 * If the specified locale is null, the default locale is used.
086 * <p>
087 * The default implementation returns getAsText(get(instant), locale).
088 *
089 * @param instant the milliseconds from 1970-01-01T00:00:00Z to query
090 * @param locale the locale to use for selecting a text symbol, null means default
091 * @return the text value of the field
092 */
093 public String getAsText(long instant, Locale locale) {
094 return getAsText(get(instant), locale);
095 }
096
097 /**
098 * Get the human-readable, text value of this field from the milliseconds.
099 * <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 }