0001: /*
0002: * Copyright 2001-2005 Stephen Colebourne
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.joda.time.field;
0017:
0018: import java.util.Locale;
0019:
0020: import org.joda.time.DateTimeField;
0021: import org.joda.time.DateTimeFieldType;
0022: import org.joda.time.DurationField;
0023: import org.joda.time.IllegalFieldValueException;
0024: import org.joda.time.ReadablePartial;
0025:
0026: /**
0027: * BaseDateTimeField provides the common behaviour for DateTimeField
0028: * implementations.
0029: * <p>
0030: * This class should generally not be used directly by API users. The
0031: * DateTimeField class should be used when different kinds of DateTimeField
0032: * objects are to be referenced.
0033: * <p>
0034: * BaseDateTimeField is thread-safe and immutable, and its subclasses must
0035: * be as well.
0036: *
0037: * @author Brian S O'Neill
0038: * @since 1.0
0039: * @see DecoratedDateTimeField
0040: */
0041: public abstract class BaseDateTimeField extends DateTimeField {
0042:
0043: /** The field type. */
0044: private final DateTimeFieldType iType;
0045:
0046: /**
0047: * Constructor.
0048: */
0049: protected BaseDateTimeField(DateTimeFieldType type) {
0050: super ();
0051: if (type == null) {
0052: throw new IllegalArgumentException(
0053: "The type must not be null");
0054: }
0055: iType = type;
0056: }
0057:
0058: public final DateTimeFieldType getType() {
0059: return iType;
0060: }
0061:
0062: public final String getName() {
0063: return iType.getName();
0064: }
0065:
0066: /**
0067: * @return true always
0068: */
0069: public final boolean isSupported() {
0070: return true;
0071: }
0072:
0073: // Main access API
0074: //------------------------------------------------------------------------
0075: /**
0076: * Get the value of this field from the milliseconds.
0077: *
0078: * @param instant the milliseconds from 1970-01-01T00:00:00Z to query
0079: * @return the value of the field, in the units of the field
0080: */
0081: public abstract int get(long instant);
0082:
0083: //-----------------------------------------------------------------------
0084: /**
0085: * Get the human-readable, text value of this field from the milliseconds.
0086: * If the specified locale is null, the default locale is used.
0087: * <p>
0088: * The default implementation returns getAsText(get(instant), locale).
0089: *
0090: * @param instant the milliseconds from 1970-01-01T00:00:00Z to query
0091: * @param locale the locale to use for selecting a text symbol, null means default
0092: * @return the text value of the field
0093: */
0094: public String getAsText(long instant, Locale locale) {
0095: return getAsText(get(instant), locale);
0096: }
0097:
0098: /**
0099: * Get the human-readable, text value of this field from the milliseconds.
0100: * <p>
0101: * The default implementation calls {@link #getAsText(long, Locale)}.
0102: *
0103: * @param instant the milliseconds from 1970-01-01T00:00:00Z to query
0104: * @return the text value of the field
0105: */
0106: public final String getAsText(long instant) {
0107: return getAsText(instant, null);
0108: }
0109:
0110: /**
0111: * Get the human-readable, text value of this field from a partial instant.
0112: * If the specified locale is null, the default locale is used.
0113: * <p>
0114: * The default implementation returns getAsText(fieldValue, locale).
0115: *
0116: * @param partial the partial instant to query
0117: * @param fieldValue the field value of this field, provided for performance
0118: * @param locale the locale to use for selecting a text symbol, null for default
0119: * @return the text value of the field
0120: */
0121: public String getAsText(ReadablePartial partial, int fieldValue,
0122: Locale locale) {
0123: return getAsText(fieldValue, locale);
0124: }
0125:
0126: /**
0127: * Get the human-readable, text value of this field from a partial instant.
0128: * If the specified locale is null, the default locale is used.
0129: * <p>
0130: * The default implementation calls {@link ReadablePartial#get(DateTimeFieldType)}
0131: * and {@link #getAsText(ReadablePartial, int, Locale)}.
0132: *
0133: * @param partial the partial instant to query
0134: * @param locale the locale to use for selecting a text symbol, null for default
0135: * @return the text value of the field
0136: */
0137: public final String getAsText(ReadablePartial partial, Locale locale) {
0138: return getAsText(partial, partial.get(getType()), locale);
0139: }
0140:
0141: /**
0142: * Get the human-readable, text value of this field from the field value.
0143: * If the specified locale is null, the default locale is used.
0144: * <p>
0145: * The default implementation returns Integer.toString(get(instant)).
0146: * <p>
0147: * Note: subclasses that override this method should also override
0148: * getMaximumTextLength.
0149: *
0150: * @param fieldValue the numeric value to convert to text
0151: * @param locale the locale to use for selecting a text symbol, null for default
0152: * @return the text value of the field
0153: */
0154: public String getAsText(int fieldValue, Locale locale) {
0155: return Integer.toString(fieldValue);
0156: }
0157:
0158: //-----------------------------------------------------------------------
0159: /**
0160: * Get the human-readable, short text value of this field from the milliseconds.
0161: * If the specified locale is null, the default locale is used.
0162: * <p>
0163: * The default implementation returns getAsShortText(get(instant), locale).
0164: *
0165: * @param instant the milliseconds from 1970-01-01T00:00:00Z to query
0166: * @param locale the locale to use for selecting a text symbol, null means default
0167: * @return the text value of the field
0168: */
0169: public String getAsShortText(long instant, Locale locale) {
0170: return getAsShortText(get(instant), locale);
0171: }
0172:
0173: /**
0174: * Get the human-readable, short text value of this field from the milliseconds.
0175: * <p>
0176: * The default implementation calls {@link #getAsShortText(long, Locale)}.
0177: *
0178: * @param instant the milliseconds from 1970-01-01T00:00:00Z to query
0179: * @return the text value of the field
0180: */
0181: public final String getAsShortText(long instant) {
0182: return getAsShortText(instant, null);
0183: }
0184:
0185: /**
0186: * Get the human-readable, short text value of this field from a partial instant.
0187: * If the specified locale is null, the default locale is used.
0188: * <p>
0189: * The default implementation returns getAsShortText(fieldValue, locale).
0190: *
0191: * @param partial the partial instant to query
0192: * @param fieldValue the field value of this field, provided for performance
0193: * @param locale the locale to use for selecting a text symbol, null for default
0194: * @return the text value of the field
0195: */
0196: public String getAsShortText(ReadablePartial partial,
0197: int fieldValue, Locale locale) {
0198: return getAsShortText(fieldValue, locale);
0199: }
0200:
0201: /**
0202: * Get the human-readable, short text value of this field from a partial instant.
0203: * If the specified locale is null, the default locale is used.
0204: * <p>
0205: * The default implementation calls {@link ReadablePartial#get(DateTimeFieldType)}
0206: * and {@link #getAsText(ReadablePartial, int, Locale)}.
0207: *
0208: * @param partial the partial instant to query
0209: * @param locale the locale to use for selecting a text symbol, null for default
0210: * @return the text value of the field
0211: */
0212: public final String getAsShortText(ReadablePartial partial,
0213: Locale locale) {
0214: return getAsShortText(partial, partial.get(getType()), locale);
0215: }
0216:
0217: /**
0218: * Get the human-readable, short text value of this field from the field value.
0219: * If the specified locale is null, the default locale is used.
0220: * <p>
0221: * The default implementation returns getAsText(fieldValue, locale).
0222: * <p>
0223: * Note: subclasses that override this method should also override
0224: * getMaximumShortTextLength.
0225: *
0226: * @param fieldValue the numeric value to convert to text
0227: * @param locale the locale to use for selecting a text symbol, null for default
0228: * @return the text value of the field
0229: */
0230: public String getAsShortText(int fieldValue, Locale locale) {
0231: return getAsText(fieldValue, locale);
0232: }
0233:
0234: //-----------------------------------------------------------------------
0235: /**
0236: * Adds a value (which may be negative) to the instant value,
0237: * overflowing into larger fields if necessary.
0238: * <p>
0239: * The value will be added to this field. If the value is too large to be
0240: * added solely to this field, larger fields will increase as required.
0241: * Smaller fields should be unaffected, except where the result would be
0242: * an invalid value for a smaller field. In this case the smaller field is
0243: * adjusted to be in range.
0244: * <p>
0245: * For example, in the ISO chronology:<br>
0246: * 2000-08-20 add six months is 2001-02-20<br>
0247: * 2000-08-20 add twenty months is 2002-04-20<br>
0248: * 2000-08-20 add minus nine months is 1999-11-20<br>
0249: * 2001-01-31 add one month is 2001-02-28<br>
0250: * 2001-01-31 add two months is 2001-03-31<br>
0251: *
0252: * @param instant the milliseconds from 1970-01-01T00:00:00Z to add to
0253: * @param value the value to add, in the units of the field
0254: * @return the updated milliseconds
0255: */
0256: public long add(long instant, int value) {
0257: return getDurationField().add(instant, value);
0258: }
0259:
0260: /**
0261: * Adds a value (which may be negative) to the instant value,
0262: * overflowing into larger fields if necessary.
0263: *
0264: * @param instant the milliseconds from 1970-01-01T00:00:00Z to add to
0265: * @param value the long value to add, in the units of the field
0266: * @return the updated milliseconds
0267: * @throws IllegalArgumentException if value is too large
0268: * @see #add(long,int)
0269: */
0270: public long add(long instant, long value) {
0271: return getDurationField().add(instant, value);
0272: }
0273:
0274: /**
0275: * Adds a value (which may be negative) to the partial instant,
0276: * throwing an exception if the maximum size of the instant is reached.
0277: * <p>
0278: * The value will be added to this field, overflowing into larger fields
0279: * if necessary. Smaller fields should be unaffected, except where the
0280: * result would be an invalid value for a smaller field. In this case the
0281: * smaller field is adjusted to be in range.
0282: * <p>
0283: * Partial instants only contain some fields. This may result in a maximum
0284: * possible value, such as TimeOfDay being limited to 23:59:59:999. If this
0285: * limit is breached by the add an exception is thrown.
0286: * <p>
0287: * For example, in the ISO chronology:<br>
0288: * 2000-08-20 add six months is 2000-02-20<br>
0289: * 2000-08-20 add twenty months is 2000-04-20<br>
0290: * 2000-08-20 add minus nine months is 2000-11-20<br>
0291: * 2001-01-31 add one month is 2001-02-28<br>
0292: * 2001-01-31 add two months is 2001-03-31<br>
0293: *
0294: * @param instant the partial instant
0295: * @param fieldIndex the index of this field in the partial
0296: * @param values the values of the partial instant which should be updated
0297: * @param valueToAdd the value to add, in the units of the field
0298: * @return the passed in values
0299: * @throws IllegalArgumentException if the value is invalid or the maximum instant is reached
0300: */
0301: public int[] add(ReadablePartial instant, int fieldIndex,
0302: int[] values, int valueToAdd) {
0303: if (valueToAdd == 0) {
0304: return values;
0305: }
0306: // there are more efficient algorithms than this (especially for time only fields)
0307: // trouble is when dealing with days and months, so we use this technique of
0308: // adding/removing one from the larger field at a time
0309: DateTimeField nextField = null;
0310:
0311: while (valueToAdd > 0) {
0312: int max = getMaximumValue(instant, values);
0313: long proposed = values[fieldIndex] + valueToAdd;
0314: if (proposed <= max) {
0315: values[fieldIndex] = (int) proposed;
0316: break;
0317: }
0318: if (nextField == null) {
0319: if (fieldIndex == 0) {
0320: throw new IllegalArgumentException(
0321: "Maximum value exceeded for add");
0322: }
0323: nextField = instant.getField(fieldIndex - 1);
0324: // test only works if this field is UTC (ie. local)
0325: if (getRangeDurationField().getType() != nextField
0326: .getDurationField().getType()) {
0327: throw new IllegalArgumentException(
0328: "Fields invalid for add");
0329: }
0330: }
0331: valueToAdd -= (max + 1) - values[fieldIndex]; // reduce the amount to add
0332: values = nextField.add(instant, fieldIndex - 1, values, 1); // add 1 to next bigger field
0333: values[fieldIndex] = getMinimumValue(instant, values); // reset this field to zero
0334: }
0335: while (valueToAdd < 0) {
0336: int min = getMinimumValue(instant, values);
0337: long proposed = values[fieldIndex] + valueToAdd;
0338: if (proposed >= min) {
0339: values[fieldIndex] = (int) proposed;
0340: break;
0341: }
0342: if (nextField == null) {
0343: if (fieldIndex == 0) {
0344: throw new IllegalArgumentException(
0345: "Maximum value exceeded for add");
0346: }
0347: nextField = instant.getField(fieldIndex - 1);
0348: if (getRangeDurationField().getType() != nextField
0349: .getDurationField().getType()) {
0350: throw new IllegalArgumentException(
0351: "Fields invalid for add");
0352: }
0353: }
0354: valueToAdd -= (min - 1) - values[fieldIndex]; // reduce the amount to add
0355: values = nextField.add(instant, fieldIndex - 1, values, -1); // subtract 1 from next bigger field
0356: values[fieldIndex] = getMaximumValue(instant, values); // reset this field to max value
0357: }
0358:
0359: return set(instant, fieldIndex, values, values[fieldIndex]); // adjusts smaller fields
0360: }
0361:
0362: /**
0363: * Adds a value (which may be negative) to the partial instant,
0364: * wrapping the whole partial if the maximum size of the partial is reached.
0365: * <p>
0366: * The value will be added to this field, overflowing into larger fields
0367: * if necessary. Smaller fields should be unaffected, except where the
0368: * result would be an invalid value for a smaller field. In this case the
0369: * smaller field is adjusted to be in range.
0370: * <p>
0371: * Partial instants only contain some fields. This may result in a maximum
0372: * possible value, such as TimeOfDay normally being limited to 23:59:59:999.
0373: * If ths limit is reached by the addition, this method will wrap back to
0374: * 00:00:00.000. In fact, you would generally only use this method for
0375: * classes that have a limitation such as this.
0376: * <p>
0377: * For example, in the ISO chronology:<br>
0378: * 10:20:30 add 20 minutes is 10:40:30<br>
0379: * 10:20:30 add 45 minutes is 11:05:30<br>
0380: * 10:20:30 add 16 hours is 02:20:30<br>
0381: *
0382: * @param instant the partial instant
0383: * @param fieldIndex the index of this field in the partial
0384: * @param values the values of the partial instant which should be updated
0385: * @param valueToAdd the value to add, in the units of the field
0386: * @return the passed in values
0387: * @throws IllegalArgumentException if the value is invalid or the maximum instant is reached
0388: */
0389: public int[] addWrapPartial(ReadablePartial instant,
0390: int fieldIndex, int[] values, int valueToAdd) {
0391: if (valueToAdd == 0) {
0392: return values;
0393: }
0394: // there are more efficient algorithms than this (especially for time only fields)
0395: // trouble is when dealing with days and months, so we use this technique of
0396: // adding/removing one from the larger field at a time
0397: DateTimeField nextField = null;
0398:
0399: while (valueToAdd > 0) {
0400: int max = getMaximumValue(instant, values);
0401: long proposed = values[fieldIndex] + valueToAdd;
0402: if (proposed <= max) {
0403: values[fieldIndex] = (int) proposed;
0404: break;
0405: }
0406: if (nextField == null) {
0407: if (fieldIndex == 0) {
0408: valueToAdd -= (max + 1) - values[fieldIndex];
0409: values[fieldIndex] = getMinimumValue(instant,
0410: values);
0411: continue;
0412: }
0413: nextField = instant.getField(fieldIndex - 1);
0414: // test only works if this field is UTC (ie. local)
0415: if (getRangeDurationField().getType() != nextField
0416: .getDurationField().getType()) {
0417: throw new IllegalArgumentException(
0418: "Fields invalid for add");
0419: }
0420: }
0421: valueToAdd -= (max + 1) - values[fieldIndex]; // reduce the amount to add
0422: values = nextField.addWrapPartial(instant, fieldIndex - 1,
0423: values, 1); // add 1 to next bigger field
0424: values[fieldIndex] = getMinimumValue(instant, values); // reset this field to zero
0425: }
0426: while (valueToAdd < 0) {
0427: int min = getMinimumValue(instant, values);
0428: long proposed = values[fieldIndex] + valueToAdd;
0429: if (proposed >= min) {
0430: values[fieldIndex] = (int) proposed;
0431: break;
0432: }
0433: if (nextField == null) {
0434: if (fieldIndex == 0) {
0435: valueToAdd -= (min - 1) - values[fieldIndex];
0436: values[fieldIndex] = getMaximumValue(instant,
0437: values);
0438: continue;
0439: }
0440: nextField = instant.getField(fieldIndex - 1);
0441: if (getRangeDurationField().getType() != nextField
0442: .getDurationField().getType()) {
0443: throw new IllegalArgumentException(
0444: "Fields invalid for add");
0445: }
0446: }
0447: valueToAdd -= (min - 1) - values[fieldIndex]; // reduce the amount to add
0448: values = nextField.addWrapPartial(instant, fieldIndex - 1,
0449: values, -1); // subtract 1 from next bigger field
0450: values[fieldIndex] = getMaximumValue(instant, values); // reset this field to max value
0451: }
0452:
0453: return set(instant, fieldIndex, values, values[fieldIndex]); // adjusts smaller fields
0454: }
0455:
0456: /**
0457: * Adds a value (which may be negative) to the instant value,
0458: * wrapping within this field.
0459: * <p>
0460: * The value will be added to this field. If the value is too large to be
0461: * added solely to this field then it wraps. Larger fields are always
0462: * unaffected. Smaller fields should be unaffected, except where the
0463: * result would be an invalid value for a smaller field. In this case the
0464: * smaller field is adjusted to be in range.
0465: * <p>
0466: * For example, in the ISO chronology:<br>
0467: * 2000-08-20 addWrapField six months is 2000-02-20<br>
0468: * 2000-08-20 addWrapField twenty months is 2000-04-20<br>
0469: * 2000-08-20 addWrapField minus nine months is 2000-11-20<br>
0470: * 2001-01-31 addWrapField one month is 2001-02-28<br>
0471: * 2001-01-31 addWrapField two months is 2001-03-31<br>
0472: * <p>
0473: * The default implementation internally calls set. Subclasses are
0474: * encouraged to provide a more efficient implementation.
0475: *
0476: * @param instant the milliseconds from 1970-01-01T00:00:00Z to add to
0477: * @param value the value to add, in the units of the field
0478: * @return the updated milliseconds
0479: */
0480: public long addWrapField(long instant, int value) {
0481: int current = get(instant);
0482: int wrapped = FieldUtils.getWrappedValue(current, value,
0483: getMinimumValue(instant), getMaximumValue(instant));
0484: return set(instant, wrapped);
0485: }
0486:
0487: /**
0488: * Adds a value (which may be negative) to the partial instant,
0489: * wrapping within this field.
0490: * <p>
0491: * The value will be added to this field. If the value is too large to be
0492: * added solely to this field then it wraps. Larger fields are always
0493: * unaffected. Smaller fields should be unaffected, except where the
0494: * result would be an invalid value for a smaller field. In this case the
0495: * smaller field is adjusted to be in range.
0496: * <p>
0497: * For example, in the ISO chronology:<br>
0498: * 2000-08-20 addWrapField six months is 2000-02-20<br>
0499: * 2000-08-20 addWrapField twenty months is 2000-04-20<br>
0500: * 2000-08-20 addWrapField minus nine months is 2000-11-20<br>
0501: * 2001-01-31 addWrapField one month is 2001-02-28<br>
0502: * 2001-01-31 addWrapField two months is 2001-03-31<br>
0503: * <p>
0504: * The default implementation internally calls set. Subclasses are
0505: * encouraged to provide a more efficient implementation.
0506: *
0507: * @param instant the partial instant
0508: * @param fieldIndex the index of this field in the instant
0509: * @param values the values of the partial instant which should be updated
0510: * @param valueToAdd the value to add, in the units of the field
0511: * @return the passed in values
0512: * @throws IllegalArgumentException if the value is invalid
0513: */
0514: public int[] addWrapField(ReadablePartial instant, int fieldIndex,
0515: int[] values, int valueToAdd) {
0516: int current = values[fieldIndex];
0517: int wrapped = FieldUtils.getWrappedValue(current, valueToAdd,
0518: getMinimumValue(instant), getMaximumValue(instant));
0519: return set(instant, fieldIndex, values, wrapped); // adjusts smaller fields
0520: }
0521:
0522: //-----------------------------------------------------------------------
0523: /**
0524: * Computes the difference between two instants, as measured in the units
0525: * of this field. Any fractional units are dropped from the result. Calling
0526: * getDifference reverses the effect of calling add. In the following code:
0527: *
0528: * <pre>
0529: * long instant = ...
0530: * int v = ...
0531: * int age = getDifference(add(instant, v), instant);
0532: * </pre>
0533: *
0534: * The value 'age' is the same as the value 'v'.
0535: *
0536: * @param minuendInstant the milliseconds from 1970-01-01T00:00:00Z to
0537: * subtract from
0538: * @param subtrahendInstant the milliseconds from 1970-01-01T00:00:00Z to
0539: * subtract off the minuend
0540: * @return the difference in the units of this field
0541: */
0542: public int getDifference(long minuendInstant, long subtrahendInstant) {
0543: return getDurationField().getDifference(minuendInstant,
0544: subtrahendInstant);
0545: }
0546:
0547: /**
0548: * Computes the difference between two instants, as measured in the units
0549: * of this field. Any fractional units are dropped from the result. Calling
0550: * getDifference reverses the effect of calling add. In the following code:
0551: *
0552: * <pre>
0553: * long instant = ...
0554: * long v = ...
0555: * long age = getDifferenceAsLong(add(instant, v), instant);
0556: * </pre>
0557: *
0558: * The value 'age' is the same as the value 'v'.
0559: *
0560: * @param minuendInstant the milliseconds from 1970-01-01T00:00:00Z to
0561: * subtract from
0562: * @param subtrahendInstant the milliseconds from 1970-01-01T00:00:00Z to
0563: * subtract off the minuend
0564: * @return the difference in the units of this field
0565: */
0566: public long getDifferenceAsLong(long minuendInstant,
0567: long subtrahendInstant) {
0568: return getDurationField().getDifferenceAsLong(minuendInstant,
0569: subtrahendInstant);
0570: }
0571:
0572: /**
0573: * Sets a value in the milliseconds supplied.
0574: * <p>
0575: * The value of this field will be set.
0576: * If the value is invalid, an exception if thrown.
0577: * <p>
0578: * If setting this field would make other fields invalid, then those fields
0579: * may be changed. For example if the current date is the 31st January, and
0580: * the month is set to February, the day would be invalid. Instead, the day
0581: * would be changed to the closest value - the 28th/29th February as appropriate.
0582: *
0583: * @param instant the milliseconds from 1970-01-01T00:00:00Z to set in
0584: * @param value the value to set, in the units of the field
0585: * @return the updated milliseconds
0586: * @throws IllegalArgumentException if the value is invalid
0587: */
0588: public abstract long set(long instant, int value);
0589:
0590: /**
0591: * Sets a value using the specified partial instant.
0592: * <p>
0593: * The value of this field (specified by the index) will be set.
0594: * If the value is invalid, an exception if thrown.
0595: * <p>
0596: * If setting this field would make other fields invalid, then those fields
0597: * may be changed. For example if the current date is the 31st January, and
0598: * the month is set to February, the day would be invalid. Instead, the day
0599: * would be changed to the closest value - the 28th/29th February as appropriate.
0600: *
0601: * @param partial the partial instant
0602: * @param fieldIndex the index of this field in the instant
0603: * @param values the values to update
0604: * @param newValue the value to set, in the units of the field
0605: * @return the updated values
0606: * @throws IllegalArgumentException if the value is invalid
0607: */
0608: public int[] set(ReadablePartial partial, int fieldIndex,
0609: int[] values, int newValue) {
0610: FieldUtils.verifyValueBounds(this , newValue, getMinimumValue(
0611: partial, values), getMaximumValue(partial, values));
0612: values[fieldIndex] = newValue;
0613:
0614: // may need to adjust smaller fields
0615: for (int i = fieldIndex + 1; i < partial.size(); i++) {
0616: DateTimeField field = partial.getField(i);
0617: if (values[i] > field.getMaximumValue(partial, values)) {
0618: values[i] = field.getMaximumValue(partial, values);
0619: }
0620: if (values[i] < field.getMinimumValue(partial, values)) {
0621: values[i] = field.getMinimumValue(partial, values);
0622: }
0623: }
0624: return values;
0625: }
0626:
0627: /**
0628: * Sets a value in the milliseconds supplied from a human-readable, text value.
0629: * If the specified locale is null, the default locale is used.
0630: * <p>
0631: * This implementation uses <code>convertText(String, Locale)</code> and
0632: * {@link #set(long, int)}.
0633: * <p>
0634: * Note: subclasses that override this method should also override
0635: * getAsText.
0636: *
0637: * @param instant the milliseconds from 1970-01-01T00:00:00Z to set in
0638: * @param text the text value to set
0639: * @param locale the locale to use for selecting a text symbol, null for default
0640: * @return the updated milliseconds
0641: * @throws IllegalArgumentException if the text value is invalid
0642: */
0643: public long set(long instant, String text, Locale locale) {
0644: int value = convertText(text, locale);
0645: return set(instant, value);
0646: }
0647:
0648: /**
0649: * Sets a value in the milliseconds supplied from a human-readable, text value.
0650: * <p>
0651: * This implementation uses {@link #set(long, String, Locale)}.
0652: * <p>
0653: * Note: subclasses that override this method should also override getAsText.
0654: *
0655: * @param instant the milliseconds from 1970-01-01T00:00:00Z to set in
0656: * @param text the text value to set
0657: * @return the updated milliseconds
0658: * @throws IllegalArgumentException if the text value is invalid
0659: */
0660: public final long set(long instant, String text) {
0661: return set(instant, text, null);
0662: }
0663:
0664: /**
0665: * Sets a value in the milliseconds supplied from a human-readable, text value.
0666: * If the specified locale is null, the default locale is used.
0667: * <p>
0668: * This implementation uses <code>convertText(String, Locale)</code> and
0669: * {@link #set(ReadablePartial, int, int[], int)}.
0670: *
0671: * @param instant the partial instant
0672: * @param fieldIndex the index of this field in the instant
0673: * @param values the values of the partial instant which should be updated
0674: * @param text the text value to set
0675: * @param locale the locale to use for selecting a text symbol, null for default
0676: * @return the passed in values
0677: * @throws IllegalArgumentException if the text value is invalid
0678: */
0679: public int[] set(ReadablePartial instant, int fieldIndex,
0680: int[] values, String text, Locale locale) {
0681: int value = convertText(text, locale);
0682: return set(instant, fieldIndex, values, value);
0683: }
0684:
0685: /**
0686: * Convert the specified text and locale into a value.
0687: *
0688: * @param text the text to convert
0689: * @param locale the locale to convert using
0690: * @return the value extracted from the text
0691: * @throws IllegalArgumentException if the text is invalid
0692: */
0693: protected int convertText(String text, Locale locale) {
0694: try {
0695: return Integer.parseInt(text);
0696: } catch (NumberFormatException ex) {
0697: throw new IllegalFieldValueException(getType(), text);
0698: }
0699: }
0700:
0701: // Extra information API
0702: //------------------------------------------------------------------------
0703: /**
0704: * Returns the duration per unit value of this field. For example, if this
0705: * field represents "hour of day", then the unit duration is an hour.
0706: *
0707: * @return the duration of this field, or UnsupportedDurationField if field
0708: * has no duration
0709: */
0710: public abstract DurationField getDurationField();
0711:
0712: /**
0713: * Returns the range duration of this field. For example, if this field
0714: * represents "hour of day", then the range duration is a day.
0715: *
0716: * @return the range duration of this field, or null if field has no range
0717: */
0718: public abstract DurationField getRangeDurationField();
0719:
0720: /**
0721: * Returns whether this field is 'leap' for the specified instant.
0722: * <p>
0723: * For example, a leap year would return true, a non leap year would return
0724: * false.
0725: * <p>
0726: * This implementation returns false.
0727: *
0728: * @return true if the field is 'leap'
0729: */
0730: public boolean isLeap(long instant) {
0731: return false;
0732: }
0733:
0734: /**
0735: * Gets the amount by which this field is 'leap' for the specified instant.
0736: * <p>
0737: * For example, a leap year would return one, a non leap year would return
0738: * zero.
0739: * <p>
0740: * This implementation returns zero.
0741: */
0742: public int getLeapAmount(long instant) {
0743: return 0;
0744: }
0745:
0746: /**
0747: * If this field were to leap, then it would be in units described by the
0748: * returned duration. If this field doesn't ever leap, null is returned.
0749: * <p>
0750: * This implementation returns null.
0751: */
0752: public DurationField getLeapDurationField() {
0753: return null;
0754: }
0755:
0756: /**
0757: * Get the minimum allowable value for this field.
0758: *
0759: * @return the minimum valid value for this field, in the units of the
0760: * field
0761: */
0762: public abstract int getMinimumValue();
0763:
0764: /**
0765: * Get the minimum value for this field evaluated at the specified time.
0766: * <p>
0767: * This implementation returns the same as {@link #getMinimumValue()}.
0768: *
0769: * @param instant the milliseconds from 1970-01-01T00:00:00Z to query
0770: * @return the minimum value for this field, in the units of the field
0771: */
0772: public int getMinimumValue(long instant) {
0773: return getMinimumValue();
0774: }
0775:
0776: /**
0777: * Get the minimum value for this field evaluated at the specified instant.
0778: * <p>
0779: * This implementation returns the same as {@link #getMinimumValue()}.
0780: *
0781: * @param instant the partial instant to query
0782: * @return the minimum value for this field, in the units of the field
0783: */
0784: public int getMinimumValue(ReadablePartial instant) {
0785: return getMinimumValue();
0786: }
0787:
0788: /**
0789: * Get the minimum value for this field using the partial instant and
0790: * the specified values.
0791: * <p>
0792: * This implementation returns the same as {@link #getMinimumValue(ReadablePartial)}.
0793: *
0794: * @param instant the partial instant to query
0795: * @param values the values to use
0796: * @return the minimum value for this field, in the units of the field
0797: */
0798: public int getMinimumValue(ReadablePartial instant, int[] values) {
0799: return getMinimumValue(instant);
0800: }
0801:
0802: /**
0803: * Get the maximum allowable value for this field.
0804: *
0805: * @return the maximum valid value for this field, in the units of the
0806: * field
0807: */
0808: public abstract int getMaximumValue();
0809:
0810: /**
0811: * Get the maximum value for this field evaluated at the specified time.
0812: * <p>
0813: * This implementation returns the same as {@link #getMaximumValue()}.
0814: *
0815: * @param instant the milliseconds from 1970-01-01T00:00:00Z to query
0816: * @return the maximum value for this field, in the units of the field
0817: */
0818: public int getMaximumValue(long instant) {
0819: return getMaximumValue();
0820: }
0821:
0822: /**
0823: * Get the maximum value for this field evaluated at the specified instant.
0824: * <p>
0825: * This implementation returns the same as {@link #getMaximumValue()}.
0826: *
0827: * @param instant the partial instant to query
0828: * @return the maximum value for this field, in the units of the field
0829: */
0830: public int getMaximumValue(ReadablePartial instant) {
0831: return getMaximumValue();
0832: }
0833:
0834: /**
0835: * Get the maximum value for this field using the partial instant and
0836: * the specified values.
0837: * <p>
0838: * This implementation returns the same as {@link #getMaximumValue(ReadablePartial)}.
0839: *
0840: * @param instant the partial instant to query
0841: * @param values the values to use
0842: * @return the maximum value for this field, in the units of the field
0843: */
0844: public int getMaximumValue(ReadablePartial instant, int[] values) {
0845: return getMaximumValue(instant);
0846: }
0847:
0848: /**
0849: * Get the maximum text value for this field. The default implementation
0850: * returns the equivalent of Integer.toString(getMaximumValue()).length().
0851: *
0852: * @param locale the locale to use for selecting a text symbol
0853: * @return the maximum text length
0854: */
0855: public int getMaximumTextLength(Locale locale) {
0856: int max = getMaximumValue();
0857: if (max >= 0) {
0858: if (max < 10) {
0859: return 1;
0860: } else if (max < 100) {
0861: return 2;
0862: } else if (max < 1000) {
0863: return 3;
0864: }
0865: }
0866: return Integer.toString(max).length();
0867: }
0868:
0869: /**
0870: * Get the maximum short text value for this field. The default
0871: * implementation returns getMaximumTextLength().
0872: *
0873: * @param locale the locale to use for selecting a text symbol
0874: * @return the maximum short text length
0875: */
0876: public int getMaximumShortTextLength(Locale locale) {
0877: return getMaximumTextLength(locale);
0878: }
0879:
0880: // Calculation API
0881: //------------------------------------------------------------------------
0882: /**
0883: * Round to the lowest whole unit of this field. After rounding, the value
0884: * of this field and all fields of a higher magnitude are retained. The
0885: * fractional millis that cannot be expressed in whole increments of this
0886: * field are set to minimum.
0887: * <p>
0888: * For example, a datetime of 2002-11-02T23:34:56.789, rounded to the
0889: * lowest whole hour is 2002-11-02T23:00:00.000.
0890: *
0891: * @param instant the milliseconds from 1970-01-01T00:00:00Z to round
0892: * @return rounded milliseconds
0893: */
0894: public abstract long roundFloor(long instant);
0895:
0896: /**
0897: * Round to the highest whole unit of this field. The value of this field
0898: * and all fields of a higher magnitude may be incremented in order to
0899: * achieve this result. The fractional millis that cannot be expressed in
0900: * whole increments of this field are set to minimum.
0901: * <p>
0902: * For example, a datetime of 2002-11-02T23:34:56.789, rounded to the
0903: * highest whole hour is 2002-11-03T00:00:00.000.
0904: * <p>
0905: * The default implementation calls roundFloor, and if the instant is
0906: * modified as a result, adds one field unit. Subclasses are encouraged to
0907: * provide a more efficient implementation.
0908: *
0909: * @param instant the milliseconds from 1970-01-01T00:00:00Z to round
0910: * @return rounded milliseconds
0911: */
0912: public long roundCeiling(long instant) {
0913: long newInstant = roundFloor(instant);
0914: if (newInstant != instant) {
0915: instant = add(newInstant, 1);
0916: }
0917: return instant;
0918: }
0919:
0920: /**
0921: * Round to the nearest whole unit of this field. If the given millisecond
0922: * value is closer to the floor or is exactly halfway, this function
0923: * behaves like roundFloor. If the millisecond value is closer to the
0924: * ceiling, this function behaves like roundCeiling.
0925: *
0926: * @param instant the milliseconds from 1970-01-01T00:00:00Z to round
0927: * @return rounded milliseconds
0928: */
0929: public long roundHalfFloor(long instant) {
0930: long floor = roundFloor(instant);
0931: long ceiling = roundCeiling(instant);
0932:
0933: long diffFromFloor = instant - floor;
0934: long diffToCeiling = ceiling - instant;
0935:
0936: if (diffFromFloor <= diffToCeiling) {
0937: // Closer to the floor, or halfway - round floor
0938: return floor;
0939: } else {
0940: return ceiling;
0941: }
0942: }
0943:
0944: /**
0945: * Round to the nearest whole unit of this field. If the given millisecond
0946: * value is closer to the floor, this function behaves like roundFloor. If
0947: * the millisecond value is closer to the ceiling or is exactly halfway,
0948: * this function behaves like roundCeiling.
0949: *
0950: * @param instant the milliseconds from 1970-01-01T00:00:00Z to round
0951: * @return rounded milliseconds
0952: */
0953: public long roundHalfCeiling(long instant) {
0954: long floor = roundFloor(instant);
0955: long ceiling = roundCeiling(instant);
0956:
0957: long diffFromFloor = instant - floor;
0958: long diffToCeiling = ceiling - instant;
0959:
0960: if (diffToCeiling <= diffFromFloor) {
0961: // Closer to the ceiling, or halfway - round ceiling
0962: return ceiling;
0963: } else {
0964: return floor;
0965: }
0966: }
0967:
0968: /**
0969: * Round to the nearest whole unit of this field. If the given millisecond
0970: * value is closer to the floor, this function behaves like roundFloor. If
0971: * the millisecond value is closer to the ceiling, this function behaves
0972: * like roundCeiling.
0973: * <p>
0974: * If the millisecond value is exactly halfway between the floor and
0975: * ceiling, the ceiling is chosen over the floor only if it makes this
0976: * field's value even.
0977: *
0978: * @param instant the milliseconds from 1970-01-01T00:00:00Z to round
0979: * @return rounded milliseconds
0980: */
0981: public long roundHalfEven(long instant) {
0982: long floor = roundFloor(instant);
0983: long ceiling = roundCeiling(instant);
0984:
0985: long diffFromFloor = instant - floor;
0986: long diffToCeiling = ceiling - instant;
0987:
0988: if (diffFromFloor < diffToCeiling) {
0989: // Closer to the floor - round floor
0990: return floor;
0991: } else if (diffToCeiling < diffFromFloor) {
0992: // Closer to the ceiling - round ceiling
0993: return ceiling;
0994: } else {
0995: // Round to the instant that makes this field even. If both values
0996: // make this field even (unlikely), favor the ceiling.
0997: if ((get(ceiling) & 1) == 0) {
0998: return ceiling;
0999: }
1000: return floor;
1001: }
1002: }
1003:
1004: /**
1005: * Returns the fractional duration milliseconds of this field. In other
1006: * words, calling remainder returns the duration that roundFloor would
1007: * subtract.
1008: * <p>
1009: * For example, on a datetime of 2002-11-02T23:34:56.789, the remainder by
1010: * hour is 34 minutes and 56.789 seconds.
1011: * <p>
1012: * The default implementation computes
1013: * <code>instant - roundFloor(instant)</code>. Subclasses are encouraged to
1014: * provide a more efficient implementation.
1015: *
1016: * @param instant the milliseconds from 1970-01-01T00:00:00Z to get the
1017: * remainder
1018: * @return remainder duration, in milliseconds
1019: */
1020: public long remainder(long instant) {
1021: return instant - roundFloor(instant);
1022: }
1023:
1024: /**
1025: * Get a suitable debug string.
1026: *
1027: * @return debug string
1028: */
1029: public String toString() {
1030: return "DateTimeField[" + getName() + ']';
1031: }
1032:
1033: }
|