Frames | No Frames |
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: }