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