001/*
002 *  Copyright 2001-2011 Stephen Colebourne
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.joda.time.base;
017
018import java.io.Serializable;
019
020import org.joda.time.Chronology;
021import org.joda.time.DateTimeUtils;
022import org.joda.time.MutableInterval;
023import org.joda.time.ReadWritableInterval;
024import org.joda.time.ReadableDuration;
025import org.joda.time.ReadableInstant;
026import org.joda.time.ReadableInterval;
027import org.joda.time.ReadablePeriod;
028import org.joda.time.chrono.ISOChronology;
029import org.joda.time.convert.ConverterManager;
030import org.joda.time.convert.IntervalConverter;
031import org.joda.time.field.FieldUtils;
032
033/**
034 * BaseInterval is an abstract implementation of ReadableInterval that stores
035 * data in two <code>long</code> millisecond fields.
036 * <p>
037 * This class should generally not be used directly by API users.
038 * The {@link ReadableInterval} interface should be used when different 
039 * kinds of interval objects are to be referenced.
040 * <p>
041 * BaseInterval subclasses may be mutable and not thread-safe.
042 *
043 * @author Brian S O'Neill
044 * @author Sean Geoghegan
045 * @author Stephen Colebourne
046 * @since 1.0
047 */
048public abstract class BaseInterval
049        extends AbstractInterval
050        implements ReadableInterval, Serializable {
051
052    /** Serialization version */
053    private static final long serialVersionUID = 576586928732749278L;
054
055    /** The chronology of the interval */
056    private volatile Chronology iChronology;
057    /** The start of the interval */
058    private volatile long iStartMillis;
059    /** The end of the interval */
060    private volatile long iEndMillis;
061
062    /**
063     * Constructs an interval from a start and end instant.
064     * 
065     * @param startInstant  start of this interval, as milliseconds from 1970-01-01T00:00:00Z.
066     * @param endInstant  end of this interval, as milliseconds from 1970-01-01T00:00:00Z.
067     * @param chrono  the chronology to use, null is ISO default
068     * @throws IllegalArgumentException if the end is before the start
069     */
070    protected BaseInterval(long startInstant, long endInstant, Chronology chrono) {
071        super();
072        iChronology = DateTimeUtils.getChronology(chrono);
073        checkInterval(startInstant, endInstant);
074        iStartMillis = startInstant;
075        iEndMillis = endInstant;
076    }
077
078    /**
079     * Constructs an interval from a start and end instant.
080     * 
081     * @param start  start of this interval, null means now
082     * @param end  end of this interval, null means now
083     * @throws IllegalArgumentException if the end is before the start
084     */
085    protected BaseInterval(ReadableInstant start, ReadableInstant end) {
086        super();
087        if (start == null && end == null) {
088            iStartMillis = iEndMillis = DateTimeUtils.currentTimeMillis();
089            iChronology = ISOChronology.getInstance();
090        } else {
091            iChronology = DateTimeUtils.getInstantChronology(start);
092            iStartMillis = DateTimeUtils.getInstantMillis(start);
093            iEndMillis = DateTimeUtils.getInstantMillis(end);
094            checkInterval(iStartMillis, iEndMillis);
095        }
096    }
097
098    /**
099     * Constructs an interval from a start instant and a duration.
100     * 
101     * @param start  start of this interval, null means now
102     * @param duration  the duration of this interval, null means zero length
103     * @throws IllegalArgumentException if the end is before the start
104     * @throws ArithmeticException if the end instant exceeds the capacity of a long
105     */
106    protected BaseInterval(ReadableInstant start, ReadableDuration duration) {
107        super();
108        iChronology = DateTimeUtils.getInstantChronology(start);
109        iStartMillis = DateTimeUtils.getInstantMillis(start);
110        long durationMillis = DateTimeUtils.getDurationMillis(duration);
111        iEndMillis = FieldUtils.safeAdd(iStartMillis, durationMillis);
112        checkInterval(iStartMillis, iEndMillis);
113    }
114
115    /**
116     * Constructs an interval from a millisecond duration and an end instant.
117     * 
118     * @param duration  the duration of this interval, null means zero length
119     * @param end  end of this interval, null means now
120     * @throws IllegalArgumentException if the end is before the start
121     * @throws ArithmeticException if the start instant exceeds the capacity of a long
122     */
123    protected BaseInterval(ReadableDuration duration, ReadableInstant end) {
124        super();
125        iChronology = DateTimeUtils.getInstantChronology(end);
126        iEndMillis = DateTimeUtils.getInstantMillis(end);
127        long durationMillis = DateTimeUtils.getDurationMillis(duration);
128        iStartMillis = FieldUtils.safeAdd(iEndMillis, -durationMillis);
129        checkInterval(iStartMillis, iEndMillis);
130    }
131
132    /**
133     * Constructs an interval from a start instant and a time period.
134     * <p>
135     * When forming the interval, the chronology from the instant is used
136     * if present, otherwise the chronology of the period is used.
137     * 
138     * @param start  start of this interval, null means now
139     * @param period  the period of this interval, null means zero length
140     * @throws IllegalArgumentException if the end is before the start
141     * @throws ArithmeticException if the end instant exceeds the capacity of a long
142     */
143    protected BaseInterval(ReadableInstant start, ReadablePeriod period) {
144        super();
145        Chronology chrono = DateTimeUtils.getInstantChronology(start);
146        iChronology = chrono;
147        iStartMillis = DateTimeUtils.getInstantMillis(start);
148        if (period == null) {
149            iEndMillis = iStartMillis;
150        } else {
151            iEndMillis = chrono.add(period, iStartMillis, 1);
152        }
153        checkInterval(iStartMillis, iEndMillis);
154    }
155
156    /**
157     * Constructs an interval from a time period and an end instant.
158     * <p>
159     * When forming the interval, the chronology from the instant is used
160     * if present, otherwise the chronology of the period is used.
161     * 
162     * @param period  the period of this interval, null means zero length
163     * @param end  end of this interval, null means now
164     * @throws IllegalArgumentException if the end is before the start
165     * @throws ArithmeticException if the start instant exceeds the capacity of a long
166     */
167    protected BaseInterval(ReadablePeriod period, ReadableInstant end) {
168        super();
169        Chronology chrono = DateTimeUtils.getInstantChronology(end);
170        iChronology = chrono;
171        iEndMillis = DateTimeUtils.getInstantMillis(end);
172        if (period == null) {
173            iStartMillis = iEndMillis;
174        } else {
175            iStartMillis = chrono.add(period, iEndMillis, -1);
176        }
177        checkInterval(iStartMillis, iEndMillis);
178    }
179
180    /**
181     * Constructs a time interval converting or copying from another object
182     * that describes an interval.
183     * 
184     * @param interval  the time interval to copy
185     * @param chrono  the chronology to use, null means let converter decide
186     * @throws IllegalArgumentException if the interval is invalid
187     */
188    protected BaseInterval(Object interval, Chronology chrono) {
189        super();
190        IntervalConverter converter = ConverterManager.getInstance().getIntervalConverter(interval);
191        if (converter.isReadableInterval(interval, chrono)) {
192            ReadableInterval input = (ReadableInterval) interval;
193            iChronology = (chrono != null ? chrono : input.getChronology());
194            iStartMillis = input.getStartMillis();
195            iEndMillis = input.getEndMillis();
196        } else if (this instanceof ReadWritableInterval) {
197            converter.setInto((ReadWritableInterval) this, interval, chrono);
198        } else {
199            MutableInterval mi = new MutableInterval();
200            converter.setInto(mi, interval, chrono);
201            iChronology = mi.getChronology();
202            iStartMillis = mi.getStartMillis();
203            iEndMillis = mi.getEndMillis();
204        }
205        checkInterval(iStartMillis, iEndMillis);
206    }
207
208    //-----------------------------------------------------------------------
209    /**
210     * Gets the chronology of this interval.
211     *
212     * @return the chronology
213     */
214    public Chronology getChronology() {
215        return iChronology;
216    }
217
218    /**
219     * Gets the start of this time interval which is inclusive.
220     *
221     * @return the start of the time interval,
222     *  millisecond instant from 1970-01-01T00:00:00Z
223     */
224    public long getStartMillis() {
225        return iStartMillis;
226    }
227
228    /**
229     * Gets the end of this time interval which is exclusive.
230     *
231     * @return the end of the time interval,
232     *  millisecond instant from 1970-01-01T00:00:00Z
233     */
234    public long getEndMillis() {
235        return iEndMillis;
236    }
237
238    //-----------------------------------------------------------------------
239    /**
240     * Sets this interval from two millisecond instants and a chronology.
241     *
242     * @param startInstant  the start of the time interval
243     * @param endInstant  the start of the time interval
244     * @param chrono  the chronology, not null
245     * @throws IllegalArgumentException if the end is before the start
246     */
247    protected void setInterval(long startInstant, long endInstant, Chronology chrono) {
248        checkInterval(startInstant, endInstant);
249        iStartMillis = startInstant;
250        iEndMillis = endInstant;
251        iChronology = DateTimeUtils.getChronology(chrono);
252    }
253
254}