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