Represents a date using an integer, in a similar fashion to the implementation in Microsoft Excel. : Date « Date Type « Android






Represents a date using an integer, in a similar fashion to the implementation in Microsoft Excel.

     
package app.test;
/* 
 * AFreeChart : a free chart library for Android(tm) platform.
 *              (based on JFreeChart and JCommon)
 * 
 *
 * (C) Copyright 2010, by Icom Systech Co., Ltd.
 * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
 * 
 * Project Info:
 *    AFreeChart: http://code.google.com/p/afreechart/
 *    JFreeChart: http://www.jfree.org/jfreechart/index.html
 *    JCommon   : http://www.jfree.org/jcommon/index.html
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * [Android is a trademark of Google Inc.]
 *
 * ---------------
 * SerialDate.java
 * ---------------
 * 
 * (C) Copyright 2010, by Icom Systech Co., Ltd.
 *
 * Original Author:  shiraki  (for Icom Systech Co., Ltd);
 * Contributor(s):   Sato Yoshiaki ;
 *                   Niwano Masayoshi;
 *
 * Changes (from 19-Nov-2010)
 * --------------------------
 * 19-Nov-2010 : port JCommon 1.0.16 to Android as "AFreeChart"
 * 
 * ------------- JFreeChart ---------------------------------------------
 * (C) Copyright 2001-2006, by Object Refinery Limited.
 *
 * Original Author:  David Gilbert (for Object Refinery Limited);
 * Contributor(s):   -;
 *
 *
 * Changes (from 11-Oct-2001)
 * --------------------------
 * 11-Oct-2001 : Re-organised the class and moved it to new package 
 *               com.jrefinery.date (DG);
 * 05-Nov-2001 : Added a getDescription() method, and eliminated NotableDate 
 *               class (DG);
 * 12-Nov-2001 : IBD requires setDescription() method, now that NotableDate 
 *               class is gone (DG);  Changed getPreviousDayOfWeek(), 
 *               getFollowingDayOfWeek() and getNearestDayOfWeek() to correct 
 *               bugs (DG);
 * 05-Dec-2001 : Fixed bug in SpreadsheetDate class (DG);
 * 29-May-2002 : Moved the month constants into a separate interface 
 *               (MonthConstants) (DG);
 * 27-Aug-2002 : Fixed bug in addMonths() method, thanks to N???levka Petr (DG);
 * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG);
 * 13-Mar-2003 : Implemented Serializable (DG);
 * 29-May-2003 : Fixed bug in addMonths method (DG);
 * 04-Sep-2003 : Implemented Comparable.  Updated the isInRange javadocs (DG);
 * 05-Jan-2005 : Fixed bug in addYears() method (1096282) (DG);
 * 
 */

import java.io.Serializable;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * Represents a date using an integer, in a similar fashion to the
 * implementation in Microsoft Excel. The range of dates supported is 1-Jan-1900
 * to 31-Dec-9999.
 * <P>
 * Be aware that there is a deliberate bug in Excel that recognises the year
 * 1900 as a leap year when in fact it is not a leap year. You can find more
 * information on the Microsoft website in article Q181370:
 * <P>
 * http://support.microsoft.com/support/kb/articles/Q181/3/70.asp
 * <P>
 * Excel uses the convention that 1-Jan-1900 = 1. This class uses the convention
 * 1-Jan-1900 = 2. The result is that the day number in this class will be
 * different to the Excel figure for January and February 1900...but then Excel
 * adds in an extra day (29-Feb-1900 which does not actually exist!) and from
 * that point forward the day numbers will match.
 * 
 * @author David Gilbert
 */
public class SpreadsheetDate extends SerialDate {

  /** For serialization. */
  private static final long serialVersionUID = -2039586705374454461L;

  /**
   * The day number (1-Jan-1900 = 2, 2-Jan-1900 = 3, ..., 31-Dec-9999 =
   * 2958465).
   */
  private final int serial;

  /** The day of the month (1 to 28, 29, 30 or 31 depending on the month). */
  private final int day;

  /** The month of the year (1 to 12). */
  private final int month;

  /** The year (1900 to 9999). */
  private final int year;

  /**
   * Creates a new date instance.
   * 
   * @param day
   *            the day (in the range 1 to 28/29/30/31).
   * @param month
   *            the month (in the range 1 to 12).
   * @param year
   *            the year (in the range 1900 to 9999).
   */
  public SpreadsheetDate(final int day, final int month, final int year) {

    if ((year >= 1900) && (year <= 9999)) {
      this.year = year;
    } else {
      throw new IllegalArgumentException(
          "The 'year' argument must be in range 1900 to 9999.");
    }

    if ((month >= MonthConstants.JANUARY)
        && (month <= MonthConstants.DECEMBER)) {
      this.month = month;
    } else {
      throw new IllegalArgumentException(
          "The 'month' argument must be in the range 1 to 12.");
    }

    if ((day >= 1) && (day <= SerialDate.lastDayOfMonth(month, year))) {
      this.day = day;
    } else {
      throw new IllegalArgumentException("Invalid 'day' argument.");
    }

    // the serial number needs to be synchronised with the day-month-year...
    this.serial = calcSerial(day, month, year);

  }

  /**
   * Standard constructor - creates a new date object representing the
   * specified day number (which should be in the range 2 to 2958465.
   * 
   * @param serial
   *            the serial number for the day (range: 2 to 2958465).
   */
  public SpreadsheetDate(final int serial) {

    if ((serial >= SERIAL_LOWER_BOUND) && (serial <= SERIAL_UPPER_BOUND)) {
      this.serial = serial;
    } else {
      throw new IllegalArgumentException(
          "SpreadsheetDate: Serial must be in range 2 to 2958465.");
    }

    // the day-month-year needs to be synchronised with the serial number...
    // get the year from the serial date
    final int days = this.serial - SERIAL_LOWER_BOUND;
    // overestimated because we ignored leap days
    final int overestimatedYYYY = 1900 + (days / 365);
    final int leaps = SerialDate.leapYearCount(overestimatedYYYY);
    final int nonleapdays = days - leaps;
    // underestimated because we overestimated years
    int underestimatedYYYY = 1900 + (nonleapdays / 365);

    if (underestimatedYYYY == overestimatedYYYY) {
      this.year = underestimatedYYYY;
    } else {
      int ss1 = calcSerial(1, 1, underestimatedYYYY);
      while (ss1 <= this.serial) {
        underestimatedYYYY = underestimatedYYYY + 1;
        ss1 = calcSerial(1, 1, underestimatedYYYY);
      }
      this.year = underestimatedYYYY - 1;
    }

    final int ss2 = calcSerial(1, 1, this.year);

    int[] daysToEndOfPrecedingMonth = AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH;

    if (isLeapYear(this.year)) {
      daysToEndOfPrecedingMonth = LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH;
    }

    // get the month from the serial date
    int mm = 1;
    int sss = ss2 + daysToEndOfPrecedingMonth[mm] - 1;
    while (sss < this.serial) {
      mm = mm + 1;
      sss = ss2 + daysToEndOfPrecedingMonth[mm] - 1;
    }
    this.month = mm - 1;

    // what's left is d(+1);
    this.day = this.serial - ss2 - daysToEndOfPrecedingMonth[this.month]
        + 1;

  }

  /**
   * Returns the serial number for the date, where 1 January 1900 = 2 (this
   * corresponds, almost, to the numbering system used in Microsoft Excel for
   * Windows and Lotus 1-2-3).
   * 
   * @return The serial number of this date.
   */
  public int toSerial() {
    return this.serial;
  }

  /**
   * Returns a <code>java.util.Date</code> equivalent to this date.
   * 
   * @return The date.
   */
  public Date toDate() {
    final Calendar calendar = Calendar.getInstance();
    calendar.set(getYYYY(), getMonth() - 1, getDayOfMonth(), 0, 0, 0);
    return calendar.getTime();
  }

  /**
   * Returns the year (assume a valid range of 1900 to 9999).
   * 
   * @return The year.
   */
  public int getYYYY() {
    return this.year;
  }

  /**
   * Returns the month (January = 1, February = 2, March = 3).
   * 
   * @return The month of the year.
   */
  public int getMonth() {
    return this.month;
  }

  /**
   * Returns the day of the month.
   * 
   * @return The day of the month.
   */
  public int getDayOfMonth() {
    return this.day;
  }

  /**
   * Returns a code representing the day of the week.
   * <P>
   * The codes are defined in the {@link SerialDate} class as:
   * <code>SUNDAY</code>, <code>MONDAY</code>, <code>TUESDAY</code>,
   * <code>WEDNESDAY</code>, <code>THURSDAY</code>, <code>FRIDAY</code>, and
   * <code>SATURDAY</code>.
   * 
   * @return A code representing the day of the week.
   */
  public int getDayOfWeek() {
    return (this.serial + 6) % 7 + 1;
  }

  /**
   * Tests the equality of this date with an arbitrary object.
   * <P>
   * This method will return true ONLY if the object is an instance of the
   * {@link SerialDate} base class, and it represents the same day as this
   * {@link SpreadsheetDate}.
   * 
   * @param object
   *            the object to compare (<code>null</code> permitted).
   * 
   * @return A boolean.
   */
  public boolean equals(final Object object) {

    if (object instanceof SerialDate) {
      final SerialDate s = (SerialDate) object;
      return (s.toSerial() == this.toSerial());
    } else {
      return false;
    }

  }

  /**
   * Returns a hash code for this object instance.
   * 
   * @return A hash code.
   */
  public int hashCode() {
    return toSerial();
  }

  /**
   * Returns the difference (in days) between this date and the specified
   * 'other' date.
   * 
   * @param other
   *            the date being compared to.
   * 
   * @return The difference (in days) between this date and the specified
   *         'other' date.
   */
  public int compare(final SerialDate other) {
    return this.serial - other.toSerial();
  }

  /**
   * Implements the method required by the Comparable interface.
   * 
   * @param other
   *            the other object (usually another SerialDate).
   * 
   * @return A negative integer, zero, or a positive integer as this object is
   *         less than, equal to, or greater than the specified object.
   */
  public int compareTo(final Object other) {
    return compare((SerialDate) other);
  }

  /**
   * Returns true if this SerialDate represents the same date as the specified
   * SerialDate.
   * 
   * @param other
   *            the date being compared to.
   * 
   * @return <code>true</code> if this SerialDate represents the same date as
   *         the specified SerialDate.
   */
  public boolean isOn(final SerialDate other) {
    return (this.serial == other.toSerial());
  }

  /**
   * Returns true if this SerialDate represents an earlier date compared to
   * the specified SerialDate.
   * 
   * @param other
   *            the date being compared to.
   * 
   * @return <code>true</code> if this SerialDate represents an earlier date
   *         compared to the specified SerialDate.
   */
  public boolean isBefore(final SerialDate other) {
    return (this.serial < other.toSerial());
  }

  /**
   * Returns true if this SerialDate represents the same date as the specified
   * SerialDate.
   * 
   * @param other
   *            the date being compared to.
   * 
   * @return <code>true</code> if this SerialDate represents the same date as
   *         the specified SerialDate.
   */
  public boolean isOnOrBefore(final SerialDate other) {
    return (this.serial <= other.toSerial());
  }

  /**
   * Returns true if this SerialDate represents the same date as the specified
   * SerialDate.
   * 
   * @param other
   *            the date being compared to.
   * 
   * @return <code>true</code> if this SerialDate represents the same date as
   *         the specified SerialDate.
   */
  public boolean isAfter(final SerialDate other) {
    return (this.serial > other.toSerial());
  }

  /**
   * Returns true if this SerialDate represents the same date as the specified
   * SerialDate.
   * 
   * @param other
   *            the date being compared to.
   * 
   * @return <code>true</code> if this SerialDate represents the same date as
   *         the specified SerialDate.
   */
  public boolean isOnOrAfter(final SerialDate other) {
    return (this.serial >= other.toSerial());
  }

  /**
   * Returns <code>true</code> if this {@link SerialDate} is within the
   * specified range (INCLUSIVE). The date order of d1 and d2 is not
   * important.
   * 
   * @param d1
   *            a boundary date for the range.
   * @param d2
   *            the other boundary date for the range.
   * 
   * @return A boolean.
   */
  public boolean isInRange(final SerialDate d1, final SerialDate d2) {
    return isInRange(d1, d2, SerialDate.INCLUDE_BOTH);
  }

  /**
   * Returns true if this SerialDate is within the specified range (caller
   * specifies whether or not the end-points are included). The order of d1
   * and d2 is not important.
   * 
   * @param d1
   *            one boundary date for the range.
   * @param d2
   *            a second boundary date for the range.
   * @param include
   *            a code that controls whether or not the start and end dates
   *            are included in the range.
   * 
   * @return <code>true</code> if this SerialDate is within the specified
   *         range.
   */
  public boolean isInRange(final SerialDate d1, final SerialDate d2,
      final int include) {
    final int s1 = d1.toSerial();
    final int s2 = d2.toSerial();
    final int start = Math.min(s1, s2);
    final int end = Math.max(s1, s2);

    final int s = toSerial();
    if (include == SerialDate.INCLUDE_BOTH) {
      return (s >= start && s <= end);
    } else if (include == SerialDate.INCLUDE_FIRST) {
      return (s >= start && s < end);
    } else if (include == SerialDate.INCLUDE_SECOND) {
      return (s > start && s <= end);
    } else {
      return (s > start && s < end);
    }
  }

  /**
   * Calculate the serial number from the day, month and year.
   * <P>
   * 1-Jan-1900 = 2.
   * 
   * @param d
   *            the day.
   * @param m
   *            the month.
   * @param y
   *            the year.
   * 
   * @return the serial number from the day, month and year.
   */
  private int calcSerial(final int d, final int m, final int y) {
    final int yy = ((y - 1900) * 365) + SerialDate.leapYearCount(y - 1);
    int mm = SerialDate.AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH[m];
    if (m > MonthConstants.FEBRUARY) {
      if (SerialDate.isLeapYear(y)) {
        mm = mm + 1;
      }
    }
    final int dd = d;
    return yy + mm + dd + 1;
  }

}

/**
 * An abstract class that defines our requirements for manipulating dates,
 * without tying down a particular implementation.
 * <P>
 * Requirement 1 : match at least what Excel does for dates; Requirement 2 : the
 * date represented by the class is immutable;
 * <P>
 * Why not just use java.util.Date? We will, when it makes sense. At times,
 * java.util.Date can be *too* precise - it represents an instant in time,
 * accurate to 1/1000th of a second (with the date itself depending on the
 * time-zone). Sometimes we just want to represent a particular day (e.g. 21
 * January 2015) without concerning ourselves about the time of day, or the
 * time-zone, or anything else. That's what we've defined SerialDate for.
 * <P>
 * You can call getInstance() to get a concrete subclass of SerialDate, without
 * worrying about the exact implementation.
 * 
 * @author David Gilbert
 */
abstract class SerialDate implements Comparable, Serializable, MonthConstants {

  /** For serialization. */
  private static final long serialVersionUID = -293716040467423637L;

  /** Date format symbols. */
  public static final DateFormatSymbols DATE_FORMAT_SYMBOLS = new SimpleDateFormat()
      .getDateFormatSymbols();

  /** The serial number for 1 January 1900. */
  public static final int SERIAL_LOWER_BOUND = 2;

  /** The serial number for 31 December 9999. */
  public static final int SERIAL_UPPER_BOUND = 2958465;

  /** The lowest year value supported by this date format. */
  public static final int MINIMUM_YEAR_SUPPORTED = 1900;

  /** The highest year value supported by this date format. */
  public static final int MAXIMUM_YEAR_SUPPORTED = 9999;

  /** Useful constant for Monday. Equivalent to java.util.Calendar.MONDAY. */
  public static final int MONDAY = Calendar.MONDAY;

  /**
   * Useful constant for Tuesday. Equivalent to java.util.Calendar.TUESDAY.
   */
  public static final int TUESDAY = Calendar.TUESDAY;

  /**
   * Useful constant for Wednesday. Equivalent to
   * java.util.Calendar.WEDNESDAY.
   */
  public static final int WEDNESDAY = Calendar.WEDNESDAY;

  /**
   * Useful constant for Thrusday. Equivalent to java.util.Calendar.THURSDAY.
   */
  public static final int THURSDAY = Calendar.THURSDAY;

  /** Useful constant for Friday. Equivalent to java.util.Calendar.FRIDAY. */
  public static final int FRIDAY = Calendar.FRIDAY;

  /**
   * Useful constant for Saturday. Equivalent to java.util.Calendar.SATURDAY.
   */
  public static final int SATURDAY = Calendar.SATURDAY;

  /** Useful constant for Sunday. Equivalent to java.util.Calendar.SUNDAY. */
  public static final int SUNDAY = Calendar.SUNDAY;

  /** The number of days in each month in non leap years. */
  static final int[] LAST_DAY_OF_MONTH = { 0, 31, 28, 31, 30, 31, 30, 31, 31,
      30, 31, 30, 31 };

  /** The number of days in a (non-leap) year up to the end of each month. */
  static final int[] AGGREGATE_DAYS_TO_END_OF_MONTH = { 0, 31, 59, 90, 120,
      151, 181, 212, 243, 273, 304, 334, 365 };

  /** The number of days in a year up to the end of the preceding month. */
  static final int[] AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH = { 0, 0, 31,
      59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };

  /** The number of days in a leap year up to the end of each month. */
  static final int[] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_MONTH = { 0, 31, 60,
      91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };

  /**
   * The number of days in a leap year up to the end of the preceding month.
   */
  static final int[] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH = {
      0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };

  /** A useful constant for referring to the first week in a month. */
  public static final int FIRST_WEEK_IN_MONTH = 1;

  /** A useful constant for referring to the second week in a month. */
  public static final int SECOND_WEEK_IN_MONTH = 2;

  /** A useful constant for referring to the third week in a month. */
  public static final int THIRD_WEEK_IN_MONTH = 3;

  /** A useful constant for referring to the fourth week in a month. */
  public static final int FOURTH_WEEK_IN_MONTH = 4;

  /** A useful constant for referring to the last week in a month. */
  public static final int LAST_WEEK_IN_MONTH = 0;

  /** Useful range constant. */
  public static final int INCLUDE_NONE = 0;

  /** Useful range constant. */
  public static final int INCLUDE_FIRST = 1;

  /** Useful range constant. */
  public static final int INCLUDE_SECOND = 2;

  /** Useful range constant. */
  public static final int INCLUDE_BOTH = 3;

  /**
   * Useful constant for specifying a day of the week relative to a fixed
   * date.
   */
  public static final int PRECEDING = -1;

  /**
   * Useful constant for specifying a day of the week relative to a fixed
   * date.
   */
  public static final int NEAREST = 0;

  /**
   * Useful constant for specifying a day of the week relative to a fixed
   * date.
   */
  public static final int FOLLOWING = 1;

  /** A description for the date. */
  private String description;

  /**
   * Default constructor.
   */
  protected SerialDate() {
  }

  /**
   * Returns <code>true</code> if the supplied integer code represents a valid
   * day-of-the-week, and <code>false</code> otherwise.
   * 
   * @param code
   *            the code being checked for validity.
   * 
   * @return <code>true</code> if the supplied integer code represents a valid
   *         day-of-the-week, and <code>false</code> otherwise.
   */
  public static boolean isValidWeekdayCode(final int code) {

    switch (code) {
    case SUNDAY:
    case MONDAY:
    case TUESDAY:
    case WEDNESDAY:
    case THURSDAY:
    case FRIDAY:
    case SATURDAY:
      return true;
    default:
      return false;
    }

  }

  /**
   * Converts the supplied string to a day of the week.
   * 
   * @param s
   *            a string representing the day of the week.
   * 
   * @return <code>-1</code> if the string is not convertable, the day of the
   *         week otherwise.
   */
  public static int stringToWeekdayCode(String s) {

    final String[] shortWeekdayNames = DATE_FORMAT_SYMBOLS
        .getShortWeekdays();
    final String[] weekDayNames = DATE_FORMAT_SYMBOLS.getWeekdays();

    int result = -1;
    s = s.trim();
    for (int i = 0; i < weekDayNames.length; i++) {
      if (s.equals(shortWeekdayNames[i])) {
        result = i;
        break;
      }
      if (s.equals(weekDayNames[i])) {
        result = i;
        break;
      }
    }
    return result;

  }

  /**
   * Returns a string representing the supplied day-of-the-week.
   * <P>
   * Need to find a better approach.
   * 
   * @param weekday
   *            the day of the week.
   * 
   * @return a string representing the supplied day-of-the-week.
   */
  public static String weekdayCodeToString(final int weekday) {

    final String[] weekdays = DATE_FORMAT_SYMBOLS.getWeekdays();
    return weekdays[weekday];

  }

  /**
   * Returns an array of month names.
   * 
   * @return an array of month names.
   */
  public static String[] getMonths() {

    return getMonths(false);

  }

  /**
   * Returns an array of month names.
   * 
   * @param shortened
   *            a flag indicating that shortened month names should be
   *            returned.
   * 
   * @return an array of month names.
   */
  public static String[] getMonths(final boolean shortened) {

    if (shortened) {
      return DATE_FORMAT_SYMBOLS.getShortMonths();
    } else {
      return DATE_FORMAT_SYMBOLS.getMonths();
    }

  }

  /**
   * Returns true if the supplied integer code represents a valid month.
   * 
   * @param code
   *            the code being checked for validity.
   * 
   * @return <code>true</code> if the supplied integer code represents a valid
   *         month.
   */
  public static boolean isValidMonthCode(final int code) {

    switch (code) {
    case JANUARY:
    case FEBRUARY:
    case MARCH:
    case APRIL:
    case MAY:
    case JUNE:
    case JULY:
    case AUGUST:
    case SEPTEMBER:
    case OCTOBER:
    case NOVEMBER:
    case DECEMBER:
      return true;
    default:
      return false;
    }

  }

  /**
   * Returns the quarter for the specified month.
   * 
   * @param code
   *            the month code (1-12).
   * 
   * @return the quarter that the month belongs to.
   */
  public static int monthCodeToQuarter(final int code) {

    switch (code) {
    case JANUARY:
    case FEBRUARY:
    case MARCH:
      return 1;
    case APRIL:
    case MAY:
    case JUNE:
      return 2;
    case JULY:
    case AUGUST:
    case SEPTEMBER:
      return 3;
    case OCTOBER:
    case NOVEMBER:
    case DECEMBER:
      return 4;
    default:
      throw new IllegalArgumentException(
          "SerialDate.monthCodeToQuarter: invalid month code.");
    }

  }

  /**
   * Returns a string representing the supplied month.
   * <P>
   * The string returned is the long form of the month name taken from the
   * default locale.
   * 
   * @param month
   *            the month.
   * 
   * @return a string representing the supplied month.
   */
  public static String monthCodeToString(final int month) {

    return monthCodeToString(month, false);

  }

  /**
   * Returns a string representing the supplied month.
   * <P>
   * The string returned is the long or short form of the month name taken
   * from the default locale.
   * 
   * @param month
   *            the month.
   * @param shortened
   *            if <code>true</code> return the abbreviation of the month.
   * 
   * @return a string representing the supplied month.
   */
  public static String monthCodeToString(final int month,
      final boolean shortened) {

    // check arguments...
    if (!isValidMonthCode(month)) {
      throw new IllegalArgumentException(
          "SerialDate.monthCodeToString: month outside valid range.");
    }

    final String[] months;

    if (shortened) {
      months = DATE_FORMAT_SYMBOLS.getShortMonths();
    } else {
      months = DATE_FORMAT_SYMBOLS.getMonths();
    }

    return months[month - 1];

  }

  /**
   * Converts a string to a month code.
   * <P>
   * This method will return one of the constants JANUARY, FEBRUARY, ...,
   * DECEMBER that corresponds to the string. If the string is not recognised,
   * this method returns -1.
   * 
   * @param s
   *            the string to parse.
   * 
   * @return <code>-1</code> if the string is not parseable, the month of the
   *         year otherwise.
   */
  public static int stringToMonthCode(String s) {

    final String[] shortMonthNames = DATE_FORMAT_SYMBOLS.getShortMonths();
    final String[] monthNames = DATE_FORMAT_SYMBOLS.getMonths();

    int result = -1;
    s = s.trim();

    // first try parsing the string as an integer (1-12)...
    try {
      result = Integer.parseInt(s);
    } catch (NumberFormatException e) {
      // suppress
    }

    // now search through the month names...
    if ((result < 1) || (result > 12)) {
      for (int i = 0; i < monthNames.length; i++) {
        if (s.equals(shortMonthNames[i])) {
          result = i + 1;
          break;
        }
        if (s.equals(monthNames[i])) {
          result = i + 1;
          break;
        }
      }
    }

    return result;

  }

  /**
   * Returns true if the supplied integer code represents a valid
   * week-in-the-month, and false otherwise.
   * 
   * @param code
   *            the code being checked for validity.
   * @return <code>true</code> if the supplied integer code represents a valid
   *         week-in-the-month.
   */
  public static boolean isValidWeekInMonthCode(final int code) {

    switch (code) {
    case FIRST_WEEK_IN_MONTH:
    case SECOND_WEEK_IN_MONTH:
    case THIRD_WEEK_IN_MONTH:
    case FOURTH_WEEK_IN_MONTH:
    case LAST_WEEK_IN_MONTH:
      return true;
    default:
      return false;
    }

  }

  /**
   * Determines whether or not the specified year is a leap year.
   * 
   * @param yyyy
   *            the year (in the range 1900 to 9999).
   * 
   * @return <code>true</code> if the specified year is a leap year.
   */
  public static boolean isLeapYear(final int yyyy) {

    if ((yyyy % 4) != 0) {
      return false;
    } else if ((yyyy % 400) == 0) {
      return true;
    } else if ((yyyy % 100) == 0) {
      return false;
    } else {
      return true;
    }

  }

  /**
   * Returns the number of leap years from 1900 to the specified year
   * INCLUSIVE.
   * <P>
   * Note that 1900 is not a leap year.
   * 
   * @param yyyy
   *            the year (in the range 1900 to 9999).
   * 
   * @return the number of leap years from 1900 to the specified year.
   */
  public static int leapYearCount(final int yyyy) {

    final int leap4 = (yyyy - 1896) / 4;
    final int leap100 = (yyyy - 1800) / 100;
    final int leap400 = (yyyy - 1600) / 400;
    return leap4 - leap100 + leap400;

  }

  /**
   * Returns the number of the last day of the month, taking into account leap
   * years.
   * 
   * @param month
   *            the month.
   * @param yyyy
   *            the year (in the range 1900 to 9999).
   * 
   * @return the number of the last day of the month.
   */
  public static int lastDayOfMonth(final int month, final int yyyy) {

    final int result = LAST_DAY_OF_MONTH[month];
    if (month != FEBRUARY) {
      return result;
    } else if (isLeapYear(yyyy)) {
      return result + 1;
    } else {
      return result;
    }

  }

  /**
   * Creates a new date by adding the specified number of days to the base
   * date.
   * 
   * @param days
   *            the number of days to add (can be negative).
   * @param base
   *            the base date.
   * 
   * @return a new date.
   */
  public static SerialDate addDays(final int days, final SerialDate base) {

    final int serialDayNumber = base.toSerial() + days;
    return SerialDate.createInstance(serialDayNumber);

  }

  /**
   * Creates a new date by adding the specified number of months to the base
   * date.
   * <P>
   * If the base date is close to the end of the month, the day on the result
   * may be adjusted slightly: 31 May + 1 month = 30 June.
   * 
   * @param months
   *            the number of months to add (can be negative).
   * @param base
   *            the base date.
   * 
   * @return a new date.
   */
  public static SerialDate addMonths(final int months, final SerialDate base) {

    final int yy = (12 * base.getYYYY() + base.getMonth() + months - 1) / 12;
    final int mm = (12 * base.getYYYY() + base.getMonth() + months - 1) % 12 + 1;
    final int dd = Math.min(base.getDayOfMonth(),
        SerialDate.lastDayOfMonth(mm, yy));
    return SerialDate.createInstance(dd, mm, yy);

  }

  /**
   * Creates a new date by adding the specified number of years to the base
   * date.
   * 
   * @param years
   *            the number of years to add (can be negative).
   * @param base
   *            the base date.
   * 
   * @return A new date.
   */
  public static SerialDate addYears(final int years, final SerialDate base) {

    final int baseY = base.getYYYY();
    final int baseM = base.getMonth();
    final int baseD = base.getDayOfMonth();

    final int targetY = baseY + years;
    final int targetD = Math.min(baseD,
        SerialDate.lastDayOfMonth(baseM, targetY));

    return SerialDate.createInstance(targetD, baseM, targetY);

  }

  /**
   * Returns the latest date that falls on the specified day-of-the-week and
   * is BEFORE the base date.
   * 
   * @param targetWeekday
   *            a code for the target day-of-the-week.
   * @param base
   *            the base date.
   * 
   * @return the latest date that falls on the specified day-of-the-week and
   *         is BEFORE the base date.
   */
  public static SerialDate getPreviousDayOfWeek(final int targetWeekday,
      final SerialDate base) {

    // check arguments...
    if (!SerialDate.isValidWeekdayCode(targetWeekday)) {
      throw new IllegalArgumentException("Invalid day-of-the-week code.");
    }

    // find the date...
    final int adjust;
    final int baseDOW = base.getDayOfWeek();
    if (baseDOW > targetWeekday) {
      adjust = Math.min(0, targetWeekday - baseDOW);
    } else {
      adjust = -7 + Math.max(0, targetWeekday - baseDOW);
    }

    return SerialDate.addDays(adjust, base);

  }

  /**
   * Returns the earliest date that falls on the specified day-of-the-week and
   * is AFTER the base date.
   * 
   * @param targetWeekday
   *            a code for the target day-of-the-week.
   * @param base
   *            the base date.
   * 
   * @return the earliest date that falls on the specified day-of-the-week and
   *         is AFTER the base date.
   */
  public static SerialDate getFollowingDayOfWeek(final int targetWeekday,
      final SerialDate base) {

    // check arguments...
    if (!SerialDate.isValidWeekdayCode(targetWeekday)) {
      throw new IllegalArgumentException("Invalid day-of-the-week code.");
    }

    // find the date...
    final int adjust;
    final int baseDOW = base.getDayOfWeek();
    if (baseDOW > targetWeekday) {
      adjust = 7 + Math.min(0, targetWeekday - baseDOW);
    } else {
      adjust = Math.max(0, targetWeekday - baseDOW);
    }

    return SerialDate.addDays(adjust, base);
  }

  /**
   * Returns the date that falls on the specified day-of-the-week and is
   * CLOSEST to the base date.
   * 
   * @param targetDOW
   *            a code for the target day-of-the-week.
   * @param base
   *            the base date.
   * 
   * @return the date that falls on the specified day-of-the-week and is
   *         CLOSEST to the base date.
   */
  public static SerialDate getNearestDayOfWeek(final int targetDOW,
      final SerialDate base) {

    // check arguments...
    if (!SerialDate.isValidWeekdayCode(targetDOW)) {
      throw new IllegalArgumentException("Invalid day-of-the-week code.");
    }

    // find the date...
    final int baseDOW = base.getDayOfWeek();
    int adjust = -Math.abs(targetDOW - baseDOW);
    if (adjust >= 4) {
      adjust = 7 - adjust;
    }
    if (adjust <= -4) {
      adjust = 7 + adjust;
    }
    return SerialDate.addDays(adjust, base);

  }

  /**
   * Rolls the date forward to the last day of the month.
   * 
   * @param base
   *            the base date.
   * 
   * @return a new serial date.
   */
  public SerialDate getEndOfCurrentMonth(final SerialDate base) {
    final int last = SerialDate.lastDayOfMonth(base.getMonth(),
        base.getYYYY());
    return SerialDate.createInstance(last, base.getMonth(), base.getYYYY());
  }

  /**
   * Returns a string corresponding to the week-in-the-month code.
   * <P>
   * Need to find a better approach.
   * 
   * @param count
   *            an integer code representing the week-in-the-month.
   * 
   * @return a string corresponding to the week-in-the-month code.
   */
  public static String weekInMonthToString(final int count) {

    switch (count) {
    case SerialDate.FIRST_WEEK_IN_MONTH:
      return "First";
    case SerialDate.SECOND_WEEK_IN_MONTH:
      return "Second";
    case SerialDate.THIRD_WEEK_IN_MONTH:
      return "Third";
    case SerialDate.FOURTH_WEEK_IN_MONTH:
      return "Fourth";
    case SerialDate.LAST_WEEK_IN_MONTH:
      return "Last";
    default:
      return "SerialDate.weekInMonthToString(): invalid code.";
    }

  }

  /**
   * Returns a string representing the supplied 'relative'.
   * <P>
   * Need to find a better approach.
   * 
   * @param relative
   *            a constant representing the 'relative'.
   * 
   * @return a string representing the supplied 'relative'.
   */
  public static String relativeToString(final int relative) {

    switch (relative) {
    case SerialDate.PRECEDING:
      return "Preceding";
    case SerialDate.NEAREST:
      return "Nearest";
    case SerialDate.FOLLOWING:
      return "Following";
    default:
      return "ERROR : Relative To String";
    }

  }

  /**
   * Factory method that returns an instance of some concrete subclass of
   * {@link SerialDate}.
   * 
   * @param day
   *            the day (1-31).
   * @param month
   *            the month (1-12).
   * @param yyyy
   *            the year (in the range 1900 to 9999).
   * 
   * @return An instance of {@link SerialDate}.
   */
  public static SerialDate createInstance(final int day, final int month,
      final int yyyy) {
    return new SpreadsheetDate(day, month, yyyy);
  }

  /**
   * Factory method that returns an instance of some concrete subclass of
   * {@link SerialDate}.
   * 
   * @param serial
   *            the serial number for the day (1 January 1900 = 2).
   * 
   * @return a instance of SerialDate.
   */
  public static SerialDate createInstance(final int serial) {
    return new SpreadsheetDate(serial);
  }

  /**
   * Factory method that returns an instance of a subclass of SerialDate.
   * 
   * @param date
   *            A Java date object.
   * 
   * @return a instance of SerialDate.
   */
  public static SerialDate createInstance(final java.util.Date date) {

    final GregorianCalendar calendar = new GregorianCalendar();
    calendar.setTime(date);
    return new SpreadsheetDate(calendar.get(Calendar.DATE),
        calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.YEAR));

  }

  /**
   * Returns the serial number for the date, where 1 January 1900 = 2 (this
   * corresponds, almost, to the numbering system used in Microsoft Excel for
   * Windows and Lotus 1-2-3).
   * 
   * @return the serial number for the date.
   */
  public abstract int toSerial();

  /**
   * Returns a java.util.Date. Since java.util.Date has more precision than
   * SerialDate, we need to define a convention for the 'time of day'.
   * 
   * @return this as <code>java.util.Date</code>.
   */
  public abstract java.util.Date toDate();

  /**
   * Returns the description that is attached to the date. It is not required
   * that a date have a description, but for some applications it is useful.
   * 
   * @return The description (possibly <code>null</code>).
   */
  public String getDescription() {
    return this.description;
  }

  /**
   * Sets the description for the date.
   * 
   * @param description
   *            the description for this date (<code>null</code> permitted).
   */
  public void setDescription(final String description) {
    this.description = description;
  }

  /**
   * Converts the date to a string.
   * 
   * @return a string representation of the date.
   */
  public String toString() {
    return getDayOfMonth() + "-" + SerialDate.monthCodeToString(getMonth())
        + "-" + getYYYY();
  }

  /**
   * Returns the year (assume a valid range of 1900 to 9999).
   * 
   * @return the year.
   */
  public abstract int getYYYY();

  /**
   * Returns the month (January = 1, February = 2, March = 3).
   * 
   * @return the month of the year.
   */
  public abstract int getMonth();

  /**
   * Returns the day of the month.
   * 
   * @return the day of the month.
   */
  public abstract int getDayOfMonth();

  /**
   * Returns the day of the week.
   * 
   * @return the day of the week.
   */
  public abstract int getDayOfWeek();

  /**
   * Returns the difference (in days) between this date and the specified
   * 'other' date.
   * <P>
   * The result is positive if this date is after the 'other' date and
   * negative if it is before the 'other' date.
   * 
   * @param other
   *            the date being compared to.
   * 
   * @return the difference between this and the other date.
   */
  public abstract int compare(SerialDate other);

  /**
   * Returns true if this SerialDate represents the same date as the specified
   * SerialDate.
   * 
   * @param other
   *            the date being compared to.
   * 
   * @return <code>true</code> if this SerialDate represents the same date as
   *         the specified SerialDate.
   */
  public abstract boolean isOn(SerialDate other);

  /**
   * Returns true if this SerialDate represents an earlier date compared to
   * the specified SerialDate.
   * 
   * @param other
   *            The date being compared to.
   * 
   * @return <code>true</code> if this SerialDate represents an earlier date
   *         compared to the specified SerialDate.
   */
  public abstract boolean isBefore(SerialDate other);

  /**
   * Returns true if this SerialDate represents the same date as the specified
   * SerialDate.
   * 
   * @param other
   *            the date being compared to.
   * 
   * @return <code>true<code> if this SerialDate represents the same date
   *         as the specified SerialDate.
   */
  public abstract boolean isOnOrBefore(SerialDate other);

  /**
   * Returns true if this SerialDate represents the same date as the specified
   * SerialDate.
   * 
   * @param other
   *            the date being compared to.
   * 
   * @return <code>true</code> if this SerialDate represents the same date as
   *         the specified SerialDate.
   */
  public abstract boolean isAfter(SerialDate other);

  /**
   * Returns true if this SerialDate represents the same date as the specified
   * SerialDate.
   * 
   * @param other
   *            the date being compared to.
   * 
   * @return <code>true</code> if this SerialDate represents the same date as
   *         the specified SerialDate.
   */
  public abstract boolean isOnOrAfter(SerialDate other);

  /**
   * Returns <code>true</code> if this {@link SerialDate} is within the
   * specified range (INCLUSIVE). The date order of d1 and d2 is not
   * important.
   * 
   * @param d1
   *            a boundary date for the range.
   * @param d2
   *            the other boundary date for the range.
   * 
   * @return A boolean.
   */
  public abstract boolean isInRange(SerialDate d1, SerialDate d2);

  /**
   * Returns <code>true</code> if this {@link SerialDate} is within the
   * specified range (caller specifies whether or not the end-points are
   * included). The date order of d1 and d2 is not important.
   * 
   * @param d1
   *            a boundary date for the range.
   * @param d2
   *            the other boundary date for the range.
   * @param include
   *            a code that controls whether or not the start and end dates
   *            are included in the range.
   * 
   * @return A boolean.
   */
  public abstract boolean isInRange(SerialDate d1, SerialDate d2, int include);

  /**
   * Returns the latest date that falls on the specified day-of-the-week and
   * is BEFORE this date.
   * 
   * @param targetDOW
   *            a code for the target day-of-the-week.
   * 
   * @return the latest date that falls on the specified day-of-the-week and
   *         is BEFORE this date.
   */
  public SerialDate getPreviousDayOfWeek(final int targetDOW) {
    return getPreviousDayOfWeek(targetDOW, this);
  }

  /**
   * Returns the earliest date that falls on the specified day-of-the-week and
   * is AFTER this date.
   * 
   * @param targetDOW
   *            a code for the target day-of-the-week.
   * 
   * @return the earliest date that falls on the specified day-of-the-week and
   *         is AFTER this date.
   */
  public SerialDate getFollowingDayOfWeek(final int targetDOW) {
    return getFollowingDayOfWeek(targetDOW, this);
  }

  /**
   * Returns the nearest date that falls on the specified day-of-the-week.
   * 
   * @param targetDOW
   *            a code for the target day-of-the-week.
   * 
   * @return the nearest date that falls on the specified day-of-the-week.
   */
  public SerialDate getNearestDayOfWeek(final int targetDOW) {
    return getNearestDayOfWeek(targetDOW, this);
  }

}

/**
 * Useful constants for months.  Note that these are NOT equivalent to the
 * constants defined by java.util.Calendar (where JANUARY=0 and DECEMBER=11).
 * <P>
 * Used by the SerialDate and RegularTimePeriod classes.
 *
 * @author David Gilbert
 */
 interface MonthConstants {

    /** Constant for January. */
    public static final int JANUARY = 1;

    /** Constant for February. */
    public static final int FEBRUARY = 2;

    /** Constant for March. */
    public static final int MARCH = 3;

    /** Constant for April. */
    public static final int APRIL = 4;

    /** Constant for May. */
    public static final int MAY = 5;

    /** Constant for June. */
    public static final int JUNE = 6;

    /** Constant for July. */
    public static final int JULY = 7;

    /** Constant for August. */
    public static final int AUGUST = 8;

    /** Constant for September. */
    public static final int SEPTEMBER = 9;

    /** Constant for October. */
    public static final int OCTOBER = 10;

    /** Constant for November. */
    public static final int NOVEMBER = 11;

    /** Constant for December. */
    public static final int DECEMBER = 12;

}

   
    
    
    
    
  








Related examples in the same category

1.Write Date to AudioTrack
2.A formatter that formats dates to show the elapsed time relative to some base date.
3.Iso Date
4.Get the string of the date using format "yyyy-MM-dd HH:mm:ss"
5.Parse a string that contains date, return a date object using format "yyyy-MM-dd HH:mm:ss"
6.Create Date from timestamp
7.Convert date in RFC2822 and UTC strings, and to build Date from string of dates in RFC2822 and UTC format
8.Gets a "HH:mm:ss.SSS EEE dd MMM yyyy" representation of a Date
9.Gets a "yyyy MMM dd, HH:mm:ss.SSS" representation of a Date
10.Gets a "EEE, dd MMM yyyy hh:mm:ss 'GMT'" representation of a Date
11.Parses a String for a "EEE, dd MMM yyyy hh:mm:ss 'GMT'" formatted Date
12.Formatting and parsing the various date formats we expect to encounter.
13.ISO8601, ISO8601, RFC822 Date format
14.Easter Date
15.Parse Date
16.compare Two dates with Day value
17.compare Two dates with Second value
18.Get start of a date
19.Get start date of a Month
20.Get start date of a year
21.Create date from year, month and day value
22.Create Date from year, month, day, hour, minute, second
23.Get year value from Date
24.Get Month value from Date
25.Get Day value from Date
26.Get now in Date and Millis-seconds
27.Add day, Month and year to a Date
28.Parse date in format of yyyyMMddHHmmss or yyyyMMdd
29.String to Date
30.Convert date value in long to YYYYMMDDHHMMSS format
31.Convert Date to Number
32.Get short and long date String
33.Convert Java Date To Xml Time
34.Convert dates to Julian dates.
35.Parse an RFC 822 date string.
36.Format a date into a format suitable for SQLite
37.Dot String to Date
38.Parses an RFC822 formatted date string.
39.Formats a Date according to RFC822.
40.DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(date);
41.yyyy-MM-dd HH:mm:ss date format
42.Utility class for formatting and parsing the various date formats we expect to encounter.
43.Get a string representation of a date as it relates to the current time.
44.get Date String from milliseconds
45.Generate a ISO 8601 date
46.Generate a Calendar from ISO 8601 date
47.parse Date for list of possible formats
48.date To String
49.Get date string for Locale tr
50.RFC3339 Date
51.Date formats
52.build Date Format day-of-week Short
53.Get end of each day
54.Get the end of each Month
55.Get end of a year
56.calculate Month Distance
57.calculate Day Distance
58.The month, and just the month, is zero-based. Add 1 for display.
59.Get hour different
60.format Millis Into Human Readable