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