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, int hour, int day, int month, int year) {
168: this (minute, new Hour(hour, new Day(day, month, year)));
169: }
170:
171: /**
172: * Returns the day.
173: *
174: * @return The day.
175: *
176: * @since 1.0.3
177: */
178: public Day getDay() {
179: return this .day;
180: }
181:
182: /**
183: * Returns the hour.
184: *
185: * @return The hour (never <code>null</code>).
186: */
187: public Hour getHour() {
188: return new Hour(this .hour, this .day);
189: }
190:
191: /**
192: * Returns the hour.
193: *
194: * @return The hour.
195: *
196: * @since 1.0.3
197: */
198: public int getHourValue() {
199: return this .hour;
200: }
201:
202: /**
203: * Returns the minute.
204: *
205: * @return The minute.
206: */
207: public int getMinute() {
208: return this .minute;
209: }
210:
211: /**
212: * Returns the first millisecond of the minute. This will be determined
213: * relative to the time zone specified in the constructor, or in the
214: * calendar instance passed in the most recent call to the
215: * {@link #peg(Calendar)} method.
216: *
217: * @return The first millisecond of the minute.
218: *
219: * @see #getLastMillisecond()
220: */
221: public long getFirstMillisecond() {
222: return this .firstMillisecond;
223: }
224:
225: /**
226: * Returns the last millisecond of the minute. This will be
227: * determined relative to the time zone specified in the constructor, or
228: * in the calendar instance passed in the most recent call to the
229: * {@link #peg(Calendar)} method.
230: *
231: * @return The last millisecond of the minute.
232: *
233: * @see #getFirstMillisecond()
234: */
235: public long getLastMillisecond() {
236: return this .lastMillisecond;
237: }
238:
239: /**
240: * Recalculates the start date/time and end date/time for this time period
241: * relative to the supplied calendar (which incorporates a time zone).
242: *
243: * @param calendar the calendar (<code>null</code> not permitted).
244: *
245: * @since 1.0.3
246: */
247: public void peg(Calendar calendar) {
248: this .firstMillisecond = getFirstMillisecond(calendar);
249: this .lastMillisecond = getLastMillisecond(calendar);
250: }
251:
252: /**
253: * Returns the minute preceding this one.
254: *
255: * @return The minute preceding this one.
256: */
257: public RegularTimePeriod previous() {
258: Minute result;
259: if (this .minute != FIRST_MINUTE_IN_HOUR) {
260: result = new Minute(this .minute - 1, getHour());
261: } else {
262: Hour h = (Hour) getHour().previous();
263: if (h != null) {
264: result = new Minute(LAST_MINUTE_IN_HOUR, h);
265: } else {
266: result = null;
267: }
268: }
269: return result;
270: }
271:
272: /**
273: * Returns the minute following this one.
274: *
275: * @return The minute following this one.
276: */
277: public RegularTimePeriod next() {
278:
279: Minute result;
280: if (this .minute != LAST_MINUTE_IN_HOUR) {
281: result = new Minute(this .minute + 1, getHour());
282: } else { // we are at the last minute in the hour...
283: Hour nextHour = (Hour) getHour().next();
284: if (nextHour != null) {
285: result = new Minute(FIRST_MINUTE_IN_HOUR, nextHour);
286: } else {
287: result = null;
288: }
289: }
290: return result;
291:
292: }
293:
294: /**
295: * Returns a serial index number for the minute.
296: *
297: * @return The serial index number.
298: */
299: public long getSerialIndex() {
300: long hourIndex = this .day.getSerialIndex() * 24L + this .hour;
301: return hourIndex * 60L + this .minute;
302: }
303:
304: /**
305: * Returns the first millisecond of the minute.
306: *
307: * @param calendar the calendar which defines the timezone
308: * (<code>null</code> not permitted).
309: *
310: * @return The first millisecond.
311: *
312: * @throws NullPointerException if <code>calendar</code> is
313: * <code>null</code>.
314: */
315: public long getFirstMillisecond(Calendar calendar) {
316:
317: int year = this .day.getYear();
318: int month = this .day.getMonth() - 1;
319: int day = this .day.getDayOfMonth();
320:
321: calendar.clear();
322: calendar.set(year, month, day, this .hour, this .minute, 0);
323: calendar.set(Calendar.MILLISECOND, 0);
324:
325: //return calendar.getTimeInMillis(); // this won't work for JDK 1.3
326: return calendar.getTime().getTime();
327:
328: }
329:
330: /**
331: * Returns the last millisecond of the minute.
332: *
333: * @param calendar the calendar / timezone (<code>null</code> not
334: * permitted).
335: *
336: * @return The last millisecond.
337: *
338: * @throws NullPointerException if <code>calendar</code> is
339: * <code>null</code>.
340: */
341: public long getLastMillisecond(Calendar calendar) {
342:
343: int year = this .day.getYear();
344: int month = this .day.getMonth() - 1;
345: int day = this .day.getDayOfMonth();
346:
347: calendar.clear();
348: calendar.set(year, month, day, this .hour, this .minute, 59);
349: calendar.set(Calendar.MILLISECOND, 999);
350:
351: //return calendar.getTimeInMillis(); // this won't work for JDK 1.3
352: return calendar.getTime().getTime();
353:
354: }
355:
356: /**
357: * Tests the equality of this object against an arbitrary Object.
358: * <P>
359: * This method will return true ONLY if the object is a Minute object
360: * representing the same minute as this instance.
361: *
362: * @param obj the object to compare (<code>null</code> permitted).
363: *
364: * @return <code>true</code> if the minute and hour value of this and the
365: * object are the same.
366: */
367: public boolean equals(Object obj) {
368: if (obj == this ) {
369: return true;
370: }
371: if (!(obj instanceof Minute)) {
372: return false;
373: }
374: Minute that = (Minute) obj;
375: if (this .minute != that.minute) {
376: return false;
377: }
378: if (this .hour != that.hour) {
379: return false;
380: }
381: return true;
382: }
383:
384: /**
385: * Returns a hash code for this object instance. The approach described
386: * by Joshua Bloch in "Effective Java" has been used here:
387: * <p>
388: * <code>http://developer.java.sun.com/developer/Books/effectivejava
389: * /Chapter3.pdf</code>
390: *
391: * @return A hash code.
392: */
393: public int hashCode() {
394: int result = 17;
395: result = 37 * result + this .minute;
396: result = 37 * result + this .hour;
397: result = 37 * result + this .day.hashCode();
398: return result;
399: }
400:
401: /**
402: * Returns an integer indicating the order of this Minute object relative
403: * to the specified object:
404: *
405: * negative == before, zero == same, positive == after.
406: *
407: * @param o1 object to compare.
408: *
409: * @return negative == before, zero == same, positive == after.
410: */
411: public int compareTo(Object o1) {
412:
413: int result;
414:
415: // CASE 1 : Comparing to another Minute object
416: // -------------------------------------------
417: if (o1 instanceof Minute) {
418: Minute m = (Minute) o1;
419: result = getHour().compareTo(m.getHour());
420: if (result == 0) {
421: result = this .minute - m.getMinute();
422: }
423: }
424:
425: // CASE 2 : Comparing to another TimePeriod object
426: // -----------------------------------------------
427: else if (o1 instanceof RegularTimePeriod) {
428: // more difficult case - evaluate later...
429: result = 0;
430: }
431:
432: // CASE 3 : Comparing to a non-TimePeriod object
433: // ---------------------------------------------
434: else {
435: // consider time periods to be ordered after general objects
436: result = 1;
437: }
438:
439: return result;
440:
441: }
442:
443: /**
444: * Creates a Minute instance by parsing a string. The string is assumed to
445: * be in the format "YYYY-MM-DD HH:MM", perhaps with leading or trailing
446: * whitespace.
447: *
448: * @param s the minute string to parse.
449: *
450: * @return <code>null</code>, if the string is not parseable, the minute
451: * otherwise.
452: */
453: public static Minute parseMinute(String s) {
454:
455: Minute result = null;
456: s = s.trim();
457:
458: String daystr = s.substring(0, Math.min(10, s.length()));
459: Day day = Day.parseDay(daystr);
460: if (day != null) {
461: String hmstr = s.substring(Math.min(daystr.length() + 1, s
462: .length()), s.length());
463: hmstr = hmstr.trim();
464:
465: String hourstr = hmstr.substring(0, Math.min(2, hmstr
466: .length()));
467: int hour = Integer.parseInt(hourstr);
468:
469: if ((hour >= 0) && (hour <= 23)) {
470: String minstr = hmstr.substring(Math.min(hourstr
471: .length() + 1, hmstr.length()), hmstr.length());
472: int minute = Integer.parseInt(minstr);
473: if ((minute >= 0) && (minute <= 59)) {
474: result = new Minute(minute, new Hour(hour, day));
475: }
476: }
477: }
478:
479: return result;
480:
481: }
482:
483: }
|