org.integratedmodelling.engine.time.literals.TimeValue Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (C) 2007, 2015:
*
* - Ferdinando Villa
* - integratedmodelling.org
* - any other authors listed in @author annotations
*
* All rights reserved. This file is part of the k.LAB software suite,
* meant to enable modular, collaborative, integrated
* development of interoperable data and model components. For
* details, see http://integratedmodelling.org.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Affero General Public License
* Version 3 or any later version.
*
* This program 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
* Affero General Public License for more details.
*
* You should have received a copy of the Affero General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* The license is also available at: https://www.gnu.org/licenses/agpl.html
*******************************************************************************/
package org.integratedmodelling.engine.time.literals;
import org.integratedmodelling.api.lang.IParseable;
import org.integratedmodelling.api.time.ITimeInstant;
import org.integratedmodelling.common.time.TemporalPrecision;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabRuntimeException;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
* A parsed literal representing one point in time, parsed from a string that satisfies the JODA Time parser.
* The typical input is an ISO8601 date/time string, such as "2006-12-13T21:39:45.618-08:00". Linked to the
* DateTimeValue type as an extended literal. The same syntax can be used to define an entire time observation
* of type TemporalRecord using a literal.
*
* TODO parser should support yyyy, mm-yyyy and dd-mm-yyyy by simple pattern recognition, and set
* correspondent PRECISION values (YEAR, MONTH, DAY). Whatever doesn't match should be given to Yoda for ISO
* parsing and assumed MILLISECOND precision.
*
* @author Ferdinando Villa
*
*/
public class TimeValue implements ITimeInstant, IParseable {
int precision = TemporalPrecision.MILLISECOND;
public static String matchYear = "[0-2][0-9][0-9][0-9]";
public static String matchMonthYear = "[0-1][0-9]-[0-2][0-9][0-9][0-9]";
public static String matchDayMonthYear = "[0-3][0-9]-[0-1][0-9]-[0-2][0-9][0-9][0-9]";
DateTime value;
/* set to "now" */
public TimeValue() throws KlabException {
value = new DateTime();
}
public TimeValue(DateTime date) {
value = date;
}
public void parse(String s) {
try {
if (s.matches(matchYear)) {
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy");
value = fmt.parseDateTime(s);
precision = TemporalPrecision.YEAR;
} else if (s.matches(matchMonthYear)) {
DateTimeFormatter fmt = DateTimeFormat.forPattern("MM-yyyy");
value = fmt.parseDateTime(s);
precision = TemporalPrecision.MONTH;
} else if (s.matches(matchDayMonthYear)) {
DateTimeFormatter fmt = DateTimeFormat.forPattern("dd-MM-yyyy");
value = fmt.parseDateTime(s);
precision = TemporalPrecision.DAY;
} else {
value = new DateTime(s);
}
} catch (Exception e) {
throw new KlabRuntimeException(e);
}
}
public TimeValue(String s) {
parse(s);
}
public TimeValue(long d) {
this(new DateTime(d));
}
public int getValuesCount() {
return 1;
}
public DateTime getTimeData() {
return value;
}
public int getPrecision() {
return precision;
}
/**
* Compare at the resolution intrinsic in the date. Code should read easily.
*
* TODO no normalization of time zones is done - we should ensure both TZ are the same before comparing.
*
* @param v1
* @return true if comparable
*/
public boolean comparable(TimeValue v1) {
if (precision == TemporalPrecision.YEAR) {
return value.year().getMaximumValue() == v1.value.year().getMaximumValue();
} else if (precision == TemporalPrecision.MONTH) {
return value.year().equals(v1.value.year()) && value.monthOfYear().equals(v1.value.monthOfYear());
} else if (precision == TemporalPrecision.DAY) {
return value.year().equals(v1.value.year()) && value.monthOfYear().equals(v1.value.monthOfYear())
&& value.dayOfMonth().equals(v1.value.dayOfMonth());
}
return v1.value.equals(value);
}
@Override
public String toString() {
if (precision == TemporalPrecision.YEAR) {
return value.toString("yyyy");
} else if (precision == TemporalPrecision.MONTH) {
return value.toString("MM-yyyy");
} else if (precision == TemporalPrecision.DAY) {
return value.toString("dd-MM-yyyy");
}
return value.toString();
}
public boolean isIdentical(TimeValue obj) {
return (precision == obj.precision) && value.equals(obj.value);
}
public TimeValue leastPrecise(TimeValue v) {
if (comparable(v)) {
return new Integer(precision).compareTo(v.precision) > 0 ? this : v;
}
return null;
}
public TimeValue mostPrecise(TimeValue v) {
if (comparable(v)) {
return new Integer(precision).compareTo(v.precision) > 0 ? v : this;
}
return null;
}
public int getDayOfMonth() {
return value.getDayOfMonth();
}
public int getJulianDay() {
return value.getDayOfYear();
}
public int getMonth() {
return value.getMonthOfYear();
}
public int getYear() {
return value.getYear();
}
public int getNDaysInMonth() {
return value.dayOfMonth().withMaximumValue().getDayOfMonth();
}
public int getNDaysInYear() {
return value.dayOfYear().withMaximumValue().getDayOfYear();
}
@Override
public boolean equals(Object other) {
boolean ret = (other instanceof TimeValue);
if (ret) {
ret = precision == ((TimeValue) other).precision;
}
if (ret) {
ret = value.equals(((TimeValue) other).value);
}
return ret;
}
@Override
public int hashCode() {
return toString().hashCode();
}
/**
* Return the end of the implied extent according to the precision in the generating string.
*
* @return end of implied extent
*/
public TimeValue getEndOfImpliedExtent() {
TimeValue end = null;
switch (precision) {
case TemporalPrecision.MILLISECOND:
end = new TimeValue(value.plus(1));
break;
case TemporalPrecision.SECOND:
end = new TimeValue(value.plusSeconds(1));
break;
case TemporalPrecision.MINUTE:
end = new TimeValue(value.plusMinutes(1));
break;
case TemporalPrecision.HOUR:
end = new TimeValue(value.plusHours(1));
break;
case TemporalPrecision.DAY:
end = new TimeValue(value.plusDays(1));
break;
case TemporalPrecision.MONTH:
end = new TimeValue(value.plusMonths(1));
break;
case TemporalPrecision.YEAR:
end = new TimeValue(value.plusYears(1));
break;
}
return end;
}
// @Override
// public boolean is(Object object) {
// // TODO Auto-generated method stub
// return false;
// }
@Override
public String asText() {
// TODO luke did this - seemed straightforward - double check for correctness
return value.toString();
}
public int compareTo(TimeValue other) {
return this.value.compareTo(other.value);
}
/**
* Overridden DateTime.compareTo() function. Extracting milliseconds from two DateTime/ITimeInstant objects is
* probably more efficient than creating a new DateTime from other.getMillis() just for one comparison.
*/
@Override
public int compareTo(ITimeInstant other) {
long millis = value.getMillis();
long otherMillis = other.getMillis();
if (millis == otherMillis) {
return 0;
}
if (millis < otherMillis) {
return -1;
}
return 1;
}
@Override
public long getMillis() {
return value.getMillis();
}
}