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