    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 * Millisecond.java
    029 * ----------------
    030 * (C) Copyright 2001-2009, 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 * 19-Dec-2001 : Added new constructors as suggested by Paul English (DG);
    039 * 26-Feb-2002 : Added new getStart() and getEnd() methods (DG);
    040 * 29-Mar-2002 : Fixed bug in getStart(), getEnd() and compareTo() methods (DG);
    041 * 10-Sep-2002 : Added getSerialIndex() method (DG);
    042 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    043 * 10-Jan-2003 : Changed base class and method names (DG);
    044 * 13-Mar-2003 : Moved to com.jrefinery.data.time package and implemented
    045 *               Serializable (DG);
    046 * 21-Oct-2003 : Added hashCode() method (DG);
    047 * ------------- JFREECHART 1.0.x ---------------------------------------------
    048 * 05-Oct-2006 : Updated API docs (DG);
    049 * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG);
    050 * 04-Apr-2007 : In Millisecond(Date, TimeZone), peg milliseconds to the
    051 *               specified zone (DG);
    052 * 06-Jun-2008 : Added handling for general RegularTimePeriod in compareTo()
    053 *               method:
    054 *               see http://www.jfree.org/phpBB2/viewtopic.php?t=24805 (DG);
    055 * 16-Sep-2008 : Deprecated DEFAULT_TIME_ZONE (DG);
    056 * 02-Mar-2009 : Added new constructor with Locale (DG);
    057 *
    058 */
    059
    060package org.jfree.data.time;
    061
    062import java.io.Serializable;
    063import java.util.Calendar;
    064import java.util.Date;
    065import java.util.Locale;
    066import java.util.TimeZone;
    067
    068/**
    069 * Represents a millisecond.  This class is immutable, which is a requirement
    070 * for all {@link RegularTimePeriod} subclasses.
    071 */
    072public class Millisecond extends RegularTimePeriod implements Serializable {
    073
    074    /** For serialization. */
    075    static final long serialVersionUID = -5316836467277638485L;
    076
    077    /** A constant for the first millisecond in a second. */
    078    public static final int FIRST_MILLISECOND_IN_SECOND = 0;
    079
    080    /** A constant for the last millisecond in a second. */
    081    public static final int LAST_MILLISECOND_IN_SECOND = 999;
    082
    083    /** The day. */
    084    private Day day;
    085
    086    /** The hour in the day. */
    087    private byte hour;
    088
    089    /** The minute. */
    090    private byte minute;
    091
    092    /** The second. */
    093    private byte second;
    094
    095    /** The millisecond. */
    096    private int millisecond;
    097
    098    /**
    099     * The pegged millisecond.
    100     */
    101    private long firstMillisecond;
    102
    103    /**
    104     * Constructs a millisecond based on the current system time.
    105     */
    106    public Millisecond() {
    107        this(new Date());
    108    }
    109
    110    /**
    111     * Constructs a millisecond.
    112     *
    113     * @param millisecond  the millisecond (0-999).
    114     * @param second  the second.
    115     */
    116    public Millisecond(int millisecond, Second second) {
    117        this.millisecond = millisecond;
    118        this.second = (byte) second.getSecond();
    119        this.minute = (byte) second.getMinute().getMinute();
    120        this.hour = (byte) second.getMinute().getHourValue();
    121        this.day = second.getMinute().getDay();
    122        peg(Calendar.getInstance());
    123    }
    124
    125    /**
    126     * Creates a new millisecond.
    127     *
    128     * @param millisecond  the millisecond (0-999).
    129     * @param second  the second (0-59).
    130     * @param minute  the minute (0-59).
    131     * @param hour  the hour (0-23).
    132     * @param day  the day (1-31).
    133     * @param month  the month (1-12).
    134     * @param year  the year (1900-9999).
    135     */
    136    public Millisecond(int millisecond, int second, int minute, int hour,
    137                       int day, int month, int year) {
    138
    139        this(millisecond, new Second(second, minute, hour, day, month, year));
    140
    141    }
    142
    143    /**
    144     * Constructs a new millisecond using the default time zone.
    145     *
    146     * @param time  the time.
    147     *
    148     * @see #Millisecond(Date, TimeZone)
    149     */
    150    public Millisecond(Date time) {
    151        this(time, TimeZone.getDefault(), Locale.getDefault());
    152    }
    153
    154    /**
    155     * Creates a millisecond.
    156     *
    157     * @param time  the instant in time.
    158     * @param zone  the time zone.
    159     *
    160     * @deprecated As of 1.0.13, use the constructor that specifies the locale
    161     *     also.
    162     */
    163    public Millisecond(Date time, TimeZone zone) {
    164        this(time, zone, Locale.getDefault());
    165    }
    166
    167    /**
    168     * Creates a millisecond.
    169     *
    170     * @param time  the date-time (<code>null</code> not permitted).
    171     * @param zone  the time zone (<code>null</code> not permitted).
    172     * @param locale  the locale (<code>null</code> not permitted).
    173     *
    174     * @since 1.0.13
    175     */
    176    public Millisecond(Date time, TimeZone zone, Locale locale) {
    177        Calendar calendar = Calendar.getInstance(zone, locale);
    178        calendar.setTime(time);
    179        this.millisecond = calendar.get(Calendar.MILLISECOND);
    180        this.second = (byte) calendar.get(Calendar.SECOND);
    181        this.minute = (byte) calendar.get(Calendar.MINUTE);
    182        this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY);
    183        this.day = new Day(time, zone, locale);
    184        peg(calendar);
    185    }
    186
    187    /**
    188     * Returns the second.
    189     *
    190     * @return The second.
    191     */
    192    public Second getSecond() {
    193        return new Second(this.second, this.minute, this.hour,
    194                this.day.getDayOfMonth(), this.day.getMonth(),
    195                this.day.getYear());
    196    }
    197
    198    /**
    199     * Returns the millisecond.
    200     *
    201     * @return The millisecond.
    202     */
    203    public long getMillisecond() {
    204        return this.millisecond;
    205    }
    206
    207    /**
    208     * Returns the first millisecond of the second.  This will be determined
    209     * relative to the time zone specified in the constructor, or in the
    210     * calendar instance passed in the most recent call to the
    211     * {@link #peg(Calendar)} method.
    212     *
    213     * @return The first millisecond of the second.
    214     *
    215     * @see #getLastMillisecond()
    216     */
    217    public long getFirstMillisecond() {
    218        return this.firstMillisecond;
    219    }
    220
    221    /**
    222     * Returns the last millisecond of the second.  This will be
    223     * determined relative to the time zone specified in the constructor, or
    224     * in the calendar instance passed in the most recent call to the
    225     * {@link #peg(Calendar)} method.
    226     *
    227     * @return The last millisecond of the second.
    228     *
    229     * @see #getFirstMillisecond()
    230     */
    231    public long getLastMillisecond() {
    232        return this.firstMillisecond;
    233    }
    234
    235    /**
    236     * Recalculates the start date/time and end date/time for this time period
    237     * relative to the supplied calendar (which incorporates a time zone).
    238     *
    239     * @param calendar  the calendar (<code>null</code> not permitted).
    240     *
    241     * @since 1.0.3
    242     */
    243    public void peg(Calendar calendar) {
    244        this.firstMillisecond = getFirstMillisecond(calendar);
    245    }
    246
    247    /**
    248     * Returns the millisecond preceding this one.
    249     *
    250     * @return The millisecond preceding this one.
    251     */
    252    public RegularTimePeriod previous() {
    253        RegularTimePeriod result = null;
    254        if (this.millisecond != FIRST_MILLISECOND_IN_SECOND) {
    255            result = new Millisecond(this.millisecond - 1, getSecond());
    256        }
    257        else {
    258            Second previous = (Second) getSecond().previous();
    259            if (previous != null) {
    260                result = new Millisecond(LAST_MILLISECOND_IN_SECOND, previous);
    261            }
    262        }
    263        return result;
    264    }
    265
    266    /**
    267     * Returns the millisecond following this one.
    268     *
    269     * @return The millisecond following this one.
    270     */
    271    public RegularTimePeriod next() {
    272        RegularTimePeriod result = null;
    273        if (this.millisecond != LAST_MILLISECOND_IN_SECOND) {
    274            result = new Millisecond(this.millisecond + 1, getSecond());
    275        }
    276        else {
    277            Second next = (Second) getSecond().next();
    278            if (next != null) {
    279                result = new Millisecond(FIRST_MILLISECOND_IN_SECOND, next);
    280            }
    281        }
    282        return result;
    283    }
    284
    285    /**
    286     * Returns a serial index number for the millisecond.
    287     *
    288     * @return The serial index number.
    289     */
    290    public long getSerialIndex() {
    291        long hourIndex = this.day.getSerialIndex() * 24L + this.hour;
    292        long minuteIndex = hourIndex * 60L + this.minute;
    293        long secondIndex = minuteIndex * 60L + this.second;
    294        return secondIndex * 1000L + this.millisecond;
    295    }
    296
    297    /**
    298     * Tests the equality of this object against an arbitrary Object.
    299     * <P>
    300     * This method will return true ONLY if the object is a Millisecond object
    301     * representing the same millisecond as this instance.
    302     *
    303     * @param obj  the object to compare
    304     *
    305     * @return <code>true</code> if milliseconds and seconds of this and object
    306     *      are the same.
    307     */
    308    public boolean equals(Object obj) {
    309        if (obj == this) {
    310            return true;
    311        }
    312        if (!(obj instanceof Millisecond)) {
    313            return false;
    314        }
    315        Millisecond that = (Millisecond) obj;
    316        if (this.millisecond != that.millisecond) {
    317            return false;
    318        }
    319        if (this.second != that.second) {
    320            return false;
    321        }
    322        if (this.minute != that.minute) {
    323            return false;
    324        }
    325        if (this.hour != that.hour) {
    326            return false;
    327        }
    328        if (!this.day.equals(that.day)) {
    329            return false;
    330        }
    331        return true;
    332    }
    333
    334    /**
    335     * Returns a hash code for this object instance.  The approach described by
    336     * Joshua Bloch in "Effective Java" has been used here:
    337     * <p>
    338     * <code>http://developer.java.sun.com/developer/Books/effectivejava
    339     * /Chapter3.pdf</code>
    340     *
    341     * @return A hashcode.
    342     */
    343    public int hashCode() {
    344        int result = 17;
    345        result = 37 * result + this.millisecond;
    346        result = 37 * result + getSecond().hashCode();
    347        return result;
    348    }
    349
    350    /**
    351     * Returns an integer indicating the order of this Millisecond object
    352     * relative to the specified object:
    353     *
    354     * negative == before, zero == same, positive == after.
    355     *
    356     * @param obj  the object to compare
    357     *
    358     * @return negative == before, zero == same, positive == after.
    359     */
    360    public int compareTo(Object obj) {
    361        int result;
    362        long difference;
    363
    364        // CASE 1 : Comparing to another Second object
    365        // -------------------------------------------
    366        if (obj instanceof Millisecond) {
    367            Millisecond ms = (Millisecond) obj;
    368            difference = getFirstMillisecond() - ms.getFirstMillisecond();
    369            if (difference > 0) {
    370                result = 1;
    371            }
    372            else {
    373                if (difference < 0) {
    374                    result = -1;
    375                }
    376                else {
    377                    result = 0;
    378                }
    379            }
    380        }
    381
    382        // CASE 2 : Comparing to another TimePeriod object
    383        // -----------------------------------------------
    384        else if (obj instanceof RegularTimePeriod) {
    385            RegularTimePeriod rtp = (RegularTimePeriod) obj;
    386            final long thisVal = this.getFirstMillisecond();
    387            final long anotherVal = rtp.getFirstMillisecond();
    388            result = (thisVal < anotherVal ? -1
    389                    : (thisVal == anotherVal ? 0 : 1));
    390        }
    391
    392        // CASE 3 : Comparing to a non-TimePeriod object
    393        // ---------------------------------------------
    394        else {
    395            // consider time periods to be ordered after general objects
    396            result = 1;
    397        }
    398
    399        return result;
    400    }
    401
    402    /**
    403     * Returns the first millisecond of the time period.
    404     *
    405     * @param calendar  the calendar (<code>null</code> not permitted).
    406     *
    407     * @return The first millisecond of the time period.
    408     *
    409     * @throws NullPointerException if <code>calendar</code> is
    410     *     <code>null</code>.
    411     */
    412    public long getFirstMillisecond(Calendar calendar) {
    413        int year = this.day.getYear();
    414        int month = this.day.getMonth() - 1;
    415        int day = this.day.getDayOfMonth();
    416        calendar.clear();
    417        calendar.set(year, month, day, this.hour, this.minute, this.second);
    418        calendar.set(Calendar.MILLISECOND, this.millisecond);
    419        //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
    420        return calendar.getTime().getTime();
    421    }
    422
    423    /**
    424     * Returns the last millisecond of the time period.
    425     *
    426     * @param calendar  the calendar (<code>null</code> not permitted).
    427     *
    428     * @return The last millisecond of the time period.
    429     *
    430     * @throws NullPointerException if <code>calendar</code> is
    431     *     <code>null</code>.
    432     */
    433    public long getLastMillisecond(Calendar calendar) {
    434        return getFirstMillisecond(calendar);
    435    }
    436
    437}