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     */
016    package org.joda.time.base;
017    
018    import java.io.Serializable;
019    
020    import org.joda.time.Chronology;
021    import org.joda.time.DateTimeUtils;
022    import org.joda.time.MutableInterval;
023    import org.joda.time.ReadWritableInterval;
024    import org.joda.time.ReadableDuration;
025    import org.joda.time.ReadableInstant;
026    import org.joda.time.ReadableInterval;
027    import org.joda.time.ReadablePeriod;
028    import org.joda.time.chrono.ISOChronology;
029    import org.joda.time.convert.ConverterManager;
030    import org.joda.time.convert.IntervalConverter;
031    import 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     */
048    public 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    }