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 * 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 Hour(Date) constructor (DG); 041 * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to 042 * evaluate with reference to a particular time zone (DG); 043 * 15-Mar-2002 : Changed API (DG); 044 * 16-Apr-2002 : Fixed small time zone bug in constructor (DG); 045 * 10-Sep-2002 : Added getSerialIndex() method (DG); 046 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG); 047 * 10-Jan-2003 : Changed base class and method names (DG); 048 * 13-Mar-2003 : Moved to com.jrefinery.data.time package, and implemented 049 * Serializable (DG); 050 * 21-Oct-2003 : Added hashCode() method, and new constructor for 051 * convenience (DG); 052 * 30-Sep-2004 : Replaced getTime().getTime() with getTimeInMillis() (DG); 053 * 04-Nov-2004 : Reverted change of 30-Sep-2004, because it won't work for 054 * JDK 1.3 (DG); 055 * ------------- JFREECHART 1.0.x --------------------------------------------- 056 * 05-Oct-2006 : Updated API docs (DG); 057 * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG); 058 * 04-Apr-2007 : In Hour(Date, TimeZone), peg milliseconds using specified 059 * time zone (DG); 060 * 061 */ 062 063 package org.jfree.data.time; 064 065 import java.io.Serializable; 066 import java.util.Calendar; 067 import java.util.Date; 068 import java.util.TimeZone; 069 070 /** 071 * Represents an hour in a specific day. This class is immutable, which is a 072 * requirement for all {@link RegularTimePeriod} subclasses. 073 */ 074 public class Hour extends RegularTimePeriod implements Serializable { 075 076 /** For serialization. */ 077 private static final long serialVersionUID = -835471579831937652L; 078 079 /** Useful constant for the first hour in the day. */ 080 public static final int FIRST_HOUR_IN_DAY = 0; 081 082 /** Useful constant for the last hour in the day. */ 083 public static final int LAST_HOUR_IN_DAY = 23; 084 085 /** The day. */ 086 private Day day; 087 088 /** The hour. */ 089 private byte hour; 090 091 /** The first millisecond. */ 092 private long firstMillisecond; 093 094 /** The last millisecond. */ 095 private long lastMillisecond; 096 097 /** 098 * Constructs a new Hour, based on the system date/time. 099 */ 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); 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 }