/*
 *  Copyright 2001-2009 Stephen Colebourne
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.joda.time;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.joda.time.field.FieldUtils;

Controls a period implementation by specifying which duration fields are to be used.

The following implementations are provided:

  • Standard - years, months, weeks, days, hours, minutes, seconds, millis
  • YearMonthDayTime - years, months, days, hours, minutes, seconds, millis
  • YearMonthDay - years, months, days
  • YearWeekDayTime - years, weeks, days, hours, minutes, seconds, millis
  • YearWeekDay - years, weeks, days
  • YearDayTime - years, days, hours, minutes, seconds, millis
  • YearDay - years, days, hours
  • DayTime - days, hours, minutes, seconds, millis
  • Time - hours, minutes, seconds, millis
  • plus one for each single type

PeriodType is thread-safe and immutable, and all subclasses must be as well.

Author:Brian S O'Neill, Stephen Colebourne
Since:1.0
/** * Controls a period implementation by specifying which duration fields are to be used. * <p> * The following implementations are provided: * <ul> * <li>Standard - years, months, weeks, days, hours, minutes, seconds, millis * <li>YearMonthDayTime - years, months, days, hours, minutes, seconds, millis * <li>YearMonthDay - years, months, days * <li>YearWeekDayTime - years, weeks, days, hours, minutes, seconds, millis * <li>YearWeekDay - years, weeks, days * <li>YearDayTime - years, days, hours, minutes, seconds, millis * <li>YearDay - years, days, hours * <li>DayTime - days, hours, minutes, seconds, millis * <li>Time - hours, minutes, seconds, millis * <li>plus one for each single type * </ul> * * <p> * PeriodType is thread-safe and immutable, and all subclasses must be as well. * * @author Brian S O'Neill * @author Stephen Colebourne * @since 1.0 */
public class PeriodType implements Serializable {
Serialization version
/** Serialization version */
private static final long serialVersionUID = 2274324892792009998L;
Cache of all the known types.
/** Cache of all the known types. */
private static final Map<PeriodType, Object> cTypes = new HashMap<PeriodType, Object>(32); static int YEAR_INDEX = 0; static int MONTH_INDEX = 1; static int WEEK_INDEX = 2; static int DAY_INDEX = 3; static int HOUR_INDEX = 4; static int MINUTE_INDEX = 5; static int SECOND_INDEX = 6; static int MILLI_INDEX = 7; private static PeriodType cStandard; private static PeriodType cYMDTime; private static PeriodType cYMD; private static PeriodType cYWDTime; private static PeriodType cYWD; private static PeriodType cYDTime; private static PeriodType cYD; private static PeriodType cDTime; private static PeriodType cTime; private static PeriodType cYears; private static PeriodType cMonths; private static PeriodType cWeeks; private static PeriodType cDays; private static PeriodType cHours; private static PeriodType cMinutes; private static PeriodType cSeconds; private static PeriodType cMillis;
Gets a type that defines all standard fields.
  • years
  • months
  • weeks
  • days
  • hours
  • minutes
  • seconds
  • milliseconds
Returns:the period type
/** * Gets a type that defines all standard fields. * <ul> * <li>years * <li>months * <li>weeks * <li>days * <li>hours * <li>minutes * <li>seconds * <li>milliseconds * </ul> * * @return the period type */
public static PeriodType standard() { PeriodType type = cStandard; if (type == null) { type = new PeriodType( "Standard", new DurationFieldType[] { DurationFieldType.years(), DurationFieldType.months(), DurationFieldType.weeks(), DurationFieldType.days(), DurationFieldType.hours(), DurationFieldType.minutes(), DurationFieldType.seconds(), DurationFieldType.millis(), }, new int[] { 0, 1, 2, 3, 4, 5, 6, 7, } ); cStandard = type; } return type; }
Gets a type that defines all standard fields except weeks.
  • years
  • months
  • days
  • hours
  • minutes
  • seconds
  • milliseconds
Returns:the period type
/** * Gets a type that defines all standard fields except weeks. * <ul> * <li>years * <li>months * <li>days * <li>hours * <li>minutes * <li>seconds * <li>milliseconds * </ul> * * @return the period type */
public static PeriodType yearMonthDayTime() { PeriodType type = cYMDTime; if (type == null) { type = new PeriodType( "YearMonthDayTime", new DurationFieldType[] { DurationFieldType.years(), DurationFieldType.months(), DurationFieldType.days(), DurationFieldType.hours(), DurationFieldType.minutes(), DurationFieldType.seconds(), DurationFieldType.millis(), }, new int[] { 0, 1, -1, 2, 3, 4, 5, 6, } ); cYMDTime = type; } return type; }
Gets a type that defines the year, month and day fields.
  • years
  • months
  • days
Returns:the period type
Since:1.1
/** * Gets a type that defines the year, month and day fields. * <ul> * <li>years * <li>months * <li>days * </ul> * * @return the period type * @since 1.1 */
public static PeriodType yearMonthDay() { PeriodType type = cYMD; if (type == null) { type = new PeriodType( "YearMonthDay", new DurationFieldType[] { DurationFieldType.years(), DurationFieldType.months(), DurationFieldType.days(), }, new int[] { 0, 1, -1, 2, -1, -1, -1, -1, } ); cYMD = type; } return type; }
Gets a type that defines all standard fields except months.
  • years
  • weeks
  • days
  • hours
  • minutes
  • seconds
  • milliseconds
Returns:the period type
/** * Gets a type that defines all standard fields except months. * <ul> * <li>years * <li>weeks * <li>days * <li>hours * <li>minutes * <li>seconds * <li>milliseconds * </ul> * * @return the period type */
public static PeriodType yearWeekDayTime() { PeriodType type = cYWDTime; if (type == null) { type = new PeriodType( "YearWeekDayTime", new DurationFieldType[] { DurationFieldType.years(), DurationFieldType.weeks(), DurationFieldType.days(), DurationFieldType.hours(), DurationFieldType.minutes(), DurationFieldType.seconds(), DurationFieldType.millis(), }, new int[] { 0, -1, 1, 2, 3, 4, 5, 6, } ); cYWDTime = type; } return type; }
Gets a type that defines year, week and day fields.
  • years
  • weeks
  • days
Returns:the period type
Since:1.1
/** * Gets a type that defines year, week and day fields. * <ul> * <li>years * <li>weeks * <li>days * </ul> * * @return the period type * @since 1.1 */
public static PeriodType yearWeekDay() { PeriodType type = cYWD; if (type == null) { type = new PeriodType( "YearWeekDay", new DurationFieldType[] { DurationFieldType.years(), DurationFieldType.weeks(), DurationFieldType.days(), }, new int[] { 0, -1, 1, 2, -1, -1, -1, -1, } ); cYWD = type; } return type; }
Gets a type that defines all standard fields except months and weeks.
  • years
  • days
  • hours
  • minutes
  • seconds
  • milliseconds
Returns:the period type
/** * Gets a type that defines all standard fields except months and weeks. * <ul> * <li>years * <li>days * <li>hours * <li>minutes * <li>seconds * <li>milliseconds * </ul> * * @return the period type */
public static PeriodType yearDayTime() { PeriodType type = cYDTime; if (type == null) { type = new PeriodType( "YearDayTime", new DurationFieldType[] { DurationFieldType.years(), DurationFieldType.days(), DurationFieldType.hours(), DurationFieldType.minutes(), DurationFieldType.seconds(), DurationFieldType.millis(), }, new int[] { 0, -1, -1, 1, 2, 3, 4, 5, } ); cYDTime = type; } return type; }
Gets a type that defines the year and day fields.
  • years
  • days
Returns:the period type
Since:1.1
/** * Gets a type that defines the year and day fields. * <ul> * <li>years * <li>days * </ul> * * @return the period type * @since 1.1 */
public static PeriodType yearDay() { PeriodType type = cYD; if (type == null) { type = new PeriodType( "YearDay", new DurationFieldType[] { DurationFieldType.years(), DurationFieldType.days(), }, new int[] { 0, -1, -1, 1, -1, -1, -1, -1, } ); cYD = type; } return type; }
Gets a type that defines all standard fields from days downwards.
  • days
  • hours
  • minutes
  • seconds
  • milliseconds
Returns:the period type
/** * Gets a type that defines all standard fields from days downwards. * <ul> * <li>days * <li>hours * <li>minutes * <li>seconds * <li>milliseconds * </ul> * * @return the period type */
public static PeriodType dayTime() { PeriodType type = cDTime; if (type == null) { type = new PeriodType( "DayTime", new DurationFieldType[] { DurationFieldType.days(), DurationFieldType.hours(), DurationFieldType.minutes(), DurationFieldType.seconds(), DurationFieldType.millis(), }, new int[] { -1, -1, -1, 0, 1, 2, 3, 4, } ); cDTime = type; } return type; }
Gets a type that defines all standard time fields.
  • hours
  • minutes
  • seconds
  • milliseconds
Returns:the period type
/** * Gets a type that defines all standard time fields. * <ul> * <li>hours * <li>minutes * <li>seconds * <li>milliseconds * </ul> * * @return the period type */
public static PeriodType time() { PeriodType type = cTime; if (type == null) { type = new PeriodType( "Time", new DurationFieldType[] { DurationFieldType.hours(), DurationFieldType.minutes(), DurationFieldType.seconds(), DurationFieldType.millis(), }, new int[] { -1, -1, -1, -1, 0, 1, 2, 3, } ); cTime = type; } return type; }
Gets a type that defines just the years field.
Returns:the period type
/** * Gets a type that defines just the years field. * * @return the period type */
public static PeriodType years() { PeriodType type = cYears; if (type == null) { type = new PeriodType( "Years", new DurationFieldType[] { DurationFieldType.years() }, new int[] { 0, -1, -1, -1, -1, -1, -1, -1, } ); cYears = type; } return type; }
Gets a type that defines just the months field.
Returns:the period type
/** * Gets a type that defines just the months field. * * @return the period type */
public static PeriodType months() { PeriodType type = cMonths; if (type == null) { type = new PeriodType( "Months", new DurationFieldType[] { DurationFieldType.months() }, new int[] { -1, 0, -1, -1, -1, -1, -1, -1, } ); cMonths = type; } return type; }
Gets a type that defines just the weeks field.
Returns:the period type
/** * Gets a type that defines just the weeks field. * * @return the period type */
public static PeriodType weeks() { PeriodType type = cWeeks; if (type == null) { type = new PeriodType( "Weeks", new DurationFieldType[] { DurationFieldType.weeks() }, new int[] { -1, -1, 0, -1, -1, -1, -1, -1, } ); cWeeks = type; } return type; }
Gets a type that defines just the days field.
Returns:the period type
/** * Gets a type that defines just the days field. * * @return the period type */
public static PeriodType days() { PeriodType type = cDays; if (type == null) { type = new PeriodType( "Days", new DurationFieldType[] { DurationFieldType.days() }, new int[] { -1, -1, -1, 0, -1, -1, -1, -1, } ); cDays = type; } return type; }
Gets a type that defines just the hours field.
Returns:the period type
/** * Gets a type that defines just the hours field. * * @return the period type */
public static PeriodType hours() { PeriodType type = cHours; if (type == null) { type = new PeriodType( "Hours", new DurationFieldType[] { DurationFieldType.hours() }, new int[] { -1, -1, -1, -1, 0, -1, -1, -1, } ); cHours = type; } return type; }
Gets a type that defines just the minutes field.
Returns:the period type
/** * Gets a type that defines just the minutes field. * * @return the period type */
public static PeriodType minutes() { PeriodType type = cMinutes; if (type == null) { type = new PeriodType( "Minutes", new DurationFieldType[] { DurationFieldType.minutes() }, new int[] { -1, -1, -1, -1, -1, 0, -1, -1, } ); cMinutes = type; } return type; }
Gets a type that defines just the seconds field.
Returns:the period type
/** * Gets a type that defines just the seconds field. * * @return the period type */
public static PeriodType seconds() { PeriodType type = cSeconds; if (type == null) { type = new PeriodType( "Seconds", new DurationFieldType[] { DurationFieldType.seconds() }, new int[] { -1, -1, -1, -1, -1, -1, 0, -1, } ); cSeconds = type; } return type; }
Gets a type that defines just the millis field.
Returns:the period type
/** * Gets a type that defines just the millis field. * * @return the period type */
public static PeriodType millis() { PeriodType type = cMillis; if (type == null) { type = new PeriodType( "Millis", new DurationFieldType[] { DurationFieldType.millis() }, new int[] { -1, -1, -1, -1, -1, -1, -1, 0, } ); cMillis = type; } return type; }
Gets a period type that contains the duration types of the array.

Only the 8 standard duration field types are supported.

Params:
  • types – the types to include in the array.
Returns:the period type
Since:1.1
/** * Gets a period type that contains the duration types of the array. * <p> * Only the 8 standard duration field types are supported. * * @param types the types to include in the array. * @return the period type * @since 1.1 */
public static synchronized PeriodType forFields(DurationFieldType[] types) { if (types == null || types.length == 0) { throw new IllegalArgumentException("Types array must not be null or empty"); } for (int i = 0; i < types.length; i++) { if (types[i] == null) { throw new IllegalArgumentException("Types array must not contain null"); } } Map<PeriodType, Object> cache = cTypes; if (cache.isEmpty()) { cache.put(standard(), standard()); cache.put(yearMonthDayTime(), yearMonthDayTime()); cache.put(yearMonthDay(), yearMonthDay()); cache.put(yearWeekDayTime(), yearWeekDayTime()); cache.put(yearWeekDay(), yearWeekDay()); cache.put(yearDayTime(), yearDayTime()); cache.put(yearDay(), yearDay()); cache.put(dayTime(), dayTime()); cache.put(time(), time()); cache.put(years(), years()); cache.put(months(), months()); cache.put(weeks(), weeks()); cache.put(days(), days()); cache.put(hours(), hours()); cache.put(minutes(), minutes()); cache.put(seconds(), seconds()); cache.put(millis(), millis()); } PeriodType inPartType = new PeriodType(null, types, null); Object cached = cache.get(inPartType); if (cached instanceof PeriodType) { return (PeriodType) cached; } if (cached != null) { throw new IllegalArgumentException("PeriodType does not support fields: " + cached); } PeriodType type = standard(); List<DurationFieldType> list = new ArrayList<DurationFieldType>(Arrays.asList(types)); if (list.remove(DurationFieldType.years()) == false) { type = type.withYearsRemoved(); } if (list.remove(DurationFieldType.months()) == false) { type = type.withMonthsRemoved(); } if (list.remove(DurationFieldType.weeks()) == false) { type = type.withWeeksRemoved(); } if (list.remove(DurationFieldType.days()) == false) { type = type.withDaysRemoved(); } if (list.remove(DurationFieldType.hours()) == false) { type = type.withHoursRemoved(); } if (list.remove(DurationFieldType.minutes()) == false) { type = type.withMinutesRemoved(); } if (list.remove(DurationFieldType.seconds()) == false) { type = type.withSecondsRemoved(); } if (list.remove(DurationFieldType.millis()) == false) { type = type.withMillisRemoved(); } if (list.size() > 0) { cache.put(inPartType, list); throw new IllegalArgumentException("PeriodType does not support fields: " + list); } // recheck cache in case initial array order was wrong PeriodType checkPartType = new PeriodType(null, type.iTypes, null); PeriodType checkedType = (PeriodType) cache.get(checkPartType); if (checkedType != null) { cache.put(checkPartType, checkedType); return checkedType; } cache.put(checkPartType, type); return type; } //-----------------------------------------------------------------------
The name of the type
/** The name of the type */
private final String iName;
The array of types
/** The array of types */
private final DurationFieldType[] iTypes;
The array of indices
/** The array of indices */
private final int[] iIndices;
Constructor.
Params:
  • name – the name
  • types – the types
  • indices – the indices
/** * Constructor. * * @param name the name * @param types the types * @param indices the indices */
protected PeriodType(String name, DurationFieldType[] types, int[] indices) { super(); iName = name; iTypes = types; iIndices = indices; } //-----------------------------------------------------------------------
Gets the name of the period type.
Returns:the name
/** * Gets the name of the period type. * * @return the name */
public String getName() { return iName; }
Gets the number of fields in the period type.
Returns:the number of fields
/** * Gets the number of fields in the period type. * * @return the number of fields */
public int size() { return iTypes.length; }
Gets the field type by index.
Params:
  • index – the index to retrieve
Throws:
Returns:the field type
/** * Gets the field type by index. * * @param index the index to retrieve * @return the field type * @throws IndexOutOfBoundsException if the index is invalid */
public DurationFieldType getFieldType(int index) { return iTypes[index]; }
Checks whether the field specified is supported by this period.
Params:
  • type – the type to check, may be null which returns false
Returns:true if the field is supported
/** * Checks whether the field specified is supported by this period. * * @param type the type to check, may be null which returns false * @return true if the field is supported */
public boolean isSupported(DurationFieldType type) { return (indexOf(type) >= 0); }
Gets the index of the field in this period.
Params:
  • type – the type to check, may be null which returns -1
Returns:the index of -1 if not supported
/** * Gets the index of the field in this period. * * @param type the type to check, may be null which returns -1 * @return the index of -1 if not supported */
public int indexOf(DurationFieldType type) { for (int i = 0, isize = size(); i < isize; i++) { if (iTypes[i] == type) { return i; } } return -1; }
Gets a debugging to string.
Returns:a string
/** * Gets a debugging to string. * * @return a string */
public String toString() { return "PeriodType[" + getName() + "]"; } //-----------------------------------------------------------------------
Gets the indexed field part of the period.
Params:
  • period – the period to query
  • index – the index to use
Returns:the value of the field, zero if unsupported
/** * Gets the indexed field part of the period. * * @param period the period to query * @param index the index to use * @return the value of the field, zero if unsupported */
int getIndexedField(ReadablePeriod period, int index) { int realIndex = iIndices[index]; return (realIndex == -1 ? 0 : period.getValue(realIndex)); }
Sets the indexed field part of the period.
Params:
  • period – the period to query
  • index – the index to use
  • values – the array to populate
  • newValue – the value to set
Throws:
/** * Sets the indexed field part of the period. * * @param period the period to query * @param index the index to use * @param values the array to populate * @param newValue the value to set * @throws UnsupportedOperationException if not supported */
boolean setIndexedField(ReadablePeriod period, int index, int[] values, int newValue) { int realIndex = iIndices[index]; if (realIndex == -1) { throw new UnsupportedOperationException("Field is not supported"); } values[realIndex] = newValue; return true; }
Adds to the indexed field part of the period.
Params:
  • period – the period to query
  • index – the index to use
  • values – the array to populate
  • valueToAdd – the value to add
Throws:
Returns:true if the array is updated
/** * Adds to the indexed field part of the period. * * @param period the period to query * @param index the index to use * @param values the array to populate * @param valueToAdd the value to add * @return true if the array is updated * @throws UnsupportedOperationException if not supported */
boolean addIndexedField(ReadablePeriod period, int index, int[] values, int valueToAdd) { if (valueToAdd == 0) { return false; } int realIndex = iIndices[index]; if (realIndex == -1) { throw new UnsupportedOperationException("Field is not supported"); } values[realIndex] = FieldUtils.safeAdd(values[realIndex], valueToAdd); return true; } //-----------------------------------------------------------------------
Returns a version of this PeriodType instance that does not support years.
Returns:a new period type that supports the original set of fields except years
/** * Returns a version of this PeriodType instance that does not support years. * * @return a new period type that supports the original set of fields except years */
public PeriodType withYearsRemoved() { return withFieldRemoved(0, "NoYears"); }
Returns a version of this PeriodType instance that does not support months.
Returns:a new period type that supports the original set of fields except months
/** * Returns a version of this PeriodType instance that does not support months. * * @return a new period type that supports the original set of fields except months */
public PeriodType withMonthsRemoved() { return withFieldRemoved(1, "NoMonths"); }
Returns a version of this PeriodType instance that does not support weeks.
Returns:a new period type that supports the original set of fields except weeks
/** * Returns a version of this PeriodType instance that does not support weeks. * * @return a new period type that supports the original set of fields except weeks */
public PeriodType withWeeksRemoved() { return withFieldRemoved(2, "NoWeeks"); }
Returns a version of this PeriodType instance that does not support days.
Returns:a new period type that supports the original set of fields except days
/** * Returns a version of this PeriodType instance that does not support days. * * @return a new period type that supports the original set of fields except days */
public PeriodType withDaysRemoved() { return withFieldRemoved(3, "NoDays"); }
Returns a version of this PeriodType instance that does not support hours.
Returns:a new period type that supports the original set of fields except hours
/** * Returns a version of this PeriodType instance that does not support hours. * * @return a new period type that supports the original set of fields except hours */
public PeriodType withHoursRemoved() { return withFieldRemoved(4, "NoHours"); }
Returns a version of this PeriodType instance that does not support minutes.
Returns:a new period type that supports the original set of fields except minutes
/** * Returns a version of this PeriodType instance that does not support minutes. * * @return a new period type that supports the original set of fields except minutes */
public PeriodType withMinutesRemoved() { return withFieldRemoved(5, "NoMinutes"); }
Returns a version of this PeriodType instance that does not support seconds.
Returns:a new period type that supports the original set of fields except seconds
/** * Returns a version of this PeriodType instance that does not support seconds. * * @return a new period type that supports the original set of fields except seconds */
public PeriodType withSecondsRemoved() { return withFieldRemoved(6, "NoSeconds"); }
Returns a version of this PeriodType instance that does not support milliseconds.
Returns:a new period type that supports the original set of fields except milliseconds
/** * Returns a version of this PeriodType instance that does not support milliseconds. * * @return a new period type that supports the original set of fields except milliseconds */
public PeriodType withMillisRemoved() { return withFieldRemoved(7, "NoMillis"); }
Removes the field specified by indices index.
Params:
  • indicesIndex – the index to remove
  • name – the name addition
Returns:the new type
/** * Removes the field specified by indices index. * * @param indicesIndex the index to remove * @param name the name addition * @return the new type */
private PeriodType withFieldRemoved(int indicesIndex, String name) { int fieldIndex = iIndices[indicesIndex]; if (fieldIndex == -1) { return this; } DurationFieldType[] types = new DurationFieldType[size() - 1]; for (int i = 0; i < iTypes.length; i++) { if (i < fieldIndex) { types[i] = iTypes[i]; } else if (i > fieldIndex) { types[i - 1] = iTypes[i]; } } int[] indices = new int[8]; for (int i = 0; i < indices.length; i++) { if (i < indicesIndex) { indices[i] = iIndices[i]; } else if (i > indicesIndex) { indices[i] = (iIndices[i] == -1 ? -1 : iIndices[i] - 1); } else { indices[i] = -1; } } return new PeriodType(getName() + name, types, indices); } //-----------------------------------------------------------------------
Compares this type to another object. To be equal, the object must be a PeriodType with the same set of fields.
Params:
  • obj – the object to compare to
Returns:true if equal
/** * Compares this type to another object. * To be equal, the object must be a PeriodType with the same set of fields. * * @param obj the object to compare to * @return true if equal */
public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof PeriodType == false) { return false; } PeriodType other = (PeriodType) obj; return (Arrays.equals(iTypes, other.iTypes)); }
Returns a hashcode based on the field types.
Returns:a suitable hashcode
/** * Returns a hashcode based on the field types. * * @return a suitable hashcode */
public int hashCode() { int hash = 0; for (int i = 0; i < iTypes.length; i++) { hash += iTypes[i].hashCode(); } return hash; } }