/* * Copyright (C) 2014 Civilian Framework. * * Licensed under the Civilian License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.civilian-framework.org/license.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.civilian.util; import java.util.Calendar; import java.util.GregorianCalendar; import org.civilian.text.DateFormat; /** * A class for storing date values. A date consists of a year value, a month value * (counting from 1 to 12), and a day value (counting from 1 to 31). * Easier to use than java.util.Calendar and not so deprecated as java.util.Date. */ public class Date implements Cloneable, Comparable<Date> { /** * A week day constant for Sunday. Same as java.util.Calendar.SUNDAY */ public final static int WEEKDAY_SUNDAY = Calendar.SUNDAY; /** * A week day constant for Monday. Same as java.util.Calendar.MONDAY */ public final static int WEEKDAY_MONDAY = Calendar.MONDAY; /** * A week day constant for Tuesday. Same as java.util.Calendar.TUESDAY */ public final static int WEEKDAY_TUESDAY = Calendar.TUESDAY; /** * A week day constant for Wednesday. Same as java.util.Calendar.WEDNESDAY */ public final static int WEEKDAY_WEDNESDAY = Calendar.WEDNESDAY; /** * A week day constant for Thursday. Same as java.util.Calendar.THURSDAY */ public final static int WEEKDAY_THURSDAY = Calendar.THURSDAY; /** * A week day constant for Friday. Same as java.util.Calendar.FRIDAY */ public final static int WEEKDAY_FRIDAY = Calendar.FRIDAY; /** * A week day constant for Saturday. Same as java.util.Calendar.SATURDAY */ public final static int WEEKDAY_SATURDAY = Calendar.SATURDAY; //-------------------- // Creation //-------------------- /** * Returns a Date object for the current date. */ public static Date today() { return new Date(); } /** * Creates a Date object for the current date. */ public Date() { this(System.currentTimeMillis()); } /** * Creates a Date object for the given system time. */ public Date(long time) { this(new java.util.Date(time)); } /** * Creates a new Date. * @param year the year (the year 2001 is specified as 2001) * @param month the month (counting from 1 to 12) * @param day the day (counting from 1 to 31) * @exception IllegalArgumentException thrown if the values represent * an invalid date */ public Date(int year, int month, int day) { if (!isValidDate(year, month, day)) throw new IllegalArgumentException("invalid date arguments"); init(year, month, day); } /** * Creates a date from another date. */ public Date(Date date) { init(date.year_, date.month_, date.day_); } /** * Creates a date from a java.util.Date. */ @SuppressWarnings("deprecation") public Date(java.util.Date date) { init(date.getYear() + 1900, date.getMonth() + 1, date.getDate()); } /** * Creates a date from a Calendar. */ public Date(Calendar cal) { init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DATE)); } private void init(int year, int month, int day) { year_ = year; month_ = month; day_ = day; } //-------------------- // Accessors //-------------------- /** * Returns the day value (ranging from 1 to 31). */ public int getDay() { return day_; } /** * Returns the month value (ranging from 1 to 12). */ public int getMonth() { return month_; } /** * Returns the year value. */ public int getYear() { return year_; } /** * Returns the value for the last day of the month of this date (ranging from * 28 to 31). */ public int getLastDayOfMonth() { return getLastDayOfMonth(month_, year_); } /** * Returns the last day of the month for the given month and year (ranging from 28 to 31). * @param month a value between 1 and 12. */ public static int getLastDayOfMonth(int month, int year) { return isLeapYear(year) ? DAYS_IN_MONTH_LEAP[month-1] : DAYS_IN_MONTH_NOLEAP[month-1]; } /** * Tests if the year of this date is a leap year. */ public boolean isLeapYear() { return isLeapYear(getYear()); } /** * Tests if the given year is a leap year. */ public static boolean isLeapYear(int year) { final int gregorianCutoverYear = 1582; if (year >= gregorianCutoverYear) return (year%4 == 0) && ((year%100 != 0) || (year%400 == 0)); // Gregorian else return (year%4 == 0); // Julian } /** * Returns the day of week (ranging from 1 to 7) of this date. * Sunday = 1, Monday = 2, ..., Saturday = 7. * The constants {@link #WEEKDAY_SUNDAY}, ... encode these values. */ public int getDayOfWeek() { return ((toJulianDayNumber() + 1) % 7) + 1; } //-------------------- // Comparison //-------------------- /** * Tests if this date is equal to another date. */ @Override public boolean equals(Object date) { if ((date == null) || !(date instanceof Date)) return false; Date d = (Date) date; return (d.day_ == day_) && (d.month_ == month_) && (d.year_ == year_); } /** * Tests if this date is before the given date. */ public boolean isBefore(Date d) { return compareTo(d) < 0; } /** * Tests if this date is after the given date. */ public boolean isAfter(Date d) { return compareTo(d) > 0; } /** * Compares this date to another date. */ @Override public int compareTo(Date d) { return toInteger() - d.toInteger(); } //-------------------- // Arithmetic //-------------------- /** * Returns a new Date object representing this date * plus the given amount of years. */ public Date addYears(int years) { Date d = new Date(this); d.addYearsInternal(years); return d; } private void addYearsInternal(int years) { year_ += years; correctDayOfMonth(); } /** * Returns a new Date object representing this date plus the given amount of months. */ public Date addMonths(int months) { Date d = new Date(this); d.addMonthInternal(months); return d; } private void addMonthInternal(int months) { int month = month_ + months - 1; if (month >= 0) { year_ += (month / 12); month_ = (month % 12) + 1; } else { year_ += ((month + 1) / 12) - 1; month %= 12; if (month < 0) month += 12; month_ = month + 1; } correctDayOfMonth(); } /** * Returns a new Date object representing this date plus the given amount of days. */ public Date addDays(int days) { if (days == 0) return this; else { Calendar calendar = toCalendar(); calendar.add(Calendar.DATE, days); return new Date(calendar); } } private void correctDayOfMonth() { int lastDay = getLastDayOfMonth(); if (day_ >= lastDay) day_ = lastDay; } /** * Calculates the difference in days between two dates. */ public int difference(Date date) { return toJulianDayNumber() - date.toJulianDayNumber(); } //-------------------- // Validation //-------------------- /** * Tests if the given values represent a valid date. */ public static boolean isValidDate(int year, int month, int day) { if ((month < 1) || (month > 12)) return false; short monthBound[] = isLeapYear(year) ? DAYS_IN_MONTH_LEAP : DAYS_IN_MONTH_NOLEAP; return (day >= 1) && (day <= monthBound[month - 1]); } //-------------------- // Conversion //-------------------- /** * Returns a string representation of this date in the form yyyyMMdd. * This serves for debug purpose only. To get a locale dependent string * use the DateFormat class. * @see DateFormat */ @Override public String toString() { return StringUtil.fillLeft(String.valueOf(toInteger()), 8, '0'); } /** * Converts the date to an Calendar. */ public Calendar toCalendar() { return new GregorianCalendar(year_, month_ - 1, day_); } /** * Converts the date to an java.util.Date. */ public java.util.Date toJavaDate() { return toCalendar().getTime(); } /** * Converts the date to an java.sql.Date. */ public java.sql.Date toJavaSqlDate() { return new java.sql.Date(toJavaDate().getTime()); } /** * Returns an integer representation of this date, in the form yyyymmdd */ public int toInteger() { return toInteger(year_, month_, day_); } /** * Returns an integer representation of the given date values. */ public static int toInteger(int year, int month, int day) { return 10000 * year + 100 * month + day; } /** * Parses a date from an integer. * @see #toInteger */ public static Date fromInteger(int value) { int year = value / 10000; value %= 10000; int month = value / 100; int day = value % 100; return new Date(year, month, day); } /** * Converts a date to its Julian Day Number. * The algorithm is described at http://www.capecod.net/~pbaum/date/date0.htm. */ public int toJulianDayNumber() { int z = (month_ < 3) ? year_ - 1 : year_; int f = JULIAN_DAY_MONTH_TABLE[month_ - 1]; return day_ + f + 365*z + (int)Math.floor(z / 4d) - (int)Math.floor(z / 100d) + (int)Math.floor(z / 400d) + 1721119; } //-------------------- // misc //-------------------- /** * Clones this date. */ @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(); } } /** * Returns a hashcode for the date. */ @Override public int hashCode() { return toInteger(); } private int day_; private int month_; private int year_; private static final short DAYS_IN_MONTH_NOLEAP[] = {31,28,31,30,31,30,31,31,30,31,30,31,30,31}; private static final short DAYS_IN_MONTH_LEAP[] = {31,29,31,30,31,30,31,31,30,31,30,31,30,31}; private static final short JULIAN_DAY_MONTH_TABLE[] = {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275 }; }