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