org.projectforge.common.DateHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.projectforge.common.DateHelper.java

Source

/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
//         www.projectforge.org
//
// Copyright (C) 2001-2013 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition 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; version 3 of the License.
//
// This community edition 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/.
//
/////////////////////////////////////////////////////////////////////////////

package org.projectforge.common;

import java.io.Serializable;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.projectforge.calendar.TimePeriod;
import org.projectforge.core.ConfigXml;
import org.projectforge.core.Configuration;
import org.projectforge.user.PFUserContext;
import org.projectforge.web.calendar.DateTimeFormatter;

/**
 * Parse and formats dates.
 * 
 * @author Kai Reinhard (k.reinhard@micromata.de)
 * 
 */
public class DateHelper implements Serializable {
    private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(DateHelper.class);

    private static final long serialVersionUID = -94010735614402146L;

    /**
     * Number of milliseconds of one minute. DO NOT USE FOR exact date calculations (summer and winter time etc.)!
     */
    public static final long MILLIS_MINUTE = 60 * 1000;

    /**
     * Number of milliseconds of one hour. DO NOT USE FOR exact date calculations (summer and winter time etc.)!
     */
    public static final long MILLIS_HOUR = 60 * MILLIS_MINUTE;

    /**
     * Number of milliseconds of one day. DO NOT USE FOR exact date calculations (summer and winter time etc.)!
     */
    public static final long MILLIS_DAY = 24 * MILLIS_HOUR;

    /**
     * Europe/Berlin
     */
    public final static TimeZone EUROPE_BERLIN = TimeZone.getTimeZone("Europe/Berlin");

    public static final BigDecimal MILLIS_PER_HOUR = new BigDecimal(MILLIS_HOUR);

    public static final BigDecimal HOURS_PER_WORKING_DAY = new BigDecimal(DateTimeFormatter.DEFAULT_HOURS_OF_DAY);

    public static final BigDecimal MILLIS_PER_WORKING_DAY = new BigDecimal(
            MILLIS_HOUR * DateTimeFormatter.DEFAULT_HOURS_OF_DAY);

    public static final BigDecimal SECONDS_PER_WORKING_DAY = new BigDecimal(
            60 * 60 * DateTimeFormatter.DEFAULT_HOURS_OF_DAY);

    /**
     * UTC
     */
    public final static TimeZone UTC = TimeZone.getTimeZone("UTC");

    private static final DateFormat FORMAT_ISO_DATE = new SimpleDateFormat(DateFormats.ISO_DATE);

    private static final DateFormat FORMAT_ISO_TIMESTAMP = new SimpleDateFormat(DateFormats.ISO_TIMESTAMP_MILLIS);

    private static final DateFormat FILENAME_FORMAT_TIMESTAMP = new SimpleDateFormat(
            DateFormats.ISO_DATE + "_HH-mm");

    private static final DateFormat FILENAME_FORMAT_DATE = new SimpleDateFormat(DateFormats.ISO_DATE);

    /**
     * Compares millis. If both dates are null then they're equal.
     * @param d1
     * @param d2
     * @see Date#getTime()
     */
    public static boolean equals(final Date d1, final Date d2) {
        if (d1 == null) {
            return d2 == null;
        }
        if (d2 == null) {
            return false;
        }
        return d1.getTime() == d2.getTime();
    }

    /**
     * thread safe
     * @param timezone
     */
    public static DateFormat getIsoDateFormat(final TimeZone timezone) {
        final DateFormat df = (DateFormat) FORMAT_ISO_DATE.clone();
        if (timezone != null) {
            df.setTimeZone(timezone);
        }
        return df;
    }

    /**
     * thread safe
     * @param timezone If null then time zone is ignored.
     */
    public static DateFormat getIsoTimestampFormat(final TimeZone timezone) {
        final DateFormat df = (DateFormat) FORMAT_ISO_TIMESTAMP.clone();
        if (timezone != null) {
            df.setTimeZone(timezone);
        }
        return df;
    }

    /**
     * thread safe
     * @param timezone
     */
    public static DateFormat getFilenameFormatTimestamp(final TimeZone timezone) {
        final DateFormat df = (DateFormat) FILENAME_FORMAT_TIMESTAMP.clone();
        if (timezone != null) {
            df.setTimeZone(timezone);
        }
        return df;
    }

    /**
     * thread safe
     * @param timezone
     */
    public static DateFormat getFilenameFormatDate(final TimeZone timezone) {
        final DateFormat df = (DateFormat) FILENAME_FORMAT_DATE.clone();
        if (timezone != null) {
            df.setTimeZone(timezone);
        }
        return df;
    }

    /**
     * yyyy-MM-dd HH:mm:ss.S in UTC. Thread safe usage: FOR_TESTCASE_OUTPUT_FORMATTER.get().format(date)
     */
    public static final ThreadLocal<DateFormat> FOR_TESTCASE_OUTPUT_FORMATTER = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            final DateFormat df = new SimpleDateFormat(DateFormats.ISO_TIMESTAMP_MILLIS);
            df.setTimeZone(UTC);
            return df;
        }
    };

    /**
     * Thread safe usage: FOR_TESTCASE_OUTPUT_FORMATTER.get().format(date)
     */
    public static final ThreadLocal<DateFormat> TECHNICAL_ISO_UTC = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            final DateFormat dateFormat = new SimpleDateFormat(DateFormats.ISO_TIMESTAMP_MILLIS + " z");
            dateFormat.setTimeZone(UTC);
            return dateFormat;
        }
    };

    /**
     * @return Short name of day represented by the giving day. The context user's locale and time zone is considered.
     */
    public static final String formatShortNameOfDay(final Date date) {
        final DateFormat df = new SimpleDateFormat("EE", PFUserContext.getLocale());
        df.setTimeZone(PFUserContext.getTimeZone());
        return df.format(date);
    }

    /**
     * Formats the given date as UTC date in ISO format attached TimeZone (UTC).
     * @param date
     * @return
     */
    public static final String formatAsUTC(final Date date) {
        if (date == null) {
            return "";
        }
        return UTC_ISO_DATE.get().format(date);
    }

    /**
     * Thread safe usage: UTC_ISO_DATE.get().format(date)
     */
    public static final ThreadLocal<DateFormat> UTC_ISO_DATE = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            final DateFormat df = new SimpleDateFormat(DateFormats.ISO_TIMESTAMP_MILLIS + " Z");
            df.setTimeZone(UTC);
            return df;
        }
    };

    /**
     * Takes time zone of context user if exist.
     * @param date
     */
    public static String formatIsoDate(final Date date) {
        return getIsoDateFormat(PFUserContext.getTimeZone()).format(date);
    }

    /**
     * Takes time zone of context user if exist.
     * @param date
     */
    public static String formatIsoDate(final Date date, final TimeZone timeZone) {
        return getIsoDateFormat(timeZone).format(date);
    }

    /**
     * logError = true
     * @param str
     * @return
     * @see #parseMillis(String, boolean)
     */
    public static Date parseMillis(final String str) {
        return parseMillis(str, true);
    }

    /**
     * @param str
     * @param logError If true, any ParseException error will be logged if occured.
     * @return The parsed date or null, if not parseable.
     */
    public static Date parseMillis(final String str, final boolean logError) {
        Date date = null;
        try {
            final long millis = Long.parseLong(str);
            date = new Date(millis);
        } catch (final NumberFormatException ex) {
            if (logError == true) {
                log.error("Could not parse date string (millis expected): " + str, ex);
            }
        }
        return date;
    }

    public static String formatIsoTimestamp(final Date date) {
        return formatIsoTimestamp(date, PFUserContext.getTimeZone());
    }

    public static String formatIsoTimestamp(final Date date, final TimeZone timeZone) {
        return getIsoTimestampFormat(timeZone).format(date);
    }

    /**
     * Format yyyy-mm-dd
     * @param isoDateString
     * @return Parsed date or null if a parse error occurs.
     */
    public static Date parseIsoDate(final String isoDateString, final TimeZone timeZone) {
        final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        df.setTimeZone(timeZone);
        Date date;
        try {
            date = df.parse(isoDateString);
        } catch (final ParseException ex) {
            return null;
        }
        return date;
    }

    /**
     * Format: {@link DateFormats#ISO_TIMESTAMP_MILLIS}
     * @param isoDateString
     * @return Parsed date or null if a parse error occurs.
     */
    public static Date parseIsoTimestamp(final String isoDateString, final TimeZone timeZone) {
        final DateFormat df = new SimpleDateFormat(DateFormats.ISO_TIMESTAMP_MILLIS);
        df.setTimeZone(timeZone);
        Date date;
        try {
            date = df.parse(isoDateString);
        } catch (final ParseException ex) {
            return null;
        }
        return date;
    }

    public static String formatIsoTimePeriod(final Date fromDate, final Date toDate) {
        return formatIsoDate(fromDate) + ":" + formatIsoDate(toDate);
    }

    /**
     * Format yyyy-mm-dd:yyyy-mm-dd
     * @param isoTimePeriodString
     * @return Parsed time period or null if a parse error occurs.
     */
    public static TimePeriod parseIsoTimePeriod(final String isoTimePeriodString, final TimeZone timeZone) {
        final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        df.setTimeZone(timeZone);
        final String[] sa = isoTimePeriodString.split(":");
        if (sa.length != 2) {
            return null;
        }
        final Date fromDate = DateHelper.parseIsoDate(sa[0], DateHelper.UTC);
        final Date toDate = DateHelper.parseIsoDate(sa[1], DateHelper.UTC);
        if (fromDate == null || toDate == null) {
            return null;
        }
        return new TimePeriod(fromDate, toDate);
    }

    /**
     * Output via FOR_TESTCASE_OUTPUT_FORMATTER for test cases.<br/>
     * @param dateHolder
     * @return
     */
    public static final String getForTestCase(final DateHolder dateHolder) {
        return FOR_TESTCASE_OUTPUT_FORMATTER.get().format(dateHolder.getDate());
    }

    /**
     * Output via FOR_TESTCASE_OUTPUT_FORMATTER for test cases.
     * @param dateHolder
     * @return
     */
    public static final String getForTestCase(final Date date) {
        return FOR_TESTCASE_OUTPUT_FORMATTER.get().format(date);
    }

    public static final String getTimestampAsFilenameSuffix(final Date date) {
        if (date == null) {
            return "--";
        }
        return getFilenameFormatTimestamp(PFUserContext.getTimeZone()).format(date);
    }

    public static final String getDateAsFilenameSuffix(final Date date) {
        if (date == null) {
            return "--";
        }
        return getFilenameFormatDate(PFUserContext.getTimeZone()).format(date);
    }

    /**
     * Returns a calendar instance. If a context user is given then the user's time zone and locale will be used if given.
     */
    public static Calendar getCalendar() {
        return getCalendar(null, null);
    }

    /**
     * Returns a calendar instance. If a context user is given then the user's time zone and locale will be used if given.
     * @param locale if given this locale will overwrite any the context user's locale.
     */
    public static Calendar getCalendar(final Locale locale) {
        return getCalendar(null, locale);
    }

    public static Calendar getCalendar(TimeZone timeZone, Locale locale) {
        if (locale == null) {
            locale = PFUserContext.getLocale();
        }
        if (timeZone == null) {
            timeZone = PFUserContext.getTimeZone();
        }
        return Calendar.getInstance(timeZone, locale);
    }

    public static Calendar getUTCCalendar() {
        return getCalendar(UTC, null);
    }

    /**
     * If stopTime is before startTime a negative value will be returned.
     * @param startTime
     * @param stopTime
     * @return Duration in minutes or 0, if not computable (if start or stop time is null or stopTime is before startTime).
     */
    public static long getDuration(final Date startTime, final Date stopTime) {
        if (startTime == null || stopTime == null || stopTime.before(startTime) == true) {
            return 0;
        }
        final long millis = stopTime.getTime() - startTime.getTime();
        return millis / 60000;
    }

    /**
     * @return Formatted string without seconds, such as 5:45.
     * @param time in millis
     */
    public static String formatDuration(final long milliSeconds) {
        final long duration = milliSeconds / 60000;
        final long durationHours = duration / 60;
        final long durationMinutes = (duration % 60);
        final StringBuffer buf = new StringBuffer(10);
        buf.append(durationHours);
        if (durationMinutes < 10)
            buf.append(":0");
        else
            buf.append(':');
        buf.append(durationMinutes);
        return buf.toString();
    }

    /**
     * Initializes a new ArrayList with -1 ("--") and all 12 month with labels "01", ..., "12".
     */
    public static List<LabelValueBean<String, Integer>> getMonthList() {
        final List<LabelValueBean<String, Integer>> list = new ArrayList<LabelValueBean<String, Integer>>();
        list.add(new LabelValueBean<String, Integer>("--", -1));
        for (int month = 0; month < 12; month++) {
            list.add(new LabelValueBean<String, Integer>(StringHelper.format2DigitNumber(month + 1), month));
        }
        return list;
    }

    /**
     * @param year
     * @param month 0-11
     * @return "yyyy-mm"
     */
    public static String formatMonth(final int year, final int month) {
        final StringBuffer buf = new StringBuffer();
        buf.append(year);
        if (month >= 0) {
            buf.append('-');
            final int m = month + 1;
            if (m <= 9) {
                buf.append('0');
            }
            buf.append(m);
        }
        return buf.toString();
    }

    /**
     * Should be used application wide for getting and/or displaying the week of year!
     * @param date
     * @return Return the week of year. The week of year depends on the Locale set in the Configuration (config.xml). If given date is null
     *         then -1 is returned. For "de" the first week of year is the first week with a minimum of 4 days in the new year. For "en" the
     *         first week of the year is the first week with a minimum of 1 days in the new year.
     * @see java.util.Calendar#getMinimalDaysInFirstWeek()
     * @see Configuration#getDefaultLocale()
     */
    public static int getWeekOfYear(final Date date) {
        if (date == null) {
            return -1;
        }
        final Calendar cal = Calendar.getInstance(PFUserContext.getTimeZone(),
                ConfigXml.getInstance().getDefaultLocale());
        cal.setTime(date);
        return cal.get(Calendar.WEEK_OF_YEAR);
    }

    /**
     * Should be used application wide for getting and/or displaying the week of year!
     * @param calendar (this methods uses the year, month and day of the given Calendar)
     * @return Return the week of year. The week of year depends on the Locale set in the Configuration (config.xml). If given date is null
     *         then -1 is returned. For "de" the first week of year is the first week with a minimum of 4 days in the new year. For "en" the
     *         first week of the year is the first week with a minimum of 1 days in the new year.
     * @see java.util.Calendar#getMinimalDaysInFirstWeek()
     * @see Configuration#getDefaultLocale()
     */
    public static int getWeekOfYear(final Calendar calendar) {
        if (calendar == null) {
            return -1;
        }
        final Calendar cal = Calendar.getInstance(ConfigXml.getInstance().getDefaultLocale());
        cal.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        cal.set(Calendar.MONTH, calendar.get(Calendar.MONDAY));
        cal.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH));
        return cal.get(Calendar.WEEK_OF_YEAR);
    }

    /**
     * Should be used application wide for getting and/or displaying the week of year!
     * @param date
     * @return Return the week of year. The week of year depends on the Locale set in the Configuration (config.xml). If given date is null
     *         then -1 is returned. For "de" the first week of year is the first week with a minimum of 4 days in the new year. For "en" the
     *         first week of the year is the first week with a minimum of 1 days in the new year.
     * @see java.util.Calendar#getMinimalDaysInFirstWeek()
     * @see Configuration#getDefaultLocale()
     */
    public static int getWeekOfYear(final DateTime date) {
        if (date == null) {
            return -1;
        }
        return getWeekOfYear(date.toDate());
    }

    /**
     * @param d1
     * @param d2
     * @return True if the dates are both null or both represents the same day (year, month, day) independant of the hours, minutes etc.
     * @see DateHolder#isSameDay(Date)
     */
    public static boolean isSameDay(final Date d1, final Date d2) {
        if (d1 == null) {
            if (d2 == null) {
                return true;
            } else {
                return false;
            }
        } else if (d2 == null) {
            return false;
        }
        final DateHolder dh = new DateHolder(d1);
        return dh.isSameDay(d2);
    }

    /**
     * @param d1
     * @param d2
     * @return True if the dates are both null or both represents the same day (year, month, day) independant of the hours, minutes etc.
     * @see DateHolder#isSameDay(Date)
     */
    public static boolean isSameDay(final DateTime d1, final DateTime d2) {
        if (d1 == null) {
            if (d2 == null) {
                return true;
            } else {
                return false;
            }
        } else if (d2 == null) {
            return false;
        }
        return d1.getYear() == d2.getYear() && d1.getDayOfYear() == d2.getDayOfYear();
    }

    public static boolean dateOfYearBetween(final int month, final int dayOfMonth, final int fromMonth,
            final int fromDayOfMonth, final int toMonth, final int toDayOfMonth) {
        if (fromMonth == toMonth) {
            if (month != fromMonth) {
                return false;
            }
            if (dayOfMonth < fromDayOfMonth || dayOfMonth > toDayOfMonth) {
                return false;
            }
        } else if (fromMonth < toMonth) {
            // e. g. APR - JUN
            if (month < fromMonth || month > toMonth) {
                // e. g. FEB or JUL
                return false;
            } else if (month == fromMonth && dayOfMonth < fromDayOfMonth) {
                return false;
            } else if (month == toMonth && dayOfMonth > toDayOfMonth) {
                return false;
            }
        } else if (fromMonth > toMonth) {
            // e. g. NOV - FEB
            if (month > toMonth && month < fromMonth) {
                // e. g. MAR
                return false;
            } else if (month == fromMonth && dayOfMonth < fromDayOfMonth) {
                return false;
            } else if (month == toMonth && dayOfMonth > toDayOfMonth) {
                return false;
            }
        }
        return true;
    }

    /**
     * Sets given DateTime (UTC) as local time, meaning e. g. 08:00 UTC will be 08:00 local time.
     * @param dateTime
     * @return
     * @see DateTime#toString(String)
     * @see DateHelper#parseIsoDate(String, TimeZone)
     */
    public static long getDateTimeAsMillis(final DateTime dateTime) {
        final String isDateString = dateTime.toString(DateFormats.ISO_TIMESTAMP_MILLIS);
        final Date date = DateHelper.parseIsoTimestamp(isDateString, PFUserContext.getTimeZone());
        return date.getTime();
    }

    public final static int convertCalendarDayOfWeekToJoda(final int calendarDayOfWeek) {
        if (calendarDayOfWeek == Calendar.SUNDAY) {
            return DateTimeConstants.SUNDAY;
        }
        return calendarDayOfWeek - 1;
    }
}