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