001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2014, by Object Refinery Limited and Contributors.
006 *
007 * Project Info:  http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
022 * USA.
023 *
024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * ----------------------
028 * RegularTimePeriod.java
029 * ----------------------
030 * (C) Copyright 2001-2014, by Object Refinery Limited.
031 *
032 * Original Author:  David Gilbert (for Object Refinery Limited);
033 * Contributor(s):   -;
034 *
035 * Changes
036 * -------
037 * 11-Oct-2001 : Version 1 (DG);
038 * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to
039 *               evaluate with reference to a particular time zone (DG);
040 * 29-May-2002 : Implemented MonthConstants interface, so that these constants
041 *               are conveniently available (DG);
042 * 10-Sep-2002 : Added getSerialIndex() method (DG);
043 * 10-Jan-2003 : Renamed TimePeriod --> RegularTimePeriod (DG);
044 * 13-Mar-2003 : Moved to com.jrefinery.data.time package (DG);
045 * 29-Apr-2004 : Changed getMiddleMillisecond() methods to fix bug 943985 (DG);
046 * 25-Nov-2004 : Added utility methods (DG);
047 * ------------- JFREECHART 1.0.x ---------------------------------------------
048 * 06-Oct-2006 : Deprecated the WORKING_CALENDAR field and several methods,
049 *               added new peg() method (DG);
050 * 16-Sep-2008 : Deprecated DEFAULT_TIME_ZONE (DG);
051 * 23-Feb-2014 : Added getMillisecond() method (DG);
052 * 
053 */
054
055package org.jfree.data.time;
056
057import java.lang.reflect.Constructor;
058import java.util.Calendar;
059import java.util.Date;
060import java.util.TimeZone;
061
062import org.jfree.date.MonthConstants;
063
064/**
065 * An abstract class representing a unit of time.  Convenient methods are
066 * provided for calculating the next and previous time periods.  Conversion
067 * methods are defined that return the first and last milliseconds of the time
068 * period.  The results from these methods are timezone dependent.
069 * <P>
070 * This class is immutable, and all subclasses should be immutable also.
071 */
072public abstract class RegularTimePeriod implements TimePeriod, Comparable,
073        MonthConstants {
074
075    /**
076     * Creates a time period that includes the specified millisecond, assuming
077     * the given time zone.
078     *
079     * @param c  the time period class.
080     * @param millisecond  the time.
081     * @param zone  the time zone.
082     *
083     * @return The time period.
084     */
085    public static RegularTimePeriod createInstance(Class c, Date millisecond,
086            TimeZone zone) {
087        RegularTimePeriod result = null;
088        try {
089            Constructor constructor = c.getDeclaredConstructor(
090                    new Class[] {Date.class, TimeZone.class});
091            result = (RegularTimePeriod) constructor.newInstance(
092                    new Object[] {millisecond, zone});
093        }
094        catch (Exception e) {
095            // do nothing, so null is returned
096        }
097        return result;
098    }
099
100    /**
101     * Returns a subclass of {@link RegularTimePeriod} that is smaller than
102     * the specified class.
103     *
104     * @param c  a subclass of {@link RegularTimePeriod}.
105     *
106     * @return A class.
107     */
108    public static Class downsize(Class c) {
109        if (c.equals(Year.class)) {
110            return Quarter.class;
111        }
112        else if (c.equals(Quarter.class)) {
113            return Month.class;
114        }
115        else if (c.equals(Month.class)) {
116            return Day.class;
117        }
118        else if (c.equals(Day.class)) {
119            return Hour.class;
120        }
121        else if (c.equals(Hour.class)) {
122            return Minute.class;
123        }
124        else if (c.equals(Minute.class)) {
125            return Second.class;
126        }
127        else if (c.equals(Second.class)) {
128            return Millisecond.class;
129        }
130        else {
131            return Millisecond.class;
132        }
133    }
134
135    /**
136     * Returns the time period preceding this one, or <code>null</code> if some
137     * lower limit has been reached.
138     *
139     * @return The previous time period (possibly <code>null</code>).
140     */
141    public abstract RegularTimePeriod previous();
142
143    /**
144     * Returns the time period following this one, or <code>null</code> if some
145     * limit has been reached.
146     *
147     * @return The next time period (possibly <code>null</code>).
148     */
149    public abstract RegularTimePeriod next();
150
151    /**
152     * Returns a serial index number for the time unit.
153     *
154     * @return The serial index number.
155     */
156    public abstract long getSerialIndex();
157
158    //////////////////////////////////////////////////////////////////////////
159
160    /**
161     * The default time zone.
162     *
163     * @deprecated As of 1.0.11, we discourage the use of this field - use
164     *     {@link TimeZone#getDefault()} instead.
165     */
166    public static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getDefault();
167
168    /**
169     * A working calendar (recycle to avoid unnecessary object creation).
170     *
171     * @deprecated This was a bad idea, don't use it!
172     */
173    public static final Calendar WORKING_CALENDAR = Calendar.getInstance(
174            DEFAULT_TIME_ZONE);
175
176    /**
177     * Recalculates the start date/time and end date/time for this time period
178     * relative to the supplied calendar (which incorporates a time zone).
179     *
180     * @param calendar  the calendar (<code>null</code> not permitted).
181     *
182     * @since 1.0.3
183     */
184    public abstract void peg(Calendar calendar);
185
186    /**
187     * Returns the date/time that marks the start of the time period.  This
188     * method returns a new <code>Date</code> instance every time it is called.
189     *
190     * @return The start date/time.
191     *
192     * @see #getFirstMillisecond()
193     */
194    @Override
195    public Date getStart() {
196        return new Date(getFirstMillisecond());
197    }
198
199    /**
200     * Returns the date/time that marks the end of the time period.  This
201     * method returns a new <code>Date</code> instance every time it is called.
202     *
203     * @return The end date/time.
204     *
205     * @see #getLastMillisecond()
206     */
207    @Override
208    public Date getEnd() {
209        return new Date(getLastMillisecond());
210    }
211
212    /**
213     * Returns the first millisecond of the time period.  This will be
214     * determined relative to the time zone specified in the constructor, or
215     * in the calendar instance passed in the most recent call to the
216     * {@link #peg(Calendar)} method.
217     *
218     * @return The first millisecond of the time period.
219     *
220     * @see #getLastMillisecond()
221     */
222    public abstract long getFirstMillisecond();
223
224    /**
225     * Returns the first millisecond of the time period, evaluated within a
226     * specific time zone.
227     *
228     * @param zone  the time zone (<code>null</code> not permitted).
229     *
230     * @return The first millisecond of the time period.
231     *
232     * @deprecated As of 1.0.3, you should avoid using this method (it creates
233     *     a new Calendar instance every time it is called).  You are advised
234     *     to call {@link #getFirstMillisecond(Calendar)} instead.
235     *
236     * @see #getLastMillisecond(TimeZone)
237     */
238    public long getFirstMillisecond(TimeZone zone) {
239        Calendar calendar = Calendar.getInstance(zone);
240        return getFirstMillisecond(calendar);
241    }
242
243    /**
244     * Returns the first millisecond of the time period, evaluated using the
245     * supplied calendar (which incorporates a timezone).
246     *
247     * @param calendar  the calendar (<code>null</code> not permitted).
248     *
249     * @return The first millisecond of the time period.
250     *
251     * @throws NullPointerException if {@code calendar} is {@code null}.
252     *
253     * @see #getLastMillisecond(Calendar)
254     */
255    public abstract long getFirstMillisecond(Calendar calendar);
256
257    /**
258     * Returns the last millisecond of the time period.  This will be
259     * determined relative to the time zone specified in the constructor, or
260     * in the calendar instance passed in the most recent call to the
261     * {@link #peg(Calendar)} method.
262     *
263     * @return The last millisecond of the time period.
264     *
265     * @see #getFirstMillisecond()
266     */
267    public abstract long getLastMillisecond();
268
269    /**
270     * Returns the last millisecond of the time period, evaluated within a
271     * specific time zone.
272     *
273     * @param zone  the time zone (<code>null</code> not permitted).
274     *
275     * @return The last millisecond of the time period.
276     *
277     * @deprecated As of 1.0.3, you should avoid using this method (it creates
278     *     a new Calendar instance every time it is called).  You are advised
279     *     to call {@link #getLastMillisecond(Calendar)} instead.
280     *
281     * @see #getFirstMillisecond(TimeZone)
282     */
283    public long getLastMillisecond(TimeZone zone) {
284        Calendar calendar = Calendar.getInstance(zone);
285        return getLastMillisecond(calendar);
286    }
287
288    /**
289     * Returns the last millisecond of the time period, evaluated using the
290     * supplied calendar (which incorporates a timezone).
291     *
292     * @param calendar  the calendar (<code>null</code> not permitted).
293     *
294     * @return The last millisecond of the time period.
295     *
296     * @see #getFirstMillisecond(Calendar)
297     */
298    public abstract long getLastMillisecond(Calendar calendar);
299
300    /**
301     * Returns the millisecond closest to the middle of the time period.
302     *
303     * @return The middle millisecond.
304     */
305    public long getMiddleMillisecond() {
306        long m1 = getFirstMillisecond();
307        long m2 = getLastMillisecond();
308        return m1 + (m2 - m1) / 2;
309    }
310
311    /**
312     * Returns the millisecond closest to the middle of the time period,
313     * evaluated within a specific time zone.
314     *
315     * @param zone  the time zone (<code>null</code> not permitted).
316     *
317     * @return The middle millisecond.
318     *
319     * @deprecated As of 1.0.3, you should avoid using this method (it creates
320     *     a new Calendar instance every time it is called).  You are advised
321     *     to call {@link #getMiddleMillisecond(Calendar)} instead.
322     */
323    public long getMiddleMillisecond(TimeZone zone) {
324        Calendar calendar = Calendar.getInstance(zone);
325        long m1 = getFirstMillisecond(calendar);
326        long m2 = getLastMillisecond(calendar);
327        return m1 + (m2 - m1) / 2;
328    }
329
330    /**
331     * Returns the millisecond closest to the middle of the time period,
332     * evaluated using the supplied calendar (which incorporates a timezone).
333     *
334     * @param calendar  the calendar.
335     *
336     * @return The middle millisecond.
337     */
338    public long getMiddleMillisecond(Calendar calendar) {
339        long m1 = getFirstMillisecond(calendar);
340        long m2 = getLastMillisecond(calendar);
341        return m1 + (m2 - m1) / 2;
342    }
343
344    /**
345     * Returns the millisecond (relative to the epoch) corresponding to the 
346     * specified <code>anchor</code> using the supplied <code>calendar</code> 
347     * (which incorporates a time zone).
348     * 
349     * @param anchor  the anchor (<code>null</code> not permitted).
350     * @param calendar  the calendar (<code>null</code> not permitted).
351     * 
352     * @return Milliseconds since the epoch.
353     * 
354     * @since 1.0.18
355     */
356    public long getMillisecond(TimePeriodAnchor anchor, Calendar calendar) {
357        if (anchor.equals(TimePeriodAnchor.START)) {
358            return getFirstMillisecond(calendar);
359        } else if (anchor.equals(TimePeriodAnchor.MIDDLE)) {
360            return getMiddleMillisecond(calendar);
361        } else if (anchor.equals(TimePeriodAnchor.END)) {
362            return getLastMillisecond(calendar);
363        } else {
364            throw new IllegalStateException("Unrecognised anchor: " + anchor);
365        }
366    }
367    
368    /**
369     * Returns a string representation of the time period.
370     *
371     * @return The string.
372     */
373    @Override
374    public String toString() {
375        return String.valueOf(getStart());
376    }
377
378}