Major rework of TimeUtility and all funcionallity using it.

+ "Raw" data in the Power;Challenge page is now estimated to the "real" value.
+ Eclipse classpath cleanup
This commit is contained in:
Daniel Collin 2016-02-12 09:59:55 +01:00
parent ed3497fba4
commit 20c1f7df50
8 changed files with 487 additions and 296 deletions

View file

@ -2,7 +2,6 @@ package se.hal.deamon;
import se.hal.HalContext;
import se.hal.intf.HalDaemon;
import se.hal.intf.HalSensorData;
import se.hal.struct.Sensor;
import se.hal.intf.HalSensorData.AggregationMethod;
import se.hal.util.TimeUtility;
@ -23,6 +22,18 @@ import java.util.logging.Logger;
public class SensorDataAggregatorDaemon implements HalDaemon {
private static final Logger logger = LogUtil.getLogger();
public enum AggregationPeriodLength{
second,
minute,
fiveMinutes,
fifteenMinutes,
hour,
day,
week,
month,
year
}
public void initiate(ScheduledExecutorService executor){
executor.scheduleAtFixedRate(this, 0, TimeUtility.FIVE_MINUTES_IN_MS, TimeUnit.MILLISECONDS);
@ -50,16 +61,16 @@ public class SensorDataAggregatorDaemon implements HalDaemon {
logger.fine("The sensor is of type: " + sensor.getDeviceData().getClass().getName());
logger.fine("aggregating raw data up to a day old into five minute periods");
aggregateRawData(sensor, TimeUtility.FIVE_MINUTES_IN_MS, TimeUtility.DAY_IN_MS, 5);
aggregateRawData(sensor, AggregationPeriodLength.fiveMinutes, TimeUtility.DAY_IN_MS, 5);
logger.fine("aggregating raw data up to a week old into five minute periods");
aggregateRawData(sensor, TimeUtility.HOUR_IN_MS, TimeUtility.WEEK_IN_MS, 60);
logger.fine("aggregating raw data up to a week old into one hour periods");
aggregateRawData(sensor, AggregationPeriodLength.hour, TimeUtility.WEEK_IN_MS, 60);
logger.fine("aggregating raw data into one day periods");
aggregateRawData(sensor, TimeUtility.DAY_IN_MS, TimeUtility.INFINITY, 60*24);
aggregateRawData(sensor, AggregationPeriodLength.day, TimeUtility.INFINITY, 60*24);
logger.fine("aggregating raw data into one week periods");
aggregateRawData(sensor, TimeUtility.WEEK_IN_MS, TimeUtility.INFINITY, 60*24*7);
aggregateRawData(sensor, AggregationPeriodLength.week, TimeUtility.INFINITY, 60*24*7);
}
/**
@ -68,7 +79,7 @@ public class SensorDataAggregatorDaemon implements HalDaemon {
* @param ageLimitInMs Only aggregate up to this age
* @param toPeriodSizeInMs The period length in ms to aggregate to
*/
private void aggregateRawData(Sensor sensor, long toPeriodSizeInMs, long ageLimitInMs, int expectedSampleCount){
private void aggregateRawData(Sensor sensor, AggregationPeriodLength aggrPeriodLength, long ageLimitInMs, int expectedSampleCount){
long sensorId = sensor.getId();
AggregationMethod aggrMethod = sensor.getDeviceData().getAggregationMethod();
DBConnection db = HalContext.getDB();
@ -79,12 +90,21 @@ public class SensorDataAggregatorDaemon implements HalDaemon {
+ " WHERE sensor_id == ?"
+ " AND timestamp_end-timestamp_start == ?");
stmt.setLong(1, sensorId);
stmt.setLong(2, toPeriodSizeInMs-1);
switch(aggrPeriodLength){
case second: stmt.setLong(2, TimeUtility.SECOND_IN_MS-1); break;
case minute: stmt.setLong(2, TimeUtility.MINUTE_IN_MS-1); break;
case fiveMinutes: stmt.setLong(2, TimeUtility.FIVE_MINUTES_IN_MS-1); break;
case fifteenMinutes: stmt.setLong(2, TimeUtility.FIFTEEN_MINUTES_IN_MS-1); break;
case hour: stmt.setLong(2, TimeUtility.HOUR_IN_MS-1); break;
case day: stmt.setLong(2, TimeUtility.DAY_IN_MS-1); break;
case week: stmt.setLong(2, TimeUtility.WEEK_IN_MS-1); break;
default: logger.warning("aggregation period length is not supported."); return;
}
Long maxTimestampFoundForSensor = DBConnection.exec(stmt, new SimpleSQLResult<Long>());
if(maxTimestampFoundForSensor == null)
maxTimestampFoundForSensor = 0l;
long currentPeriodStartTimestamp = TimeUtility.getTimestampPeriodStart_UTC(toPeriodSizeInMs, System.currentTimeMillis());
long currentPeriodStartTimestamp = TimeUtility.getTimestampPeriodStart_UTC(aggrPeriodLength, System.currentTimeMillis());
logger.fine("Calculating periods... (from:"+ maxTimestampFoundForSensor +", to:"+ currentPeriodStartTimestamp +") with expected sample count: " + expectedSampleCount);
@ -98,7 +118,7 @@ public class SensorDataAggregatorDaemon implements HalDaemon {
stmt.setLong(2, maxTimestampFoundForSensor);
stmt.setLong(3, currentPeriodStartTimestamp);
stmt.setLong(4, System.currentTimeMillis()-ageLimitInMs);
DBConnection.exec(stmt, new DataAggregator(sensorId, toPeriodSizeInMs, expectedSampleCount, aggrMethod));
DBConnection.exec(stmt, new DataAggregator(sensorId, aggrPeriodLength, expectedSampleCount, aggrMethod));
} catch (SQLException e) {
logger.log(Level.SEVERE, null, e);
}
@ -108,14 +128,14 @@ public class SensorDataAggregatorDaemon implements HalDaemon {
* Internal class for aggregating data to the aggregated DB table
*/
private class DataAggregator implements SQLResultHandler<Object>{
private long sensorId = -1;
private long aggrTimeInMs = -1;
private int expectedSampleCount = -1;
private AggregationMethod aggrMethod = null;
private final long sensorId;
private final AggregationPeriodLength aggrPeriodLength;
private final int expectedSampleCount;
private final AggregationMethod aggrMethod;
public DataAggregator(long sensorId, long aggrTimeInMs, int expectedSampleCount, AggregationMethod aggrMethod) {
public DataAggregator(long sensorId, AggregationPeriodLength aggrPeriodLength, int expectedSampleCount, AggregationMethod aggrMethod) {
this.sensorId = sensorId;
this.aggrTimeInMs = aggrTimeInMs;
this.aggrPeriodLength = aggrPeriodLength;
this.expectedSampleCount = expectedSampleCount;
this.aggrMethod = aggrMethod;
}
@ -125,8 +145,9 @@ public class SensorDataAggregatorDaemon implements HalDaemon {
try{
HalContext.getDB().getConnection().setAutoCommit(false);
long currentPeriodTimestamp = 0;
int sum = 0;
long currentPeriodTimestampStart = 0;
long currentPeriodTimestampEnd = 0;
float sum = 0;
float confidenceSum = 0;
int samples = 0;
long highestSequenceId = Sensor.getHighestSequenceId(sensorId);
@ -137,35 +158,43 @@ public class SensorDataAggregatorDaemon implements HalDaemon {
throw new IllegalArgumentException("found entry for aggregation for the wrong sensorId (expecting: "+sensorId+", but was: "+result.getInt("sensor_id")+")");
}
long timestamp = result.getLong("timestamp");
long periodTimestamp = TimeUtility.getTimestampPeriodStart_UTC(this.aggrTimeInMs, timestamp);
if(currentPeriodTimestamp != 0 && periodTimestamp != currentPeriodTimestamp){
long dataPeriodTimestampStart = TimeUtility.getTimestampPeriodStart_UTC(this.aggrPeriodLength, timestamp);
long dataPerionTimestampEnd = TimeUtility.getTimestampPeriodEnd_UTC(this.aggrPeriodLength, timestamp);
if(currentPeriodTimestampStart != 0 && currentPeriodTimestampEnd != 0 && dataPeriodTimestampStart != currentPeriodTimestampStart){
float aggrConfidence = confidenceSum / (float)this.expectedSampleCount;
float data = -1;
switch(aggrMethod){
case SUM: data = sum; break;
case AVERAGE: data = sum/samples; break;
}
logger.finer("Calculated period starting at timestamp: " + currentPeriodTimestamp + ", data: " + sum + ", confidence: " + aggrConfidence + ", samples: " + samples + ", aggrMethod: " + aggrMethod);
logger.finer("Calculated period starting at timestamp: " + currentPeriodTimestampStart + ", data: " + sum + ", confidence: " + aggrConfidence + ", samples: " + samples + ", aggrMethod: " + aggrMethod);
preparedInsertStmt.setInt(1, result.getInt("sensor_id"));
preparedInsertStmt.setLong(2, ++highestSequenceId);
preparedInsertStmt.setLong(3, currentPeriodTimestamp);
preparedInsertStmt.setLong(4, currentPeriodTimestamp + this.aggrTimeInMs - 1);
preparedInsertStmt.setLong(3, currentPeriodTimestampStart);
preparedInsertStmt.setLong(4, currentPeriodTimestampEnd);
preparedInsertStmt.setFloat(5, data);
preparedInsertStmt.setFloat(6, aggrConfidence);
preparedInsertStmt.addBatch();
// Reset variables
currentPeriodTimestamp = periodTimestamp;
currentPeriodTimestampStart = dataPeriodTimestampStart;
currentPeriodTimestampEnd = dataPerionTimestampEnd;
confidenceSum = 0;
sum = 0;
samples = 0;
}
if(currentPeriodTimestamp == 0) currentPeriodTimestamp = periodTimestamp;
sum += result.getInt("data");
if(currentPeriodTimestampStart == 0){
currentPeriodTimestampStart = dataPeriodTimestampStart;
}
if(currentPeriodTimestampEnd == 0){
currentPeriodTimestampEnd = dataPerionTimestampEnd;
}
sum += result.getFloat("data");
confidenceSum += result.getFloat("confidence");
++samples;
}
DBConnection.execBatch(preparedInsertStmt);
HalContext.getDB().getConnection().commit();

View file

@ -1,6 +1,7 @@
package se.hal.deamon;
import se.hal.HalContext;
import se.hal.deamon.SensorDataAggregatorDaemon.AggregationPeriodLength;
import se.hal.intf.HalDaemon;
import se.hal.struct.Sensor;
import se.hal.util.TimeUtility;
@ -41,10 +42,10 @@ public class SensorDataCleanupDaemon implements HalDaemon {
public void cleanupSensor(Sensor sensor) {
if (sensor.getUser() != null) {
cleanupSensorData(sensor.getId(), TimeUtility.FIVE_MINUTES_IN_MS, TimeUtility.DAY_IN_MS); //clear 5-minute data older than a day
cleanupSensorData(sensor.getId(), TimeUtility.HOUR_IN_MS, TimeUtility.WEEK_IN_MS); //clear 1-hour data older than a week
//cleanupSensorData(sensor.getId(), TimeUtility.DAY_IN_MS, TimeUtility.INFINITY); //clear 1-day data older than infinity
//cleanupSensorData(sensor.getId(), TimeUtility.WEEK_IN_MS, TimeUtility.INFINITY); //clear 1-week data older than infinity
cleanupSensorData(sensor.getId(), AggregationPeriodLength.fiveMinutes, TimeUtility.DAY_IN_MS); //clear 5-minute data older than a day
cleanupSensorData(sensor.getId(), AggregationPeriodLength.hour, TimeUtility.WEEK_IN_MS); //clear 1-hour data older than a week
//cleanupSensorData(sensor.getId(), AggregationPeriodLength.day, TimeUtility.INFINITY); //clear 1-day data older than infinity
//cleanupSensorData(sensor.getId(), AggregationPeriodLength.week, TimeUtility.INFINITY); //clear 1-week data older than infinity
}
}
@ -56,7 +57,7 @@ public class SensorDataCleanupDaemon implements HalDaemon {
* @Param clearPeriodlength Will clear periods with this length
* @param olderThan Data must be older than this many ms to be cleared from the DB
*/
private void cleanupSensorData(long sensorId, long clearPeriodlength, long olderThan){
private void cleanupSensorData(long sensorId, AggregationPeriodLength cleanupPeriodlength, long olderThan){
DBConnection db = HalContext.getDB();
PreparedStatement stmt = null;
try {
@ -66,7 +67,16 @@ public class SensorDataCleanupDaemon implements HalDaemon {
+ "AND timestamp_end-timestamp_start == ?"
+ "AND timestamp_end < ?");
stmt.setLong(1, sensorId);
stmt.setLong(2, clearPeriodlength-1);
switch(cleanupPeriodlength){
case second: stmt.setLong(2, TimeUtility.SECOND_IN_MS-1); break;
case minute: stmt.setLong(2, TimeUtility.MINUTE_IN_MS-1); break;
case fiveMinutes: stmt.setLong(2, TimeUtility.FIVE_MINUTES_IN_MS-1); break;
case fifteenMinutes: stmt.setLong(2, TimeUtility.FIFTEEN_MINUTES_IN_MS-1); break;
case hour: stmt.setLong(2, TimeUtility.HOUR_IN_MS-1); break;
case day: stmt.setLong(2, TimeUtility.DAY_IN_MS-1); break;
case week: stmt.setLong(2, TimeUtility.WEEK_IN_MS-1); break;
default: logger.warning("cleanup period length is not supported."); return;
}
stmt.setLong(3, System.currentTimeMillis()-olderThan);
DBConnection.exec(stmt, new AggregateDataDeleter(sensorId));
} catch (SQLException e) {
@ -91,7 +101,7 @@ public class SensorDataCleanupDaemon implements HalDaemon {
if(sensorId != result.getInt("sensor_id")){
throw new IllegalArgumentException("Found entry for aggregation for the wrong sensorId (expecting: "+sensorId+", but was: "+result.getInt("sensor_id")+")");
}
logger.finer("Deleting sensor(id: "+ sensorId +") aggregate entry timestamp: "+ result.getLong("timestamp_start") +" - "+ result.getLong("timestamp_end") + " (" + TimeUtility.msToString(result.getLong("timestamp_end")-result.getLong("timestamp_start")) + ")");
logger.finer("Deleting sensor(id: "+ sensorId +") aggregate entry timestamp: "+ result.getLong("timestamp_start") +" - "+ result.getLong("timestamp_end") + " (" + TimeUtility.timeInMsToString(result.getLong("timestamp_end")-result.getLong("timestamp_start")) + ")");
preparedDeleteStmt.setInt(1, result.getInt("sensor_id"));
preparedDeleteStmt.setLong(2, result.getLong("sequence_id"));
preparedDeleteStmt.addBatch();

View file

@ -1,9 +1,11 @@
package se.hal.page;
import se.hal.HalContext;
import se.hal.deamon.SensorDataAggregatorDaemon.AggregationPeriodLength;
import se.hal.intf.HalHttpPage;
import se.hal.util.AggregateDataListSqlResult;
import se.hal.util.AggregateDataListSqlResult.*;
import se.hal.util.TimeUtility;
import se.hal.struct.Sensor;
import se.hal.struct.User;
import zutil.db.DBConnection;
@ -41,13 +43,13 @@ public class PCOverviewHttpPage extends HalHttpPage {
for(User user : users){
List<Sensor> userSensors = Sensor.getSensors(db, user);
for(Sensor sensor : userSensors){
minDataList.addAll(AggregateDataListSqlResult.getFiveMinuteAggregateData(db, sensor));
minDataList.addAll(AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, AggregationPeriodLength.fiveMinutes, TimeUtility.DAY_IN_MS));
hourDataList.addAll(AggregateDataListSqlResult.getHourAggregateData(db, sensor));
hourDataList.addAll(AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, AggregationPeriodLength.hour, TimeUtility.WEEK_IN_MS));
dayDataList.addAll(AggregateDataListSqlResult.getDayAggregateData(db, sensor));
dayDataList.addAll(AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, AggregationPeriodLength.day, TimeUtility.INFINITY));
weekDataList.addAll(AggregateDataListSqlResult.getWeekAggregateData(db, sensor));
weekDataList.addAll(AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, AggregationPeriodLength.week, TimeUtility.INFINITY));
}
}

View file

@ -1,15 +1,14 @@
package se.hal.page;
import se.hal.ControllerManager;
import se.hal.HalContext;
import se.hal.deamon.SensorDataAggregatorDaemon.AggregationPeriodLength;
import se.hal.intf.HalHttpPage;
import se.hal.struct.Event;
import se.hal.struct.Sensor;
import se.hal.struct.SwitchEventData;
import se.hal.util.AggregateDataListSqlResult;
import zutil.db.DBConnection;
import se.hal.util.HistoryDataListSqlResult;
import se.hal.util.HistoryDataListSqlResult.*;
import se.hal.util.TimeUtility;
import zutil.io.file.FileUtil;
import zutil.parser.Templator;
@ -52,7 +51,7 @@ public class SensorOverviewHttpPage extends HalHttpPage {
Templator tmpl = new Templator(FileUtil.find(DETAIL_TEMPLATE));
tmpl.set("sensor", sensor);
tmpl.set("history", history);
tmpl.set("aggregation", AggregateDataListSqlResult.getHourAggregateData(db, sensor));
tmpl.set("aggregation", AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, AggregationPeriodLength.hour, TimeUtility.WEEK_IN_MS));
return tmpl;
}
else {

View file

@ -1,5 +1,6 @@
package se.hal.util;
import se.hal.deamon.SensorDataAggregatorDaemon.AggregationPeriodLength;
import se.hal.struct.Sensor;
import zutil.db.DBConnection;
import zutil.db.SQLResultHandler;
@ -23,32 +24,14 @@ public class AggregateDataListSqlResult implements SQLResultHandler<ArrayList<Ag
this.username = uname;
}
}
public static List<AggregateData> getFiveMinuteAggregateData(DBConnection db, Sensor sensor) throws SQLException {
return getAggregateDataForPeriod(db, sensor, TimeUtility.FIVE_MINUTES_IN_MS, TimeUtility.DAY_IN_MS);
}
public static List<AggregateData> getHourAggregateData(DBConnection db, Sensor sensor) throws SQLException {
return getAggregateDataForPeriod(db, sensor, TimeUtility.HOUR_IN_MS, TimeUtility.WEEK_IN_MS);
}
public static List<AggregateData> getDayAggregateData(DBConnection db, Sensor sensor) throws SQLException {
return getAggregateDataForPeriod(db, sensor, TimeUtility.DAY_IN_MS, TimeUtility.INFINITY);
}
public static List<AggregateData> getWeekAggregateData(DBConnection db, Sensor sensor) throws SQLException {
return getAggregateDataForPeriod(db, sensor, TimeUtility.WEEK_IN_MS, TimeUtility.INFINITY);
}
private static List<AggregateData> getAggregateDataForPeriod(DBConnection db, Sensor sensor, long periodLengthInMs, long ageLimitInMs) throws SQLException {
public static List<AggregateData> getAggregateDataForPeriod(DBConnection db, Sensor sensor, AggregationPeriodLength aggrPeriodLength, long ageLimitInMs) throws SQLException {
PreparedStatement stmt = db.getPreparedStatement(
"SELECT user.username as username,"
+ " sensor_data_aggr.timestamp_start as timestamp_start,"
+ " sensor_data_aggr.timestamp_end as timestamp_end,"
+ " sensor_data_aggr.data as data,"
+ " sensor_data_aggr.confidence as confidence,"
+ periodLengthInMs + " as period_length"
+ " sensor_data_aggr.confidence as confidence "
+ " FROM sensor_data_aggr, user, sensor"
+ " WHERE sensor.id = sensor_data_aggr.sensor_id"
+ " AND sensor.id = ?"
@ -59,7 +42,16 @@ public class AggregateDataListSqlResult implements SQLResultHandler<ArrayList<Ag
+ " ORDER BY timestamp_start ASC");
stmt.setLong(1, sensor.getId());
stmt.setLong(2, sensor.getUser().getId());
stmt.setLong(3, periodLengthInMs-1);
switch(aggrPeriodLength){
case second: stmt.setLong(3, TimeUtility.SECOND_IN_MS-1); break;
case minute: stmt.setLong(3, TimeUtility.MINUTE_IN_MS-1); break;
case fiveMinutes: stmt.setLong(3, TimeUtility.FIVE_MINUTES_IN_MS-1); break;
case fifteenMinutes: stmt.setLong(3, TimeUtility.FIFTEEN_MINUTES_IN_MS-1); break;
case hour: stmt.setLong(3, TimeUtility.HOUR_IN_MS-1); break;
case day: stmt.setLong(3, TimeUtility.DAY_IN_MS-1); break;
case week: stmt.setLong(3, TimeUtility.WEEK_IN_MS-1); break;
default: throw new IllegalArgumentException("selected aggrPeriodLength is not supported");
}
stmt.setLong(4, (System.currentTimeMillis() - ageLimitInMs) );
return DBConnection.exec(stmt , new AggregateDataListSqlResult());
}
@ -73,21 +65,19 @@ public class AggregateDataListSqlResult implements SQLResultHandler<ArrayList<Ag
long timestampStart = result.getLong("timestamp_start");
long timestampEnd = result.getLong("timestamp_end");
long periodLength = result.getLong("period_length");
float data = result.getFloat("data");
String username = result.getString("username");
float confidence = result.getFloat("confidence");
//Calculate values for future use
//Calculate the data point
float data = result.getFloat("data"); //the "raw" recorded data
float estimatedData = data/confidence; //estimate the "real" value of the data by looking at the confidence value
Float limitedEstimatedData = (confidence > 0.5 ? estimatedData : null); //only present data with a confidence over 50%
//add null data point to list if one or more periods of data is missing before this
if(previousTimestampEnd != -1 && timestampStart-previousTimestampEnd > periodLength){
if(previousTimestampEnd != -1 && previousTimestampEnd+1 < timestampStart){
list.add(new AggregateData(previousTimestampEnd+1, "null", username));
}
list.add(new AggregateData(timestampStart, ""+ (data/1000.0), username)); //add this data point to list
list.add(new AggregateData(timestampStart, ""+ (estimatedData/1000.0), username)); //add this data point to list
//update previous end timestamp
previousTimestampEnd = timestampEnd;

View file

@ -3,146 +3,210 @@ package se.hal.util;
import java.util.Calendar;
import java.util.TimeZone;
public class TimeUtility {
import se.hal.deamon.SensorDataAggregatorDaemon.AggregationPeriodLength;
public class TimeUtility {
public static final long SECOND_IN_MS = 1000;
public static final long MINUTES_IN_MS = SECOND_IN_MS * 60;
public static final long FIVE_MINUTES_IN_MS = MINUTES_IN_MS * 5;
public static final long HOUR_IN_MS = MINUTES_IN_MS * 60;
public static final long MINUTE_IN_MS = SECOND_IN_MS * 60;
public static final long FIVE_MINUTES_IN_MS = MINUTE_IN_MS * 5;
public static final long FIFTEEN_MINUTES_IN_MS = MINUTE_IN_MS * 15;
public static final long HOUR_IN_MS = MINUTE_IN_MS * 60;
public static final long DAY_IN_MS = HOUR_IN_MS * 24;
public static final long WEEK_IN_MS = DAY_IN_MS * 7;
public static final long INFINITY = Long.MAX_VALUE; //sort of true
public static long getTimestampPeriodStart_UTC(long periodLengthInMs, long timestamp) throws NumberFormatException{
//return timestamp - (timestamp % periodLengthInMs);
return getTimestampPeriodStart(periodLengthInMs, timestamp, Calendar.getInstance(TimeZone.getTimeZone("UTC")));
}
/**
* Get the timstamp for the given timestamp floored with the period length. The result should point to the beginning of the timestamps period.
* @param periodLengthInMs The periods length to floor the timestamp with
* @param timestamp The timestamp to floor.
* @return
*/
public static long getTimestampPeriodStart_LOCAL(long periodLengthInMs, long timestamp) throws NumberFormatException{
return getTimestampPeriodStart(periodLengthInMs, timestamp, Calendar.getInstance());
}
private static long getTimestampPeriodStart(long periodLengthInMs, long timestamp, Calendar cal) throws NumberFormatException{
if(periodLengthInMs < 0 || timestamp < 0)
throw new NumberFormatException("argument must be positive");
public static long getTimestampPeriodStart_UTC(AggregationPeriodLength aggrPeriodLength, long timestamp) throws NumberFormatException{
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(timestamp);
boolean clear = false;
int weeks = getWeeksFromTimestamp(periodLengthInMs);
if(weeks > 0){
int currentWeek = cal.get(Calendar.WEEK_OF_YEAR);
cal.set(Calendar.WEEK_OF_YEAR, (currentWeek/weeks)*weeks);
clear = true;
switch(aggrPeriodLength){
case year:
cal.set(Calendar.DAY_OF_YEAR, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
break;
case month:
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
break;
case week:
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
break;
case day:
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
break;
case hour:
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
break;
case fiveMinutes:
cal.set(Calendar.MINUTE, (cal.get(Calendar.MINUTE)/5)*5);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
break;
case fifteenMinutes:
cal.set(Calendar.MINUTE, (cal.get(Calendar.MINUTE)/15)*15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
break;
case minute:
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
break;
case second:
cal.set(Calendar.MILLISECOND, 0);
break;
}
int days = getDaysFromTimestamp(periodLengthInMs);
if(days%7 > 0){
int currentDay = cal.get(Calendar.DAY_OF_YEAR);
cal.set(Calendar.DAY_OF_YEAR, (currentDay/days)*days);
clear = true;
}else if(clear){
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
}
int hours = getHourOfDayFromTimestamp(periodLengthInMs);
if(hours > 0){
int currentHour = cal.get(Calendar.HOUR_OF_DAY);
cal.set(Calendar.HOUR_OF_DAY, (currentHour/hours)*hours);
clear = true;
}else if(clear){
cal.set(Calendar.HOUR_OF_DAY, 0);
}
int minutes = getMinuteOfHourFromTimestamp(periodLengthInMs);
if(minutes > 0){
int currentMinute = cal.get(Calendar.MINUTE);
cal.set(Calendar.MINUTE, (currentMinute/minutes)*minutes);
clear = true;
}else if(clear){
cal.set(Calendar.MINUTE, 0);
}
int seconds = getSecondOfMinuteFromTimestamp(periodLengthInMs);
if(seconds > 0){
int currentSecond = cal.get(Calendar.SECOND);
cal.set(Calendar.SECOND, (currentSecond/seconds)*seconds);
clear = true;
}else if(clear){
cal.set(Calendar.SECOND, 0);
}
int milliseconds = getMillisecondInSecondFromTimestamp(periodLengthInMs);
if(milliseconds > 0){
int currentMillisecond = cal.get(Calendar.MILLISECOND);
cal.set(Calendar.MILLISECOND, (currentMillisecond/milliseconds)*milliseconds);
}else if(clear){
cal.set(Calendar.MILLISECOND, 0);
}
return cal.getTimeInMillis();
}
public static long getTimestampPeriodEnd_UTC(AggregationPeriodLength aggrPeriodLength, long timestamp) throws NumberFormatException{
long start = getTimestampPeriodStart_UTC(aggrPeriodLength, timestamp);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(start);
switch(aggrPeriodLength){
case year:
cal.add(Calendar.YEAR, 1);
break;
case month:
cal.add(Calendar.MONTH, 1);
break;
case week:
cal.add(Calendar.WEEK_OF_YEAR, 1);
break;
case day:
cal.add(Calendar.DAY_OF_YEAR, 1);
break;
case hour:
cal.add(Calendar.HOUR, 1);
break;
case fiveMinutes:
cal.add(Calendar.MINUTE, 5);
break;
case fifteenMinutes:
cal.add(Calendar.MINUTE, 15);
break;
case minute:
cal.add(Calendar.MINUTE, 1);
break;
case second:
cal.add(Calendar.SECOND, 1);
break;
}
return cal.getTimeInMillis()-1; //subtract one
}
public static int getMillisecondInSecondFromTimestamp(long ms) throws NumberFormatException{
if(ms < 0)
throw new NumberFormatException("argument must be positive");
return (int) (ms % SECOND_IN_MS);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(ms);
return cal.get(Calendar.MILLISECOND);
}
public static int getSecondOfMinuteFromTimestamp(long ms) throws NumberFormatException{
if(ms < 0)
throw new NumberFormatException("argument must be positive");
return (int) ((ms % MINUTES_IN_MS) / SECOND_IN_MS);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(ms);
return cal.get(Calendar.SECOND);
}
public static int getMinuteOfHourFromTimestamp(long ms) throws NumberFormatException{
if(ms < 0)
throw new NumberFormatException("argument must be positive");
return (int) ((ms % HOUR_IN_MS) / MINUTES_IN_MS);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(ms);
return cal.get(Calendar.MINUTE);
}
public static int getHourOfDayFromTimestamp(long ms) throws NumberFormatException{
if(ms < 0)
throw new NumberFormatException("argument must be positive");
return (int) ((ms % DAY_IN_MS) / HOUR_IN_MS);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(ms);
return cal.get(Calendar.HOUR_OF_DAY);
}
public static int getDaysFromTimestamp(long ms) throws NumberFormatException{
public static int getDayOfWeekFromTimestamp(long ms) throws NumberFormatException{
if(ms < 0)
throw new NumberFormatException("argument must be positive");
return (int) (ms / DAY_IN_MS);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(ms);
return cal.get(Calendar.DAY_OF_WEEK);
}
public static int getWeeksFromTimestamp(long ms) throws NumberFormatException{
public static int getDayOfMonthFromTimestamp(long ms) throws NumberFormatException{
if(ms < 0)
throw new NumberFormatException("argument must be positive");
return (int) (ms / WEEK_IN_MS);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(ms);
return cal.get(Calendar.DAY_OF_MONTH);
}
public static String msToString(long ms) throws NumberFormatException{
public static int getDayOfYearFromTimestamp(long ms) throws NumberFormatException{
if(ms < 0)
throw new NumberFormatException("argument must be positive");
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(ms);
return cal.get(Calendar.DAY_OF_YEAR);
}
public static int getWeekOfYearFromTimestamp(long ms) throws NumberFormatException{
if(ms < 0)
throw new NumberFormatException("argument must be positive");
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(ms);
return cal.get(Calendar.WEEK_OF_YEAR);
}
public static int getMonthOfYearFromTimestamp(long ms) throws NumberFormatException{
if(ms < 0)
throw new NumberFormatException("argument must be positive");
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(ms);
return cal.get(Calendar.MONTH);
}
public static int getYearFromTimestamp(long ms) throws NumberFormatException{
if(ms < 0)
throw new NumberFormatException("argument must be positive");
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(ms);
return cal.get(Calendar.YEAR);
}
public static String timeInMsToString(long ms) throws NumberFormatException{
if(ms < 0)
throw new NumberFormatException("argument must be positive");
String retval = "";
int weeks = getWeeksFromTimestamp(ms);
int weeks = (int) (ms / WEEK_IN_MS);
if(weeks > 0){
retval += weeks + "w+";
}
int days = getDaysFromTimestamp(ms) % 7;
int days = ((int) (ms / DAY_IN_MS)) % 7;
if(days > 0){
retval += days + "d+";
}
int hours = getHourOfDayFromTimestamp(ms);
int hours = (int) ((ms % DAY_IN_MS) / HOUR_IN_MS);
retval += (hours<10?"0"+hours:hours);
int minutes = getMinuteOfHourFromTimestamp(ms);
int minutes = (int) ((ms % HOUR_IN_MS) / MINUTE_IN_MS);
retval += ":" + (minutes<10?"0"+minutes:minutes);
int seconds = getSecondOfMinuteFromTimestamp(ms);
int seconds = (int) ((ms % MINUTE_IN_MS) / SECOND_IN_MS);
retval += ":" + (seconds<10?"0"+seconds:seconds);
int milliseconds = getMillisecondInSecondFromTimestamp(ms);
int milliseconds = (int) (ms % SECOND_IN_MS);
retval += "." + (milliseconds<100?"0"+(milliseconds<10?"0"+milliseconds:milliseconds):milliseconds);
return retval;
}