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     * Minute.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: Minute.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 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     *
060     */
061    
062    package org.jfree.data.time;
063    
064    import java.io.Serializable;
065    import java.util.Calendar;
066    import java.util.Date;
067    import java.util.TimeZone;
068    
069    /**
070     * Represents a minute.  This class is immutable, which is a requirement for 
071     * all {@link RegularTimePeriod} subclasses.
072     */
073    public class Minute extends RegularTimePeriod implements Serializable {
074    
075        /** For serialization. */
076        private static final long serialVersionUID = 2144572840034842871L;
077        
078        /** Useful constant for the first minute in a day. */
079        public static final int FIRST_MINUTE_IN_HOUR = 0;
080    
081        /** Useful constant for the last minute in a day. */
082        public static final int LAST_MINUTE_IN_HOUR = 59;
083    
084        /** The hour in which the minute falls. */
085        private Hour hour;
086    
087        /** The minute. */
088        private int minute;
089    
090        /**
091         * Constructs a new Minute, based on the system date/time.
092         */
093        public Minute() {
094            this(new Date());
095        }
096    
097        /**
098         * Constructs a new Minute.
099         *
100         * @param minute  the minute (0 to 59).
101         * @param hour  the hour (<code>null</code> not permitted).
102         */
103        public Minute(int minute, Hour hour) {
104            if (hour == null) {
105                throw new IllegalArgumentException("Null 'hour' argument.");
106            }
107            this.minute = minute;
108            this.hour = hour;
109        }
110    
111        /**
112         * Constructs a new Minute, based on the supplied date/time.
113         *
114         * @param time  the time (<code>null</code> not permitted).
115         */
116        public Minute(Date time) {
117            // defer argument checking
118            this(time, RegularTimePeriod.DEFAULT_TIME_ZONE);
119        }
120    
121        /**
122         * Constructs a new Minute, based on the supplied date/time and timezone.
123         *
124         * @param time  the time (<code>null</code> not permitted).
125         * @param zone  the time zone (<code>null</code> not permitted).
126         */
127        public Minute(Date time, TimeZone zone) {
128    
129            if (time == null) {
130                throw new IllegalArgumentException("Null 'time' argument.");
131            }
132            if (zone == null) {
133                throw new IllegalArgumentException("Null 'zone' argument.");
134            }
135            Calendar calendar = Calendar.getInstance(zone);
136            calendar.setTime(time);
137            int min = calendar.get(Calendar.MINUTE);
138            this.minute = min;
139            this.hour = new Hour(time, zone);
140    
141        }
142        
143        /**
144         * Creates a new minute.
145         * 
146         * @param minute  the minute (0-59).
147         * @param hour  the hour (0-23).
148         * @param day  the day (1-31).
149         * @param month  the month (1-12).
150         * @param year  the year (1900-9999).
151         */
152        public Minute(int minute, 
153                      int hour, 
154                      int day, 
155                      int month, 
156                      int year) {
157            this(minute, new Hour(hour, new Day(day, month, year)));
158        }
159    
160        /**
161         * Returns the hour.
162         *
163         * @return The hour (never <code>null</code>).
164         */
165        public Hour getHour() {
166            return this.hour;
167        }
168    
169        /**
170         * Returns the minute.
171         *
172         * @return The minute.
173         */
174        public int getMinute() {
175            return this.minute;
176        }
177    
178        /**
179         * Returns the minute preceding this one.
180         *
181         * @return The minute preceding this one.
182         */
183        public RegularTimePeriod previous() {
184    
185            Minute result;
186            if (this.minute != FIRST_MINUTE_IN_HOUR) {
187                result = new Minute(this.minute - 1, this.hour);
188            }
189            else { // we are at the first minute in the hour...
190                Hour prevHour = (Hour) this.hour.previous();
191                if (prevHour != null) {
192                    result = new Minute(LAST_MINUTE_IN_HOUR, prevHour);
193                }
194                else {
195                    result = null;
196                }
197            }
198            return result;
199    
200        }
201    
202        /**
203         * Returns the minute following this one.
204         *
205         * @return The minute following this one.
206         */
207        public RegularTimePeriod next() {
208    
209            Minute result;
210            if (this.minute != LAST_MINUTE_IN_HOUR) {
211                result = new Minute(this.minute + 1, this.hour);
212            }
213            else { // we are at the last minute in the hour...
214                Hour nextHour = (Hour) this.hour.next();
215                if (nextHour != null) {
216                    result = new Minute(FIRST_MINUTE_IN_HOUR, nextHour);
217                }
218                else {
219                    result = null;
220                }
221            }
222            return result;
223    
224        }
225    
226        /**
227         * Returns a serial index number for the minute.
228         *
229         * @return The serial index number.
230         */
231        public long getSerialIndex() {
232            return this.hour.getSerialIndex() * 60L + this.minute;
233        }
234    
235        /**
236         * Returns the first millisecond of the minute.
237         *
238         * @param calendar  the calendar (which defines the timezone).
239         *
240         * @return The first millisecond.
241         */
242        public long getFirstMillisecond(Calendar calendar) {
243    
244            int year = this.hour.getDay().getYear();
245            int month = this.hour.getDay().getMonth() - 1;
246            int day = this.hour.getDay().getDayOfMonth();
247    
248            calendar.clear();
249            calendar.set(year, month, day, this.hour.getHour(), this.minute, 0);
250            calendar.set(Calendar.MILLISECOND, 0);
251    
252            //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
253            return calendar.getTime().getTime();
254    
255        }
256    
257        /**
258         * Returns the last millisecond of the minute.
259         *
260         * @param calendar  the calendar and timezone.
261         *
262         * @return The last millisecond.
263         */
264        public long getLastMillisecond(Calendar calendar) {
265    
266            int year = this.hour.getDay().getYear();
267            int month = this.hour.getDay().getMonth() - 1;
268            int day = this.hour.getDay().getDayOfMonth();
269    
270            calendar.clear();
271            calendar.set(year, month, day, this.hour.getHour(), this.minute, 59);
272            calendar.set(Calendar.MILLISECOND, 999);
273    
274            //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
275            return calendar.getTime().getTime();
276    
277        }
278    
279        /**
280         * Tests the equality of this object against an arbitrary Object.
281         * <P>
282         * This method will return true ONLY if the object is a Minute object
283         * representing the same minute as this instance.
284         *
285         * @param obj  the object to compare (<code>null</code> permitted).
286         *
287         * @return <code>true</code> if the minute and hour value of this and the
288         *      object are the same.
289         */
290        public boolean equals(Object obj) {
291            if (obj == this) {
292                return true;
293            }
294            if (!(obj instanceof Minute)) {
295                return false;
296            }
297            Minute that = (Minute) obj;
298            if (this.minute != that.minute) {
299                return false;
300            }
301            if (!this.hour.equals(that.hour)) {
302                return false;
303            }
304            return true;
305        }
306    
307        /**
308         * Returns a hash code for this object instance.  The approach described 
309         * by Joshua Bloch in "Effective Java" has been used here:
310         * <p>
311         * <code>http://developer.java.sun.com/developer/Books/effectivejava
312         * /Chapter3.pdf</code>
313         * 
314         * @return A hash code.
315         */
316        public int hashCode() {
317            int result = 17;
318            result = 37 * result + this.minute;
319            result = 37 * result + this.hour.hashCode();
320            return result;
321        }
322    
323        /**
324         * Returns an integer indicating the order of this Minute object relative
325         * to the specified object:
326         *
327         * negative == before, zero == same, positive == after.
328         *
329         * @param o1  object to compare.
330         *
331         * @return negative == before, zero == same, positive == after.
332         */
333        public int compareTo(Object o1) {
334    
335            int result;
336    
337            // CASE 1 : Comparing to another Minute object
338            // -------------------------------------------
339            if (o1 instanceof Minute) {
340                Minute m = (Minute) o1;
341                result = getHour().compareTo(m.getHour());
342                if (result == 0) {
343                    result = this.minute - m.getMinute();
344                }
345            }
346    
347            // CASE 2 : Comparing to another TimePeriod object
348            // -----------------------------------------------
349            else if (o1 instanceof RegularTimePeriod) {
350                // more difficult case - evaluate later...
351                result = 0;
352            }
353    
354            // CASE 3 : Comparing to a non-TimePeriod object
355            // ---------------------------------------------
356            else {
357                // consider time periods to be ordered after general objects
358                result = 1;
359            }
360    
361            return result;
362    
363        }
364    
365        /**
366         * Creates a Minute instance by parsing a string.  The string is assumed to
367         * be in the format "YYYY-MM-DD HH:MM", perhaps with leading or trailing
368         * whitespace.
369         *
370         * @param s  the minute string to parse.
371         *
372         * @return <code>null</code>, if the string is not parseable, the minute
373         *      otherwise.
374         */
375        public static Minute parseMinute(String s) {
376    
377            Minute result = null;
378            s = s.trim();
379    
380            String daystr = s.substring(0, Math.min(10, s.length()));
381            Day day = Day.parseDay(daystr);
382            if (day != null) {
383                String hmstr = s.substring(
384                    Math.min(daystr.length() + 1, s.length()), s.length()
385                );
386                hmstr = hmstr.trim();
387    
388                String hourstr = hmstr.substring(0, Math.min(2, hmstr.length()));
389                int hour = Integer.parseInt(hourstr);
390    
391                if ((hour >= 0) && (hour <= 23)) {
392                    String minstr = hmstr.substring(
393                        Math.min(hourstr.length() + 1, hmstr.length()), 
394                        hmstr.length()
395                    );
396                    int minute = Integer.parseInt(minstr);
397                    if ((minute >= 0) && (minute <= 59)) {
398                        result = new Minute(minute, new Hour(hour, day));
399                    }
400                }
401            }
402    
403            return result;
404    
405        }
406    
407    }