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