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