Source for org.jfree.data.time.Day

   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:  * Day.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:  * 15-Nov-2001 : Updated Javadoc comments (DG);
  39:  * 04-Dec-2001 : Added static method to parse a string into a Day object (DG);
  40:  * 19-Dec-2001 : Added new constructor as suggested by Paul English (DG);
  41:  * 29-Jan-2002 : Changed getDay() method to getSerialDate() (DG);
  42:  * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to 
  43:  *               evaluate with reference to a particular time zone (DG);
  44:  * 19-Mar-2002 : Changed the API for the TimePeriod classes (DG);
  45:  * 29-May-2002 : Fixed bug in equals method (DG);
  46:  * 24-Jun-2002 : Removed unnecessary imports (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 (DG);
  53:  * 30-Sep-2004 : Replaced getTime().getTime() with getTimeInMillis() (DG);
  54:  * 04-Nov-2004 : Reverted change of 30-Sep-2004, because it won't work for 
  55:  *               JDK 1.3 (DG);
  56:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  57:  * 05-Oct-2006 : Updated API docs (DG);
  58:  * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG);
  59:  * 
  60:  */
  61: 
  62: package org.jfree.data.time;
  63: 
  64: import java.io.Serializable;
  65: import java.text.DateFormat;
  66: import java.text.ParseException;
  67: import java.text.SimpleDateFormat;
  68: import java.util.Calendar;
  69: import java.util.Date;
  70: import java.util.TimeZone;
  71: 
  72: import org.jfree.date.SerialDate;
  73: 
  74: /**
  75:  * Represents a single day in the range 1-Jan-1900 to 31-Dec-9999.  This class 
  76:  * is immutable, which is a requirement for all {@link RegularTimePeriod} 
  77:  * subclasses.
  78:  */
  79: public class Day extends RegularTimePeriod implements Serializable {
  80: 
  81:     /** For serialization. */
  82:     private static final long serialVersionUID = -7082667380758962755L;
  83:     
  84:     /** A standard date formatter. */
  85:     protected static final DateFormat DATE_FORMAT 
  86:         = new SimpleDateFormat("yyyy-MM-dd");
  87: 
  88:     /** A date formatter for the default locale. */
  89:     protected static final DateFormat
  90:         DATE_FORMAT_SHORT = DateFormat.getDateInstance(DateFormat.SHORT);
  91: 
  92:     /** A date formatter for the default locale. */
  93:     protected static final DateFormat
  94:         DATE_FORMAT_MEDIUM = DateFormat.getDateInstance(DateFormat.MEDIUM);
  95: 
  96:     /** A date formatter for the default locale. */
  97:     protected static final DateFormat
  98:         DATE_FORMAT_LONG = DateFormat.getDateInstance(DateFormat.LONG);
  99: 
 100:     /** The day (uses SerialDate for convenience). */
 101:     private SerialDate serialDate;
 102: 
 103:     /** The first millisecond. */
 104:     private long firstMillisecond;
 105:     
 106:     /** The last millisecond. */
 107:     private long lastMillisecond;
 108: 
 109:     /**
 110:      * Creates a new instance, derived from the system date/time (and assuming 
 111:      * the default timezone).
 112:      */
 113:     public Day() {
 114:         this(new Date());
 115:     }
 116: 
 117:     /**
 118:      * Constructs a new one day time period.
 119:      *
 120:      * @param day  the day-of-the-month.
 121:      * @param month  the month (1 to 12).
 122:      * @param year  the year (1900 <= year <= 9999).
 123:      */
 124:     public Day(int day, int month, int year) {
 125:         this.serialDate = SerialDate.createInstance(day, month, year);
 126:         peg(Calendar.getInstance());
 127:     }
 128: 
 129:     /**
 130:      * Constructs a new one day time period.
 131:      *
 132:      * @param serialDate  the day (<code>null</code> not permitted).
 133:      */
 134:     public Day(SerialDate serialDate) {
 135:         if (serialDate == null) {
 136:             throw new IllegalArgumentException("Null 'serialDate' argument.");
 137:         }
 138:         this.serialDate = serialDate;
 139:         peg(Calendar.getInstance());
 140:     }
 141: 
 142:     /**
 143:      * Constructs a new instance, based on a particular date/time and the 
 144:      * default time zone.
 145:      *
 146:      * @param time  the time (<code>null</code> not permitted).
 147:      */
 148:     public Day(Date time) {
 149:         // defer argument checking...
 150:         this(time, RegularTimePeriod.DEFAULT_TIME_ZONE);
 151:     }
 152: 
 153:     /**
 154:      * Constructs a new instance, based on a particular date/time and time zone.
 155:      *
 156:      * @param time  the date/time.
 157:      * @param zone  the time zone.
 158:      */
 159:     public Day(Date time, TimeZone zone) {
 160:         if (time == null) {
 161:             throw new IllegalArgumentException("Null 'time' argument.");
 162:         }
 163:         if (zone == null) {
 164:             throw new IllegalArgumentException("Null 'zone' argument.");
 165:         }
 166:         Calendar calendar = Calendar.getInstance(zone);
 167:         calendar.setTime(time);
 168:         int d = calendar.get(Calendar.DAY_OF_MONTH);
 169:         int m = calendar.get(Calendar.MONTH) + 1;
 170:         int y = calendar.get(Calendar.YEAR);
 171:         this.serialDate = SerialDate.createInstance(d, m, y);
 172:         peg(calendar);
 173:     }
 174: 
 175:     /**
 176:      * Returns the day as a {@link SerialDate}.  Note: the reference that is 
 177:      * returned should be an instance of an immutable {@link SerialDate} 
 178:      * (otherwise the caller could use the reference to alter the state of 
 179:      * this <code>Day</code> instance, and <code>Day</code> is supposed
 180:      * to be immutable).
 181:      *
 182:      * @return The day as a {@link SerialDate}.
 183:      */
 184:     public SerialDate getSerialDate() {
 185:         return this.serialDate;
 186:     }
 187: 
 188:     /**
 189:      * Returns the year.
 190:      *
 191:      * @return The year.
 192:      */
 193:     public int getYear() {
 194:         return this.serialDate.getYYYY();
 195:     }
 196: 
 197:     /**
 198:      * Returns the month.
 199:      *
 200:      * @return The month.
 201:      */
 202:     public int getMonth() {
 203:         return this.serialDate.getMonth();
 204:     }
 205: 
 206:     /**
 207:      * Returns the day of the month.
 208:      *
 209:      * @return The day of the month.
 210:      */
 211:     public int getDayOfMonth() {
 212:         return this.serialDate.getDayOfMonth();
 213:     }
 214: 
 215:     /**
 216:      * Returns the first millisecond of the day.  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 day.
 222:      * 
 223:      * @see #getLastMillisecond()
 224:      */
 225:     public long getFirstMillisecond() {
 226:         return this.firstMillisecond;
 227:     }
 228: 
 229:     /**
 230:      * Returns the last millisecond of the day.  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 day.
 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 day preceding this one.
 258:      *
 259:      * @return The day preceding this one.
 260:      */
 261:     public RegularTimePeriod previous() {
 262: 
 263:         Day result;
 264:         int serial = this.serialDate.toSerial();
 265:         if (serial > SerialDate.SERIAL_LOWER_BOUND) {
 266:             SerialDate yesterday = SerialDate.createInstance(serial - 1);
 267:             return new Day(yesterday);
 268:         }
 269:         else {
 270:             result = null;
 271:         }
 272:         return result;
 273: 
 274:     }
 275: 
 276:     /**
 277:      * Returns the day following this one, or <code>null</code> if some limit 
 278:      * has been reached.
 279:      *
 280:      * @return The day following this one, or <code>null</code> if some limit 
 281:      *         has been reached.
 282:      */
 283:     public RegularTimePeriod next() {
 284: 
 285:         Day result;
 286:         int serial = this.serialDate.toSerial();
 287:         if (serial < SerialDate.SERIAL_UPPER_BOUND) {
 288:             SerialDate tomorrow = SerialDate.createInstance(serial + 1);
 289:             return new Day(tomorrow);
 290:         }
 291:         else {
 292:             result = null;
 293:         }
 294:         return result;
 295: 
 296:     }
 297: 
 298:     /**
 299:      * Returns a serial index number for the day.
 300:      *
 301:      * @return The serial index number.
 302:      */
 303:     public long getSerialIndex() {
 304:         return this.serialDate.toSerial();
 305:     }
 306: 
 307:     /**
 308:      * Returns the first millisecond of the day, evaluated using the supplied
 309:      * calendar (which determines the time zone).
 310:      *
 311:      * @param calendar  calendar to use (<code>null</code> not permitted).
 312:      *
 313:      * @return The start of the day as milliseconds since 01-01-1970.
 314:      *
 315:      * @throws NullPointerException if <code>calendar</code> is 
 316:      *     <code>null</code>.
 317:      */
 318:     public long getFirstMillisecond(Calendar calendar) {
 319:         int year = this.serialDate.getYYYY();
 320:         int month = this.serialDate.getMonth();
 321:         int day = this.serialDate.getDayOfMonth();
 322:         calendar.clear();
 323:         calendar.set(year, month - 1, day, 0, 0, 0);
 324:         calendar.set(Calendar.MILLISECOND, 0);
 325:         //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
 326:         return calendar.getTime().getTime();
 327:     }
 328: 
 329:     /**
 330:      * Returns the last millisecond of the day, evaluated using the supplied
 331:      * calendar (which determines the time zone).
 332:      *
 333:      * @param calendar  calendar to use (<code>null</code> not permitted).
 334:      *
 335:      * @return The end of the day as milliseconds since 01-01-1970.
 336:      *
 337:      * @throws NullPointerException if <code>calendar</code> is 
 338:      *     <code>null</code>.
 339:      */
 340:     public long getLastMillisecond(Calendar calendar) {
 341:         int year = this.serialDate.getYYYY();
 342:         int month = this.serialDate.getMonth();
 343:         int day = this.serialDate.getDayOfMonth();
 344:         calendar.clear();
 345:         calendar.set(year, month - 1, day, 23, 59, 59);
 346:         calendar.set(Calendar.MILLISECOND, 999);
 347:         //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
 348:         return calendar.getTime().getTime();
 349:     }
 350: 
 351:     /**
 352:      * Tests the equality of this Day object to an arbitrary object.  Returns
 353:      * true if the target is a Day instance or a SerialDate instance
 354:      * representing the same day as this object. In all other cases,
 355:      * returns false.
 356:      *
 357:      * @param obj  the object (<code>null</code> permitted).
 358:      *
 359:      * @return A flag indicating whether or not an object is equal to this day.
 360:      */
 361:     public boolean equals(Object obj) {
 362:         
 363:         if (obj == this) {
 364:             return true;
 365:         }
 366:         if (!(obj instanceof Day)) {
 367:             return false;
 368:         }
 369:         Day that = (Day) obj;
 370:         if (!this.serialDate.equals(that.getSerialDate())) {
 371:             return false;
 372:         }
 373:         return true;
 374:         
 375:     }
 376: 
 377:     /**
 378:      * Returns a hash code for this object instance.  The approach described by
 379:      * Joshua Bloch in "Effective Java" has been used here:
 380:      * <p>
 381:      * <code>http://developer.java.sun.com/developer/Books/effectivejava
 382:      * /Chapter3.pdf</code>
 383:      * 
 384:      * @return A hash code.
 385:      */
 386:     public int hashCode() {
 387:         return this.serialDate.hashCode();
 388:     }
 389: 
 390:     /**
 391:      * Returns an integer indicating the order of this Day object relative to
 392:      * the specified object:
 393:      *
 394:      * negative == before, zero == same, positive == after.
 395:      *
 396:      * @param o1  the object to compare.
 397:      *
 398:      * @return negative == before, zero == same, positive == after.
 399:      */
 400:     public int compareTo(Object o1) {
 401: 
 402:         int result;
 403: 
 404:         // CASE 1 : Comparing to another Day object
 405:         // ----------------------------------------
 406:         if (o1 instanceof Day) {
 407:             Day d = (Day) o1;
 408:             result = -d.getSerialDate().compare(this.serialDate);
 409:         }
 410: 
 411:         // CASE 2 : Comparing to another TimePeriod object
 412:         // -----------------------------------------------
 413:         else if (o1 instanceof RegularTimePeriod) {
 414:             // more difficult case - evaluate later...
 415:             result = 0;
 416:         }
 417: 
 418:         // CASE 3 : Comparing to a non-TimePeriod object
 419:         // ---------------------------------------------
 420:         else {
 421:             // consider time periods to be ordered after general objects
 422:             result = 1;
 423:         }
 424: 
 425:         return result;
 426: 
 427:     }
 428: 
 429:     /**
 430:      * Returns a string representing the day.
 431:      *
 432:      * @return A string representing the day.
 433:      */
 434:     public String toString() {
 435:         return this.serialDate.toString();
 436:     }
 437: 
 438:     /**
 439:      * Parses the string argument as a day.
 440:      * <P>
 441:      * This method is required to recognise YYYY-MM-DD as a valid format.
 442:      * Anything else, for now, is a bonus.
 443:      *
 444:      * @param s  the date string to parse.
 445:      *
 446:      * @return <code>null</code> if the string does not contain any parseable
 447:      *      string, the day otherwise.
 448:      */
 449:     public static Day parseDay(String s) {
 450: 
 451:         try {
 452:             return new Day (Day.DATE_FORMAT.parse(s));
 453:         }
 454:         catch (ParseException e1) {
 455:             try {
 456:                 return new Day (Day.DATE_FORMAT_SHORT.parse(s));
 457:             }
 458:             catch (ParseException e2) {
 459:               // ignore
 460:             }
 461:         }
 462:         return null;
 463: 
 464:     }
 465: 
 466: }