/*
 *  Copyright 2001-2011 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.base;

import java.io.Serializable;

import org.joda.time.Chronology;
import org.joda.time.DateTimeUtils;
import org.joda.time.Duration;
import org.joda.time.DurationFieldType;
import org.joda.time.MutablePeriod;
import org.joda.time.PeriodType;
import org.joda.time.ReadWritablePeriod;
import org.joda.time.ReadableDuration;
import org.joda.time.ReadableInstant;
import org.joda.time.ReadablePartial;
import org.joda.time.ReadablePeriod;
import org.joda.time.chrono.ISOChronology;
import org.joda.time.convert.ConverterManager;
import org.joda.time.convert.PeriodConverter;
import org.joda.time.field.FieldUtils;

BasePeriod is an abstract implementation of ReadablePeriod that stores data in a PeriodType and an int[].

This class should generally not be used directly by API users. The ReadablePeriod interface should be used when different kinds of period objects are to be referenced.

BasePeriod subclasses may be mutable and not thread-safe.

Author:Brian S O'Neill, Stephen Colebourne
Since:1.0
/** * BasePeriod is an abstract implementation of ReadablePeriod that stores * data in a <code>PeriodType</code> and an <code>int[]</code>. * <p> * This class should generally not be used directly by API users. * The {@link ReadablePeriod} interface should be used when different * kinds of period objects are to be referenced. * <p> * BasePeriod subclasses may be mutable and not thread-safe. * * @author Brian S O'Neill * @author Stephen Colebourne * @since 1.0 */
public abstract class BasePeriod extends AbstractPeriod implements ReadablePeriod, Serializable {
Serialization version
/** Serialization version */
private static final long serialVersionUID = -2110953284060001145L;
Serialization version
/** Serialization version */
private static final ReadablePeriod DUMMY_PERIOD = new AbstractPeriod() { public int getValue(int index) { return 0; } public PeriodType getPeriodType() { return PeriodType.time(); } };
The type of period
/** The type of period */
private final PeriodType iType;
The values
/** The values */
private final int[] iValues; //-----------------------------------------------------------------------
Creates a period from a set of field values.
Params:
  • years – amount of years in this period, which must be zero if unsupported
  • months – amount of months in this period, which must be zero if unsupported
  • weeks – amount of weeks in this period, which must be zero if unsupported
  • days – amount of days in this period, which must be zero if unsupported
  • hours – amount of hours in this period, which must be zero if unsupported
  • minutes – amount of minutes in this period, which must be zero if unsupported
  • seconds – amount of seconds in this period, which must be zero if unsupported
  • millis – amount of milliseconds in this period, which must be zero if unsupported
  • type – which set of fields this period supports
Throws:
/** * Creates a period from a set of field values. * * @param years amount of years in this period, which must be zero if unsupported * @param months amount of months in this period, which must be zero if unsupported * @param weeks amount of weeks in this period, which must be zero if unsupported * @param days amount of days in this period, which must be zero if unsupported * @param hours amount of hours in this period, which must be zero if unsupported * @param minutes amount of minutes in this period, which must be zero if unsupported * @param seconds amount of seconds in this period, which must be zero if unsupported * @param millis amount of milliseconds in this period, which must be zero if unsupported * @param type which set of fields this period supports * @throws IllegalArgumentException if period type is invalid * @throws IllegalArgumentException if an unsupported field's value is non-zero */
protected BasePeriod(int years, int months, int weeks, int days, int hours, int minutes, int seconds, int millis, PeriodType type) { super(); type = checkPeriodType(type); iType = type; iValues = setPeriodInternal(years, months, weeks, days, hours, minutes, seconds, millis); // internal method }
Creates a period from the given interval endpoints.
Params:
  • startInstant – interval start, in milliseconds
  • endInstant – interval end, in milliseconds
  • type – which set of fields this period supports, null means standard
  • chrono – the chronology to use, null means ISO default
Throws:
/** * Creates a period from the given interval endpoints. * * @param startInstant interval start, in milliseconds * @param endInstant interval end, in milliseconds * @param type which set of fields this period supports, null means standard * @param chrono the chronology to use, null means ISO default * @throws IllegalArgumentException if period type is invalid */
protected BasePeriod(long startInstant, long endInstant, PeriodType type, Chronology chrono) { super(); type = checkPeriodType(type); chrono = DateTimeUtils.getChronology(chrono); iType = type; iValues = chrono.get(this, startInstant, endInstant); }
Creates a period from the given interval endpoints.
Params:
  • startInstant – interval start, null means now
  • endInstant – interval end, null means now
  • type – which set of fields this period supports, null means standard
Throws:
/** * Creates a period from the given interval endpoints. * * @param startInstant interval start, null means now * @param endInstant interval end, null means now * @param type which set of fields this period supports, null means standard * @throws IllegalArgumentException if period type is invalid */
protected BasePeriod(ReadableInstant startInstant, ReadableInstant endInstant, PeriodType type) { super(); type = checkPeriodType(type); if (startInstant == null && endInstant == null) { iType = type; iValues = new int[size()]; } else { long startMillis = DateTimeUtils.getInstantMillis(startInstant); long endMillis = DateTimeUtils.getInstantMillis(endInstant); Chronology chrono = DateTimeUtils.getIntervalChronology(startInstant, endInstant); iType = type; iValues = chrono.get(this, startMillis, endMillis); } }
Creates a period from the given duration and end point.

The two partials must contain the same fields, thus you can specify two LocalDate objects, or two LocalTime objects, but not one of each. As these are Partial objects, time zones have no effect on the result.

The two partials must also both be contiguous - see DateTimeUtils.isContiguous(ReadablePartial) for a definition. Both LocalDate and LocalTime are contiguous.

Params:
  • start – the start of the period, must not be null
  • end – the end of the period, must not be null
  • type – which set of fields this period supports, null means standard
Throws:
Since:1.1
/** * Creates a period from the given duration and end point. * <p> * The two partials must contain the same fields, thus you can * specify two <code>LocalDate</code> objects, or two <code>LocalTime</code> * objects, but not one of each. * As these are Partial objects, time zones have no effect on the result. * <p> * The two partials must also both be contiguous - see * {@link DateTimeUtils#isContiguous(ReadablePartial)} for a * definition. Both <code>LocalDate</code> and <code>LocalTime</code> are contiguous. * * @param start the start of the period, must not be null * @param end the end of the period, must not be null * @param type which set of fields this period supports, null means standard * @throws IllegalArgumentException if the partials are null or invalid * @since 1.1 */
protected BasePeriod(ReadablePartial start, ReadablePartial end, PeriodType type) { super(); if (start == null || end == null) { throw new IllegalArgumentException("ReadablePartial objects must not be null"); } if (start instanceof BaseLocal && end instanceof BaseLocal && start.getClass() == end.getClass()) { // for performance type = checkPeriodType(type); long startMillis = ((BaseLocal) start).getLocalMillis(); long endMillis = ((BaseLocal) end).getLocalMillis(); Chronology chrono = start.getChronology(); chrono = DateTimeUtils.getChronology(chrono); iType = type; iValues = chrono.get(this, startMillis, endMillis); } else { if (start.size() != end.size()) { throw new IllegalArgumentException("ReadablePartial objects must have the same set of fields"); } for (int i = 0, isize = start.size(); i < isize; i++) { if (start.getFieldType(i) != end.getFieldType(i)) { throw new IllegalArgumentException("ReadablePartial objects must have the same set of fields"); } } if (DateTimeUtils.isContiguous(start) == false) { throw new IllegalArgumentException("ReadablePartial objects must be contiguous"); } iType = checkPeriodType(type); Chronology chrono = DateTimeUtils.getChronology(start.getChronology()).withUTC(); iValues = chrono.get(this, chrono.set(start, 0L), chrono.set(end, 0L)); } }
Creates a period from the given start point and duration.
Params:
  • startInstant – the interval start, null means now
  • duration – the duration of the interval, null means zero-length
  • type – which set of fields this period supports, null means standard
/** * Creates a period from the given start point and duration. * * @param startInstant the interval start, null means now * @param duration the duration of the interval, null means zero-length * @param type which set of fields this period supports, null means standard */
protected BasePeriod(ReadableInstant startInstant, ReadableDuration duration, PeriodType type) { super(); type = checkPeriodType(type); long startMillis = DateTimeUtils.getInstantMillis(startInstant); long durationMillis = DateTimeUtils.getDurationMillis(duration); long endMillis = FieldUtils.safeAdd(startMillis, durationMillis); Chronology chrono = DateTimeUtils.getInstantChronology(startInstant); iType = type; iValues = chrono.get(this, startMillis, endMillis); }
Creates a period from the given duration and end point.
Params:
  • duration – the duration of the interval, null means zero-length
  • endInstant – the interval end, null means now
  • type – which set of fields this period supports, null means standard
/** * Creates a period from the given duration and end point. * * @param duration the duration of the interval, null means zero-length * @param endInstant the interval end, null means now * @param type which set of fields this period supports, null means standard */
protected BasePeriod(ReadableDuration duration, ReadableInstant endInstant, PeriodType type) { super(); type = checkPeriodType(type); long durationMillis = DateTimeUtils.getDurationMillis(duration); long endMillis = DateTimeUtils.getInstantMillis(endInstant); long startMillis = FieldUtils.safeSubtract(endMillis, durationMillis); Chronology chrono = DateTimeUtils.getInstantChronology(endInstant); iType = type; iValues = chrono.get(this, startMillis, endMillis); }
Creates a period from the given millisecond duration with the standard period type and ISO rules, ensuring that the calculation is performed with the time-only period type.

The calculation uses the hour, minute, second and millisecond fields.

Params:
  • duration – the duration, in milliseconds
/** * Creates a period from the given millisecond duration with the standard period type * and ISO rules, ensuring that the calculation is performed with the time-only period type. * <p> * The calculation uses the hour, minute, second and millisecond fields. * * @param duration the duration, in milliseconds */
protected BasePeriod(long duration) { super(); // bug [3264409] // calculation uses period type from a period object (bad design) // thus we use a dummy period object with the time type iType = PeriodType.standard(); int[] values = ISOChronology.getInstanceUTC().get(DUMMY_PERIOD, duration); iValues = new int[8]; System.arraycopy(values, 0, iValues, 4, 4); }
Creates a period from the given millisecond duration, which is only really suitable for durations less than one day.

Only fields that are precise will be used. Thus the largest precise field may have a large value.

Params:
  • duration – the duration, in milliseconds
  • type – which set of fields this period supports, null means standard
  • chrono – the chronology to use, null means ISO default
Throws:
/** * Creates a period from the given millisecond duration, which is only really * suitable for durations less than one day. * <p> * Only fields that are precise will be used. * Thus the largest precise field may have a large value. * * @param duration the duration, in milliseconds * @param type which set of fields this period supports, null means standard * @param chrono the chronology to use, null means ISO default * @throws IllegalArgumentException if period type is invalid */
protected BasePeriod(long duration, PeriodType type, Chronology chrono) { super(); type = checkPeriodType(type); chrono = DateTimeUtils.getChronology(chrono); iType = type; iValues = chrono.get(this, duration); }
Creates a new period based on another using the ConverterManager.
Params:
  • period – the period to convert
  • type – which set of fields this period supports, null means use type from object
  • chrono – the chronology to use, null means ISO default
Throws:
/** * Creates a new period based on another using the {@link ConverterManager}. * * @param period the period to convert * @param type which set of fields this period supports, null means use type from object * @param chrono the chronology to use, null means ISO default * @throws IllegalArgumentException if period is invalid * @throws IllegalArgumentException if an unsupported field's value is non-zero */
protected BasePeriod(Object period, PeriodType type, Chronology chrono) { super(); PeriodConverter converter = ConverterManager.getInstance().getPeriodConverter(period); type = (type == null ? converter.getPeriodType(period) : type); type = checkPeriodType(type); iType = type; if (this instanceof ReadWritablePeriod) { iValues = new int[size()]; chrono = DateTimeUtils.getChronology(chrono); converter.setInto((ReadWritablePeriod) this, period, chrono); } else { iValues = new MutablePeriod(period, type, chrono).getValues(); } }
Constructor used when we trust ourselves. Do not expose publicly.
Params:
  • values – the values to use, not null, not cloned
  • type – which set of fields this period supports, not null
/** * Constructor used when we trust ourselves. * Do not expose publicly. * * @param values the values to use, not null, not cloned * @param type which set of fields this period supports, not null */
protected BasePeriod(int[] values, PeriodType type) { super(); iType = type; iValues = values; } //-----------------------------------------------------------------------
Validates a period type, converting nulls to a default value and checking the type is suitable for this instance.
Params:
  • type – the type to check, may be null
Throws:
Returns:the validated type to use, not null
/** * Validates a period type, converting nulls to a default value and * checking the type is suitable for this instance. * * @param type the type to check, may be null * @return the validated type to use, not null * @throws IllegalArgumentException if the period type is invalid */
protected PeriodType checkPeriodType(PeriodType type) { return DateTimeUtils.getPeriodType(type); } //-----------------------------------------------------------------------
Gets the period type.
Returns:the period type
/** * Gets the period type. * * @return the period type */
public PeriodType getPeriodType() { return iType; }
Gets the value at the specified index.
Params:
  • index – the index to retrieve
Throws:
Returns:the value of the field at the specified index
/** * Gets the value at the specified index. * * @param index the index to retrieve * @return the value of the field at the specified index * @throws IndexOutOfBoundsException if the index is invalid */
public int getValue(int index) { return iValues[index]; } //-----------------------------------------------------------------------
Gets the total millisecond duration of this period relative to a start instant.

This method adds the period to the specified instant in order to calculate the duration.

An instant must be supplied as the duration of a period varies. For example, a period of 1 month could vary between the equivalent of 28 and 31 days in milliseconds due to different length months. Similarly, a day can vary at Daylight Savings cutover, typically between 23 and 25 hours.

Params:
  • startInstant – the instant to add the period to, thus obtaining the duration
Throws:
Returns:the total length of the period as a duration relative to the start instant
/** * Gets the total millisecond duration of this period relative to a start instant. * <p> * This method adds the period to the specified instant in order to * calculate the duration. * <p> * An instant must be supplied as the duration of a period varies. * For example, a period of 1 month could vary between the equivalent of * 28 and 31 days in milliseconds due to different length months. * Similarly, a day can vary at Daylight Savings cutover, typically between * 23 and 25 hours. * * @param startInstant the instant to add the period to, thus obtaining the duration * @return the total length of the period as a duration relative to the start instant * @throws ArithmeticException if the millis exceeds the capacity of the duration */
public Duration toDurationFrom(ReadableInstant startInstant) { long startMillis = DateTimeUtils.getInstantMillis(startInstant); Chronology chrono = DateTimeUtils.getInstantChronology(startInstant); long endMillis = chrono.add(this, startMillis, 1); return new Duration(startMillis, endMillis); }
Gets the total millisecond duration of this period relative to an end instant.

This method subtracts the period from the specified instant in order to calculate the duration.

An instant must be supplied as the duration of a period varies. For example, a period of 1 month could vary between the equivalent of 28 and 31 days in milliseconds due to different length months. Similarly, a day can vary at Daylight Savings cutover, typically between 23 and 25 hours.

Params:
  • endInstant – the instant to subtract the period from, thus obtaining the duration
Throws:
Returns:the total length of the period as a duration relative to the end instant
/** * Gets the total millisecond duration of this period relative to an * end instant. * <p> * This method subtracts the period from the specified instant in order * to calculate the duration. * <p> * An instant must be supplied as the duration of a period varies. * For example, a period of 1 month could vary between the equivalent of * 28 and 31 days in milliseconds due to different length months. * Similarly, a day can vary at Daylight Savings cutover, typically between * 23 and 25 hours. * * @param endInstant the instant to subtract the period from, thus obtaining the duration * @return the total length of the period as a duration relative to the end instant * @throws ArithmeticException if the millis exceeds the capacity of the duration */
public Duration toDurationTo(ReadableInstant endInstant) { long endMillis = DateTimeUtils.getInstantMillis(endInstant); Chronology chrono = DateTimeUtils.getInstantChronology(endInstant); long startMillis = chrono.add(this, endMillis, -1); return new Duration(startMillis, endMillis); } //-----------------------------------------------------------------------
Checks whether a field type is supported, and if so adds the new value to the relevant index in the specified array.
Params:
  • type – the field type
  • values – the array to update
  • newValue – the new value to store if successful
/** * Checks whether a field type is supported, and if so adds the new value * to the relevant index in the specified array. * * @param type the field type * @param values the array to update * @param newValue the new value to store if successful */
private void checkAndUpdate(DurationFieldType type, int[] values, int newValue) { int index = indexOf(type); if (index == -1) { if (newValue != 0) { throw new IllegalArgumentException( "Period does not support field '" + type.getName() + "'"); } } else { values[index] = newValue; } } //-----------------------------------------------------------------------
Sets all the fields of this period from another.
Params:
  • period – the period to copy from, not null
Throws:
/** * Sets all the fields of this period from another. * * @param period the period to copy from, not null * @throws IllegalArgumentException if an unsupported field's value is non-zero */
protected void setPeriod(ReadablePeriod period) { if (period == null) { setValues(new int[size()]); } else { setPeriodInternal(period); } }
Private method called from constructor.
/** * Private method called from constructor. */
private void setPeriodInternal(ReadablePeriod period) { int[] newValues = new int[size()]; for (int i = 0, isize = period.size(); i < isize; i++) { DurationFieldType type = period.getFieldType(i); int value = period.getValue(i); checkAndUpdate(type, newValues, value); } setValues(newValues); }
Sets the eight standard the fields in one go.
Params:
  • years – amount of years in this period, which must be zero if unsupported
  • months – amount of months in this period, which must be zero if unsupported
  • weeks – amount of weeks in this period, which must be zero if unsupported
  • days – amount of days in this period, which must be zero if unsupported
  • hours – amount of hours in this period, which must be zero if unsupported
  • minutes – amount of minutes in this period, which must be zero if unsupported
  • seconds – amount of seconds in this period, which must be zero if unsupported
  • millis – amount of milliseconds in this period, which must be zero if unsupported
Throws:
/** * Sets the eight standard the fields in one go. * * @param years amount of years in this period, which must be zero if unsupported * @param months amount of months in this period, which must be zero if unsupported * @param weeks amount of weeks in this period, which must be zero if unsupported * @param days amount of days in this period, which must be zero if unsupported * @param hours amount of hours in this period, which must be zero if unsupported * @param minutes amount of minutes in this period, which must be zero if unsupported * @param seconds amount of seconds in this period, which must be zero if unsupported * @param millis amount of milliseconds in this period, which must be zero if unsupported * @throws IllegalArgumentException if an unsupported field's value is non-zero */
protected void setPeriod(int years, int months, int weeks, int days, int hours, int minutes, int seconds, int millis) { int[] newValues = setPeriodInternal(years, months, weeks, days, hours, minutes, seconds, millis); setValues(newValues); }
Private method called from constructor.
/** * Private method called from constructor. */
private int[] setPeriodInternal(int years, int months, int weeks, int days, int hours, int minutes, int seconds, int millis) { int[] newValues = new int[size()]; checkAndUpdate(DurationFieldType.years(), newValues, years); checkAndUpdate(DurationFieldType.months(), newValues, months); checkAndUpdate(DurationFieldType.weeks(), newValues, weeks); checkAndUpdate(DurationFieldType.days(), newValues, days); checkAndUpdate(DurationFieldType.hours(), newValues, hours); checkAndUpdate(DurationFieldType.minutes(), newValues, minutes); checkAndUpdate(DurationFieldType.seconds(), newValues, seconds); checkAndUpdate(DurationFieldType.millis(), newValues, millis); return newValues; } //-----------------------------------------------------------------------
Sets the value of a field in this period.
Params:
  • field – the field to set
  • value – the value to set
Throws:
/** * Sets the value of a field in this period. * * @param field the field to set * @param value the value to set * @throws IllegalArgumentException if field is is null or not supported. */
protected void setField(DurationFieldType field, int value) { setFieldInto(iValues, field, value); }
Sets the value of a field in this period.
Params:
  • values – the array of values to update
  • field – the field to set
  • value – the value to set
Throws:
/** * Sets the value of a field in this period. * * @param values the array of values to update * @param field the field to set * @param value the value to set * @throws IllegalArgumentException if field is null or not supported. */
protected void setFieldInto(int[] values, DurationFieldType field, int value) { int index = indexOf(field); if (index == -1) { if (value != 0 || field == null) { throw new IllegalArgumentException( "Period does not support field '" + field + "'"); } } else { values[index] = value; } }
Adds the value of a field in this period.
Params:
  • field – the field to set
  • value – the value to set
Throws:
/** * Adds the value of a field in this period. * * @param field the field to set * @param value the value to set * @throws IllegalArgumentException if field is is null or not supported. */
protected void addField(DurationFieldType field, int value) { addFieldInto(iValues, field, value); }
Adds the value of a field in this period.
Params:
  • values – the array of values to update
  • field – the field to set
  • value – the value to set
Throws:
/** * Adds the value of a field in this period. * * @param values the array of values to update * @param field the field to set * @param value the value to set * @throws IllegalArgumentException if field is is null or not supported. */
protected void addFieldInto(int[] values, DurationFieldType field, int value) { int index = indexOf(field); if (index == -1) { if (value != 0 || field == null) { throw new IllegalArgumentException( "Period does not support field '" + field + "'"); } } else { values[index] = FieldUtils.safeAdd(values[index], value); } }
Merges the fields from another period.
Params:
  • period – the period to add from, not null
Throws:
/** * Merges the fields from another period. * * @param period the period to add from, not null * @throws IllegalArgumentException if an unsupported field's value is non-zero */
protected void mergePeriod(ReadablePeriod period) { if (period != null) { setValues(mergePeriodInto(getValues(), period)); } }
Merges the fields from another period.
Params:
  • values – the array of values to update
  • period – the period to add from, not null
Throws:
Returns:the updated values
/** * Merges the fields from another period. * * @param values the array of values to update * @param period the period to add from, not null * @return the updated values * @throws IllegalArgumentException if an unsupported field's value is non-zero */
protected int[] mergePeriodInto(int[] values, ReadablePeriod period) { for (int i = 0, isize = period.size(); i < isize; i++) { DurationFieldType type = period.getFieldType(i); int value = period.getValue(i); checkAndUpdate(type, values, value); } return values; }
Adds the fields from another period.
Params:
  • period – the period to add from, not null
Throws:
/** * Adds the fields from another period. * * @param period the period to add from, not null * @throws IllegalArgumentException if an unsupported field's value is non-zero */
protected void addPeriod(ReadablePeriod period) { if (period != null) { setValues(addPeriodInto(getValues(), period)); } }
Adds the fields from another period.
Params:
  • values – the array of values to update
  • period – the period to add from, not null
Throws:
Returns:the updated values
/** * Adds the fields from another period. * * @param values the array of values to update * @param period the period to add from, not null * @return the updated values * @throws IllegalArgumentException if an unsupported field's value is non-zero */
protected int[] addPeriodInto(int[] values, ReadablePeriod period) { for (int i = 0, isize = period.size(); i < isize; i++) { DurationFieldType type = period.getFieldType(i); int value = period.getValue(i); if (value != 0) { int index = indexOf(type); if (index == -1) { throw new IllegalArgumentException( "Period does not support field '" + type.getName() + "'"); } else { values[index] = FieldUtils.safeAdd(getValue(index), value); } } } return values; } //-----------------------------------------------------------------------
Sets the value of the field at the specified index.
Params:
  • index – the index
  • value – the value to set
Throws:
/** * Sets the value of the field at the specified index. * * @param index the index * @param value the value to set * @throws IndexOutOfBoundsException if the index is invalid */
protected void setValue(int index, int value) { iValues[index] = value; }
Sets the values of all fields.

In version 2.0 and later, this method copies the array into the original. This is because the instance variable has been changed to be final to satisfy the Java Memory Model. This only impacts subclasses that are mutable.

Params:
  • values – the array of values
/** * Sets the values of all fields. * <p> * In version 2.0 and later, this method copies the array into the original. * This is because the instance variable has been changed to be final to satisfy the Java Memory Model. * This only impacts subclasses that are mutable. * * @param values the array of values */
protected void setValues(int[] values) { System.arraycopy(values, 0, iValues, 0, iValues.length); } }