    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2005, 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     * Hour.java
    029     * ---------
    030     * (C) Copyright 2001-2004, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * $Id: Hour.java,v 1.5.2.1 2005/10/25 21:35:24 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 11-Oct-2001 : Version 1 (DG);
    040     * 18-Dec-2001 : Changed order of parameters in constructor (DG);
    041     * 19-Dec-2001 : Added a new constructor as suggested by Paul English (DG);
    042     * 14-Feb-2002 : Fixed bug in Hour(Date) constructor (DG);
    043     * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to
    044     *               evaluate with reference to a particular time zone (DG);
    045     * 15-Mar-2002 : Changed API (DG);
    046     * 16-Apr-2002 : Fixed small time zone bug in constructor (DG);
    047     * 10-Sep-2002 : Added getSerialIndex() method (DG);
    048     * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    049     * 10-Jan-2003 : Changed base class and method names (DG);
    050     * 13-Mar-2003 : Moved to com.jrefinery.data.time package, and implemented
    051     *               Serializable (DG);
    052     * 21-Oct-2003 : Added hashCode() method, and new constructor for
    053     *               convenience (DG);
    054     * 30-Sep-2004 : Replaced getTime().getTime() with getTimeInMillis() (DG);
    055     * 04-Nov-2004 : Reverted change of 30-Sep-2004, because it won't work for
    056     *               JDK 1.3 (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.TimeZone;
    066
    067    /**
    068     * Represents an hour in a specific day.  This class is immutable, which is a
    069     * requirement for all {@link RegularTimePeriod} subclasses.
    070     */
    071    public class Hour extends RegularTimePeriod implements Serializable {
    072
    073        /** For serialization. */
    074        private static final long serialVersionUID = -835471579831937652L;
    075
    076        /** Useful constant for the first hour in the day. */
    077        public static final int FIRST_HOUR_IN_DAY = 0;
    078
    079        /** Useful constant for the last hour in the day. */
    080        public static final int LAST_HOUR_IN_DAY = 23;
    081
    082        /** The day. */
    083        private Day day;
    084
    085        /** The hour. */
    086        private int hour;
    087
    088        /**
    089         * Constructs a new Hour, based on the system date/time.
    090         */
    091        public Hour() {
    092            this(new Date());
    093        }
    094
    095        /**
    096         * Constructs a new Hour.
    097         *
    098         * @param hour  the hour (in the range 0 to 23).
    099         * @param day  the day (<code>null</code> not permitted).
    100         */
    101        public Hour(int hour, Day day) {
    102            if (day == null) {
    103                throw new IllegalArgumentException("Null 'day' argument.");
    104            }
    105            this.hour = hour;
    106            this.day = day;
    107        }
    108
    109        /**
    110         * Creates a new hour.
    111         *
    112         * @param hour  the hour (0-23).
    113         * @param day  the day (1-31).
    114         * @param month  the month (1-12).
    115         * @param year  the year (1900-9999).
    116         */
    117        public Hour(int hour, int day, int month, int year) {
    118            this(hour, new Day(day, month, year));
    119        }
    120
    121        /**
    122         * Constructs a new Hour, based on the supplied date/time.
    123         *
    124         * @param time  the date-time (<code>null</code> not permitted).
    125         */
    126        public Hour(Date time) {
    127            // defer argument checking...
    128            this(time, RegularTimePeriod.DEFAULT_TIME_ZONE);
    129        }
    130
    131        /**
    132         * Constructs a new Hour, based on the supplied date/time evaluated in the
    133         * specified time zone.
    134         *
    135         * @param time  the date-time (<code>null</code> not permitted).
    136         * @param zone  the time zone (<code>null</code> not permitted).
    137         */
    138        public Hour(Date time, TimeZone zone) {
    139            if (time == null) {
    140                throw new IllegalArgumentException("Null 'time' argument.");
    141            }
    142            if (zone == null) {
    143                throw new IllegalArgumentException("Null 'zone' argument.");
    144            }
    145            Calendar calendar = Calendar.getInstance(zone);
    146            calendar.setTime(time);
    147            this.hour = calendar.get(Calendar.HOUR_OF_DAY);
    148            this.day = new Day(time, zone);
    149        }
    150
    151        /**
    152         * Returns the hour.
    153         *
    154         * @return The hour (0 <= hour <= 23).
    155         */
    156        public int getHour() {
    157            return this.hour;
    158        }
    159
    160        /**
    161         * Returns the day in which this hour falls.
    162         *
    163         * @return The day.
    164         */
    165        public Day getDay() {
    166            return this.day;
    167        }
    168
    169        /**
    170         * Returns the year in which this hour falls.
    171         *
    172         * @return The year.
    173         */
    174        public int getYear() {
    175            return this.day.getYear();
    176        }
    177
    178        /**
    179         * Returns the month in which this hour falls.
    180         *
    181         * @return The month.
    182         */
    183        public int getMonth() {
    184            return this.day.getMonth();
    185        }
    186
    187        /**
    188         * Returns the day-of-the-month in which this hour falls.
    189         *
    190         * @return The day-of-the-month.
    191         */
    192        public int getDayOfMonth() {
    193            return this.day.getDayOfMonth();
    194        }
    195
    196        /**
    197         * Returns the hour preceding this one.
    198         *
    199         * @return The hour preceding this one.
    200         */
    201        public RegularTimePeriod previous() {
    202
    203            Hour result;
    204            if (this.hour != FIRST_HOUR_IN_DAY) {
    205                result = new Hour(this.hour - 1, this.day);
    206            }
    207            else { // we are at the first hour in the day...
    208                Day prevDay = (Day) this.day.previous();
    209                if (prevDay != null) {
    210                    result = new Hour(LAST_HOUR_IN_DAY, prevDay);
    211                }
    212                else {
    213                    result = null;
    214                }
    215            }
    216            return result;
    217
    218        }
    219
    220        /**
    221         * Returns the hour following this one.
    222         *
    223         * @return The hour following this one.
    224         */
    225        public RegularTimePeriod next() {
    226
    227            Hour result;
    228            if (this.hour != LAST_HOUR_IN_DAY) {
    229                result = new Hour(this.hour + 1, this.day);
    230            }
    231            else { // we are at the last hour in the day...
    232                Day nextDay = (Day) this.day.next();
    233                if (nextDay != null) {
    234                    result = new Hour(FIRST_HOUR_IN_DAY, nextDay);
    235                }
    236                else {
    237                    result = null;
    238                }
    239            }
    240            return result;
    241
    242        }
    243
    244        /**
    245         * Returns a serial index number for the hour.
    246         *
    247         * @return The serial index number.
    248         */
    249        public long getSerialIndex() {
    250            return this.day.getSerialIndex() * 24L + this.hour;
    251        }
    252
    253        /**
    254         * Returns the first millisecond of the hour.
    255         *
    256         * @param calendar  the calendar/timezone.
    257         *
    258         * @return The first millisecond.
    259         */
    260        public long getFirstMillisecond(Calendar calendar) {
    261
    262            int year = this.day.getYear();
    263            int month = this.day.getMonth() - 1;
    264            int dom = this.day.getDayOfMonth();
    265
    266            calendar.set(year, month, dom, this.hour, 0, 0);
    267            calendar.set(Calendar.MILLISECOND, 0);
    268
    269            //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
    270            return calendar.getTime().getTime();
    271
    272        }
    273
    274        /**
    275         * Returns the last millisecond of the hour.
    276         *
    277         * @param calendar  the calendar/timezone.
    278         *
    279         * @return The last millisecond.
    280         */
    281        public long getLastMillisecond(Calendar calendar) {
    282
    283            int year = this.day.getYear();
    284            int month = this.day.getMonth() - 1;
    285            int dom = this.day.getDayOfMonth();
    286
    287            calendar.set(year, month, dom, this.hour, 59, 59);
    288            calendar.set(Calendar.MILLISECOND, 999);
    289
    290            //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
    291            return calendar.getTime().getTime();
    292
    293        }
    294
    295        /**
    296         * Tests the equality of this object against an arbitrary Object.
    297         * <P>
    298         * This method will return true ONLY if the object is an Hour object
    299         * representing the same hour as this instance.
    300         *
    301         * @param obj  the object to compare (<code>null</code> permitted).
    302         *
    303         * @return <code>true</code> if the hour and day value of the object
    304         *      is the same as this.
    305         */
    306        public boolean equals(Object obj) {
    307            if (obj == this) {
    308                return true;
    309            }
    310            if (!(obj instanceof Hour)) {
    311                return false;
    312            }
    313            Hour that = (Hour) obj;
    314            if (this.hour != that.hour) {
    315                return false;
    316            }
    317            if (!this.day.equals(that.day)) {
    318                return false;
    319            }
    320            return true;
    321        }
    322
    323        /**
    324         * Returns a hash code for this object instance.  The approach described by
    325         * Joshua Bloch in "Effective Java" has been used here:
    326         * <p>
    327         * <code>http://developer.java.sun.com/developer/Books/effectivejava
    328         * /Chapter3.pdf</code>
    329         *
    330         * @return A hash code.
    331         */
    332        public int hashCode() {
    333            int result = 17;
    334            result = 37 * result + this.hour;
    335            result = 37 * result + this.day.hashCode();
    336            return result;
    337        }
    338
    339        /**
    340         * Returns an integer indicating the order of this Hour object relative to
    341         * the specified object:
    342         *
    343         * negative == before, zero == same, positive == after.
    344         *
    345         * @param o1  the object to compare.
    346         *
    347         * @return negative == before, zero == same, positive == after.
    348         */
    349        public int compareTo(Object o1) {
    350
    351            int result;
    352
    353            // CASE 1 : Comparing to another Hour object
    354            // -----------------------------------------
    355            if (o1 instanceof Hour) {
    356                Hour h = (Hour) o1;
    357                result = getDay().compareTo(h.getDay());
    358                if (result == 0) {
    359                    result = this.hour - h.getHour();
    360                }
    361            }
    362
    363            // CASE 2 : Comparing to another TimePeriod object
    364            // -----------------------------------------------
    365            else if (o1 instanceof RegularTimePeriod) {
    366                // more difficult case - evaluate later...
    367                result = 0;
    368            }
    369
    370            // CASE 3 : Comparing to a non-TimePeriod object
    371            // ---------------------------------------------
    372            else {
    373                // consider time periods to be ordered after general objects
    374                result = 1;
    375            }
    376
    377            return result;
    378
    379        }
    380
    381        /**
    382         * Creates an Hour instance by parsing a string.  The string is assumed to
    383         * be in the format "YYYY-MM-DD HH", perhaps with leading or trailing
    384         * whitespace.
    385         *
    386         * @param s  the hour string to parse.
    387         *
    388         * @return <code>null</code> if the string is not parseable, the hour
    389         *         otherwise.
    390         */
    391        public static Hour parseHour(String s) {
    392
    393            Hour result = null;
    394            s = s.trim();
    395
    396            String daystr = s.substring(0, Math.min(10, s.length()));
    397            Day day = Day.parseDay(daystr);
    398            if (day != null) {
    399                String hourstr = s.substring(
    400                    Math.min(daystr.length() + 1, s.length()), s.length()
    401                );
    402                hourstr = hourstr.trim();
    403                int hour = Integer.parseInt(hourstr);
    404                // if the hour is 0 - 23 then create an hour
    405                if ((hour >= FIRST_HOUR_IN_DAY) && (hour <= LAST_HOUR_IN_DAY)) {
    406                    result = new Hour(hour, day);
    407                }
    408            }
    409
    410            return result;
    411
    412        }
    413
    414    }