001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2013, 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-2008, 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 *
052 */
053
054package org.jfree.data.time;
055
056import java.lang.reflect.Constructor;
057import java.util.Calendar;
058import java.util.Date;
059import java.util.TimeZone;
060
061import org.jfree.date.MonthConstants;
062
063/**
064 * An abstract class representing a unit of time.  Convenient methods are
065 * provided for calculating the next and previous time periods.  Conversion
066 * methods are defined that return the first and last milliseconds of the time
067 * period.  The results from these methods are timezone dependent.
068 * <P>
069 * This class is immutable, and all subclasses should be immutable also.
070 */
071public abstract class RegularTimePeriod implements TimePeriod, Comparable,
072                                                   MonthConstants {
073
074    /**
075     * Creates a time period that includes the specified millisecond, assuming
076     * the given time zone.
077     *
078     * @param c  the time period class.
079     * @param millisecond  the time.
080     * @param zone  the time zone.
081     *
082     * @return The time period.
083     */
084    public static RegularTimePeriod createInstance(Class c, Date millisecond,
085                                                   TimeZone zone) {
086        RegularTimePeriod result = null;
087        try {
088            Constructor constructor = c.getDeclaredConstructor(
089                    new Class[] {Date.class, TimeZone.class});
090            result = (RegularTimePeriod) constructor.newInstance(
091                    new Object[] {millisecond, zone});
092        }
093        catch (Exception e) {
094            // do nothing, so null is returned
095        }
096        return result;
097    }
098
099    /**
100     * Returns a subclass of {@link RegularTimePeriod} that is smaller than
101     * the specified class.
102     *
103     * @param c  a subclass of {@link RegularTimePeriod}.
104     *
105     * @return A class.
106     */
107    public static Class downsize(Class c) {
108        if (c.equals(Year.class)) {
109            return Quarter.class;
110        }
111        else if (c.equals(Quarter.class)) {
112            return Month.class;
113        }
114        else if (c.equals(Month.class)) {
115            return Day.class;
116        }
117        else if (c.equals(Day.class)) {
118            return Hour.class;
119        }
120        else if (c.equals(Hour.class)) {
121            return Minute.class;
122        }
123        else if (c.equals(Minute.class)) {
124            return Second.class;
125        }
126        else if (c.equals(Second.class)) {
127            return Millisecond.class;
128        }
129        else {
130            return Millisecond.class;
131        }
132    }
133
134    /**
135     * Returns the time period preceding this one, or <code>null</code> if some
136     * lower limit has been reached.
137     *
138     * @return The previous time period (possibly <code>null</code>).
139     */
140    public abstract RegularTimePeriod previous();
141
142    /**
143     * Returns the time period following this one, or <code>null</code> if some
144     * limit has been reached.
145     *
146     * @return The next time period (possibly <code>null</code>).
147     */
148    public abstract RegularTimePeriod next();
149
150    /**
151     * Returns a serial index number for the time unit.
152     *
153     * @return The serial index number.
154     */
155    public abstract long getSerialIndex();
156
157    //////////////////////////////////////////////////////////////////////////
158
159    /**
160     * The default time zone.
161     *
162     * @deprecated As of 1.0.11, we discourage the use of this field - use
163     *     {@link TimeZone#getDefault()} instead.
164     */
165    public static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getDefault();
166
167    /**
168     * A working calendar (recycle to avoid unnecessary object creation).
169     *
170     * @deprecated This was a bad idea, don't use it!
171     */
172    public static final Calendar WORKING_CALENDAR = Calendar.getInstance(
173            DEFAULT_TIME_ZONE);
174
175    /**
176     * Recalculates the start date/time and end date/time for this time period
177     * relative to the supplied calendar (which incorporates a time zone).
178     *
179     * @param calendar  the calendar (<code>null</code> not permitted).
180     *
181     * @since 1.0.3
182     */
183    public abstract void peg(Calendar calendar);
184
185    /**
186     * Returns the date/time that marks the start of the time period.  This
187     * method returns a new <code>Date</code> instance every time it is called.
188     *
189     * @return The start date/time.
190     *
191     * @see #getFirstMillisecond()
192     */
193    @Override
194    public Date getStart() {
195        return new Date(getFirstMillisecond());
196    }
197
198    /**
199     * Returns the date/time that marks the end of the time period.  This
200     * method returns a new <code>Date</code> instance every time it is called.
201     *
202     * @return The end date/time.
203     *
204     * @see #getLastMillisecond()
205     */
206    @Override
207    public Date getEnd() {
208        return new Date(getLastMillisecond());
209    }
210
211    /**
212     * Returns the first millisecond of the time period.  This will be
213     * determined relative to the time zone specified in the constructor, or
214     * in the calendar instance passed in the most recent call to the
215     * {@link #peg(Calendar)} method.
216     *
217     * @return The first millisecond of the time period.
218     *
219     * @see #getLastMillisecond()
220     */
221    public abstract long getFirstMillisecond();
222
223    /**
224     * Returns the first millisecond of the time period, evaluated within a
225     * specific time zone.
226     *
227     * @param zone  the time zone (<code>null</code> not permitted).
228     *
229     * @return The first millisecond of the time period.
230     *
231     * @deprecated As of 1.0.3, you should avoid using this method (it creates
232     *     a new Calendar instance every time it is called).  You are advised
233     *     to call {@link #getFirstMillisecond(Calendar)} instead.
234     *
235     * @see #getLastMillisecond(TimeZone)
236     */
237    public long getFirstMillisecond(TimeZone zone) {
238        Calendar calendar = Calendar.getInstance(zone);
239        return getFirstMillisecond(calendar);
240    }
241
242    /**
243     * Returns the first millisecond of the time period, evaluated using the
244     * supplied calendar (which incorporates a timezone).
245     *
246     * @param calendar  the calendar (<code>null</code> not permitted).
247     *
248     * @return The first millisecond of the time period.
249     *
250     * @throws NullPointerException if <code>calendar,/code> is
251     *     </code>null</code>.
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 a string representation of the time period.
346     *
347     * @return The string.
348     */
349    @Override
350    public String toString() {
351        return String.valueOf(getStart());
352    }
353
354}