Tstamp.java
package org.wattdepot.common.util.tstamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.List;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
/**
* Utility class that facilitates Timestamp representation and processing. There
* are too many classes already named "Timestamp", thus the abbreviated name.
*
* @author Philip Johnson
*/
public final class Tstamp {
/** Make this class noninstantiable. */
private Tstamp() {
// Do nothing.
}
private static final String factoryErrorMsg = "Bad DataTypeFactory";
private static long MILLISECS_PER_DAY = 24 * 60 * 60 * 1000;
/**
* Returns true if the passed string can be parsed into an
* XMLGregorianCalendar object.
*
* @param lexicalRepresentation The string representation.
* @return True if the string is a legal XMLGregorianCalendar.
*/
public static boolean isTimestamp(String lexicalRepresentation) {
try {
DatatypeFactory factory = DatatypeFactory.newInstance();
factory.newXMLGregorianCalendar(lexicalRepresentation);
return true;
}
catch (Exception e) {
return false;
}
}
/**
* Returns an XMLGregorianCalendar, given its string representation. Missing
* hours, minutes, second, millisecond, and timezone fields are given
* defaults.
*
* @param rep The string representation.
* @return The timestamp.
* @throws Exception If the string cannot be parsed into a timestamp.
*/
public static XMLGregorianCalendar makeTimestamp(String rep) throws Exception {
DatatypeFactory factory = DatatypeFactory.newInstance();
long mills = factory.newXMLGregorianCalendar(rep).toGregorianCalendar().getTimeInMillis();
return makeTimestamp(mills);
}
/**
* Converts a javax.sql.Timestamp into a
* javax.xml.datatype.XMLGregorianCalendar.
*
* @param tstamp The javax.sql.Timestamp
* @return A new instance of a javax.xml.datatype.XmlGregorianCalendar
*/
public static XMLGregorianCalendar makeTimestamp(java.sql.Timestamp tstamp) {
DatatypeFactory factory = null;
try {
factory = DatatypeFactory.newInstance();
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTimeInMillis(tstamp.getTime());
return factory.newXMLGregorianCalendar(calendar);
}
catch (DatatypeConfigurationException e) {
throw new RuntimeException(factoryErrorMsg, e);
}
}
/**
* Converts the specified time in milliseconds into a
* javax.xml.datatype.XMLGregorianCalendar.
*
* @param timeInMillis the specified time in milliseconds to convert.
* @return A new instance of a javax.xml.datatype.XmlGregorianCalendar
*/
public static XMLGregorianCalendar makeTimestamp(long timeInMillis) {
DatatypeFactory factory = null;
try {
factory = DatatypeFactory.newInstance();
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTimeInMillis(timeInMillis);
return factory.newXMLGregorianCalendar(calendar);
}
catch (DatatypeConfigurationException e) {
throw new RuntimeException(factoryErrorMsg, e);
}
}
// /**
// * Converts the specified Day into a
// javax.xml.datatype.XMLGregorianCalendar.
// * @param day The day to be converted.
// * @return A new instance of a javax.xml.datatype.XmlGregorianCalendar.
// */
// public static XMLGregorianCalendar makeTimestamp(Day day) {
// DatatypeFactory factory = null;
// try {
// factory = DatatypeFactory.newInstance();
// GregorianCalendar calendar = new GregorianCalendar();
// calendar.setTimeInMillis(day.getDate().getTime());
// return factory.newXMLGregorianCalendar(calendar);
// }
// catch (DatatypeConfigurationException e) {
// throw new RuntimeException(factoryErrorMsg, e);
// }
// }
/**
* Returns a new XMLGregorianCalendar corresponding to the passed tstamp
* incremented by the number of days.
*
* @param tstamp The base date and time.
* @param days The number of days to increment. This can be a negative number.
* @return A new XMLGregorianCalendar instance representing the inc'd time.
*/
public static XMLGregorianCalendar incrementDays(XMLGregorianCalendar tstamp, int days) {
DatatypeFactory factory = null;
try {
factory = DatatypeFactory.newInstance();
GregorianCalendar calendar = new GregorianCalendar();
long millis = tstamp.toGregorianCalendar().getTimeInMillis();
millis += 1000L * 60 * 60 * 24 * days;
calendar.setTimeInMillis(millis);
return factory.newXMLGregorianCalendar(calendar);
}
catch (DatatypeConfigurationException e) {
throw new RuntimeException(factoryErrorMsg, e);
}
}
/**
* Returns a new XMLGregorianCalendar corresponding to the passed tstamp
* incremented by the number of hours.
*
* @param tstamp The base date and time.
* @param hours The number of hours to increment. This can be a negative
* number.
* @return A new XMLGregorianCalendar instance representing the inc'd time.
*/
public static XMLGregorianCalendar incrementHours(XMLGregorianCalendar tstamp, int hours) {
DatatypeFactory factory = null;
try {
factory = DatatypeFactory.newInstance();
GregorianCalendar calendar = new GregorianCalendar();
long millis = tstamp.toGregorianCalendar().getTimeInMillis();
millis += 1000L * 60 * 60 * hours;
calendar.setTimeInMillis(millis);
return factory.newXMLGregorianCalendar(calendar);
}
catch (DatatypeConfigurationException e) {
throw new RuntimeException(factoryErrorMsg, e);
}
}
/**
* Returns a new XMLGregorianCalendar corresponding to the passed tstamp
* incremented by the number of minutes.
*
* @param tstamp The base date and time.
* @param minutes The number of minutes to increment. This can be a negative
* number.
* @return A new XMLGregorianCalendar instance representing the inc'd time.
*/
public static XMLGregorianCalendar incrementMinutes(XMLGregorianCalendar tstamp, int minutes) {
DatatypeFactory factory = null;
try {
factory = DatatypeFactory.newInstance();
GregorianCalendar calendar = new GregorianCalendar();
long millis = tstamp.toGregorianCalendar().getTimeInMillis();
millis += 1000L * 60 * minutes;
calendar.setTimeInMillis(millis);
return factory.newXMLGregorianCalendar(calendar);
}
catch (DatatypeConfigurationException e) {
throw new RuntimeException(factoryErrorMsg, e);
}
}
/**
* Returns a new XMLGregorianCalendar corresponding to the passed tstamp
* incremented by the number of seconds.
*
* @param tstamp The base date and time.
* @param seconds The number of seconds to increment. This can be a negative
* number.
* @return A new XMLGregorianCalendar instance representing the inc'd time.
*/
public static XMLGregorianCalendar incrementSeconds(XMLGregorianCalendar tstamp, int seconds) {
DatatypeFactory factory = null;
try {
factory = DatatypeFactory.newInstance();
GregorianCalendar calendar = new GregorianCalendar();
long millis = tstamp.toGregorianCalendar().getTimeInMillis();
millis += 1000L * seconds;
calendar.setTimeInMillis(millis);
return factory.newXMLGregorianCalendar(calendar);
}
catch (DatatypeConfigurationException e) {
throw new RuntimeException(factoryErrorMsg, e);
}
}
/**
* Returns a new XMLGregorianCalendar corresponding to the passed tstamp
* incremented by the number of milliseconds.
*
* @param tstamp The base date and time.
* @param milliseconds The number of milliseconds to increment. This can be a
* negative number.
* @return A new XMLGregorianCalendar instance representing the inc'd time.
*/
public static XMLGregorianCalendar incrementMilliseconds(XMLGregorianCalendar tstamp,
long milliseconds) {
DatatypeFactory factory = null;
try {
factory = DatatypeFactory.newInstance();
GregorianCalendar calendar = new GregorianCalendar();
long millis = tstamp.toGregorianCalendar().getTimeInMillis();
millis += milliseconds;
calendar.setTimeInMillis(millis);
return factory.newXMLGregorianCalendar(calendar);
}
catch (DatatypeConfigurationException e) {
throw new RuntimeException(factoryErrorMsg, e);
}
}
/**
* Returns a new java.sql.Timestamp created from a
* javax.xml.datatype.XMLGregorianCalendar.
*
* @param calendar The XML timestamp.
* @return The SQL timestamp.
*/
public static java.sql.Timestamp makeTimestamp(XMLGregorianCalendar calendar) {
return new java.sql.Timestamp(calendar.toGregorianCalendar().getTimeInMillis());
}
/**
* Returns an XMLGregorianCalendar corresponding to the current time.
*
* @return The timestamp.
*/
public static XMLGregorianCalendar makeTimestamp() {
try {
DatatypeFactory factory = DatatypeFactory.newInstance();
return factory.newXMLGregorianCalendar(new GregorianCalendar());
}
catch (Exception e) {
throw new RuntimeException(factoryErrorMsg, e);
}
}
/**
* Returns true if tstamp is equal to or between start and end.
*
* @param start The start time.
* @param tstamp The timestamp to test.
* @param end The end time.
* @return True if tstamp is between start and end.
*/
public static boolean inBetween(XMLGregorianCalendar start, XMLGregorianCalendar tstamp,
XMLGregorianCalendar end) {
long startMillis = start.toGregorianCalendar().getTimeInMillis();
long endMillis = end.toGregorianCalendar().getTimeInMillis();
long tstampMillis = tstamp.toGregorianCalendar().getTimeInMillis();
return tstampMillis >= startMillis && tstampMillis <= endMillis;
}
/**
* Returns true if time1 > time2.
*
* @param time1 The first time.
* @param time2 The second time.
* @return True if time1 > time2
*/
public static boolean greaterThan(XMLGregorianCalendar time1, XMLGregorianCalendar time2) {
long time1Millis = time1.toGregorianCalendar().getTimeInMillis();
long time2Millis = time2.toGregorianCalendar().getTimeInMillis();
return time1Millis > time2Millis;
}
/**
* Returns the number of days between time1 and time2. Returns a negative
* number if day1 is after day2. Takes into account daylight savings time
* issues.
*
* @param day1 The first day.
* @param day2 The second day.
* @return The number of days between the two days.
*/
public static int daysBetween(XMLGregorianCalendar day1, XMLGregorianCalendar day2) {
return (int) (getUnixDay(day2) - getUnixDay(day1));
}
/**
* Returns a long representing the number of days since the Unix epoch to the
* passed day.
*
* @param day The day of interest.
* @return The number of days since the epoch.
*/
private static long getUnixDay(XMLGregorianCalendar day) {
GregorianCalendar greg = day.toGregorianCalendar();
long offset = greg.get(Calendar.ZONE_OFFSET) + greg.get(Calendar.DST_OFFSET);
long daysSinceEpoch = (long) Math.floor((double) (greg.getTime().getTime() + offset)
/ ((double) MILLISECS_PER_DAY));
return daysSinceEpoch;
}
/**
* Returns true if timeString1 > timeString2. Throws an unchecked
* IllegalArgument exception if the strings can't be converted to timestamps.
*
* @param timeString1 The first time.
* @param timeString2 The second time.
* @return True if time1 > time2
*/
public static boolean greaterThan(String timeString1, String timeString2) {
try {
DatatypeFactory factory = DatatypeFactory.newInstance();
XMLGregorianCalendar time1 = factory.newXMLGregorianCalendar(timeString1);
XMLGregorianCalendar time2 = factory.newXMLGregorianCalendar(timeString2);
return greaterThan(time1, time2);
}
catch (Exception e) {
throw new IllegalArgumentException("Illegal timestring", e);
}
}
/**
* Returns true if time1 < time2.
*
* @param time1 The first time.
* @param time2 The second time.
* @return True if time1 < time2
*/
public static boolean lessThan(XMLGregorianCalendar time1, XMLGregorianCalendar time2) {
long time1Millis = time1.toGregorianCalendar().getTimeInMillis();
long time2Millis = time2.toGregorianCalendar().getTimeInMillis();
return time1Millis < time2Millis;
}
/**
* Returns true if time1 equals time2.
*
* @param time1 The first time.
* @param time2 The second time.
* @return True if time1 equals time2
*/
public static boolean equal(XMLGregorianCalendar time1, XMLGregorianCalendar time2) {
long millis1 = time1.toGregorianCalendar().getTimeInMillis();
long millis2 = time2.toGregorianCalendar().getTimeInMillis();
return millis1 == millis2;
}
/**
* Returns differences between time1 and time2 in milliseconds.
*
* @param time1 Start.
* @param time2 End.
* @return Difference between two times in milliseconds.
*/
public static long diff(XMLGregorianCalendar time1, XMLGregorianCalendar time2) {
long millis1 = time1.toGregorianCalendar().getTimeInMillis();
long millis2 = time2.toGregorianCalendar().getTimeInMillis();
return millis2 - millis1;
}
/**
* Returns true if the passed timestamp indicates some time today or some time
* in the future.
*
* @param timestamp The timestamp of interest.
* @return True if it's today or some day in the future.
*/
public static boolean isTodayOrLater(XMLGregorianCalendar timestamp) {
XMLGregorianCalendar today = Tstamp.makeTimestamp();
boolean isToday = today.getYear() == timestamp.getYear()
&& today.getMonth() == timestamp.getMonth()
&& today.getDay() == timestamp.getDay();
boolean afterToday = today.toGregorianCalendar().getTimeInMillis() < timestamp
.toGregorianCalendar().getTimeInMillis();
return isToday || afterToday;
}
/**
* Returns true if the passed timestamp indicates some time yesterday or some
* time in the future. This is useful for the Commit/Churn DPDs, where the
* sensor typically sends data not from the current day, but from the day
* before.
*
* @param timestamp The timestamp of interest.
* @return True if it's today or some day in the future.
*/
public static boolean isYesterdayOrLater(XMLGregorianCalendar timestamp) {
XMLGregorianCalendar yesterday = Tstamp.incrementDays(Tstamp.makeTimestamp(), -1);
boolean isYesterday = yesterday.getYear() == timestamp.getYear()
&& yesterday.getMonth() == timestamp.getMonth()
&& yesterday.getDay() == timestamp.getDay();
boolean afterYesterday = yesterday.toGregorianCalendar().getTimeInMillis() < timestamp
.toGregorianCalendar().getTimeInMillis();
return isYesterday || afterYesterday;
}
/**
* Returns a newly created sorted list of tstamps from the passed collection.
*
* @param tstamps The timestamps to be sorted.
* @return A new list of tstamps, now in sorted order.
*/
public static List<XMLGregorianCalendar> sort(Collection<XMLGregorianCalendar> tstamps) {
List<XMLGregorianCalendar> tstampList = new ArrayList<XMLGregorianCalendar>(tstamps);
Collections.sort(tstampList, new TstampComparator());
return tstampList;
}
/**
* Helper function that prepares a List timestamps, between the start time and
* end time, at the given sampling interval.
*
* @param startTime The start of the range requested.
* @param endTime The start of the range requested.
* @param intervalMinutes The sampling interval requested in minutes.
* @return The List of XMLGregorianCalendars.
*/
public static List<XMLGregorianCalendar> getTimestampList(XMLGregorianCalendar startTime,
XMLGregorianCalendar endTime, int intervalMinutes) {
long intervalMilliseconds;
long rangeLength = Tstamp.diff(startTime, endTime);
long minutesToMilliseconds = 60L * 1000L;
if (intervalMinutes < 0) {
return null;
}
if (rangeLength <= 0) {
// either startTime == endTime, or startTime > endTime
return null;
}
else if (intervalMinutes == 0) {
// use default interval
intervalMilliseconds = rangeLength / 10;
}
else if ((intervalMinutes * minutesToMilliseconds) > rangeLength) {
// TODO BOGUS, should throw an exception so callers can distinguish
// between problems
return null;
}
else {
// got a good interval
intervalMilliseconds = intervalMinutes * minutesToMilliseconds;
}
// DEBUG
// System.out.format("%nstartTime=%s, endTime=%s, interval=%d min%n",
// startTime, endTime,
// intervalMilliseconds / minutesToMilliseconds);
// Build list of timestamps, starting with startTime, separated by
// intervalMilliseconds
List<XMLGregorianCalendar> timestampList = new ArrayList<XMLGregorianCalendar>();
XMLGregorianCalendar timestamp = startTime;
while (Tstamp.lessThan(timestamp, endTime)) {
timestampList.add(timestamp);
// System.out.format("timestamp=%s%n", timestamp); // DEBUG
timestamp = Tstamp.incrementMilliseconds(timestamp, intervalMilliseconds);
}
// add endTime to cover the last runt interval which is <=
// intervalMilliseconds
timestampList.add(endTime);
// System.out.format("timestamp=%s%n", endTime); // DEBUG
return timestampList;
}
/**
* Produces a list of n + 1 XMLGregorianCalendar instances that span the given
* time interval by dividing the interval by n.
*
* @param n The number of timestamps to generate.
* @param startTime The start of the interval.
* @param endTime The end of the interval.
* @return A List<XMLGregorianCalendar> of size n.
*/
public static List<XMLGregorianCalendar> getNTimestampList(int n, XMLGregorianCalendar startTime,
XMLGregorianCalendar endTime) {
long rangeLength = Tstamp.diff(startTime, endTime);
long sampleIntervalMilliseconds = rangeLength / n;
// Build list of timestamps, starting with startTime, separated by
// intervalMilliseconds
List<XMLGregorianCalendar> timestampList = new ArrayList<XMLGregorianCalendar>();
XMLGregorianCalendar timestamp = startTime;
while (Tstamp.lessThan(timestamp, endTime) || Tstamp.equal(timestamp, endTime)) {
timestampList.add(timestamp);
// System.out.format("timestamp=%s%n", timestamp); // DEBUG
timestamp = Tstamp.incrementMilliseconds(timestamp, sampleIntervalMilliseconds);
}
// add endTime to cover the last runt interval which is <=
// intervalMilliseconds
timestampList.add(endTime);
// System.out.format("timestamp=%s%n", endTime); // DEBUG
return timestampList;
}
}