Source for org.jfree.data.time.Hour

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * ---------
  28:  * Hour.java
  29:  * ---------
  30:  * (C) Copyright 2001-2006, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * $Id: Hour.java,v 1.5.2.3 2006/10/06 14:00:15 mungady Exp $
  36:  *
  37:  * Changes
  38:  * -------
  39:  * 11-Oct-2001 : Version 1 (DG);
  40:  * 18-Dec-2001 : Changed order of parameters in constructor (DG);
  41:  * 19-Dec-2001 : Added a new constructor as suggested by Paul English (DG);
  42:  * 14-Feb-2002 : Fixed bug in Hour(Date) constructor (DG);
  43:  * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to 
  44:  *               evaluate with reference to a particular time zone (DG);
  45:  * 15-Mar-2002 : Changed API (DG);
  46:  * 16-Apr-2002 : Fixed small time zone bug in constructor (DG);
  47:  * 10-Sep-2002 : Added getSerialIndex() method (DG);
  48:  * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  49:  * 10-Jan-2003 : Changed base class and method names (DG);
  50:  * 13-Mar-2003 : Moved to com.jrefinery.data.time package, and implemented 
  51:  *               Serializable (DG);
  52:  * 21-Oct-2003 : Added hashCode() method, and new constructor for 
  53:  *               convenience (DG);
  54:  * 30-Sep-2004 : Replaced getTime().getTime() with getTimeInMillis() (DG);
  55:  * 04-Nov-2004 : Reverted change of 30-Sep-2004, because it won't work for 
  56:  *               JDK 1.3 (DG);
  57:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  58:  * 05-Oct-2006 : Updated API docs (DG);
  59:  * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG);
  60:  *
  61:  */
  62: 
  63: package org.jfree.data.time;
  64: 
  65: import java.io.Serializable;
  66: import java.util.Calendar;
  67: import java.util.Date;
  68: import java.util.TimeZone;
  69: 
  70: /**
  71:  * Represents an hour in a specific day.  This class is immutable, which is a 
  72:  * requirement for all {@link RegularTimePeriod} subclasses.
  73:  */
  74: public class Hour extends RegularTimePeriod implements Serializable {
  75: 
  76:     /** For serialization. */
  77:     private static final long serialVersionUID = -835471579831937652L;
  78:     
  79:     /** Useful constant for the first hour in the day. */
  80:     public static final int FIRST_HOUR_IN_DAY = 0;
  81: 
  82:     /** Useful constant for the last hour in the day. */
  83:     public static final int LAST_HOUR_IN_DAY = 23;
  84: 
  85:     /** The day. */
  86:     private Day day;
  87: 
  88:     /** The hour. */
  89:     private byte hour;
  90: 
  91:     /** The first millisecond. */
  92:     private long firstMillisecond;
  93:     
  94:     /** The last millisecond. */
  95:     private long lastMillisecond;
  96: 
  97:     /**
  98:      * Constructs a new Hour, based on the system date/time.
  99:      */
 100:     public Hour() {
 101:         this(new Date());
 102:     }
 103: 
 104:     /**
 105:      * Constructs a new Hour.
 106:      *
 107:      * @param hour  the hour (in the range 0 to 23).
 108:      * @param day  the day (<code>null</code> not permitted).
 109:      */
 110:     public Hour(int hour, Day day) {
 111:         if (day == null) {
 112:             throw new IllegalArgumentException("Null 'day' argument.");
 113:         }
 114:         this.hour = (byte) hour;
 115:         this.day = day;
 116:         peg(Calendar.getInstance());
 117:     }
 118: 
 119:     /**
 120:      * Creates a new hour.
 121:      * 
 122:      * @param hour  the hour (0-23).
 123:      * @param day  the day (1-31).
 124:      * @param month  the month (1-12).
 125:      * @param year  the year (1900-9999).
 126:      */
 127:     public Hour(int hour, int day, int month, int year) {
 128:         this(hour, new Day(day, month, year));
 129:     }
 130:     
 131:     /**
 132:      * Constructs a new Hour, based on the supplied date/time.
 133:      *
 134:      * @param time  the date-time (<code>null</code> not permitted).
 135:      */
 136:     public Hour(Date time) {
 137:         // defer argument checking...
 138:         this(time, RegularTimePeriod.DEFAULT_TIME_ZONE);
 139:     }
 140: 
 141:     /**
 142:      * Constructs a new Hour, based on the supplied date/time evaluated in the
 143:      * specified time zone.
 144:      *
 145:      * @param time  the date-time (<code>null</code> not permitted).
 146:      * @param zone  the time zone (<code>null</code> not permitted).
 147:      */
 148:     public Hour(Date time, TimeZone zone) {
 149:         if (time == null) {
 150:             throw new IllegalArgumentException("Null 'time' argument.");
 151:         }
 152:         if (zone == null) {
 153:             throw new IllegalArgumentException("Null 'zone' argument.");
 154:         }
 155:         Calendar calendar = Calendar.getInstance(zone);
 156:         calendar.setTime(time);
 157:         this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY);
 158:         this.day = new Day(time, zone);
 159:         peg(Calendar.getInstance());
 160:     }
 161: 
 162:     /**
 163:      * Returns the hour.
 164:      *
 165:      * @return The hour (0 <= hour <= 23).
 166:      */
 167:     public int getHour() {
 168:         return this.hour;
 169:     }
 170: 
 171:     /**
 172:      * Returns the day in which this hour falls.
 173:      *
 174:      * @return The day.
 175:      */
 176:     public Day getDay() {
 177:         return this.day;
 178:     }
 179: 
 180:     /**
 181:      * Returns the year in which this hour falls.
 182:      *
 183:      * @return The year.
 184:      */
 185:     public int getYear() {
 186:         return this.day.getYear();
 187:     }
 188: 
 189:     /**
 190:      * Returns the month in which this hour falls.
 191:      *
 192:      * @return The month.
 193:      */
 194:     public int getMonth() {
 195:         return this.day.getMonth();
 196:     }
 197: 
 198:     /**
 199:      * Returns the day-of-the-month in which this hour falls.
 200:      *
 201:      * @return The day-of-the-month.
 202:      */
 203:     public int getDayOfMonth() {
 204:         return this.day.getDayOfMonth();
 205:     }
 206: 
 207:     /**
 208:      * Returns the first millisecond of the hour.  This will be determined 
 209:      * relative to the time zone specified in the constructor, or in the 
 210:      * calendar instance passed in the most recent call to the 
 211:      * {@link #peg(Calendar)} method.
 212:      *
 213:      * @return The first millisecond of the hour.
 214:      * 
 215:      * @see #getLastMillisecond()
 216:      */
 217:     public long getFirstMillisecond() {
 218:         return this.firstMillisecond;
 219:     }
 220: 
 221:     /**
 222:      * Returns the last millisecond of the hour.  This will be 
 223:      * determined relative to the time zone specified in the constructor, or
 224:      * in the calendar instance passed in the most recent call to the 
 225:      * {@link #peg(Calendar)} method.
 226:      *
 227:      * @return The last millisecond of the hour.
 228:      * 
 229:      * @see #getFirstMillisecond()
 230:      */
 231:     public long getLastMillisecond() {
 232:         return this.lastMillisecond;
 233:     }
 234:     
 235:     /** 
 236:      * Recalculates the start date/time and end date/time for this time period 
 237:      * relative to the supplied calendar (which incorporates a time zone).
 238:      * 
 239:      * @param calendar  the calendar (<code>null</code> not permitted).
 240:      * 
 241:      * @since 1.0.3
 242:      */
 243:     public void peg(Calendar calendar) {
 244:         this.firstMillisecond = getFirstMillisecond(calendar);
 245:         this.lastMillisecond = getLastMillisecond(calendar);
 246:     }
 247: 
 248:     /**
 249:      * Returns the hour preceding this one.
 250:      *
 251:      * @return The hour preceding this one.
 252:      */
 253:     public RegularTimePeriod previous() {
 254: 
 255:         Hour result;
 256:         if (this.hour != FIRST_HOUR_IN_DAY) {
 257:             result = new Hour(this.hour - 1, this.day);
 258:         }
 259:         else { // we are at the first hour in the day...
 260:             Day prevDay = (Day) this.day.previous();
 261:             if (prevDay != null) {
 262:                 result = new Hour(LAST_HOUR_IN_DAY, prevDay);
 263:             }
 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:         }
 283:         else { // we are at the last hour in the day...
 284:             Day nextDay = (Day) this.day.next();
 285:             if (nextDay != null) {
 286:                 result = new Hour(FIRST_HOUR_IN_DAY, nextDay);
 287:             }
 288:             else {
 289:                 result = null;
 290:             }
 291:         }
 292:         return result;
 293: 
 294:     }
 295: 
 296:     /**
 297:      * Returns a serial index number for the hour.
 298:      *
 299:      * @return The serial index number.
 300:      */
 301:     public long getSerialIndex() {
 302:         return this.day.getSerialIndex() * 24L + this.hour;
 303:     }
 304: 
 305:     /**
 306:      * Returns the first millisecond of the hour.
 307:      *
 308:      * @param calendar  the calendar/timezone (<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:         int year = this.day.getYear();
 317:         int month = this.day.getMonth() - 1;
 318:         int dom = this.day.getDayOfMonth();
 319:         calendar.set(year, month, dom, this.hour, 0, 0);
 320:         calendar.set(Calendar.MILLISECOND, 0);
 321:         //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
 322:         return calendar.getTime().getTime();
 323:     }
 324: 
 325:     /**
 326:      * Returns the last millisecond of the hour.
 327:      *
 328:      * @param calendar  the calendar/timezone (<code>null</code> not permitted).
 329:      *
 330:      * @return The last millisecond.
 331:      *
 332:      * @throws NullPointerException if <code>calendar</code> is 
 333:      *     <code>null</code>.
 334:      */
 335:     public long getLastMillisecond(Calendar calendar) {
 336:         int year = this.day.getYear();
 337:         int month = this.day.getMonth() - 1;
 338:         int dom = this.day.getDayOfMonth();
 339:         calendar.set(year, month, dom, this.hour, 59, 59);
 340:         calendar.set(Calendar.MILLISECOND, 999);
 341:         //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
 342:         return calendar.getTime().getTime();
 343:     }
 344: 
 345:     /**
 346:      * Tests the equality of this object against an arbitrary Object.
 347:      * <P>
 348:      * This method will return true ONLY if the object is an Hour object
 349:      * representing the same hour as this instance.
 350:      *
 351:      * @param obj  the object to compare (<code>null</code> permitted).
 352:      *
 353:      * @return <code>true</code> if the hour and day value of the object
 354:      *      is the same as this.
 355:      */
 356:     public boolean equals(Object obj) {
 357:         if (obj == this) {
 358:             return true;
 359:         }
 360:         if (!(obj instanceof Hour)) {
 361:             return false;
 362:         }
 363:         Hour that = (Hour) obj;
 364:         if (this.hour != that.hour) {
 365:             return false;
 366:         }
 367:         if (!this.day.equals(that.day)) {
 368:             return false;
 369:         }
 370:         return true;
 371:     }
 372: 
 373:     /**
 374:      * Returns a hash code for this object instance.  The approach described by 
 375:      * Joshua Bloch in "Effective Java" has been used here:
 376:      * <p>
 377:      * <code>http://developer.java.sun.com/developer/Books/effectivejava
 378:      * /Chapter3.pdf</code>
 379:      * 
 380:      * @return A hash code.
 381:      */
 382:     public int hashCode() {
 383:         int result = 17;
 384:         result = 37 * result + this.hour;
 385:         result = 37 * result + this.day.hashCode();
 386:         return result;
 387:     }
 388: 
 389:     /**
 390:      * Returns an integer indicating the order of this Hour object relative to
 391:      * the specified object:
 392:      *
 393:      * negative == before, zero == same, positive == after.
 394:      *
 395:      * @param o1  the object to compare.
 396:      *
 397:      * @return negative == before, zero == same, positive == after.
 398:      */
 399:     public int compareTo(Object o1) {
 400: 
 401:         int result;
 402: 
 403:         // CASE 1 : Comparing to another Hour object
 404:         // -----------------------------------------
 405:         if (o1 instanceof Hour) {
 406:             Hour h = (Hour) o1;
 407:             result = getDay().compareTo(h.getDay());
 408:             if (result == 0) {
 409:                 result = this.hour - h.getHour();
 410:             }
 411:         }
 412: 
 413:         // CASE 2 : Comparing to another TimePeriod object
 414:         // -----------------------------------------------
 415:         else if (o1 instanceof RegularTimePeriod) {
 416:             // more difficult case - evaluate later...
 417:             result = 0;
 418:         }
 419: 
 420:         // CASE 3 : Comparing to a non-TimePeriod object
 421:         // ---------------------------------------------
 422:         else {
 423:             // consider time periods to be ordered after general objects
 424:             result = 1;
 425:         }
 426: 
 427:         return result;
 428: 
 429:     }
 430: 
 431:     /**
 432:      * Creates an Hour instance by parsing a string.  The string is assumed to
 433:      * be in the format "YYYY-MM-DD HH", perhaps with leading or trailing
 434:      * whitespace.
 435:      *
 436:      * @param s  the hour string to parse.
 437:      *
 438:      * @return <code>null</code> if the string is not parseable, the hour 
 439:      *         otherwise.
 440:      */
 441:     public static Hour parseHour(String s) {
 442: 
 443:         Hour result = null;
 444:         s = s.trim();
 445: 
 446:         String daystr = s.substring(0, Math.min(10, s.length()));
 447:         Day day = Day.parseDay(daystr);
 448:         if (day != null) {
 449:             String hourstr = s.substring(
 450:                 Math.min(daystr.length() + 1, s.length()), s.length()
 451:             );
 452:             hourstr = hourstr.trim();
 453:             int hour = Integer.parseInt(hourstr);
 454:             // if the hour is 0 - 23 then create an hour
 455:             if ((hour >= FIRST_HOUR_IN_DAY) && (hour <= LAST_HOUR_IN_DAY)) {
 456:                 result = new Hour(hour, day);
 457:             }
 458:         }
 459: 
 460:         return result;
 461: 
 462:     }
 463: 
 464: }