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    }