/*
* Date.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST 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
* of the License, or (at your option) any later version.
*
* BEAST 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 BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.evolution.util;
import dr.util.Attribute;
import dr.util.NumberFormatter;
import java.util.Calendar;
import java.util.TimeZone;
/**
* A data class.
*
* @version $Id: Date.java,v 1.26 2005/05/24 20:25:57 rambaut Exp $
*
* @author Alexei Drummond
* @author Andrew Rambaut
*/
public class Date extends TimeScale implements Attribute {
public static final String DATE = "date";
private double precision = 0.0;
/**
* Constructor for relative to origin
* @param time the time in units relative to origin
* @param units the units of the given time
* @param backwards true if the time is earlier than the origin
* @param origin the absolute origin at a Date.
*/
public Date(double time, Type units, boolean backwards, java.util.Date origin) {
super(units, backwards, origin);
this.time = time;
}
/**
* Constructor for an absolute date.
* @param date the date
*/
public Date(java.util.Date date) {
super(Units.Type.YEARS, false);
origin = -1970.0;
initUsingDate(date);
}
/**
* Constructor for an absolute date.
* @param date the date
* @param units the units
*/
public Date(java.util.Date date, Type units) {
super(units, false);
initUsingDate(date);
}
/**
* Constructor for an absolute date with origin specified
* @param date the date
* @param units the units
* @param origin the origin as a date
*/
public Date(java.util.Date date, Type units, java.util.Date origin) {
super(units, false, origin);
initUsingDate(date);
}
/**
* Constructor of a relative age
* @param time the time relative to arbitrary zero point.
* @param units the units the time is measured in
* @param backwards true of the time is earlier than the zero point.
*/
public Date(double time, Type units, boolean backwards) {
super(units, backwards);
this.time = time;
}
/**
* Constructor for time a relative to origin
* @param origin the origin in given units from Jan 1st 1970
*/
private Date(double time, Type units, boolean backwards, double origin) {
super(units, backwards, origin);
this.time = time;
}
//************************************************************************
// Factory methods
//************************************************************************
/**
* Create an age representing the given age (time ago) in the given units
*/
public static Date createRelativeAge(double age, Type units) {
return new Date(age, units, true);
}
/**
* Create an age representing the given age (time ago) in the given units
* with an origin of the given date.
* The age represents the number units back in time from the origin.
*/
public static Date createTimeAgoFromOrigin(double age, Type units, java.util.Date origin) {
return new Date(age, units, true, origin);
}
/**
* Create an age representing the given age (time ago) in the given units
* with an origin as the given number of units since 1970.
* The age represents the number units back in time from the origin.
*/
public static Date createTimeAgoFromOrigin(double age, Type units, double origin) {
return new Date(age, units, true, origin);
}
/**
* Create an age representing the given age (time since) in the given units
* with an origin of the given date.
* The age represents the number units back in time from the origin.
*/
public static Date createTimeSinceOrigin(double age, Type units, java.util.Date origin) {
return new Date(age, units, false, origin);
}
/**
* Create an age representing the given age (time since) in the given units
* with an origin as the given number of units since 1970.
* The age represents the number units forwards in time from the origin.
*/
public static Date createTimeSinceOrigin(double age, Type units, double origin) {
return new Date(age, units, false, origin);
}
/**
* Create a date an sets units to Units.YEARS
*/
public static Date createDate(java.util.Date date) {
return new Date(date, Units.Type.YEARS);
}
//************************************************************************
// Private methods
//************************************************************************
private void initUsingDate(java.util.Date date) {
// get the number of milliseconds this date is after the 1st January 1970
long millisAhead = date.getTime();
double daysAhead = ((double)millisAhead)/MILLIS_PER_DAY;
switch (units) {
case DAYS: time = daysAhead;
break;
case MONTHS: time = daysAhead / DAYS_PER_MONTH;
break;
case YEARS:
//time = daysAhead / DAYS_PER_YEAR;
// more precise (so 1st Jan 2013 is 2013.0)
// to avoid timezone specific differences in date calculations, all dates and calendars are
// set to GMT.
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTime(date);
int year = cal.get(Calendar.YEAR);
long millis1 = cal.getTimeInMillis();
cal.set(year, Calendar.JANUARY, 1, 0, 0);
long millis2 = cal.getTimeInMillis();
cal.set(year + 1, Calendar.JANUARY, 1, 0, 0);
long millis3 = cal.getTimeInMillis();
double fractionalYear = ((double)(millis1 - millis2)) / (millis3 - millis2);
time = fractionalYear + year - 1970;
break;
default: throw new IllegalArgumentException();
}
if (time < getOrigin()) {
time = getOrigin() - time;
backwards = true;
} else {
time = time - getOrigin();
backwards = false;
}
}
/**
* Returns the time value that is relative to the origin
*/
public double getTimeValue() {
return time;
}
/**
* Returns the absolute time value (i.e., relative to zero).
*/
public double getAbsoluteTimeValue() {
if (isBackwards()) {
return getOrigin() - getTimeValue();
}
return getOrigin() + getTimeValue();
}
public boolean before(Date date) {
double newTime = convertTime(date.getTimeValue(), date);
if (isBackwards()) {
return getTimeValue() > newTime;
}
return getTimeValue() < newTime;
}
public boolean after(Date date) {
double newTime = convertTime(date.getTimeValue(), date);
if (isBackwards()) {
return getTimeValue() < newTime;
}
return getTimeValue() > newTime;
}
public boolean equals(Date date) {
double newTime = convertTime(date.getTimeValue(), date);
return getTimeValue() == newTime;
}
public String getAttributeName() { return DATE; }
public Object getAttributeValue() { return this; }
public String toString() {
if (isBackwards()) {
return formatter.format(time).trim() + " " + unitString(time) + " ago";
} else {
return formatter.format(time).trim() + " " + unitString(time);
}
}
private double time;
private NumberFormatter formatter = new NumberFormatter(5);
public void setPrecision(double precision) {
this.precision = precision;
}
public double getPrecision() {
return precision;
}
}