Source for org.jfree.data.time.Minute

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, 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:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * -----------
  28:  * Minute.java
  29:  * -----------
  30:  * (C) Copyright 2001-2007, 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:  *
  62:  */
  63: 
  64: package org.jfree.data.time;
  65: 
  66: import java.io.Serializable;
  67: import java.util.Calendar;
  68: import java.util.Date;
  69: import java.util.TimeZone;
  70: 
  71: /**
  72:  * Represents a minute.  This class is immutable, which is a requirement for 
  73:  * all {@link RegularTimePeriod} subclasses.
  74:  */
  75: public class Minute extends RegularTimePeriod implements Serializable {
  76: 
  77:     /** For serialization. */
  78:     private static final long serialVersionUID = 2144572840034842871L;
  79:     
  80:     /** Useful constant for the first minute in a day. */
  81:     public static final int FIRST_MINUTE_IN_HOUR = 0;
  82: 
  83:     /** Useful constant for the last minute in a day. */
  84:     public static final int LAST_MINUTE_IN_HOUR = 59;
  85: 
  86:     /** The day. */
  87:     private Day day;
  88:     
  89:     /** The hour in which the minute falls. */
  90:     private byte hour;
  91: 
  92:     /** The minute. */
  93:     private byte minute;
  94: 
  95:     /** The first millisecond. */
  96:     private long firstMillisecond;
  97:     
  98:     /** The last millisecond. */
  99:     private long lastMillisecond;
 100: 
 101:     /**
 102:      * Constructs a new Minute, based on the system date/time.
 103:      */
 104:     public Minute() {
 105:         this(new Date());
 106:     }
 107: 
 108:     /**
 109:      * Constructs a new Minute.
 110:      *
 111:      * @param minute  the minute (0 to 59).
 112:      * @param hour  the hour (<code>null</code> not permitted).
 113:      */
 114:     public Minute(int minute, Hour hour) {
 115:         if (hour == null) {
 116:             throw new IllegalArgumentException("Null 'hour' argument.");
 117:         }
 118:         this.minute = (byte) minute;
 119:         this.hour = (byte) hour.getHour();
 120:         this.day = hour.getDay();
 121:         peg(Calendar.getInstance());
 122:     }
 123: 
 124:     /**
 125:      * Constructs a new Minute, based on the supplied date/time.
 126:      *
 127:      * @param time  the time (<code>null</code> not permitted).
 128:      */
 129:     public Minute(Date time) {
 130:         // defer argument checking
 131:         this(time, RegularTimePeriod.DEFAULT_TIME_ZONE);
 132:     }
 133: 
 134:     /**
 135:      * Constructs a new Minute, based on the supplied date/time and timezone.
 136:      *
 137:      * @param time  the time (<code>null</code> not permitted).
 138:      * @param zone  the time zone (<code>null</code> not permitted).
 139:      */
 140:     public Minute(Date time, TimeZone zone) {
 141:         if (time == null) {
 142:             throw new IllegalArgumentException("Null 'time' argument.");
 143:         }
 144:         if (zone == null) {
 145:             throw new IllegalArgumentException("Null 'zone' argument.");
 146:         }
 147:         Calendar calendar = Calendar.getInstance(zone);
 148:         calendar.setTime(time);
 149:         int min = calendar.get(Calendar.MINUTE);
 150:         this.minute = (byte) min;
 151:         this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY);
 152:         this.day = new Day(time, zone);
 153:         peg(calendar);
 154:     }
 155:     
 156:     /**
 157:      * Creates a new minute.
 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, 
 166:                   int hour, 
 167:                   int day, 
 168:                   int month, 
 169:                   int year) {
 170:         this(minute, new Hour(hour, new Day(day, month, year)));
 171:     }
 172: 
 173:     /**
 174:      * Returns the day.
 175:      * 
 176:      * @return The day.
 177:      * 
 178:      * @since 1.0.3
 179:      */
 180:     public Day getDay() {
 181:         return this.day;
 182:     }
 183:     
 184:     /**
 185:      * Returns the hour.
 186:      *
 187:      * @return The hour (never <code>null</code>).
 188:      */
 189:     public Hour getHour() {
 190:         return new Hour(this.hour, this.day);
 191:     }
 192:     
 193:     /**
 194:      * Returns the hour.
 195:      * 
 196:      * @return The hour.
 197:      * 
 198:      * @since 1.0.3
 199:      */
 200:     public int getHourValue() {
 201:         return this.hour;
 202:     }
 203: 
 204:     /**
 205:      * Returns the minute.
 206:      *
 207:      * @return The minute.
 208:      */
 209:     public int getMinute() {
 210:         return this.minute;
 211:     }
 212: 
 213:     /**
 214:      * Returns the first millisecond of the minute.  This will be determined 
 215:      * relative to the time zone specified in the constructor, or in the 
 216:      * calendar instance passed in the most recent call to the 
 217:      * {@link #peg(Calendar)} method.
 218:      *
 219:      * @return The first millisecond of the minute.
 220:      * 
 221:      * @see #getLastMillisecond()
 222:      */
 223:     public long getFirstMillisecond() {
 224:         return this.firstMillisecond;
 225:     }
 226: 
 227:     /**
 228:      * Returns the last millisecond of the minute.  This will be 
 229:      * determined relative to the time zone specified in the constructor, or
 230:      * in the calendar instance passed in the most recent call to the 
 231:      * {@link #peg(Calendar)} method.
 232:      *
 233:      * @return The last millisecond of the minute.
 234:      * 
 235:      * @see #getFirstMillisecond()
 236:      */
 237:     public long getLastMillisecond() {
 238:         return this.lastMillisecond;
 239:     }
 240:     
 241:     /** 
 242:      * Recalculates the start date/time and end date/time for this time period 
 243:      * relative to the supplied calendar (which incorporates a time zone).
 244:      * 
 245:      * @param calendar  the calendar (<code>null</code> not permitted).
 246:      * 
 247:      * @since 1.0.3
 248:      */
 249:     public void peg(Calendar calendar) {
 250:         this.firstMillisecond = getFirstMillisecond(calendar);
 251:         this.lastMillisecond = getLastMillisecond(calendar);
 252:     }
 253: 
 254:     /**
 255:      * Returns the minute preceding this one.
 256:      *
 257:      * @return The minute preceding this one.
 258:      */
 259:     public RegularTimePeriod previous() {
 260:         Minute result;
 261:         if (this.minute != FIRST_MINUTE_IN_HOUR) {
 262:             result = new Minute(this.minute - 1, getHour());
 263:         }
 264:         else {
 265:             Hour h = (Hour) getHour().previous();
 266:             if (h != null) {
 267:                 result = new Minute(LAST_MINUTE_IN_HOUR, h);
 268:             }
 269:             else {
 270:                 result = null;
 271:             }
 272:         }
 273:         return result;
 274:     }
 275: 
 276:     /**
 277:      * Returns the minute following this one.
 278:      *
 279:      * @return The minute following this one.
 280:      */
 281:     public RegularTimePeriod next() {
 282: 
 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:     /**
 301:      * Returns a serial index number for the minute.
 302:      *
 303:      * @return The serial index number.
 304:      */
 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</code> not permitted).
 315:      *
 316:      * @return The first millisecond.
 317:      *
 318:      * @throws NullPointerException if <code>calendar</code> is 
 319:      *     <code>null</code>.
 320:      */
 321:     public long getFirstMillisecond(Calendar calendar) {
 322: 
 323:         int year = this.day.getYear();
 324:         int month = this.day.getMonth() - 1;
 325:         int day = this.day.getDayOfMonth();
 326: 
 327:         calendar.clear();
 328:         calendar.set(year, month, day, this.hour, this.minute, 0);
 329:         calendar.set(Calendar.MILLISECOND, 0);
 330: 
 331:         //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
 332:         return calendar.getTime().getTime();
 333: 
 334:     }
 335: 
 336:     /**
 337:      * Returns the last millisecond of the minute.
 338:      *
 339:      * @param calendar  the calendar / timezone (<code>null</code> not 
 340:      *     permitted).
 341:      *
 342:      * @return The last millisecond.
 343:      *
 344:      * @throws NullPointerException if <code>calendar</code> is 
 345:      *     <code>null</code>.
 346:      */
 347:     public long getLastMillisecond(Calendar calendar) {
 348: 
 349:         int year = this.day.getYear();
 350:         int month = this.day.getMonth() - 1;
 351:         int day = this.day.getDayOfMonth();
 352: 
 353:         calendar.clear();
 354:         calendar.set(year, month, day, this.hour, this.minute, 59);
 355:         calendar.set(Calendar.MILLISECOND, 999);
 356: 
 357:         //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
 358:         return calendar.getTime().getTime();
 359: 
 360:     }
 361: 
 362:     /**
 363:      * Tests the equality of this object against an arbitrary Object.
 364:      * <P>
 365:      * This method will return true ONLY if the object is a Minute object
 366:      * representing the same minute as this instance.
 367:      *
 368:      * @param obj  the object to compare (<code>null</code> permitted).
 369:      *
 370:      * @return <code>true</code> if the minute and hour value of this and the
 371:      *      object are the same.
 372:      */
 373:     public boolean equals(Object obj) {
 374:         if (obj == this) {
 375:             return true;
 376:         }
 377:         if (!(obj instanceof Minute)) {
 378:             return false;
 379:         }
 380:         Minute that = (Minute) obj;
 381:         if (this.minute != that.minute) {
 382:             return false;
 383:         }
 384:         if (this.hour != that.hour) {
 385:             return false;
 386:         }
 387:         return true;
 388:     }
 389: 
 390:     /**
 391:      * Returns a hash code for this object instance.  The approach described 
 392:      * by Joshua Bloch in "Effective Java" has been used here:
 393:      * <p>
 394:      * <code>http://developer.java.sun.com/developer/Books/effectivejava
 395:      * /Chapter3.pdf</code>
 396:      * 
 397:      * @return A hash code.
 398:      */
 399:     public int hashCode() {
 400:         int result = 17;
 401:         result = 37 * result + this.minute;
 402:         result = 37 * result + this.hour;
 403:         result = 37 * result + this.day.hashCode();
 404:         return result;
 405:     }
 406: 
 407:     /**
 408:      * Returns an integer indicating the order of this Minute object relative
 409:      * to the specified object:
 410:      *
 411:      * negative == before, zero == same, positive == after.
 412:      *
 413:      * @param o1  object to compare.
 414:      *
 415:      * @return negative == before, zero == same, positive == after.
 416:      */
 417:     public int compareTo(Object o1) {
 418: 
 419:         int result;
 420: 
 421:         // CASE 1 : Comparing to another Minute object
 422:         // -------------------------------------------
 423:         if (o1 instanceof Minute) {
 424:             Minute m = (Minute) o1;
 425:             result = getHour().compareTo(m.getHour());
 426:             if (result == 0) {
 427:                 result = this.minute - m.getMinute();
 428:             }
 429:         }
 430: 
 431:         // CASE 2 : Comparing to another TimePeriod object
 432:         // -----------------------------------------------
 433:         else if (o1 instanceof RegularTimePeriod) {
 434:             // more difficult case - evaluate later...
 435:             result = 0;
 436:         }
 437: 
 438:         // CASE 3 : Comparing to a non-TimePeriod object
 439:         // ---------------------------------------------
 440:         else {
 441:             // consider time periods to be ordered after general objects
 442:             result = 1;
 443:         }
 444: 
 445:         return result;
 446: 
 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</code>, if the string is not parseable, the minute
 457:      *      otherwise.
 458:      */
 459:     public static Minute parseMinute(String s) {
 460: 
 461:         Minute result = null;
 462:         s = s.trim();
 463: 
 464:         String daystr = s.substring(0, Math.min(10, s.length()));
 465:         Day day = Day.parseDay(daystr);
 466:         if (day != null) {
 467:             String hmstr = s.substring(
 468:                 Math.min(daystr.length() + 1, s.length()), s.length()
 469:             );
 470:             hmstr = hmstr.trim();
 471: 
 472:             String hourstr = hmstr.substring(0, Math.min(2, hmstr.length()));
 473:             int hour = Integer.parseInt(hourstr);
 474: 
 475:             if ((hour >= 0) && (hour <= 23)) {
 476:                 String minstr = hmstr.substring(
 477:                     Math.min(hourstr.length() + 1, hmstr.length()), 
 478:                     hmstr.length()
 479:                 );
 480:                 int minute = Integer.parseInt(minstr);
 481:                 if ((minute >= 0) && (minute <= 59)) {
 482:                     result = new Minute(minute, new Hour(hour, day));
 483:                 }
 484:             }
 485:         }
 486: 
 487:         return result;
 488: 
 489:     }
 490: 
 491: }