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