1 /* =========================================================== 2 * JFreeChart : a free chart library for the Java(tm) platform 3 * =========================================================== 4 * 5 * (C) Copyright 2000-2013, by Object Refinery Limited and Contributors. 6 * 7 * Project Info: http://www.jfree.org/jfreechart/index.html 8 * 9 * This library is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU Lesser General Public License as published by 11 * the Free Software Foundation; either version 2.1 of the License, or 12 * (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17 * License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22 * USA. 23 * 24 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 25 * Other names may be trademarks of their respective owners.] 26 * 27 * ----------- 28 * Minute.java 29 * ----------- 30 * (C) Copyright 2001-2013, by Object Refinery Limited. 31 * 32 * Original Author: David Gilbert (for Object Refinery Limited); 33 * Contributor(s): -; 34 * 35 * Changes 36 * ------- 37 * 11-Oct-2001 : Version 1 (DG); 38 * 18-Dec-2001 : Changed order of parameters in constructor (DG); 39 * 19-Dec-2001 : Added a new constructor as suggested by Paul English (DG); 40 * 14-Feb-2002 : Fixed bug in Minute(Date) constructor, and changed the range 41 * to start from zero instead of one (DG); 42 * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to 43 * evaluate with reference to a particular time zone (DG); 44 * 13-Mar-2002 : Added parseMinute() method (DG); 45 * 19-Mar-2002 : Changed API, the minute is now defined in relation to an 46 * Hour (DG); 47 * 10-Sep-2002 : Added getSerialIndex() method (DG); 48 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG); 49 * 10-Jan-2003 : Changed base class and method names (DG); 50 * 13-Mar-2003 : Moved to com.jrefinery.data.time package and implemented 51 * Serializable (DG); 52 * 21-Oct-2003 : Added hashCode() method, and new constructor for 53 * convenience (DG); 54 * 30-Sep-2004 : Replaced getTime().getTime() with getTimeInMillis() (DG); 55 * 04-Nov-2004 : Reverted change of 30-Sep-2004, because it won't work for 56 * JDK 1.3 (DG); 57 * ------------- JFREECHART 1.0.x --------------------------------------------- 58 * 05-Oct-2006 : Updated API docs (DG); 59 * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG); 60 * 11-Dec-2006 : Fix for previous() - bug 1611872 (DG); 61 * 16-Sep-2008 : Deprecated DEFAULT_TIME_ZONE (DG); 62 * 02-Mar-2009 : Added new constructor that specifies Locale (DG); 63 * 05-Jul-2012 : Replaced getTime().getTime() with getTimeInMillis() (DG); 64 * 03-Jul-2013 : Use ParamChecks (DG); 65 * 66 */ 67 68 package org.jfree.data.time; 69 70 import java.io.Serializable; 71 import java.util.Calendar; 72 import java.util.Date; 73 import java.util.Locale; 74 import java.util.TimeZone; 75 import org.jfree.chart.util.ParamChecks; 76 77 /** 78 * Represents a minute. This class is immutable, which is a requirement for 79 * all {@link RegularTimePeriod} subclasses. 80 */ 81 public class Minute extends RegularTimePeriod implements Serializable { 82 83 /** For serialization. */ 84 private static final long serialVersionUID = 2144572840034842871L; 85 86 /** Useful constant for the first minute in a day. */ 87 public static final int FIRST_MINUTE_IN_HOUR = 0; 88 89 /** Useful constant for the last minute in a day. */ 90 public static final int LAST_MINUTE_IN_HOUR = 59; 91 92 /** The day. */ 93 private Day day; 94 95 /** The hour in which the minute falls. */ 96 private byte hour; 97 98 /** The minute. */ 99 private byte minute; 100 101 /** The first millisecond. */ 102 private long firstMillisecond; 103 104 /** The last millisecond. */ 105 private long lastMillisecond; 106 107 /** 108 * Constructs a new Minute, based on the system date/time. 109 */ Minute()110 public Minute() { 111 this(new Date()); 112 } 113 114 /** 115 * Constructs a new Minute. 116 * 117 * @param minute the minute (0 to 59). 118 * @param hour the hour (<code>null</code> not permitted). 119 */ Minute(int minute, Hour hour)120 public Minute(int minute, Hour hour) { 121 ParamChecks.nullNotPermitted(hour, "hour"); 122 this.minute = (byte) minute; 123 this.hour = (byte) hour.getHour(); 124 this.day = hour.getDay(); 125 peg(Calendar.getInstance()); 126 } 127 128 /** 129 * Constructs a new instance, based on the supplied date/time and 130 * the default time zone. 131 * 132 * @param time the time (<code>null</code> not permitted). 133 * 134 * @see #Minute(Date, TimeZone) 135 */ Minute(Date time)136 public Minute(Date time) { 137 // defer argument checking 138 this(time, TimeZone.getDefault(), Locale.getDefault()); 139 } 140 141 /** 142 * Constructs a new Minute, based on the supplied date/time and timezone. 143 * 144 * @param time the time (<code>null</code> not permitted). 145 * @param zone the time zone (<code>null</code> not permitted). 146 * 147 * @deprecated As of 1.0.13, use the constructor that specifies the locale 148 * also. 149 */ Minute(Date time, TimeZone zone)150 public Minute(Date time, TimeZone zone) { 151 this(time, zone, Locale.getDefault()); 152 } 153 154 /** 155 * Constructs a new Minute, based on the supplied date/time and timezone. 156 * 157 * @param time the time (<code>null</code> not permitted). 158 * @param zone the time zone (<code>null</code> not permitted). 159 * @param locale the locale (<code>null</code> not permitted). 160 * 161 * @since 1.0.13 162 */ Minute(Date time, TimeZone zone, Locale locale)163 public Minute(Date time, TimeZone zone, Locale locale) { 164 ParamChecks.nullNotPermitted(time, "time"); 165 ParamChecks.nullNotPermitted(zone, "zone"); 166 ParamChecks.nullNotPermitted(locale, "locale"); 167 Calendar calendar = Calendar.getInstance(zone, locale); 168 calendar.setTime(time); 169 int min = calendar.get(Calendar.MINUTE); 170 this.minute = (byte) min; 171 this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); 172 this.day = new Day(time, zone, locale); 173 peg(calendar); 174 } 175 176 /** 177 * Creates a new minute. 178 * 179 * @param minute the minute (0-59). 180 * @param hour the hour (0-23). 181 * @param day the day (1-31). 182 * @param month the month (1-12). 183 * @param year the year (1900-9999). 184 */ Minute(int minute, int hour, int day, int month, int year)185 public Minute(int minute, int hour, int day, int month, int year) { 186 this(minute, new Hour(hour, new Day(day, month, year))); 187 } 188 189 /** 190 * Returns the day. 191 * 192 * @return The day. 193 * 194 * @since 1.0.3 195 */ getDay()196 public Day getDay() { 197 return this.day; 198 } 199 200 /** 201 * Returns the hour. 202 * 203 * @return The hour (never <code>null</code>). 204 */ getHour()205 public Hour getHour() { 206 return new Hour(this.hour, this.day); 207 } 208 209 /** 210 * Returns the hour. 211 * 212 * @return The hour. 213 * 214 * @since 1.0.3 215 */ getHourValue()216 public int getHourValue() { 217 return this.hour; 218 } 219 220 /** 221 * Returns the minute. 222 * 223 * @return The minute. 224 */ getMinute()225 public int getMinute() { 226 return this.minute; 227 } 228 229 /** 230 * Returns the first millisecond of the minute. This will be determined 231 * relative to the time zone specified in the constructor, or in the 232 * calendar instance passed in the most recent call to the 233 * {@link #peg(Calendar)} method. 234 * 235 * @return The first millisecond of the minute. 236 * 237 * @see #getLastMillisecond() 238 */ 239 @Override getFirstMillisecond()240 public long getFirstMillisecond() { 241 return this.firstMillisecond; 242 } 243 244 /** 245 * Returns the last millisecond of the minute. This will be 246 * determined relative to the time zone specified in the constructor, or 247 * in the calendar instance passed in the most recent call to the 248 * {@link #peg(Calendar)} method. 249 * 250 * @return The last millisecond of the minute. 251 * 252 * @see #getFirstMillisecond() 253 */ 254 @Override getLastMillisecond()255 public long getLastMillisecond() { 256 return this.lastMillisecond; 257 } 258 259 /** 260 * Recalculates the start date/time and end date/time for this time period 261 * relative to the supplied calendar (which incorporates a time zone). 262 * 263 * @param calendar the calendar (<code>null</code> not permitted). 264 * 265 * @since 1.0.3 266 */ 267 @Override peg(Calendar calendar)268 public void peg(Calendar calendar) { 269 this.firstMillisecond = getFirstMillisecond(calendar); 270 this.lastMillisecond = getLastMillisecond(calendar); 271 } 272 273 /** 274 * Returns the minute preceding this one. 275 * 276 * @return The minute preceding this one. 277 */ 278 @Override previous()279 public RegularTimePeriod previous() { 280 Minute result; 281 if (this.minute != FIRST_MINUTE_IN_HOUR) { 282 result = new Minute(this.minute - 1, getHour()); 283 } 284 else { 285 Hour h = (Hour) getHour().previous(); 286 if (h != null) { 287 result = new Minute(LAST_MINUTE_IN_HOUR, h); 288 } 289 else { 290 result = null; 291 } 292 } 293 return result; 294 } 295 296 /** 297 * Returns the minute following this one. 298 * 299 * @return The minute following this one. 300 */ 301 @Override next()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 @Override getSerialIndex()325 public long getSerialIndex() { 326 long hourIndex = this.day.getSerialIndex() * 24L + this.hour; 327 return hourIndex * 60L + this.minute; 328 } 329 330 /** 331 * Returns the first millisecond of the minute. 332 * 333 * @param calendar the calendar which defines the timezone 334 * (<code>null</code> not permitted). 335 * 336 * @return The first millisecond. 337 * 338 * @throws NullPointerException if <code>calendar</code> is 339 * <code>null</code>. 340 */ 341 @Override getFirstMillisecond(Calendar calendar)342 public long getFirstMillisecond(Calendar calendar) { 343 int year = this.day.getYear(); 344 int month = this.day.getMonth() - 1; 345 int d = this.day.getDayOfMonth(); 346 347 calendar.clear(); 348 calendar.set(year, month, d, this.hour, this.minute, 0); 349 calendar.set(Calendar.MILLISECOND, 0); 350 351 return calendar.getTimeInMillis(); 352 } 353 354 /** 355 * Returns the last millisecond of the minute. 356 * 357 * @param calendar the calendar / timezone (<code>null</code> not 358 * permitted). 359 * 360 * @return The last millisecond. 361 * 362 * @throws NullPointerException if <code>calendar</code> is 363 * <code>null</code>. 364 */ 365 @Override getLastMillisecond(Calendar calendar)366 public long getLastMillisecond(Calendar calendar) { 367 int year = this.day.getYear(); 368 int month = this.day.getMonth() - 1; 369 int d = this.day.getDayOfMonth(); 370 371 calendar.clear(); 372 calendar.set(year, month, d, this.hour, this.minute, 59); 373 calendar.set(Calendar.MILLISECOND, 999); 374 375 return calendar.getTimeInMillis(); 376 } 377 378 /** 379 * Tests the equality of this object against an arbitrary Object. 380 * <P> 381 * This method will return true ONLY if the object is a Minute object 382 * representing the same minute as this instance. 383 * 384 * @param obj the object to compare (<code>null</code> permitted). 385 * 386 * @return <code>true</code> if the minute and hour value of this and the 387 * object are the same. 388 */ 389 @Override equals(Object obj)390 public boolean equals(Object obj) { 391 if (obj == this) { 392 return true; 393 } 394 if (!(obj instanceof Minute)) { 395 return false; 396 } 397 Minute that = (Minute) obj; 398 if (this.minute != that.minute) { 399 return false; 400 } 401 if (this.hour != that.hour) { 402 return false; 403 } 404 return true; 405 } 406 407 /** 408 * Returns a hash code for this object instance. The approach described 409 * by Joshua Bloch in "Effective Java" has been used here: 410 * <p> 411 * <code>http://developer.java.sun.com/developer/Books/effectivejava 412 * /Chapter3.pdf</code> 413 * 414 * @return A hash code. 415 */ 416 @Override hashCode()417 public int hashCode() { 418 int result = 17; 419 result = 37 * result + this.minute; 420 result = 37 * result + this.hour; 421 result = 37 * result + this.day.hashCode(); 422 return result; 423 } 424 425 /** 426 * Returns an integer indicating the order of this Minute object relative 427 * to the specified object: 428 * 429 * negative == before, zero == same, positive == after. 430 * 431 * @param o1 object to compare. 432 * 433 * @return negative == before, zero == same, positive == after. 434 */ 435 @Override compareTo(Object o1)436 public int compareTo(Object o1) { 437 int result; 438 439 // CASE 1 : Comparing to another Minute object 440 // ------------------------------------------- 441 if (o1 instanceof Minute) { 442 Minute m = (Minute) o1; 443 result = getHour().compareTo(m.getHour()); 444 if (result == 0) { 445 result = this.minute - m.getMinute(); 446 } 447 } 448 449 // CASE 2 : Comparing to another TimePeriod object 450 // ----------------------------------------------- 451 else if (o1 instanceof RegularTimePeriod) { 452 // more difficult case - evaluate later... 453 result = 0; 454 } 455 456 // CASE 3 : Comparing to a non-TimePeriod object 457 // --------------------------------------------- 458 else { 459 // consider time periods to be ordered after general objects 460 result = 1; 461 } 462 463 return result; 464 } 465 466 /** 467 * Creates a Minute instance by parsing a string. The string is assumed to 468 * be in the format "YYYY-MM-DD HH:MM", perhaps with leading or trailing 469 * whitespace. 470 * 471 * @param s the minute string to parse. 472 * 473 * @return <code>null</code>, if the string is not parseable, the minute 474 * otherwise. 475 */ parseMinute(String s)476 public static Minute parseMinute(String s) { 477 Minute result = null; 478 s = s.trim(); 479 480 String daystr = s.substring(0, Math.min(10, s.length())); 481 Day day = Day.parseDay(daystr); 482 if (day != null) { 483 String hmstr = s.substring( 484 Math.min(daystr.length() + 1, s.length()), s.length() 485 ); 486 hmstr = hmstr.trim(); 487 488 String hourstr = hmstr.substring(0, Math.min(2, hmstr.length())); 489 int hour = Integer.parseInt(hourstr); 490 491 if ((hour >= 0) && (hour <= 23)) { 492 String minstr = hmstr.substring( 493 Math.min(hourstr.length() + 1, hmstr.length()), 494 hmstr.length() 495 ); 496 int minute = Integer.parseInt(minstr); 497 if ((minute >= 0) && (minute <= 59)) { 498 result = new Minute(minute, new Hour(hour, day)); 499 } 500 } 501 } 502 return result; 503 } 504 505 } 506