    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    006     *
    007     * Project Info:  http://www.jfree.org/jfreechart/index.html
    008     *
    009     * This library is free software; you can redistribute it and/or modify it
    010     * under the terms of the GNU Lesser General Public License as published by
    011     * the Free Software Foundation; either version 2.1 of the License, or
    012     * (at your option) any later version.
    013     *
    014     * This library is distributed in the hope that it will be useful, but
    015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017     * License for more details.
    018     *
    019     * You should have received a copy of the GNU Lesser General Public
    020     * License along with this library; if not, write to the Free Software
    021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022     * USA.
    023     *
    024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * --------
    028     * Day.java
    029     * --------
    030     * (C) Copyright 2001-2004, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * $Id: Day.java,v 1.7.2.1 2005/10/25 21:35:24 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 11-Oct-2001 : Version 1 (DG);
    040     * 15-Nov-2001 : Updated Javadoc comments (DG);
    041     * 04-Dec-2001 : Added static method to parse a string into a Day object (DG);
    042     * 19-Dec-2001 : Added new constructor as suggested by Paul English (DG);
    043     * 29-Jan-2002 : Changed getDay() method to getSerialDate() (DG);
    044     * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to
    045     *               evaluate with reference to a particular time zone (DG);
    046     * 19-Mar-2002 : Changed the API for the TimePeriod classes (DG);
    047     * 29-May-2002 : Fixed bug in equals method (DG);
    048     * 24-Jun-2002 : Removed unnecessary imports (DG);
    049     * 10-Sep-2002 : Added getSerialIndex() method (DG);
    050     * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    051     * 10-Jan-2003 : Changed base class and method names (DG);
    052     * 13-Mar-2003 : Moved to com.jrefinery.data.time package, and implemented
    053     *               Serializable (DG);
    054     * 21-Oct-2003 : Added hashCode() method (DG);
    055     * 30-Sep-2004 : Replaced getTime().getTime() with getTimeInMillis() (DG);
    056     * 04-Nov-2004 : Reverted change of 30-Sep-2004, because it won't work for
    057     *               JDK 1.3 (DG);
    058     *
    059     */
    060
    061    package org.jfree.data.time;
    062
    063    import java.io.Serializable;
    064    import java.text.DateFormat;
    065    import java.text.ParseException;
    066    import java.text.SimpleDateFormat;
    067    import java.util.Calendar;
    068    import java.util.Date;
    069    import java.util.TimeZone;
    070
    071    import org.jfree.date.SerialDate;
    072
    073    /**
    074     * Represents a single day in the range 1-Jan-1900 to 31-Dec-9999.  This class
    075     * is immutable, which is a requirement for all {@link RegularTimePeriod}
    076     * subclasses.
    077     */
    078    public class Day extends RegularTimePeriod implements Serializable {
    079
    080        /** For serialization. */
    081        private static final long serialVersionUID = -7082667380758962755L;
    082
    083        /** A standard date formatter. */
    084        protected static final DateFormat DATE_FORMAT
    085            = new SimpleDateFormat("yyyy-MM-dd");
    086
    087        /** A date formatter for the default locale. */
    088        protected static final DateFormat
    089            DATE_FORMAT_SHORT = DateFormat.getDateInstance(DateFormat.SHORT);
    090
    091        /** A date formatter for the default locale. */
    092        protected static final DateFormat
    093            DATE_FORMAT_MEDIUM = DateFormat.getDateInstance(DateFormat.MEDIUM);
    094
    095        /** A date formatter for the default locale. */
    096        protected static final DateFormat
    097            DATE_FORMAT_LONG = DateFormat.getDateInstance(DateFormat.LONG);
    098
    099        /** The day (uses SerialDate for convenience). */
    100        private SerialDate serialDate;
    101
    102        /**
    103         * Creates a new instance, derived from the system date/time (and assuming
    104         * the default timezone).
    105         */
    106        public Day() {
    107            this(new Date());
    108        }
    109
    110        /**
    111         * Constructs a new one day time period.
    112         *
    113         * @param day  the day-of-the-month.
    114         * @param month  the month (1 to 12).
    115         * @param year  the year (1900 <= year <= 9999).
    116         */
    117        public Day(int day, int month, int year) {
    118            this.serialDate = SerialDate.createInstance(day, month, year);
    119        }
    120
    121        /**
    122         * Constructs a new one day time period.
    123         *
    124         * @param serialDate  the day (<code>null</code> not permitted).
    125         */
    126        public Day(SerialDate serialDate) {
    127            if (serialDate == null) {
    128                throw new IllegalArgumentException("Null 'serialDate' argument.");
    129            }
    130            this.serialDate = serialDate;
    131        }
    132
    133        /**
    134         * Constructs a new instance, based on a particular date/time and the
    135         * default time zone.
    136         *
    137         * @param time  the time (<code>null</code> not permitted).
    138         */
    139        public Day(Date time) {
    140            // defer argument checking...
    141            this(time, RegularTimePeriod.DEFAULT_TIME_ZONE);
    142        }
    143
    144        /**
    145         * Constructs a new instance, based on a particular date/time and time zone.
    146         *
    147         * @param time  the date/time.
    148         * @param zone  the time zone.
    149         */
    150        public Day(Date time, TimeZone zone) {
    151            if (time == null) {
    152                throw new IllegalArgumentException("Null 'time' argument.");
    153            }
    154            if (zone == null) {
    155                throw new IllegalArgumentException("Null 'zone' argument.");
    156            }
    157            Calendar calendar = Calendar.getInstance(zone);
    158            calendar.setTime(time);
    159            int d = calendar.get(Calendar.DAY_OF_MONTH);
    160            int m = calendar.get(Calendar.MONTH) + 1;
    161            int y = calendar.get(Calendar.YEAR);
    162            this.serialDate = SerialDate.createInstance(d, m, y);
    163        }
    164
    165        /**
    166         * Returns the day as a {@link SerialDate}.  Note: the reference that is
    167         * returned should be an instance of an immutable {@link SerialDate}
    168         * (otherwise the caller could use the reference to alter the state of
    169         * this <code>Day</code> instance, and <code>Day</code> is supposed
    170         * to be immutable).
    171         *
    172         * @return The day as a {@link SerialDate}.
    173         */
    174        public SerialDate getSerialDate() {
    175            return this.serialDate;
    176        }
    177
    178        /**
    179         * Returns the year.
    180         *
    181         * @return The year.
    182         */
    183        public int getYear() {
    184            return this.serialDate.getYYYY();
    185        }
    186
    187        /**
    188         * Returns the month.
    189         *
    190         * @return The month.
    191         */
    192        public int getMonth() {
    193            return this.serialDate.getMonth();
    194        }
    195
    196        /**
    197         * Returns the day of the month.
    198         *
    199         * @return The day of the month.
    200         */
    201        public int getDayOfMonth() {
    202            return this.serialDate.getDayOfMonth();
    203        }
    204
    205        /**
    206         * Returns the day preceding this one.
    207         *
    208         * @return The day preceding this one.
    209         */
    210        public RegularTimePeriod previous() {
    211
    212            Day result;
    213            int serial = this.serialDate.toSerial();
    214            if (serial > SerialDate.SERIAL_LOWER_BOUND) {
    215                SerialDate yesterday = SerialDate.createInstance(serial - 1);
    216                return new Day(yesterday);
    217            }
    218            else {
    219                result = null;
    220            }
    221            return result;
    222
    223        }
    224
    225        /**
    226         * Returns the day following this one, or <code>null</code> if some limit
    227         * has been reached.
    228         *
    229         * @return The day following this one, or <code>null</code> if some limit
    230         *         has been reached.
    231         */
    232        public RegularTimePeriod next() {
    233
    234            Day result;
    235            int serial = this.serialDate.toSerial();
    236            if (serial < SerialDate.SERIAL_UPPER_BOUND) {
    237                SerialDate tomorrow = SerialDate.createInstance(serial + 1);
    238                return new Day(tomorrow);
    239            }
    240            else {
    241                result = null;
    242            }
    243            return result;
    244
    245        }
    246
    247        /**
    248         * Returns a serial index number for the day.
    249         *
    250         * @return The serial index number.
    251         */
    252        public long getSerialIndex() {
    253            return this.serialDate.toSerial();
    254        }
    255
    256        /**
    257         * Returns the first millisecond of the day, evaluated using the supplied
    258         * calendar (which determines the time zone).
    259         *
    260         * @param calendar  calendar to use.
    261         *
    262         * @return The start of the day as milliseconds since 01-01-1970.
    263         */
    264        public long getFirstMillisecond(Calendar calendar) {
    265
    266            int year = this.serialDate.getYYYY();
    267            int month = this.serialDate.getMonth();
    268            int day = this.serialDate.getDayOfMonth();
    269            calendar.clear();
    270            calendar.set(year, month - 1, day, 0, 0, 0);
    271            calendar.set(Calendar.MILLISECOND, 0);
    272            //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
    273            return calendar.getTime().getTime();
    274
    275        }
    276
    277        /**
    278         * Returns the last millisecond of the day, evaluated using the supplied
    279         * calendar (which determines the time zone).
    280         *
    281         * @param calendar  calendar to use.
    282         *
    283         * @return The end of the day as milliseconds since 01-01-1970.
    284         */
    285        public long getLastMillisecond(Calendar calendar) {
    286
    287            int year = this.serialDate.getYYYY();
    288            int month = this.serialDate.getMonth();
    289            int day = this.serialDate.getDayOfMonth();
    290            calendar.clear();
    291            calendar.set(year, month - 1, day, 23, 59, 59);
    292            calendar.set(Calendar.MILLISECOND, 999);
    293            //return calendar.getTimeInMillis();  // this won't work for JDK 1.3
    294            return calendar.getTime().getTime();
    295
    296        }
    297
    298        /**
    299         * Tests the equality of this Day object to an arbitrary object.  Returns
    300         * true if the target is a Day instance or a SerialDate instance
    301         * representing the same day as this object. In all other cases,
    302         * returns false.
    303         *
    304         * @param obj  the object.
    305         *
    306         * @return A flag indicating whether or not an object is equal to this day.
    307         */
    308        public boolean equals(Object obj) {
    309
    310            if (obj == this) {
    311                return true;
    312            }
    313            if (!(obj instanceof Day)) {
    314                return false;
    315            }
    316            Day that = (Day) obj;
    317            if (!this.serialDate.equals(that.getSerialDate())) {
    318                return false;
    319            }
    320            return true;
    321
    322        }
    323
    324        /**
    325         * Returns a hash code for this object instance.  The approach described by
    326         * Joshua Bloch in "Effective Java" has been used here:
    327         * <p>
    328         * <code>http://developer.java.sun.com/developer/Books/effectivejava
    329         * /Chapter3.pdf</code>
    330         *
    331         * @return A hash code.
    332         */
    333        public int hashCode() {
    334            return this.serialDate.hashCode();
    335        }
    336
    337        /**
    338         * Returns an integer indicating the order of this Day object relative to
    339         * the specified object:
    340         *
    341         * negative == before, zero == same, positive == after.
    342         *
    343         * @param o1  the object to compare.
    344         *
    345         * @return negative == before, zero == same, positive == after.
    346         */
    347        public int compareTo(Object o1) {
    348
    349            int result;
    350
    351            // CASE 1 : Comparing to another Day object
    352            // ----------------------------------------
    353            if (o1 instanceof Day) {
    354                Day d = (Day) o1;
    355                result = -d.getSerialDate().compare(this.serialDate);
    356            }
    357
    358            // CASE 2 : Comparing to another TimePeriod object
    359            // -----------------------------------------------
    360            else if (o1 instanceof RegularTimePeriod) {
    361                // more difficult case - evaluate later...
    362                result = 0;
    363            }
    364
    365            // CASE 3 : Comparing to a non-TimePeriod object
    366            // ---------------------------------------------
    367            else {
    368                // consider time periods to be ordered after general objects
    369                result = 1;
    370            }
    371
    372            return result;
    373
    374        }
    375
    376        /**
    377         * Returns a string representing the day.
    378         *
    379         * @return A string representing the day.
    380         */
    381        public String toString() {
    382            return this.serialDate.toString();
    383        }
    384
    385        /**
    386         * Parses the string argument as a day.
    387         * <P>
    388         * This method is required to recognise YYYY-MM-DD as a valid format.
    389         * Anything else, for now, is a bonus.
    390         *
    391         * @param s  the date string to parse.
    392         *
    393         * @return <code>null</code> if the string does not contain any parseable
    394         *      string, the day otherwise.
    395         */
    396        public static Day parseDay(String s) {
    397
    398            try {
    399                return new Day (Day.DATE_FORMAT.parse(s));
    400            }
    401            catch (ParseException e1) {
    402                try {
    403                    return new Day (Day.DATE_FORMAT_SHORT.parse(s));
    404                }
    405                catch (ParseException e2) {
    406                  // ignore
    407                }
    408            }
    409            return null;
    410
    411        }
    412
    413    }