blob: af7aa645d3261f36b416f07fc9b8cba2b1d159c8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.commons;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public final class DateUtility {
private DateUtility() {
}
public static final long DAY_MILLIS = 24L * 3600L * 1000L;
//2 letter code countries for different weekends worldwide
private static final List<String> SUN_WEEKEND_DAYS_COUNTRIES = Arrays.asList(new String[]{"GQ", "IN", "TH", "UG"});
private static final List<String> FRY_WEEKEND_DAYS_COUNTRIES = Arrays.asList(new String[]{"DJ", "IR"});
private static final List<String> FRY_SUN_WEEKEND_DAYS_COUNTRIES = Arrays.asList(new String[]{"BN"});
private static final List<String> THU_FRY_WEEKEND_DAYS_COUNTRIES = Arrays.asList(new String[]{"AF"});
private static final List<String> FRY_SAT_WEEKEND_DAYS_COUNTRIES = Arrays.asList(new String[]{"AE", "DZ", "BH", "BD", "EG", "IQ", "IL", "JO", "KW", "LY", "MV", "MR", "OM", "PS", "QA", "SA", "SD", "SY", "YE"});
/**
* format date with {@value DateFormat#DEFAULT} pattern
*/
public static String formatDate(Date d) {
if (d == null) {
return "";
}
Locale loc = LocaleThreadLocal.get();
return DateFormat.getDateInstance(DateFormat.DEFAULT, loc).format(d);
}
/**
* format time with {@value DateFormat#SHORT} pattern
*/
public static String formatTime(Date d) {
if (d == null) {
return "";
}
Locale loc = LocaleThreadLocal.get();
return DateFormat.getTimeInstance(DateFormat.SHORT, loc).format(d);
}
/**
* format time with {@value DateFormat#SHORT}, {@value DateFormat#SHORT} patterns
*/
public static String formatDateTime(Date d) {
if (d == null) {
return "";
}
Locale loc = LocaleThreadLocal.get();
return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, loc).format(d);
}
/**
* format date with specific pattern as defined in {@link java.text.SimpleDateFormat}
*/
public static String format(Date d, String pattern) {
if (d == null || !StringUtility.hasText(pattern)) {
return "";
}
Locale loc = LocaleThreadLocal.get();
return new SimpleDateFormat(pattern, loc).format(d);
}
public static Date parse(String s, String pattern) {
if (s == null) {
return null;
}
try {
Locale loc = LocaleThreadLocal.get();
return new SimpleDateFormat(pattern, loc).parse(s);
}
catch (ParseException e) {
throw new IllegalArgumentException("parse(\"" + s + "\",\"" + pattern + "\") failed", e);
}
}
public static Date addMilliseconds(Date d, int milliseconds) {
return addTime(d, Calendar.MILLISECOND, milliseconds);
}
public static Date addSeconds(Date d, int seconds) {
return addTime(d, Calendar.SECOND, seconds);
}
public static Date addMinutes(Date d, int minutes) {
return addTime(d, Calendar.MINUTE, minutes);
}
public static Date addHours(Date d, int hours) {
return addTime(d, Calendar.HOUR_OF_DAY, hours);
}
public static Date addTime(Date d, int field, int amount) {
if (d == null) {
return null;
}
Calendar cal = Calendar.getInstance();
cal.setTime(d);
cal.add(field, amount);
return cal.getTime();
}
/**
* Adds a number of days to a date.
*
* @param count
* days is truncated to second and can be negative
* @param d
* may be <code>null</code>
*/
public static Date addDays(Date d, double count) {
if (d == null) {
return null;
}
int sign = 1;
if (count < 0) {
count = -count;
sign = -1;
}
double roundingFactor = (sign > 0) ? 0.000004 : 0.0000017;
int sec = (int) ((count + roundingFactor) * 3600 * 24);
Calendar cal = Calendar.getInstance();
cal.setTime(d);
cal.add(Calendar.DATE, sign * (sec / 3600 / 24));
cal.add(Calendar.HOUR_OF_DAY, sign * ((sec / 3600) % 24));
cal.add(Calendar.MINUTE, sign * ((sec / 60) % 60));
cal.add(Calendar.SECOND, (int) (sign * ((sec) % 60)));
return cal.getTime();
}
public static Date addMonths(Date d, int count) {
if (d == null) {
return null;
}
Calendar cal = Calendar.getInstance();
cal.setTime(d);
cal.add(Calendar.MONTH, count);
return cal.getTime();
}
public static Date addYears(Date d, int count) {
if (d == null) {
return null;
}
Calendar cal = Calendar.getInstance();
cal.setTime(d);
cal.add(Calendar.YEAR, count);
return cal.getTime();
}
/**
* determines the day of the week
*
* @param d
* @return int with the the day of the week (sunday=1)
*/
public static int getWeekday(Date d) {
if (d == null) {
return -1;
}
Calendar cal = Calendar.getInstance();
cal.setTime(d);
int w = cal.get(Calendar.DAY_OF_WEEK);
return w;
}
/**
* truncate the date to a day with time 00:00:00.000
*/
public static Date truncDate(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
truncCalendar(c);
return c.getTime();
}
public static Date truncDateToMinute(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
return c.getTime();
}
public static Date truncDateToSecond(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
c.set(Calendar.MILLISECOND, 0);
return c.getTime();
}
/**
* truncate the date to month
*/
public static Date truncDateToWeek(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
truncCalendarToWeek(c, -1);
return c.getTime();
}
/**
* truncate the date to month
*/
public static Date truncDateToMonth(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
truncCalendarToMonth(c);
return c.getTime();
}
/**
* truncate the date to year
*/
public static Date truncDateToYear(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
truncCalendarToYear(c);
return c.getTime();
}
/**
* truncate the date to half year (i.e. jan 1 or jul 1 of the given year)
*
* @since 4.2
*/
public static Date truncDateToHalfYear(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
truncCalendarToHalfYear(c);
return c.getTime();
}
/**
* truncate the date to quarter year (i.e. jan 1, apr 1, jul 1 or oct 1 of the given year)
*
* @since 4.2
*/
public static Date truncDateToQuarter(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
truncCalendarToQuarter(c);
return c.getTime();
}
/**
* truncate the date to hour
*
* @since 4.2
*/
public static Date truncDateToHour(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
truncCalendarToHour(c);
return c.getTime();
}
/**
* truncate the calendar to a day with time 00:00:00.000
*/
public static void truncCalendar(Calendar c) {
if (c == null) {
return;
}
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
}
/**
* truncate the calendar to week
*
* @param adjustIncrement
* +1 or -1
*/
public static void truncCalendarToWeek(Calendar c, int adjustIncrement) {
if (c == null) {
return;
}
if (adjustIncrement < -1) {
adjustIncrement = -1;
}
if (adjustIncrement > 1) {
adjustIncrement = 1;
}
if (adjustIncrement == 0) {
adjustIncrement = -1;
}
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
int firstDayOfWeek = Calendar.getInstance().getFirstDayOfWeek();
while (c.get(Calendar.DAY_OF_WEEK) != firstDayOfWeek) {
c.add(Calendar.DATE, adjustIncrement);
}
}
/**
* truncate the calendar to month
*/
public static void truncCalendarToMonth(Calendar c) {
if (c == null) {
return;
}
c.set(Calendar.DATE, 1);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
}
/**
* truncate the calendar to year
*/
public static void truncCalendarToYear(Calendar c) {
if (c == null) {
return;
}
c.set(Calendar.MONTH, Calendar.JANUARY);
c.set(Calendar.DATE, 1);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
}
/**
* truncate the calendar to half year (i.e. jan 1 or jul 1 of the given year)
*
* @since 4.2
*/
public static void truncCalendarToHalfYear(Calendar c) {
if (c == null) {
return;
}
int month = c.get(Calendar.MONTH);
truncCalendarToYear(c);
if (month >= Calendar.JULY) {
c.set(Calendar.MONTH, Calendar.JULY);
}
}
/**
* truncate the calendar to half year (i.e. jan 1, apr 1, jul 1 or oct 1 of the given year)
*
* @since 4.2
*/
public static void truncCalendarToQuarter(Calendar c) {
if (c == null) {
return;
}
final int month = c.get(Calendar.MONTH);
truncCalendarToYear(c);
int quarterMonth = Calendar.JANUARY;
switch (month) {
case Calendar.APRIL:
case Calendar.MAY:
case Calendar.JUNE:
quarterMonth = Calendar.APRIL;
break;
case Calendar.JULY:
case Calendar.AUGUST:
case Calendar.SEPTEMBER:
quarterMonth = Calendar.JULY;
break;
case Calendar.OCTOBER:
case Calendar.NOVEMBER:
case Calendar.DECEMBER:
quarterMonth = Calendar.OCTOBER;
break;
}
if (quarterMonth != Calendar.JANUARY) {
c.set(Calendar.MONTH, quarterMonth);
}
}
/**
* truncate the calendar to hour
*
* @since 4.2
*/
public static void truncCalendarToHour(Calendar c) {
if (c == null) {
return;
}
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
}
/**
* @return true if d is in the range [minDate,maxDate]
*/
public static boolean isInRange(Date minDate, Date d, Date maxDate) {
if (d == null || minDate == null || maxDate == null) {
return false;
}
return minDate.compareTo(d) <= 0 && d.compareTo(maxDate) <= 0;
}
/**
* @return true if the ranges intersect minDate and maxDate must not be null
* fromDate or toDate may be null
*/
public static boolean intersects(Date fromDate, Date toDate, Date minDate, Date maxDate) {
if (minDate == null || maxDate == null) {
return false;
}
if (fromDate == null && toDate == null) {
return false;
}
if (fromDate == null) {
return toDate.compareTo(minDate) >= 0;
}
else if (toDate == null) {
return fromDate.compareTo(maxDate) <= 0;
}
else {
return fromDate.compareTo(maxDate) <= 0 && toDate.compareTo(minDate) >= 0;
}
}
/**
* only compares the date, so doesn't care about time
*
* @return true if d is in the date range [minDate,maxDate]
*/
public static boolean isInDateRange(Date minDate, Date d, Date maxDate) {
return isInRange(truncDate(minDate), truncDate(d), truncDate(maxDate));
}
public static Date nextDay(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
c.add(Calendar.DATE, 1);
Date dNew = c.getTime();
return dNew;
}
public static Date max(Date... a) {
Date max = null;
for (Date d : a) {
if (d != null) {
if (max == null) {
max = d;
}
else if (d.compareTo(max) > 0) {
max = d;
}
}
}
return max;
}
public static Date min(Date... a) {
Date min = null;
for (Date d : a) {
if (d != null) {
if (min == null) {
min = d;
}
else if (d.compareTo(min) < 0) {
min = d;
}
}
}
return min;
}
public static boolean equals(Date a, Date b) {
return a == b || (a != null && b != null && a.compareTo(b) == 0);
}
public static boolean isSameDay(Date a, Date b) {
a = truncDate(a);
b = truncDate(b);
return equals(a, b);
}
public static boolean isSameMonth(Date d1, Date d2) {
Calendar c = Calendar.getInstance();
c.setTime(d1);
int m1 = c.get(Calendar.MONTH);
c.setTime(d2);
int m2 = c.get(Calendar.MONTH);
return (m1 == m2);
}
public static boolean isWeekend(Date d) {
return isWeekend(d, LocaleThreadLocal.get());
}
public static boolean isWeekend(Date d, Locale locale) {
Calendar c = Calendar.getInstance();
c.setTime(d);
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
int[] weekendDays = getWeekendDays(locale);
for (int weekendDay : weekendDays) {
if (dayOfWeek == weekendDay) {
return true;
}
}
return false;
}
private static int[] getWeekendDays(Locale locale) {
if (THU_FRY_WEEKEND_DAYS_COUNTRIES.contains(locale.getCountry())) {
return new int[]{Calendar.THURSDAY, Calendar.FRIDAY};
}
else if (FRY_SUN_WEEKEND_DAYS_COUNTRIES.contains(locale.getCountry())) {
return new int[]{Calendar.FRIDAY, Calendar.SUNDAY};
}
else if (FRY_WEEKEND_DAYS_COUNTRIES.contains(locale.getCountry())) {
return new int[]{Calendar.FRIDAY};
}
else if (SUN_WEEKEND_DAYS_COUNTRIES.contains(locale.getCountry())) {
return new int[]{Calendar.SUNDAY};
}
else if (FRY_SAT_WEEKEND_DAYS_COUNTRIES.contains(locale.getCountry())) {
return new int[]{Calendar.FRIDAY, Calendar.SATURDAY};
}
else {
return new int[]{Calendar.SATURDAY, Calendar.SUNDAY};
}
}
/**
* Correctly calculate covered days of a day range. 13.3.2008 00:00 -
* 14.3.2008 00:00 only covers 1 day (13.3.) 13.3.2008 12:00 - 14.3.2008 12:00
* covers 2 days (13.3., 14.3.)
*
* @return array of days that with time set to 00:00:00.000
*/
public static Date[] getCoveredDays(Date from, Date to) {
if (from == null) {
from = to;
}
if (to == null) {
to = from;
}
if (from.compareTo(to) > 0) {
to = from;
}
//
if (from.compareTo(to) == 0) {
return new Date[]{truncDate(from)};
}
else {
Calendar a = Calendar.getInstance();
a.setTime(from);
truncCalendar(a);
Calendar b = Calendar.getInstance();
b.setTime(to);
b.add(Calendar.MILLISECOND, -1);
truncCalendar(b);
long dayCount = ((b.getTimeInMillis() + DAY_MILLIS / 2 - a.getTimeInMillis()) / DAY_MILLIS) + 1;
Date[] array = new Date[(int) dayCount];
for (int i = 0; i < array.length; i++) {
array[i] = a.getTime();
a.add(Calendar.DATE, 1);
}
return array;
}
}
public static <T> T nvl(T value, T valueWhenNull) {
if (value != null) {
return value;
}
else {
return valueWhenNull;
}
}
public static Date convertCalendar(Calendar c) {
if (c == null) {
return null;
}
return new Date(c.getTimeInMillis());
}
public static Calendar convertDate(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
return c;
}
/**
* convert a (possible) subclass of {@link Date} to {@link Date}
*/
public static Date toUtilDate(Date d) {
if (d != null && d.getClass() != Date.class) {
d = new Date(d.getTime());
}
return d;
}
/**
* combine a date (yy/mm/dd) and a time (hh:mm:ss) into a combined timestamp-date containing both date and time.
*/
public static Date createDateTime(Date date, Date time) {
Calendar cal1 = Calendar.getInstance();
cal1.setTime(date);
Calendar cal2 = Calendar.getInstance();
cal2.setTime(time);
cal1.set(Calendar.HOUR_OF_DAY, cal2.get(Calendar.HOUR_OF_DAY));
cal1.set(Calendar.MINUTE, cal2.get(Calendar.MINUTE));
cal1.set(Calendar.SECOND, cal2.get(Calendar.SECOND));
cal1.set(Calendar.MILLISECOND, cal2.get(Calendar.MILLISECOND));
return cal1.getTime();
}
/**
* @param d
* a value in [0..1[ representing a day
* @return the time value as date in the range from 00:00 - 23:59:59
* @see #convertDateToDoubleTime(Date) inverse function
*/
public static Date convertDoubleTimeToDate(Number d) {
if (d == null) {
return null;
}
int m;
if (d.doubleValue() < 0) {
m = (int) (((long) (d.doubleValue() * DAY_MILLIS - 0.5)) % DAY_MILLIS);
}
else {
m = (int) (((long) (d.doubleValue() * DAY_MILLIS + 0.5)) % DAY_MILLIS);
}
Calendar c = Calendar.getInstance();
c.clear();
c.set(Calendar.MILLISECOND, m % 1000);
m = m / 1000;
c.set(Calendar.SECOND, m % 60);
m = m / 60;
c.set(Calendar.MINUTE, m % 60);
m = m / 60;
c.set(Calendar.HOUR_OF_DAY, m % 24);
if (m < 0) {
c.add(Calendar.DAY_OF_MONTH, 1);
}
return c.getTime();
}
/**
* @param time
* a time (hh:mm:ss) in the interval 00:00:00 - 23:59:59
* @return the time value as a double in the range from [0..1[
* @see #convertDoubleTimeToDate(Number) inverse function
*/
public static Double convertDateToDoubleTime(Date time) {
if (time == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(time);
double t = ((c.get(Calendar.HOUR_OF_DAY) * 60 + c.get(Calendar.MINUTE)) * 60 + c.get(Calendar.SECOND)) * 1000 + c.get(Calendar.MILLISECOND);
Double d = new Double(t / DAY_MILLIS);
// range check;
if (d.doubleValue() < 0) {
d = new Double(0);
}
if (d.doubleValue() > 1) {
d = new Double(1);
}
return d;
}
/**
* Calculates the number of days between <code>start</code> and <code>end</code>.
* If the end date is before the start date, the result will be positive as well.
* Example:
* <ul>
* <li>start = 1.1.2000, end = 2.1.2000 --> getDaysBetween(start, end) = 1
* <li>start = 2.1.2000, end = 1.1.2000 --> getDaysBetween(start, end) = 1
* </ul>
* returns <code>-1</code> in case of an error (e.g. parameter is null)
*/
public static int getDaysBetween(Date start, Date end) {
if (start == null || end == null) {
return -1;
}
Calendar startDate = convertDate(start);
truncCalendar(startDate);
Calendar endDate = convertDate(end);
truncCalendar(endDate);
long endL = endDate.getTimeInMillis() + endDate.getTimeZone().getOffset(endDate.getTimeInMillis());
long startL = startDate.getTimeInMillis() + startDate.getTimeZone().getOffset(startDate.getTimeInMillis());
int numDays = (int) ((endL - startL) / DAY_MILLIS);
return Math.abs(numDays);
}
}