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