Java tutorial
///////////////////////////////////////////////////////////////////////////// // // 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; } }