/*
* file: DateHelper.java
* author: Jon Iles
* copyright: (c) Packwood Software 2006
* date: Jan 18, 2006
*/
/*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
package net.sf.mpxj.common;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import net.sf.mpxj.Duration;
import net.sf.mpxj.ProjectCalendar;
import net.sf.mpxj.Task;
import net.sf.mpxj.TimeUnit;
/**
* Utility methods for manipulating dates.
*/
public final class DateHelper
{
/**
* Constructor.
*/
private DateHelper()
{
// private constructor to prevent instantiation
}
/**
* Returns a new Date instance whose value
* represents the start of the day (i.e. the time of day is 00:00:00.000)
*
* @param date date to convert
* @return day start date
*/
public static Date getDayStartDate(Date date)
{
if (date != null)
{
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
date = cal.getTime();
}
return (date);
}
/**
* Returns a new Date instance whose value
* represents the end of the day (i.e. the time of days is 11:59:59.999)
*
* @param date date to convert
* @return day start date
*/
public static Date getDayEndDate(Date date)
{
if (date != null)
{
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.MILLISECOND, 999);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.HOUR_OF_DAY, 23);
date = cal.getTime();
}
return (date);
}
/**
* This method resets the date part of a date time value to
* a standard date (1/1/1). This is used to allow times to
* be compared and manipulated.
*
* @param date date time value
* @return date time with date set to a standard value
*/
public static Date getCanonicalTime(Date date)
{
if (date != null)
{
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.DAY_OF_YEAR, 1);
cal.set(Calendar.YEAR, 1);
cal.set(Calendar.MILLISECOND, 0);
date = cal.getTime();
}
return (date);
}
/**
* This method compares a target date with a date range. The method will
* return 0 if the date is within the range, less than zero if the date
* is before the range starts, and greater than zero if the date is after
* the range ends.
*
* @param startDate range start date
* @param endDate range end date
* @param targetDate target date
* @return comparison result
*/
public static int compare(Date startDate, Date endDate, Date targetDate)
{
return (compare(startDate, endDate, targetDate.getTime()));
}
/**
* This method compares a target date with a date range. The method will
* return 0 if the date is within the range, less than zero if the date
* is before the range starts, and greater than zero if the date is after
* the range ends.
*
* @param startDate range start date
* @param endDate range end date
* @param targetDate target date in milliseconds
* @return comparison result
*/
public static int compare(Date startDate, Date endDate, long targetDate)
{
int result = 0;
if (targetDate < startDate.getTime())
{
result = -1;
}
else
{
if (targetDate > endDate.getTime())
{
result = 1;
}
}
return (result);
}
/**
* Compare two dates, handling null values.
* TODO: correct the comparison order to align with Date.compareTo
*
* @param d1 Date instance
* @param d2 Date instance
* @return int comparison result
*/
public static int compare(Date d1, Date d2)
{
int result;
if (d1 == null || d2 == null)
{
result = (d1 == d2 ? 0 : (d1 == null ? 1 : -1));
}
else
{
long diff = d1.getTime() - d2.getTime();
result = ((diff == 0) ? 0 : ((diff > 0) ? 1 : -1));
}
return (result);
}
/**
* This utility method calculates the difference in working
* time between two dates, given the context of a task.
*
* @param task parent task
* @param date1 first date
* @param date2 second date
* @param format required format for the resulting duration
* @return difference in working time between the two dates
*/
public static Duration getVariance(Task task, Date date1, Date date2, TimeUnit format)
{
Duration variance = null;
if (date1 != null && date2 != null)
{
ProjectCalendar calendar = task.getCalendar();
if (calendar == null)
{
calendar = task.getParentFile().getDefaultCalendar();
}
if (calendar != null)
{
variance = calendar.getWork(date1, date2, format);
}
}
if (variance == null)
{
variance = Duration.getInstance(0, format);
}
return (variance);
}
/**
* Creates a date from the equivalent long value. This conversion
* takes account of the time zone.
*
* @param date date expressed as a long integer
* @return new Date instance
*/
public static Date getDateFromLong(long date)
{
TimeZone tz = TimeZone.getDefault();
return (new Date(date - tz.getRawOffset()));
}
/**
* Creates a timestamp from the equivalent long value. This conversion
* takes account of the time zone and any daylight savings time.
*
* @param timestamp timestamp expressed as a long integer
* @return new Date instance
*/
public static Date getTimestampFromLong(long timestamp)
{
TimeZone tz = TimeZone.getDefault();
Date result = new Date(timestamp - tz.getRawOffset());
if (tz.inDaylightTime(result) == true)
{
int savings;
if (HAS_DST_SAVINGS == true)
{
savings = tz.getDSTSavings();
}
else
{
savings = DEFAULT_DST_SAVINGS;
}
result = new Date(result.getTime() - savings);
}
return (result);
}
/**
* Create a Date instance representing a specific time.
*
* @param hour hour 0-23
* @param minutes minutes 0-59
* @return new Date instance
*/
public static Date getTime(int hour, int minutes)
{
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minutes);
cal.set(Calendar.SECOND, 0);
return (cal.getTime());
}
/**
* Given a date represented by a Calendar instance, set the time
* component of the date based on the hours and minutes of the
* time supplied by the Date instance.
*
* @param cal Calendar instance representing the date
* @param time Date instance representing the time of day
*/
public static void setTime(Calendar cal, Date time)
{
if (time != null)
{
Calendar startCalendar = Calendar.getInstance();
startCalendar.setTime(time);
cal.set(Calendar.HOUR_OF_DAY, startCalendar.get(Calendar.HOUR_OF_DAY));
cal.set(Calendar.MINUTE, startCalendar.get(Calendar.MINUTE));
cal.set(Calendar.SECOND, startCalendar.get(Calendar.SECOND));
}
}
/**
* Given a date represented by a Date instance, set the time
* component of the date based on the hours and minutes of the
* time supplied by the Date instance.
*
* @param date Date instance representing the date
* @param canonicalTime Date instance representing the time of day
* @return new Date instance with the required time set
*/
public static Date setTime(Date date, Date canonicalTime)
{
Date result;
if (canonicalTime == null)
{
result = date;
}
else
{
result = DateHelper.getDayStartDate(date);
long offset = canonicalTime.getTime() - CANONICAL_EPOCH.getTime();
result = new Date(result.getTime() + offset);
}
return result;
}
/**
* This internal method is used to convert from an integer representing
* minutes past midnight into a Date instance whose time component
* represents the start time.
*
* @param time integer representing the start time in minutes past midnight
* @return Date instance
*/
public static Date getTimeFromMinutesPastMidnight(Integer time)
{
Date result = null;
if (time != null)
{
int minutes = time.intValue();
int hours = minutes / 60;
minutes -= (hours * 60);
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, minutes);
cal.set(Calendar.HOUR_OF_DAY, hours);
result = cal.getTime();
}
return result;
}
/**
* First date supported by Microsoft Project: January 01 00:00:00 1984.
*/
public static final Date FIRST_DATE = DateHelper.getTimestampFromLong(441763200000L);
/**
* Last date supported by Microsoft Project: Friday December 31 23:59:00 2049.
*/
public static final Date LAST_DATE = DateHelper.getTimestampFromLong(2524607946000L);
/**
* Default value to use for DST savings if we are using a version
* of Java < 1.4.
*/
private static final int DEFAULT_DST_SAVINGS = 3600000;
private static Date CANONICAL_EPOCH = getCanonicalTime(getDayStartDate(new Date()));
/**
* Flag used to indicate the existence of the getDSTSavings
* method that was introduced in Java 1.4.
*/
private static boolean HAS_DST_SAVINGS;
static
{
Class<TimeZone> tz = TimeZone.class;
try
{
tz.getMethod("getDSTSavings", (Class[]) null);
HAS_DST_SAVINGS = true;
}
catch (NoSuchMethodException ex)
{
HAS_DST_SAVINGS = false;
}
}
}