    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2009, 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     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    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
    060    package org.jfree.data.time;
    061
    062    import java.io.Serializable;
    063    import java.util.Calendar;
    064    import java.util.Date;
    065    import java.util.Locale;
    066    import java.util.TimeZone;
    067
    068    /**
    069     * Represents a millisecond.  This class is immutable, which is a requirement
    070     * for all {@link RegularTimePeriod} subclasses.
    071     */
    072    public 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    }