org.nuclos.common2.DateUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.nuclos.common2.DateUtils.java

Source

//Copyright (C) 2010  Novabit Informationssysteme GmbH
//
//This file is part of Nuclos.
//
//Nuclos is free software: you can redistribute it and/or modify
//it under the terms of the GNU Affero General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//Nuclos 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 Affero General Public License for more details.
//
//You should have received a copy of the GNU Affero General Public License
//along with Nuclos.  If not, see <http://www.gnu.org/licenses/>.
package org.nuclos.common2;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;

import org.nuclos.common.collection.Pair;

/**
 * Utility methods for <code>Date</code>s.
 * <br>
 * <br>Created by Novabit Informationssysteme GmbH
 * <br>Please visit <a href="http://www.novabit.de">www.novabit.de</a>
 *
 * @author   <a href="mailto:Lars.Rueckemann@novabit.de">Lars Rueckemann</a>
 * @author   <a href="mailto:Christoph.Radig@novabit.de">Christoph Radig</a>
 * @author Thomas Pasch
 */
public class DateUtils {

    /**
     * the number of milliseconds per day (where a day has 24 hours).
     */
    public static final int MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;

    public static final int QUARTER = 9211;

    private DateUtils() {
        // Never invoked.
    }

    /**
     * @return a Date object with today's date and time set to 00:00:00.
     * @postcondition result != null
     * @postcondition isPure(result)
     */
    public static Date today() {
        final Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        final Date result = calendar.getTime();
        assert result != null;
        assert isPure(result);
        return result;
    }

    /**
     * @return a Date object with today's date and the current time.
     * @postcondition result != null
     */
    public static Date now() {
        return new Date();
    }

    /**
     * @param date is not changed by this method. May be <code>null</code>.
     * @return the pure date (aka "day", that is the date without time of day) of the given Date object (if any).
     * @postcondition date != null <--> result != null
     * @postcondition date != null --> result.getHours() == 0
     * @postcondition date != null --> result.getMinutes() == 0
     * @postcondition date != null --> result.getSeconds() == 0
     * @todo consider calling this method getDay(). On the other hand, that name could be confused with Date.getDay()...
     */
    public static Date getPureDate(Date date) {
        final Date result = (date == null) ? null
                : org.apache.commons.lang.time.DateUtils.truncate(date, Calendar.DATE);
        assert (date != null) == (result != null);

        assert (date == null) || result.getHours() == 0;
        assert (date == null) || result.getMinutes() == 0;
        assert (date == null) || result.getSeconds() == 0;
        return result;
    }

    /**
     * @param date is not altered.
     * @return Is the given date pure?
     * @precondition date != null
     * @postcondition result --> date.getHours() == 0
     * @postcondition result --> date.getMinutes() == 0
     * @postcondition result --> date.getSeconds() == 0
     * @see   #getPureDate(Date)
     */
    public static boolean isPure(Date date) {
        final boolean result = date.equals(getPureDate(date));
        assert !result || date.getHours() == 0;
        assert !result || date.getMinutes() == 0;
        assert !result || date.getSeconds() == 0;
        return result;
    }

    /**
     * @param date1
     * @param date2
     * @return Do the given Date objects represent the same day? The time of day (hours, minutes, seconds, millis) is ignored.
     * @precondition date1 != null
     * @precondition date2 != null
     */
    public static boolean equalsPureDate(Date date1, Date date2) {
        return getPureDate(date1).equals(getPureDate(date2));
    }

    /**
     * copies the given date (useful making for defensive copies, as described in Josh Bloch: "Effective Java", item 24).
     * @param date may be null.
     * @return null or a new Date object that equals <code>date</code>.
     * @postcondition date == null --> result == null
     * @postcondition date != null --> result != date
     * @postcondition LangUtils.equals(result, date);
     */
    public static Date copyDate(Date date) {
        final Date result = (date == null) ? null : new Date(date.getTime());
        assert LangUtils.implies(date == null, result == null);
        assert LangUtils.implies(date != null, result != date);
        assert LangUtils.equals(result, date);
        return result;
    }

    /**
     * @param date may be <code>null</code>.
     * @return a decent localized String representation of the given date, as in <code>DateFormat.getDateInstance().format(date)</code>.
     * @postcondition result != null <--> date != null
     */
    public static String toString(Date date) {
        return toString(date, null);
    }

    /**
     * @param date may be <code>null</code>.
     * @param sNullRepresentation the String representation for a <code>null</code> date.
     * @return a decent localized String representation of the given date, as in <code>DateFormat.getDateInstance().format(date)</code>.
     * @postcondition (date != null) --> result != null
     * @postcondition (date == null) --> result == sNullRepresentation
     */
    public static String toString(Date date, String sNullRepresentation) {
        final String result = (date == null) ? sNullRepresentation
                : SpringLocaleDelegate.getInstance().formatDate(date); // DateFormat.getDateInstance().format(date);
        assert !(date != null) || result != null;
        assert !(date == null) || result == sNullRepresentation;

        return result;
    }

    /**
     * @param date is not changed.
     * @param iDayCount number of days to add (may be 0 or negative).
     * @return a new date resulting from adding the given number of days to the given date.
     */
    public static Date addDays(Date date, int iDayCount) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, iDayCount);
        return calendar.getTime();
        //return new Date(date.getTime() + iDayCount * MILLISECONDS_PER_DAY);
    }

    /**
     * @param date
     * @param iMonthCount number of months to add (may be 0 or negative).
     * @return a new date resulting from adding the given number of months to the given date.
     */
    public static Date addMonths(Date date, int iMonthCount) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, iMonthCount);
        return calendar.getTime();
    }

    /**
     * compares two "from" dates where <code>null</code> means -infinity.
     * @param dateFrom1
     * @param dateFrom2
     * @return -1/0/+1 (as usual)
     */
    public static int compareDateFrom(Date dateFrom1, Date dateFrom2) {
        final int result;

        if (dateFrom1 == null) {
            result = (dateFrom2 == null ? 0 : -1);
        } else if (dateFrom2 == null) {
            result = 1;
        } else {
            result = dateFrom1.compareTo(dateFrom2);
        }
        return result;
    }

    /**
     * compares two "until" dates where <code>null</code> means +infinity.
     * @param dateUntil1
     * @param dateUntil2
     * @return -1/0/+1 (as usual)
     */
    public static int compareDateUntil(Date dateUntil1, Date dateUntil2) {
        final int result;

        if (dateUntil1 == null) {
            result = (dateUntil2 == null ? 0 : 1);
        } else if (dateUntil2 == null) {
            result = -1;
        } else {
            result = dateUntil1.compareTo(dateUntil2);
        }
        return result;
    }

    /**
     * get actual date and time as string
     */
    public static String getActualDateAndTime() {
        //DateFormat df = SimpleDateFormat.getDateTimeInstance();
        //return df.format(new Date());
        return SpringLocaleDelegate.getInstance().formatDateTime(now());
    }

    /**
     * get the given date and time as string
     * @param date
     */
    public static String getDateAndTime(Date date) {
        //DateFormat df = SimpleDateFormat.getDateTimeInstance();
        //return df.format(date);
        return SpringLocaleDelegate.getInstance().formatDateTime(date);
    }

    static final long ONE_HOUR = 60 * 60 * 1000L;

    public static int daysBetween(Date d1, Date d2) {
        return new Long((getPureDate(d2).getTime() - getPureDate(d1).getTime() + ONE_HOUR) / (ONE_HOUR * 24))
                .intValue();
    }

    private static Map<String, Integer> mapCalcStrings = new HashMap<String, Integer>();
    static {
        mapCalcStrings.put("D", Calendar.DAY_OF_YEAR);
        mapCalcStrings.put("T", Calendar.DAY_OF_YEAR);
        mapCalcStrings.put("W", Calendar.WEEK_OF_YEAR);
        mapCalcStrings.put("M", Calendar.MONTH);
        mapCalcStrings.put("Y", Calendar.YEAR);
        mapCalcStrings.put("J", Calendar.YEAR);
    }

    private static enum CalcFunction {
        ADD, SUBTRACT, SET;
    }

    private static class CalcPair extends Pair<Integer, Integer> {
    };

    private static CalcPair getCalcPair(String input) {
        CalcPair result = new CalcPair();

        String end = input.substring(input.length() - 1, input.length());
        if ("[".equals(end) || "]".equals(end)) {
            // set start or end
            String field = input.substring(input.length() - 2, input.length() - 1);
            result.x = mapCalcStrings.get(field);
            result.y = ("[".equals(end)) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
        } else {
            String value = input.substring(0, input.length() - 1);
            result.x = mapCalcStrings.get(end);
            result.y = Integer.parseInt(value);
        }

        return result;
    }

    private static void calc(GregorianCalendar result, CalcFunction cf, CalcPair cp) {
        switch (cf) {
        case ADD:
            result.add(cp.x, cp.y);
            break;
        case SUBTRACT:
            result.add(cp.x, cp.y * (-1));
            break;
        case SET:
            switch (cp.x) {
            case Calendar.YEAR:
                result.set(Calendar.DAY_OF_YEAR,
                        cp.y == Integer.MIN_VALUE ? result.getActualMinimum(Calendar.DAY_OF_YEAR)
                                : result.getActualMaximum(Calendar.DAY_OF_YEAR));
                break;
            case Calendar.MONTH:
                result.set(Calendar.DAY_OF_MONTH,
                        cp.y == Integer.MIN_VALUE ? result.getActualMinimum(Calendar.DAY_OF_MONTH)
                                : result.getActualMaximum(Calendar.DAY_OF_MONTH));
                break;
            case Calendar.WEEK_OF_YEAR:
                result.set(Calendar.DAY_OF_WEEK, cp.y == Integer.MIN_VALUE ? Calendar.MONDAY : Calendar.SUNDAY);
                break;
            }
            break;
        }
    }

    public static Date calc(String calculation) {
        calculation = calculation.toUpperCase();
        GregorianCalendar result = new GregorianCalendar();
        result.setTime(today());

        StringBuffer operand = new StringBuffer();
        CalcFunction cf = null;
        for (int i = 0; i < calculation.length(); i++) {
            char c = calculation.charAt(i);

            switch (c) {
            case '+':
            case '-':
            case '.':
                if (cf != null) {
                    calc(result, cf, getCalcPair(operand.toString()));
                    operand = new StringBuffer();
                }
                break;
            }

            switch (c) {
            case '+':
                cf = CalcFunction.ADD;
                break;
            case '-':
                cf = CalcFunction.SUBTRACT;
                break;
            case '.':
                cf = CalcFunction.SET;
                break;
            default:
                operand.append(c);
            }
        }

        if (operand.length() > 0 && cf != null) {
            calc(result, cf, getCalcPair(operand.toString()));
        }

        return result.getTime();
    }

    /**
     * Give the duration in millis that passes from the quantized
     * date before the given date to the next quantized date after the given date.
     */
    public static long getMillis(Date date, int quantizer) {
        final Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        final int quant = getQuant(cal, quantizer);
        // side effect: modifies cal
        dateFromQuant(cal, quant, quantizer);
        final long low = cal.getTimeInMillis();

        // cal.set(quantizer, quant + 1);
        montoneAdd(cal, quantizer, 1);
        dateFromQuant(cal, getQuant(cal, quantizer), quantizer);

        long result = cal.getTimeInMillis() - low;
        if (result < 0) {
            montoneAdd(cal, Calendar.YEAR, 1);
            dateFromQuant(cal, cal.get(quantizer), quantizer);
            result = cal.getTimeInMillis() - low;
            assert result > 0;
        }
        assert result > 0;
        return result;
    }

    /**
     * Return the next quantized date before the given date.
     */
    public static Date floorBound(Date date, int quantizer) {
        final Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        final int quant = getQuant(cal, quantizer);
        return dateFromQuant(cal, quant, quantizer);
    }

    public static int getQuant(Calendar cal, int quantizer) {
        final int quant;
        if (quantizer == QUARTER) {
            quant = cal.get(Calendar.MONTH) / 3;
        } else {
            quant = cal.get(quantizer);
        }
        return quant;
    }

    /**
     * Return last Date within the next quantized date before the given date.
     */
    public static Date topBound(Date date, int quantizer) {
        final Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        montoneAdd(cal, quantizer, 1);
        // side effect on cal
        dateFromQuant(cal, getQuant(cal, quantizer), quantizer);
        montoneAdd(cal, Calendar.SECOND, -2);
        return cal.getTime();
    }

    /**
     * Return the next quantized date after the given date.
     */
    public static Date ceilBound(Date date, int quantizer) {
        final Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        // final int quant = cal.get(quantizer) + 1;
        // cal.set(quantizer, cal.get(quantizer) + 1);
        montoneAdd(cal, quantizer, 1);
        return dateFromQuant(cal, getQuant(cal, quantizer), quantizer);
    }

    private static Date dateFromQuant(Calendar cal, int quant, int quantizer) {
        switch (quantizer) {
        case Calendar.WEEK_OF_YEAR:
            final Date old = cal.getTime();

            cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 1);
            cal.set(Calendar.MILLISECOND, 0);

            cal.set(Calendar.WEEK_OF_YEAR, quant);
            // cal.set(Calendar.DAY_OF_WEEK, WEEK_START_DAY);

            if (old.before(cal.getTime())) {
                montoneAdd(cal, Calendar.YEAR, -1);
                montoneAdd(cal, Calendar.WEEK_OF_YEAR, 1);
                cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
                assert cal.getTime().before(old);
            }
            break;
        case Calendar.DAY_OF_WEEK:
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 1);
            cal.set(Calendar.MILLISECOND, 0);

            cal.set(Calendar.DAY_OF_WEEK, quant);
            break;
        case Calendar.DAY_OF_MONTH:
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 1);
            cal.set(Calendar.MILLISECOND, 0);

            cal.set(Calendar.DAY_OF_MONTH, quant);
            break;
        case Calendar.MONTH:
            cal.set(Calendar.DAY_OF_MONTH, 1);
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 1);
            cal.set(Calendar.MILLISECOND, 0);

            cal.set(Calendar.MONTH, quant);
            break;
        case QUARTER:
            cal.set(Calendar.DAY_OF_MONTH, 1);
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 1);
            cal.set(Calendar.MILLISECOND, 0);

            cal.set(Calendar.MONTH, quant * 3);
            break;
        case Calendar.YEAR:
            cal.set(Calendar.DAY_OF_MONTH, 1);
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 1);
            cal.set(Calendar.MILLISECOND, 0);
            cal.set(Calendar.MONTH, 0);
            break;
        default:
            throw new IllegalArgumentException();
        }
        final Date result = cal.getTime();
        return result;
    }

    /**
     * Add a amount of field to given Calendar. 
     * The returned date is always after given date if amount is positive.
     */
    public static void montoneAdd(Calendar cal, int field, int amount) {
        if (amount == 0) {
            return;
        }
        final int sign = amount > 0 ? 1 : -1;

        final long millis = cal.getTimeInMillis();
        if (field == QUARTER) {
            cal.add(Calendar.MONTH, amount * 3);
        } else {
            cal.add(field, amount);
        }

        while ((sign > 0 && cal.getTimeInMillis() < millis) || (sign < 0 && cal.getTimeInMillis() > millis)) {
            switch (field) {
            case Calendar.WEEK_OF_YEAR:
                field = Calendar.YEAR;
                cal.add(field, sign);
                break;
            case Calendar.DAY_OF_WEEK:
                field = Calendar.WEEK_OF_YEAR;
                cal.add(field, sign);
                break;
            case Calendar.DAY_OF_MONTH:
                field = Calendar.MONTH;
                cal.add(field, sign);
                break;
            case Calendar.MONTH:
                field = Calendar.YEAR;
                cal.add(field, sign);
                break;
            case QUARTER:
                field = Calendar.YEAR;
                cal.add(field, sign);
                break;
            case Calendar.YEAR:
                // do nothing
                break;
            default:
                throw new IllegalArgumentException();
            }
        }
    }

    public static void main(String[] args) {
        Calendar cal = Calendar.getInstance();
        cal.set(2012, 11, 24, 0, 0, 1);
        System.out.println("date: " + cal.getTime());
        long l = getMillis(cal.getTime(), Calendar.WEEK_OF_YEAR);
        System.out.println("millis: " + l);
    }

} // class DateUtils