blob: 3558c5cabcecc0a70a9e7a7b62ba41839f5eb6ee [file] [log] [blame]
/*
* Copyright (c) 2010-2018 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.rt.platform.util.date;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import org.eclipse.scout.rt.platform.BEANS;
import org.eclipse.scout.rt.platform.nls.NlsLocale;
import org.eclipse.scout.rt.platform.util.ObjectUtility;
import org.eclipse.scout.rt.platform.util.StringUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class DateUtility {
private DateUtility() {
}
public static final long HOUR_MILLIS = 3600L * 1000L;
public static final long DAY_MILLIS = 24L * HOUR_MILLIS;
private static final Logger LOG = LoggerFactory.getLogger(DateUtility.class);
//2 letter code countries for different weekends worldwide
private static final List<String> SUN_WEEKEND_DAYS_COUNTRIES = Arrays.asList("GQ", "IN", "TH", "UG");
private static final List<String> FRY_WEEKEND_DAYS_COUNTRIES = Arrays.asList("DJ", "IR");
private static final List<String> FRY_SUN_WEEKEND_DAYS_COUNTRIES = Arrays.asList("BN");
private static final List<String> THU_FRY_WEEKEND_DAYS_COUNTRIES = Arrays.asList("AF");
private static final List<String> FRY_SAT_WEEKEND_DAYS_COUNTRIES = Arrays.asList("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 = NlsLocale.get();
return BEANS.get(DateFormatProvider.class).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 = NlsLocale.get();
return BEANS.get(DateFormatProvider.class).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 = NlsLocale.get();
return BEANS.get(DateFormatProvider.class).getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, loc).format(d);
}
/**
* format date with specific pattern as defined in {@link SimpleDateFormat}
*/
public static String format(Date d, String pattern) {
if (d == null || !StringUtility.hasText(pattern)) {
return "";
}
Locale loc = NlsLocale.get();
return new SimpleDateFormat(pattern, loc).format(d);
}
/**
* Creates a {@link SimpleDateFormat} to parse the first argument according to the provided pattern. Disables lenient
* behavior of {@link SimpleDateFormat}.
*/
public static Date parse(String s, String pattern) {
if (s == null) {
return null;
}
try {
Locale loc = NlsLocale.get();
SimpleDateFormat df = new SimpleDateFormat(pattern, loc);
// default for SimpleDateFormat is a lenient behavior, e.g. 13.13.13, 952.1238.2010, 12.01.191;ABC would also be valid dates
// disable lenient behavior, could lead to unexpected results
df.setLenient(false);
return df.parse(s);
}
catch (ParseException e) {
throw new IllegalArgumentException("parse(\"" + s + "\",\"" + pattern + "\") failed", e);
}
}
/**
* Returns <code>true</code> if, and only if the given String is a valid date according to the given date format.
*
* @param s
* date
* @param pattern
* date format
* @return <code>true</code> if, and only if the given String is a valid date according to the given date format.
*/
public static boolean isValidDate(String s, String pattern) {
try {
parse(s, pattern);
return true;
}
catch (IllegalArgumentException e) {
LOG.debug("could not parse date from given text [s='{}',pattern='{}']", s, pattern, e);
}
return false;
}
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, 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
*
* @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();
}
/**
* 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();
}
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 week (depends on locale)
*/
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 week (does not depend on locale, monday is always the first day in a week)
*
* @see "ISO 8601"
*/
public static Date truncDateToIsoWeek(Date d) {
if (d == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(d);
truncCalendarToIsoWeek(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 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);
}
public static void truncCalendarToWeek(Calendar c, int adjustIncrement) {
truncCalendarToWeek(c, adjustIncrement, Calendar.getInstance().getFirstDayOfWeek());
}
/**
* Truncates to monday, see ISO 8601
*
* @param c
* Calendar to truncate
* @param adjustIncrement
* -1 to back in time, +1 to go forward
*/
public static void truncCalendarToIsoWeek(Calendar c, int adjustIncrement) {
truncCalendarToWeek(c, adjustIncrement, Calendar.MONDAY);
}
/**
* truncate the calendar to week
*
* @param adjustIncrement
* +1 or -1
*/
public static void truncCalendarToWeek(Calendar c, int adjustIncrement, int firstDayOfWeek) {
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);
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 compareTo(minDate, d) <= 0 && compareTo(d, 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 compareTo(toDate, minDate) >= 0;
}
else if (toDate == null) {
return compareTo(fromDate, maxDate) <= 0;
}
else {
return compareTo(fromDate, maxDate) <= 0 && compareTo(toDate, 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 (compareTo(d, 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 (compareTo(d, min) < 0) {
min = d;
}
}
}
return min;
}
/**
* <b>Timestamp and Date:</b> {@link Timestamp#equals(Object)} is not symmetric. E.g. when comparing a Date d and
* Timestamp t, d.equals(t) may return true while t.equals(d) will always return false. This is not "expected" and
* inconvenient when performing operations like sorting on collections containing both Dates and Timestamps.
* Therefore, this method handles <code>java.sql.Timestamp</code> specifically to provide a symmetric implementation
* of the equivalence comparison.
* <p>
* <code>java.sql.Timestamp</code> is a subclass of <code>java.util.Date</code>, which additionally allows to specify
* fractional seconds to a precision of nanoseconds. This method returns <code>true</code>, if and only if both
* arguments of Type <code>java.util.Date</code> or <code>java.sql.Timestamp</code> represent the same point in time
* at full precision.
*/
@SuppressWarnings("squid:S1201")
public static boolean equals(Date a, Date b) {
return a == b || (a != null && b != null && compareTo(a, b) == 0);
}
/**
* Both parameters must be non-null. Implementation of {@code a.compareTo(b)} that works for Timestamp and Date.
* <p>
* <b>Timestamp and Date:</b> {@link Timestamp#compareTo(Date)} is not symmetric. E.g. when comparing a Date d and
* Timestamp t, d.compareTo(t) may return true while t.compareTo(d) returns false. This is not "expected" and
* inconvenient when performing operations like sorting on collections containing both Dates and Timestamps.
* Therefore, this method handles <code>java.sql.Timestamp</code> specifically to provide a symmetric implementation
* of the comparison operation.
* <p>
* On Java 8 and below, {@link Date#compareTo} returns erroneous results when the argument is a Timestamp, due to
* ignoring the fractional seconds part of the Timestamp (see JDK-8135055). Due to this and in order to retain full
* precision, {@link Timestamp#compareTo} must be called. This method returns <code>0</code> if and only if both
* arguments of Type <code>java.util.Date</code> or <code>java.sql.Timestamp</code> represent the same point in time
* at full precision.
* <p>
* Not null safe, see {@link ObjectUtility#compareTo} for a null-safe implementation of compareTo.
*/
public static int compareTo(Date a, Date b) {
if (b instanceof java.sql.Timestamp && !(a instanceof java.sql.Timestamp)) {
// ensure Timestamp.compareTo is called, not Date.compareTo
// note: could also call use -1 * b.compareTo(a) here, but that equivalence is not specified and a new Timestamp object would be created regardless (in Timestamp.compareTo)
return (new Timestamp(a.getTime())).compareTo(b);
}
return a.compareTo(b);
}
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, NlsLocale.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};
}
}
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 = t / DAY_MILLIS;
// range check
if (d < 0) {
d = 0d;
}
if (d > 1) {
d = 1d;
}
return d;
}
/**
* Converts a {@link Date} to a {@link LocalDate} using the default time-zone of the system.
*/
public static LocalDate toLocalDate(Date d) {
if (d == null) {
return null;
}
return d.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
}
/**
* Converts a {@link Date} to a {@link LocalDateTime} using the default time-zone of the system.
*/
public static LocalDateTime toLocalDateTime(Date d) {
if (d == null) {
return null;
}
return d.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
}
/**
* Converts a {@link LocalDate} to a {@link Date} using the default time-zone of the system. The time will be set to
* midnight.
*/
public static Date toUtilDate(LocalDate localDate) {
if (localDate == null) {
return null;
}
return Date.from(localDate.atStartOfDay()
.atZone(ZoneId.systemDefault())
.toInstant());
}
/**
* Converts a {@link LocalDateTime} to a {@link Date} using the default time-zone of the system.
*/
public static Date toUtilDate(LocalDateTime localDateTime) {
if (localDateTime == null) {
return null;
}
return Date.from(localDateTime
.atZone(ZoneId.systemDefault())
.toInstant());
}
/**
* 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>
*
* @param start
* the start date, inclusive
* @param end
* the end date, exclusive
* @return <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);
}
/**
* Returns the absolute value of hours between <code>start</code> and <code>end</code>. Both input values are first
* truncated to hours, i.e. any hour fractions (minutes, seconds) will be ignored.
* <p>
* <b>Examples:</b>
* <li>start = 2020-01-01 15:02:03, end = 2020-01-02 13:01:05, result = 22
* <li>start = 2020-01-02 13:01:05, end = 2020-01-01 15:02:03, result = 22
* <li>start = 2020-01-01 15:02:03, end = 2020-01-01 15:02:03, result = 0
* <li>start = 2020-01-01 15:02:03, end = 2020-01-01 15:59:59, result = 0
* <li>start = 2020-01-01 15:02:03, end = 2020-01-01 16:00:00, result = 1
* </ul>
*
* @param start
* the start date, inclusive
* @param end
* the end date, exclusive
* @return <code>-1</code> in case of an error (e.g. parameter is null)
*/
public static int getHoursBetween(Date start, Date end) {
if (start == null || end == null) {
return -1;
}
Calendar startDate = DateUtility.convertDate(start);
DateUtility.truncCalendarToHour(startDate);
Calendar endDate = DateUtility.convertDate(end);
DateUtility.truncCalendarToHour(endDate);
long endL = endDate.getTimeInMillis() + endDate.getTimeZone().getOffset(endDate.getTimeInMillis());
long startL = startDate.getTimeInMillis() + startDate.getTimeZone().getOffset(startDate.getTimeInMillis());
int numHours = (int) ((endL - startL) / HOUR_MILLIS);
return Math.abs(numHours);
}
}