    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2006, 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     * Minute.java
    029     * -----------
    030     * (C) Copyright 2001-2006, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * $Id: Minute.java,v 1.5.2.4 2006/12/11 10:03:22 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 Minute(Date) constructor, and changed the range
    043     *               to start from zero instead of one (DG);
    044     * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to
    045     *               evaluate with reference to a particular time zone (DG);
    046     * 13-Mar-2002 : Added parseMinute() method (DG);
    047     * 19-Mar-2002 : Changed API, the minute is now defined in relation to an
    048     *               Hour (DG);
    049     * 10-Sep-2002 : Added getSerialIndex() method (DG);
    050     * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    051     * 10-Jan-2003 : Changed base class and method names (DG);
    052     * 13-Mar-2003 : Moved to com.jrefinery.data.time package and implemented
    053     *               Serializable (DG);
    054     * 21-Oct-2003 : Added hashCode() method, and new constructor for
    055     *               convenience (DG);
    056     * 30-Sep-2004 : Replaced getTime().getTime() with getTimeInMillis() (DG);
    057     * 04-Nov-2004 : Reverted change of 30-Sep-2004, because it won't work for
    058     *               JDK 1.3 (DG);
    059     * ------------- JFREECHART 1.0.x ---------------------------------------------
    060     * 05-Oct-2006 : Updated API docs (DG);
    061     * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG);
    062     * 11-Dec-2006 : Fix for previous() - bug 1611872 (DG);
    063     *
    064     */
    065
    066    package org.jfree.data.time;
    067
    068    import java.io.Serializable;
    069    import java.util.Calendar;
    070    import java.util.Date;
    071    import java.util.TimeZone;
    072
    073    /**
    074     * Represents a minute.  This class is immutable, which is a requirement for
    075     * all {@link RegularTimePeriod} subclasses.
    076     */
    077    public class Minute extends RegularTimePeriod implements Serializable {
    078
    079        /** For serialization. */
    080        private static final long serialVersionUID = 2144572840034842871L;
    081
    082        /** Useful constant for the first minute in a day. */
    083        public static final int FIRST_MINUTE_IN_HOUR = 0;
    084
    085        /** Useful constant for the last minute in a day. */
    086        public static final int LAST_MINUTE_IN_HOUR = 59;
    087
    088        /** The day. */
    089        private Day day;
    090
    091        /** The hour in which the minute falls. */
    092        private byte hour;
    093
    094        /** The minute. */
    095        private byte minute;
    096
    097        /** The first millisecond. */
    098        private long firstMillisecond;
    099
    100        /** The last millisecond. */
    101        private long lastMillisecond;
    102
    103        /**
    104         * Constructs a new Minute, based on the system date/time.
    105         */
    106        public Minute() {
    107            this(new Date());
    108        }
    109
    110        /**
    111         * Constructs a new Minute.
    112         *
    113         * @param minute  the minute (0 to 59).
    114         * @param hour  the hour (<code>null</code> not permitted).
    115         */
    116        public Minute(int minute, Hour hour) {
    117            if (hour == null) {
    118                throw new IllegalArgumentException("Null 'hour' argument.");
    119            }
    120            this.minute = (byte) minute;
    121            this.hour = (byte) hour.getHour();
    122            this.day = hour.getDay();
    123            peg(Calendar.getInstance());
    124        }
    125
    126        /**
    127         * Constructs a new Minute, based on the supplied date/time.
    128         *
    129         * @param time  the time (<code>null</code> not permitted).
    130         */
    131        public Minute(Date time) {
    132            // defer argument checking
    133            this(time, RegularTimePeriod.DEFAULT_TIME_ZONE);
    134        }
    135
    136        /**
    137         * Constructs a new Minute, based on the supplied date/time and timezone.
    138         *
    139         * @param time  the time (<code>null</code> not permitted).
    140         * @param zone  the time zone (<code>null</code> not permitted).
    141         */
    142        public Minute(Date time, TimeZone zone) {
    143            if (time == null) {
    144                throw new IllegalArgumentException("Null 'time' argument.");
    145            }
    146            if (zone == null) {
    147                throw new IllegalArgumentException("Null 'zone' argument.");
    148            }
    149            Calendar calendar = Calendar.getInstance(zone);
    150            calendar.setTime(time);
    151            int min = calendar.get(Calendar.MINUTE);
    152            this.minute = (byte) min;
    153            this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY);
    154            this.day = new Day(time, zone);
    155            peg(calendar);
    156        }
    157
    158        /**
    159         * Creates a new minute.
    160         *
    161         * @param minute  the minute (0-59).
    162         * @param hour  the hour (0-23).
    163         * @param day  the day (1-31).
    164         * @param month  the month (1-12).
    165         * @param year  the year (1900-9999).
    166         */
    167        public Minute(int minute,
    168                      int hour,
    169                      int day,
    170                      int month,
    171                      int year) {
    172            this(minute, new Hour(hour, new Day(day, month, year)));
    173        }
    174
    175        /**
    176         * Returns the day.
    177         *
    178         * @return The day.
    179         *
    180         * @since 1.0.3
    181         */
    182        public Day getDay() {
    183            return this.day;
    184        }
    185
    186        /**
    187         * Returns the hour.
    188         *
    189         * @return The hour (never <code>null</code>).
    190         */
    191        public Hour getHour() {
    192            return new Hour(this.hour, this.day);
    193        }
    194
    195        /**
    196         * Returns the hour.
    197         *
    198         * @return The hour.
    199         *
    200         * @since 1.0.3
    201         */
    202        public int getHourValue() {
    203            return this.hour;
    204        }
    205
    206        /**
    207         * Returns the minute.
    208         *
    209         * @return The minute.
    210         */
    211        public int getMinute() {
    212            return this.minute;
    213        }
    214
    215        /**
    216         * Returns the first millisecond of the minute.  This will be determined
    217         * relative to the time zone specified in the constructor, or in the
    218         * calendar instance passed in the most recent call to the
    219         * {@link #peg(Calendar)} method.
    220         *
    221         * @return The first millisecond of the minute.
    222         *
    223         * @see #getLastMillisecond()
    224         */
    225        public long getFirstMillisecond() {
    226            return this.firstMillisecond;
    227        }
    228
    229        /**
    230         * Returns the last millisecond of the minute.  This will be
    231         * determined relative to the time zone specified in the constructor, or
    232         * in the calendar instance passed in the most recent call to the
    233         * {@link #peg(Calendar)} method.
    234         *
    235         * @return The last millisecond of the minute.
    236         *
    237         * @see #getFirstMillisecond()
    238         */
    239        public long getLastMillisecond() {
    240            return this.lastMillisecond;
    241        }
    242
    243        /**
    244         * Recalculates the start date/time and end date/time for this time period
    245         * relative to the supplied calendar (which incorporates a time zone).
    246         *
    247         * @param calendar  the calendar (<code>null</code> not permitted).
    248         *
    249         * @since 1.0.3
    250         */
    251        public void peg(Calendar calendar) {
    252            this.firstMillisecond = getFirstMillisecond(calendar);
    253            this.lastMillisecond = getLastMillisecond(calendar);
    254        }
    255
    256        /**
    257         * Returns the minute preceding this one.
    258         *
    259         * @return The minute preceding this one.
    260         */
    261        public RegularTimePeriod previous() {
    262            Minute result;
    263            if (this.minute != FIRST_MINUTE_IN_HOUR) {
    264                result = new Minute(this.minute - 1, getHour());
    265            }
    266            else {
    267                Hour h = (Hour) getHour().previous();
    268                if (h != null) {
    269                    result = new Minute(LAST_MINUTE_IN_HOUR, h);
    270                }
    271                else {
    272                    result = null;
    273                }
    274            }
    275            return result;
    276        }
    277
    278        /**
    279         * Returns the minute following this one.
    280         *
    281         * @return The minute following this one.
    282         */
    283        public RegularTimePeriod next() {
    284
    285            Minute result;
    286            if (this.minute != LAST_MINUTE_IN_HOUR) {
    287                result = new Minute(this.minute + 1, getHour());
    288            }
    289            else { // we are at the last minute in the hour...
    290                Hour nextHour = (Hour) getHour().next();
    291                if (nextHour != null) {
    292                    result = new Minute(FIRST_MINUTE_IN_HOUR, nextHour);
    293                }
    294                else {
    295                    result = null;
    296                }
    297            }
    298            return result;
    299
    300        }
    301
    302        /**
    303         * Returns a serial index number for the minute.
    304         *
    305         * @return The serial index number.
    306         */
    307        public long getSerialIndex() {
    308            long hourIndex = this.day.getSerialIndex() * 24L + this.hour;
    309            return hourIndex * 60L + this.minute;
    310        }
    311
    312        /**
    313         * Returns the first millisecond of the minute.
    314         *
    315         * @param calendar  the calendar which defines the timezone
    316         *     (<code>null</code> not permitted).
    317         *
    318         * @return The first millisecond.
    319         *
    320         * @throws NullPointerException if <code>calendar</code> is
    321         *     <code>null</code>.
    322         */
    323        public long getFirstMillisecond(Calendar calendar) {
    324
    325            int year = this.day.getYear();
    326            int month = this.day.getMonth() - 1;
    327            int day = this.day.getDayOfMonth();
    328
    329            calendar.clear();
    330            calendar.set(year, month, day, this.hour, this.minute, 0);
    331            calendar.set(Calendar.MILLISECOND, 0);
    332
    333            //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
    334            return calendar.getTime().getTime();
    335
    336        }
    337
    338        /**
    339         * Returns the last millisecond of the minute.
    340         *
    341         * @param calendar  the calendar / timezone (<code>null</code> not
    342         *     permitted).
    343         *
    344         * @return The last millisecond.
    345         *
    346         * @throws NullPointerException if <code>calendar</code> is
    347         *     <code>null</code>.
    348         */
    349        public long getLastMillisecond(Calendar calendar) {
    350
    351            int year = this.day.getYear();
    352            int month = this.day.getMonth() - 1;
    353            int day = this.day.getDayOfMonth();
    354
    355            calendar.clear();
    356            calendar.set(year, month, day, this.hour, this.minute, 59);
    357            calendar.set(Calendar.MILLISECOND, 999);
    358
    359            //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
    360            return calendar.getTime().getTime();
    361
    362        }
    363
    364        /**
    365         * Tests the equality of this object against an arbitrary Object.
    366         * <P>
    367         * This method will return true ONLY if the object is a Minute object
    368         * representing the same minute as this instance.
    369         *
    370         * @param obj  the object to compare (<code>null</code> permitted).
    371         *
    372         * @return <code>true</code> if the minute and hour value of this and the
    373         *      object are the same.
    374         */
    375        public boolean equals(Object obj) {
    376            if (obj == this) {
    377                return true;
    378            }
    379            if (!(obj instanceof Minute)) {
    380                return false;
    381            }
    382            Minute that = (Minute) obj;
    383            if (this.minute != that.minute) {
    384                return false;
    385            }
    386            if (this.hour != that.hour) {
    387                return false;
    388            }
    389            return true;
    390        }
    391
    392        /**
    393         * Returns a hash code for this object instance.  The approach described
    394         * by Joshua Bloch in "Effective Java" has been used here:
    395         * <p>
    396         * <code>http://developer.java.sun.com/developer/Books/effectivejava
    397         * /Chapter3.pdf</code>
    398         *
    399         * @return A hash code.
    400         */
    401        public int hashCode() {
    402            int result = 17;
    403            result = 37 * result + this.minute;
    404            result = 37 * result + this.hour;
    405            result = 37 * result + this.day.hashCode();
    406            return result;
    407        }
    408
    409        /**
    410         * Returns an integer indicating the order of this Minute object relative
    411         * to the specified object:
    412         *
    413         * negative == before, zero == same, positive == after.
    414         *
    415         * @param o1  object to compare.
    416         *
    417         * @return negative == before, zero == same, positive == after.
    418         */
    419        public int compareTo(Object o1) {
    420
    421            int result;
    422
    423            // CASE 1 : Comparing to another Minute object
    424            // -------------------------------------------
    425            if (o1 instanceof Minute) {
    426                Minute m = (Minute) o1;
    427                result = getHour().compareTo(m.getHour());
    428                if (result == 0) {
    429                    result = this.minute - m.getMinute();
    430                }
    431            }
    432
    433            // CASE 2 : Comparing to another TimePeriod object
    434            // -----------------------------------------------
    435            else if (o1 instanceof RegularTimePeriod) {
    436                // more difficult case - evaluate later...
    437                result = 0;
    438            }
    439
    440            // CASE 3 : Comparing to a non-TimePeriod object
    441            // ---------------------------------------------
    442            else {
    443                // consider time periods to be ordered after general objects
    444                result = 1;
    445            }
    446
    447            return result;
    448
    449        }
    450
    451        /**
    452         * Creates a Minute instance by parsing a string.  The string is assumed to
    453         * be in the format "YYYY-MM-DD HH:MM", perhaps with leading or trailing
    454         * whitespace.
    455         *
    456         * @param s  the minute string to parse.
    457         *
    458         * @return <code>null</code>, if the string is not parseable, the minute
    459         *      otherwise.
    460         */
    461        public static Minute parseMinute(String s) {
    462
    463            Minute result = null;
    464            s = s.trim();
    465
    466            String daystr = s.substring(0, Math.min(10, s.length()));
    467            Day day = Day.parseDay(daystr);
    468            if (day != null) {
    469                String hmstr = s.substring(
    470                    Math.min(daystr.length() + 1, s.length()), s.length()
    471                );
    472                hmstr = hmstr.trim();
    473
    474                String hourstr = hmstr.substring(0, Math.min(2, hmstr.length()));
    475                int hour = Integer.parseInt(hourstr);
    476
    477                if ((hour >= 0) && (hour <= 23)) {
    478                    String minstr = hmstr.substring(
    479                        Math.min(hourstr.length() + 1, hmstr.length()),
    480                        hmstr.length()
    481                    );
    482                    int minute = Integer.parseInt(minstr);
    483                    if ((minute >= 0) && (minute <= 59)) {
    484                        result = new Minute(minute, new Hour(hour, day));
    485                    }
    486                }
    487            }
    488
    489            return result;
    490
    491        }
    492
    493    }