    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, 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    public Date getStart() {
    194        return new Date(getFirstMillisecond());
    195    }
    196
    197    /**
    198     * Returns the date/time that marks the end of the time period.  This
    199     * method returns a new <code>Date</code> instance every time it is called.
    200     *
    201     * @return The end date/time.
    202     *
    203     * @see #getLastMillisecond()
    204     */
    205    public Date getEnd() {
    206        return new Date(getLastMillisecond());
    207    }
    208
    209    /**
    210     * Returns the first millisecond of the time period.  This will be
    211     * determined relative to the time zone specified in the constructor, or
    212     * in the calendar instance passed in the most recent call to the
    213     * {@link #peg(Calendar)} method.
    214     *
    215     * @return The first millisecond of the time period.
    216     *
    217     * @see #getLastMillisecond()
    218     */
    219    public abstract long getFirstMillisecond();
    220
    221    /**
    222     * Returns the first millisecond of the time period, evaluated within a
    223     * specific time zone.
    224     *
    225     * @param zone  the time zone (<code>null</code> not permitted).
    226     *
    227     * @return The first millisecond of the time period.
    228     *
    229     * @deprecated As of 1.0.3, you should avoid using this method (it creates
    230     *     a new Calendar instance every time it is called).  You are advised
    231     *     to call {@link #getFirstMillisecond(Calendar)} instead.
    232     *
    233     * @see #getLastMillisecond(TimeZone)
    234     */
    235    public long getFirstMillisecond(TimeZone zone) {
    236        Calendar calendar = Calendar.getInstance(zone);
    237        return getFirstMillisecond(calendar);
    238    }
    239
    240    /**
    241     * Returns the first millisecond of the time period, evaluated using the
    242     * supplied calendar (which incorporates a timezone).
    243     *
    244     * @param calendar  the calendar (<code>null</code> not permitted).
    245     *
    246     * @return The first millisecond of the time period.
    247     *
    248     * @throws NullPointerException if <code>calendar,/code> is
    249     *     </code>null</code>.
    250     *
    251     * @see #getLastMillisecond(Calendar)
    252     */
    253    public abstract long getFirstMillisecond(Calendar calendar);
    254
    255    /**
    256     * Returns the last millisecond of the time period.  This will be
    257     * determined relative to the time zone specified in the constructor, or
    258     * in the calendar instance passed in the most recent call to the
    259     * {@link #peg(Calendar)} method.
    260     *
    261     * @return The last millisecond of the time period.
    262     *
    263     * @see #getFirstMillisecond()
    264     */
    265    public abstract long getLastMillisecond();
    266
    267    /**
    268     * Returns the last millisecond of the time period, evaluated within a
    269     * specific time zone.
    270     *
    271     * @param zone  the time zone (<code>null</code> not permitted).
    272     *
    273     * @return The last millisecond of the time period.
    274     *
    275     * @deprecated As of 1.0.3, you should avoid using this method (it creates
    276     *     a new Calendar instance every time it is called).  You are advised
    277     *     to call {@link #getLastMillisecond(Calendar)} instead.
    278     *
    279     * @see #getFirstMillisecond(TimeZone)
    280     */
    281    public long getLastMillisecond(TimeZone zone) {
    282        Calendar calendar = Calendar.getInstance(zone);
    283        return getLastMillisecond(calendar);
    284    }
    285
    286    /**
    287     * Returns the last millisecond of the time period, evaluated using the
    288     * supplied calendar (which incorporates a timezone).
    289     *
    290     * @param calendar  the calendar (<code>null</code> not permitted).
    291     *
    292     * @return The last millisecond of the time period.
    293     *
    294     * @see #getFirstMillisecond(Calendar)
    295     */
    296    public abstract long getLastMillisecond(Calendar calendar);
    297
    298    /**
    299     * Returns the millisecond closest to the middle of the time period.
    300     *
    301     * @return The middle millisecond.
    302     */
    303    public long getMiddleMillisecond() {
    304        long m1 = getFirstMillisecond();
    305        long m2 = getLastMillisecond();
    306        return m1 + (m2 - m1) / 2;
    307    }
    308
    309    /**
    310     * Returns the millisecond closest to the middle of the time period,
    311     * evaluated within a specific time zone.
    312     *
    313     * @param zone  the time zone (<code>null</code> not permitted).
    314     *
    315     * @return The middle millisecond.
    316     *
    317     * @deprecated As of 1.0.3, you should avoid using this method (it creates
    318     *     a new Calendar instance every time it is called).  You are advised
    319     *     to call {@link #getMiddleMillisecond(Calendar)} instead.
    320     */
    321    public long getMiddleMillisecond(TimeZone zone) {
    322        Calendar calendar = Calendar.getInstance(zone);
    323        long m1 = getFirstMillisecond(calendar);
    324        long m2 = getLastMillisecond(calendar);
    325        return m1 + (m2 - m1) / 2;
    326    }
    327
    328    /**
    329     * Returns the millisecond closest to the middle of the time period,
    330     * evaluated using the supplied calendar (which incorporates a timezone).
    331     *
    332     * @param calendar  the calendar.
    333     *
    334     * @return The middle millisecond.
    335     */
    336    public long getMiddleMillisecond(Calendar calendar) {
    337        long m1 = getFirstMillisecond(calendar);
    338        long m2 = getLastMillisecond(calendar);
    339        return m1 + (m2 - m1) / 2;
    340    }
    341
    342    /**
    343     * Returns a string representation of the time period.
    344     *
    345     * @return The string.
    346     */
    347    public String toString() {
    348        return String.valueOf(getStart());
    349    }
    350
    351}