Dates.java

package com.tradecloud.domain.common;

import org.apache.commons.lang.StringUtils;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Utility class for routines related to dates (and to java.util.Date and
 * Calendar in particular).
 * <p>
 * Ronan - March 7th. I just brought this in from BB as is. We can tidy or
 * remove methods as required. I just thought there might be some useful stuff
 * in here. You never know!!
 */
public class Dates {

    // Extremes
    public static final Date FIRST_DATE = new Date(Long.MIN_VALUE);
    public static final Date LAST_DATE = new Date(Long.MAX_VALUE);

    private static final SimpleDateFormat FULL_DAY_OF_WEEK_FORMAT = new SimpleDateFormat("EEEE");
    private static final SimpleDateFormat ABBR_DAY_OF_WEEK_FORMAT = new SimpleDateFormat("E");

    private static final DateFormat DEF_SHORT_DATE_FORMAT = DateFormat.getDateInstance(DateFormat.SHORT);

    private static final SimpleDateFormat ZA_LONG_DATE_FORMAT = new SimpleDateFormat("d MMMM yyyy");
    private static final SimpleDateFormat ZA_YMD_FORMAT = new SimpleDateFormat("yyyy/MM/dd");
    private static final SimpleDateFormat ZA_YMDHMS_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    private static final SimpleDateFormat ZA_HMS_FORMAT = new SimpleDateFormat("HH:mm:ss");
    private static final SimpleDateFormat ZA_HM_FORMAT = new SimpleDateFormat("HH:mm");

    /*
     * Used only as a class-level singleton, so prevent instantiation.
     */
    private Dates() {
    }

    /**
     * Returns an array containing two <code>Date</code> objects: the 1st of
     * which contains a date representing the first millisecond; and the 2nd of
     * which contains a date representing the last millisecond of the day
     * represented by the given date.
     */
    public static Date[] getStartAndEndOfDay(Date date) {

        Date[] results = new Date[2];
        if (date == null)
            return results;

        Calendar cal = new GregorianCalendar();
        cal.setTime(date);

        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        results[0] = cal.getTime();

        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 999);
        results[1] = cal.getTime();

        return results;
    }

    public static Date getStartOfDay(Date date) {
        if (date == null)
            return null;
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    public static Date getEndOfDay(Date date) {
        if (date == null)
            return null;
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 999);
        return cal.getTime();
    }

    public static void setToStartOfDay(Date date) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        date.setTime(cal.getTime().getTime());
    }

    public static void setToEndOfDay(Date date) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);

        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 999);

        date.setTime(cal.getTime().getTime());
    }

    /**
     * Truncates the time part to the level specified. Usefull if comparison to
     * these levels are needed.
     *
     * @param date          object to be truncated
     * @param calendarField to what accuracy the Date object should be trucated.
     */
    public static void truncateTimeTo(Date date, int calendarField) {
        Calendar cal = new GregorianCalendar();

        cal.setTime(date);

        switch (calendarField) {
            case Calendar.DAY_OF_YEAR:
                cal.set(Calendar.HOUR_OF_DAY, 0);
            case Calendar.HOUR:
                cal.set(Calendar.MINUTE, 0);
            case Calendar.MINUTE:
                cal.set(Calendar.SECOND, 0);
            case Calendar.SECOND:
                cal.set(Calendar.MILLISECOND, 0);
        }

        date.setTime(cal.getTime().getTime());
    }

    public static java.sql.Date toSQLDate(Date date) {
        if (date == null)
            return null;
        if (date instanceof java.sql.Date)
            return (java.sql.Date) date;
        else
            return new java.sql.Date(date.getTime());
    }

    public static java.sql.Time toSQLTime(Date date) {
        if (date == null)
            return null;
        if (date instanceof java.sql.Time)
            return (java.sql.Time) date;
        else
            return new java.sql.Time(date.getTime());
    }

    public static java.sql.Timestamp toSQLTimestamp(Date date) {
        if (date == null)
            return null;
        if (date instanceof java.sql.Timestamp)
            return (java.sql.Timestamp) date;
        else
            return new java.sql.Timestamp(date.getTime());
    }

    /**
     * Calculates the difference in days between the given start and end dates.
     */
    public static int getDaysBetween(Date first, Date last) {
        if (first == null || last == null)
            throw new IllegalArgumentException("Neither first nor last date can be null.");
        Calendar calFirst = new GregorianCalendar();
        calFirst.setTime(first);
        Calendar calLast = new GregorianCalendar();
        calLast.setTime(last);
        int doyFirst, doyLast, yFirst, yLast;
        int sign;
        if (first.after(last)) {
            sign = -1;
            yFirst = calLast.get(Calendar.YEAR);
            doyFirst = calLast.get(Calendar.DAY_OF_YEAR);
            yLast = calFirst.get(Calendar.YEAR);
            doyLast = calFirst.get(Calendar.DAY_OF_YEAR);
        } else {
            sign = 1;
            yFirst = calFirst.get(Calendar.YEAR);
            doyFirst = calFirst.get(Calendar.DAY_OF_YEAR);
            yLast = calLast.get(Calendar.YEAR);
            doyLast = calLast.get(Calendar.DAY_OF_YEAR);
        }

        if (yFirst == yLast) {
            return sign * (doyLast - doyFirst);
        } else {
            int days = doyLast;
            int year = yLast - 1;
            do {
                days += getDaysInYear(year--);
            } while (year >= yFirst);
            return sign * (days - doyFirst);
        }

    }

    public static long getDaysDifferenceBetweenDates(Date beginningDate, Date endDate) {
        if (beginningDate == null && endDate == null) {
            return 0;
        }

        Calendar date1 = Calendar.getInstance();
        date1.setTime(beginningDate);
        // need to add a day as the calculations should include the current day.
        date1.add(Calendar.DAY_OF_MONTH, -1);

        Calendar date2 = Calendar.getInstance();
        date2.setTime(endDate);
        // need to add a day as the calculations should include the current day.
        date2.add(Calendar.DAY_OF_MONTH, 1);

        long timeDiffInMillis = date2.getTimeInMillis() - date1.getTimeInMillis();
        int totalHours = 24;
        int totalMinutes = 60;
        int totalSecs = 60;
        int totalMilliSecs = 1000;
        long diffDays = timeDiffInMillis / (totalHours * totalMinutes * totalSecs * totalMilliSecs);
        if (beginningDate.after(endDate))
            diffDays = 0;

        return diffDays;
    }

    public static int getDaysInYear(int year) {
        Calendar cal = new GregorianCalendar(year, Calendar.DECEMBER, 31);
        return cal.get(Calendar.DAY_OF_YEAR);
    }

    public static Date addDays(Date date, int days) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.add(Calendar.DATE, days);
        return cal.getTime();
    }

    public static Date add(Date date, int calendarField, int amount) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.add(calendarField, amount);
        return cal.getTime();
    }

    public static boolean fallsBetween(Date date, Date startDate, Date endDate) {
        if (date == null)
            return false;
        return date.equals(startDate) || date.equals(endDate) || (date.after(startDate) && date.before(endDate));
    }

    public static boolean isWeekDay(Date date) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        int dow = cal.get(Calendar.DAY_OF_WEEK);
        return dow != Calendar.SATURDAY && dow != Calendar.SUNDAY;
    }

    public static boolean isWeekDay(Date date, Locale locale, TimeZone timeZone) {
        Calendar cal = new GregorianCalendar(timeZone, locale);
        cal.setTime(date);
        int dow = cal.get(Calendar.DAY_OF_WEEK);
        return dow != Calendar.SATURDAY && dow != Calendar.SUNDAY;
    }

    public static int getCurrentYear() {
        return new GregorianCalendar().get(Calendar.YEAR);
    }

    public static int getCurrentMonth() {
        return new GregorianCalendar().get(Calendar.MONTH);
    }

    public static int getCurrentDay() {
        return new GregorianCalendar().get(Calendar.DAY_OF_MONTH);
    }

    public static int[] getYearMonthDay(Date date) {
        int[] ymd = new int[3];
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        ymd[0] = cal.get(Calendar.YEAR);
        ymd[1] = cal.get(Calendar.MONTH);
        ymd[2] = cal.get(Calendar.DAY_OF_MONTH);
        return ymd;
    }

    public static String[] getZeroPaddedYearMonthDay(Date date) {
        String[] ymd = new String[3];
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);

        ymd[0] = "" + cal.get(Calendar.YEAR);
        int m = cal.get(Calendar.MONTH) + 1;
        if (m < 10)
            ymd[1] = "0" + m;
        else
            ymd[1] = "" + m;
        int d = cal.get(Calendar.DAY_OF_MONTH);
        if (d < 10)
            ymd[2] = "0" + d;
        else
            ymd[2] = "" + d;

        return ymd;
    }

    public static int[] getHourMinuteSecond(Date date) {
        int[] hms = new int[3];
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        hms[0] = cal.get(Calendar.HOUR_OF_DAY);
        hms[1] = cal.get(Calendar.MINUTE);
        hms[2] = cal.get(Calendar.SECOND);
        return hms;
    }

    public static int[] getYMDHMS(Date date) {
        int[] ymdhms = new int[6];
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        ymdhms[0] = cal.get(Calendar.YEAR);
        ymdhms[1] = cal.get(Calendar.MONTH);
        ymdhms[2] = cal.get(Calendar.DAY_OF_MONTH);
        ymdhms[3] = cal.get(Calendar.HOUR_OF_DAY);
        ymdhms[4] = cal.get(Calendar.MINUTE);
        ymdhms[5] = cal.get(Calendar.SECOND);
        return ymdhms;
    }

    public static String[] getMonths() {
        SimpleDateFormat formatter = new SimpleDateFormat();
        return formatter.getDateFormatSymbols().getMonths();
    }

    public static int getLastDayOfMonth(Date date) {
        // Set to the first of the given month
        Calendar lastDayOfMonth = new GregorianCalendar();
        lastDayOfMonth.setTime(date);
        lastDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);

        // Roll back around to end of same month
        lastDayOfMonth.roll(Calendar.DAY_OF_MONTH, false);
        // or alternatively:
        // lastDayOfMonth.add( Calendar.MONTH, 1 );
        // lastDayOfMonth.add( Calendar.DAY_OF_MONTH, -1 );

        return lastDayOfMonth.get(Calendar.DAY_OF_MONTH);
    }

    public static int getLastDayOfMonth(int year, int month) {
        // Set to the first of the given month
        GregorianCalendar lastDayOfMonth = new GregorianCalendar(year, month, 1);

        // Roll back around to end of same month
        lastDayOfMonth.roll(Calendar.DAY_OF_MONTH, false);
        // or alternatively:
        // lastDayOfMonth.add( Calendar.MONTH, 1 );
        // lastDayOfMonth.add( Calendar.DAY_OF_MONTH, -1 );

        return lastDayOfMonth.get(Calendar.DAY_OF_MONTH);
    }

    public static int getDayOfWeek(Date date) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        return cal.get(Calendar.DAY_OF_WEEK);
    }

    public static char getDayOfWeek1stLetterOnly(Date date) {
        String dow = getDayOfWeekAsString(date);
        if (dow == null)
            return '\0';
        return Character.toUpperCase(dow.charAt(0));
    }

    public static String getDayOfWeekAbbreviated(Date date) {
        return ABBR_DAY_OF_WEEK_FORMAT.format(date);
    }

    public static String getDayOfWeekAsString(Date date) {
        return FULL_DAY_OF_WEEK_FORMAT.format(date);
    }

    public static String toShortFormat(Date date) {
        if (date == null)
            return null;
        return DEF_SHORT_DATE_FORMAT.format(date);
    }

    public static String toZALongFormat(Date date) {
        if (date == null)
            return null;
        return ZA_LONG_DATE_FORMAT.format(date);
    }

    public static String toZA_YMDFormat(Date date) {
        if (date == null)
            return null;
        return ZA_YMD_FORMAT.format(date);
    }

    public static String toZA_YMDHMSFormat(Date date) {
        if (date == null)
            return null;
        return ZA_YMDHMS_FORMAT.format(date);
    }

    public static String toZA_HMSFormat(Date date) {
        if (date == null)
            return null;
        return ZA_HMS_FORMAT.format(date);
    }

    public static String toZA_HMFormat(Date date) {
        if (date == null)
            return null;
        return ZA_HM_FORMAT.format(date);
    }

    public static Date clone(Date date) {
        if (date == null)
            return null;
        return new Date(date.getTime());
    }

    /**
     * Returns an array containing two <code>Date</code> objects: the 1st of
     * which contains a date representing the first millisecond of the first day
     * of the month; and the 2nd of which contains a date representing the last
     * millisecond of the last day of the month in which the given date falls.
     */
    public static Date[] getStartAndEndOfMonth(Date date) {

        Date[] results = new Date[2];
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);

        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        results[0] = cal.getTime();

        cal.set(Calendar.DAY_OF_MONTH, getLastDayOfMonth(date));
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 999);
        results[1] = cal.getTime();

        return results;
    }

    public static Date getStartOfMonth(Date date) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    public static Date getEndOfMonth(Date date) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, getLastDayOfMonth(date));
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 999);
        return cal.getTime();
    }

    /**
     * Combines the year, month and day fields of the given date with the hour,
     * minute, second and millisecond fields of the given time and returns the
     * resulting <code>Date</code> instance. If either date or time is null, a
     * null Date will be returned.
     */
    public static Date combine(Date date, Date time) {

        if (date == null || time == null)
            return null;

        Calendar cal = new GregorianCalendar();
        cal.setTime(date);

        Calendar cal2 = new GregorianCalendar();
        cal2.setTime(time);

        cal.set(Calendar.HOUR_OF_DAY, cal2.get(Calendar.HOUR_OF_DAY));
        cal.set(Calendar.MINUTE, cal2.get(Calendar.MINUTE));
        cal.set(Calendar.SECOND, cal2.get(Calendar.SECOND));
        cal.set(Calendar.MILLISECOND, cal2.get(Calendar.MILLISECOND));

        return cal.getTime();
    }

    /**
     * Converts the given date into a "standardized" / "normalized" time
     * relative to the epoch. Returns a new <code>java.sql.Time</code> instance
     * based on the given date's hour, minute, second and millisecond fields
     * combined with the date fields of the "epoch" (i.e. January 1st 1970).
     */
    public static java.sql.Time toEpochTime(Date dateTime) {
        if (dateTime == null)
            return null;
        Calendar cal = new GregorianCalendar();
        cal.setTime(dateTime);
        cal.set(Calendar.YEAR, 1970);
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DAY_OF_MONTH, 1);

        return new java.sql.Time(cal.getTime().getTime());
    }

    /**
     * Converts the given date into a "standardized" / "normalized" time
     * relative to the epoch. Returns a new <code>java.sql.Time</code> instance
     * based on the given date's hour, minute, second and millisecond fields
     * combined with the date fields of the "epoch" (i.e. January 1st 1970).
     * <p>
     * Synonym for <code>toEpochTime( Date )</code>.
     */
    public static java.sql.Time toNormalizedTime(Date dateTime) {
        return toEpochTime(dateTime);
    }

    /**
     * Converts the given date into a "standardized" / "normalized" date with
     * its hours, minutes, seconds and milliseconds all set to zero.
     * <p>
     * Returns a new <code>java.sql.Date</code> instance based on the given
     * date's year, month and day fields combined with zeroised time fields.
     */
    public static java.sql.Date toNormalizedSqlDate(Date dateTime) {
        if (dateTime == null)
            return null;
        return new java.sql.Date(getStartOfDay(dateTime).getTime());
    }

    /**
     * Converts the given date into a "standardized" / "normalized" date with
     * its hours, minutes, seconds and milliseconds all set to zero.
     * <p>
     * Returns a new <code>java.util.Date</code> instance based on the given
     * date's year, month and day fields combined with zeroised time fields.
     */
    public static java.util.Date toNormalizedDate(Date dateTime) {
        if (dateTime == null)
            return null;
        return new java.util.Date(getStartOfDay(dateTime).getTime());
    }

    public static Date toDate(int year, int month, int day) {
        return new GregorianCalendar(year, month - 1, day).getTime();
    }

    /**
     * Strict validation, that is the given year, month and day values must
     * represent an actual date, i.e. given 2001, 1 and 33 this method would
     * return false. This validation differs from the approach taken by
     * <code>new GregorianCalendar(y,m,d)</code> and
     * <code>DateFormate.parse(s)</code>, which accept almost any year, month
     * and day values and simply end up with a possibly different valid date,
     * for example 2001/01/33 would be converted to 2001/02/02.
     */
    public static boolean isValid(int year, int month, int day) {
        if (year < 0)
            return false;
        if (month < 1 || month > 12)
            return false;
        if (day < 1 || day > 31)
            return false;
        Calendar c = new GregorianCalendar(year, month - 1, day);
        return (c.get(Calendar.DAY_OF_MONTH) == day && c.get(Calendar.MONTH) + 1 == month && c.get(Calendar.YEAR) == year);
    }

    /**
     * Strict validation, that is the given year, month and day values must
     * represent an actual date, i.e. given 2001, 1 and 33, or given empty
     * Strings, this method would return false.
     */
    public static boolean isValid(String year, String month, String day) {
        if (year.equalsIgnoreCase("") || month.equalsIgnoreCase("") || day.equalsIgnoreCase(""))
            return false;
        int y = Integer.parseInt(year);
        int m = Integer.parseInt(month);
        int d = Integer.parseInt(day);
        return isValid(y, m, d);
    }

    public static String getDaySuffix(Date date) {
        String suffix = null;
        if (date.getDate() > 9 && date.getDate() < 20) {
            suffix = "th";
        } else {
            switch (date.getDate() % 10) {
                case 1:
                    suffix = "st";
                    break;
                case 2:
                    suffix = "nd";
                    break;
                case 3:
                    suffix = "rd";
                    break;
                default:
                    suffix = "th";
            }
        }
        return suffix;
    }

    /**
     * @return the first day of every month of the current year
     */
    public static Date[] getStartDaysOfAllMonths() {
        final Date[] startDays = new Date[12];
        Calendar cal = Calendar.getInstance();
        cal.setTime(getStartOfMonth(new Date()));
        int[] monthconstants = {
                Calendar.JANUARY, Calendar.FEBRUARY, Calendar.MARCH, Calendar.APRIL, Calendar.MAY, Calendar.JUNE, Calendar.JULY,
                Calendar.AUGUST, Calendar.SEPTEMBER, Calendar.OCTOBER, Calendar.NOVEMBER, Calendar.DECEMBER};
        for (int i = 0; i < monthconstants.length; i++) {
            cal.set(Calendar.MONTH, monthconstants[i]);
            startDays[i] = cal.getTime();
        }
        return startDays;
    }

    public static String getYear(Date date) {
        int[] ymd = date == null ? null : Dates.getYearMonthDay(date);
        return ymd == null ? null : StringUtils.leftPad(String.valueOf(ymd[0]), 4, "0");
    }

    public static String getMonth(Date date) {
        int[] ymd = date == null ? null : Dates.getYearMonthDay(date);
        return ymd == null ? null : StringUtils.leftPad(String.valueOf(ymd[1] + 1), 2, "0");
    }

    public static String getDay(Date date) {
        int[] ymd = date == null ? null : Dates.getYearMonthDay(date);
        return ymd == null ? null : StringUtils.leftPad(String.valueOf(ymd[2]), 2, "0");
    }
}