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