001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ----------- 028 * Minute.java 029 * ----------- 030 * (C) Copyright 2001-2013, 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 * 05-Jul-2012 : Replaced getTime().getTime() with getTimeInMillis() (DG); 064 * 03-Jul-2013 : Use ParamChecks (DG); 065 * 066 */ 067 068package org.jfree.data.time; 069 070import java.io.Serializable; 071import java.util.Calendar; 072import java.util.Date; 073import java.util.Locale; 074import java.util.TimeZone; 075import org.jfree.chart.util.ParamChecks; 076 077/** 078 * Represents a minute. This class is immutable, which is a requirement for 079 * all {@link RegularTimePeriod} subclasses. 080 */ 081public class Minute extends RegularTimePeriod implements Serializable { 082 083 /** For serialization. */ 084 private static final long serialVersionUID = 2144572840034842871L; 085 086 /** Useful constant for the first minute in a day. */ 087 public static final int FIRST_MINUTE_IN_HOUR = 0; 088 089 /** Useful constant for the last minute in a day. */ 090 public static final int LAST_MINUTE_IN_HOUR = 59; 091 092 /** The day. */ 093 private Day day; 094 095 /** The hour in which the minute falls. */ 096 private byte hour; 097 098 /** The minute. */ 099 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 216 public int getHourValue() { 217 return this.hour; 218 } 219 220 /** 221 * Returns the minute. 222 * 223 * @return The minute. 224 */ 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 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 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 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 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 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 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 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 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 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 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 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 */ 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}