Source for org.jfree.data.time.Second

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