/*
* chombo: Hadoop Map Reduce utility
* Author: Pranab Ghosh
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.chombo.util;
import java.util.List;
import java.util.Map;
/**
* @author pranab
*
*/
public class SeasonalAnalyzer {
private long parentCycleIndex;
private int cycleIndex;
private String seasonalCycleType;
private Map<Integer, Integer> hourRanges;
private boolean timeStampInMili;
private long timeZoneShiftSec;
private List<Pair<Integer, Integer>> timeRanges;
private long secToYear;
private int year;
public static final String QUARTER_HOUR_OF_DAY = "quarterHourOfDay";
public static final String QUARTER_HOUR_OF_WEEK_DAY = "quarterHourOfWeekDay";
public static final String QUARTER_HOUR_OF_WEEK_END_DAY = "quarterHourOfWeekEndDay";
public static final String HALF_HOUR_OF_DAY = "halfHourOfDay";
public static final String HALF_HOUR_OF_WEEK_DAY = "halfHourOfWeekDay";
public static final String HALF_HOUR_OF_WEEK_END_DAY = "halfHourOfWeekEndDay";
public static final String HOUR_OF_DAY = "hourOfDay";
public static final String HOUR_OF_WEEK_DAY = "hourOfWeekDay";
public static final String HOUR_OF_WEEK_END_DAY = "hourOfWeekEndDay";
public static final String DAY_OF_WEEK = "dayOfWeek";
public static final String WEEK_DAY_OF_WEEK = "weekDayOfWeek";
public static final String WEEK_END_DAY_OF_WEEK = "weekEndDayOfWeek";
public static final String HOUR_RANGE_OF_WEEK_DAY = "hourRangeOfWeekDay";
public static final String HOUR_RANGE_OF_WEEK_END_DAY = "hourRangeOfWeekEndDay";
public static final String WEEK_OF_YEAR = "weekOfYear";
public static final String MONTH_OF_YEAR = "monthOfYear";
public static final String ANY_TIME_RANGE = "anyTimeRange";
private static long secInWeek =7L * 24 * 60 * 60;
private static long secInDay =24L * 60 * 60;
private static long secInHour = 60L * 60;
private static long secInHalfHour = 30L * 60;
private static long secInQuarterHour = 15L * 60;
private static long secInYear = secInDay * 365;
private static int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/**
* @param seasonalCycleType
*/
public SeasonalAnalyzer(String seasonalCycleType) {
super();
this.seasonalCycleType = seasonalCycleType;
}
/**
* @param seasonalCycleType
* @param hourRanges
*/
public SeasonalAnalyzer(String seasonalCycleType,
Map<Integer, Integer> hourRanges) {
super();
this.seasonalCycleType = seasonalCycleType;
//key:hour value:hour group
this.hourRanges = hourRanges;
}
/**
* Calculates cycle index and parent cycle index
* @param timeStamp
*/
public int getCycleIndex(long timeStamp) {
//convert to sec and adjust for time stamp
if (timeStampInMili) {
timeStamp /= 1000;
}
timeStamp += timeZoneShiftSec;
long weekDayIndex = 0;
if (seasonalCycleType.equals(DAY_OF_WEEK)) {
parentCycleIndex = timeStamp / secInWeek;
cycleIndex = (int)((timeStamp % secInWeek) / secInDay);
} else if (seasonalCycleType.equals(WEEK_DAY_OF_WEEK)) {
parentCycleIndex = timeStamp / secInWeek;
cycleIndex = (int)((timeStamp % secInWeek) / secInDay);
if (cycleIndex > 4) {
cycleIndex = -1;
}
} else if (seasonalCycleType.equals(WEEK_END_DAY_OF_WEEK)) {
parentCycleIndex = timeStamp / secInWeek;
cycleIndex = (int)((timeStamp % secInWeek) / secInDay);
if (cycleIndex < 5) {
cycleIndex = -1;
}
} else if (seasonalCycleType.equals(HOUR_OF_DAY)) {
parentCycleIndex = timeStamp / secInDay;
cycleIndex = (int)((timeStamp % secInDay) / secInHour);
} else if (seasonalCycleType.equals(HOUR_OF_WEEK_DAY)) {
parentCycleIndex = timeStamp / secInDay;
weekDayIndex = parentCycleIndex % 7;
if (weekDayIndex < 5) {
cycleIndex = (int)((timeStamp % secInDay) / secInHour);
} else {
cycleIndex = -1;
}
} else if (seasonalCycleType.equals(HOUR_OF_WEEK_END_DAY)) {
parentCycleIndex = timeStamp / secInDay;
if (weekDayIndex > 4) {
cycleIndex = (int)((timeStamp % secInDay) / secInHour);
} else {
cycleIndex = -1;
}
} else if (seasonalCycleType.equals(HALF_HOUR_OF_DAY)) {
parentCycleIndex = timeStamp / secInDay;
cycleIndex = (int)((timeStamp % secInDay) / secInHalfHour);
} else if (seasonalCycleType.equals(HALF_HOUR_OF_WEEK_DAY)) {
parentCycleIndex = timeStamp / secInDay;
weekDayIndex = parentCycleIndex % 7;
if (weekDayIndex < 5) {
cycleIndex = (int)((timeStamp % secInDay) / secInHalfHour);
} else {
cycleIndex = -1;
}
} else if (seasonalCycleType.equals(HALF_HOUR_OF_WEEK_END_DAY)) {
parentCycleIndex = timeStamp / secInDay;
weekDayIndex = parentCycleIndex % 7;
if (weekDayIndex > 4) {
cycleIndex = (int)((timeStamp % secInDay) / secInHalfHour);
} else {
cycleIndex = -1;
}
} else if (seasonalCycleType.equals(QUARTER_HOUR_OF_DAY)) {
parentCycleIndex = timeStamp / secInDay;
cycleIndex = (int)((timeStamp % secInDay) / secInQuarterHour);
} else if (seasonalCycleType.equals(QUARTER_HOUR_OF_WEEK_DAY)) {
parentCycleIndex = timeStamp / secInDay;
weekDayIndex = parentCycleIndex % 7;
if (weekDayIndex < 5) {
cycleIndex = (int)((timeStamp % secInDay) / secInQuarterHour);
} else {
cycleIndex = -1;
}
} else if (seasonalCycleType.equals(QUARTER_HOUR_OF_WEEK_END_DAY)) {
parentCycleIndex = timeStamp / secInDay;
weekDayIndex = parentCycleIndex % 7;
if (weekDayIndex > 4) {
cycleIndex = (int)((timeStamp % secInDay) / secInQuarterHour);
} else {
cycleIndex = -1;
}
} else if (seasonalCycleType.equals(HOUR_RANGE_OF_WEEK_DAY)) {
parentCycleIndex = timeStamp / secInDay;
weekDayIndex = parentCycleIndex % 7;
if (weekDayIndex < 5) {
int hourCycleIndex = (int)((timeStamp % secInDay) / secInHour);
Integer hourGroup = hourRanges.get(hourCycleIndex);
cycleIndex = hourGroup != null ? hourGroup : -1;
} else {
cycleIndex = -1;
}
} else if (seasonalCycleType.equals(HOUR_RANGE_OF_WEEK_END_DAY)) {
parentCycleIndex = timeStamp / secInDay;
weekDayIndex = parentCycleIndex % 7;
if (weekDayIndex >= 5) {
int hourCycleIndex = (int)((timeStamp % secInDay) / secInHour);
Integer hourGroup = hourRanges.get(hourCycleIndex);
cycleIndex = hourGroup != null ? hourGroup : -1;
} else {
cycleIndex = -1;
}
} else if (seasonalCycleType.equals(MONTH_OF_YEAR)) {
monthOfYearCycleIndex(timeStamp);
} else if (seasonalCycleType.equals(WEEK_OF_YEAR)) {
weekOfYearCycleIndex(timeStamp);
} else if (seasonalCycleType.equals(ANY_TIME_RANGE)) {
cycleIndex = -1;
int indx = 0;
for (Pair<Integer, Integer> timeRange : timeRanges) {
if (timeStamp >= timeRange.getLeft() && timeStamp <= timeRange.getRight()) {
cycleIndex = indx;
break;
}
++indx;
}
} else {
throw new IllegalArgumentException("invalid cycle type");
}
return cycleIndex;
}
/**
* @param hourRanges
*/
public void setHourRanges(Map<Integer, Integer> hourRanges) {
this.hourRanges = hourRanges;
}
/**
* @param timeRanges
*/
public void setTimeRanges(List<Pair<Integer, Integer>> timeRanges) {
this.timeRanges = timeRanges;
}
/**
* @param timeStampInMili
*/
public void setTimeStampInMili(boolean timeStampInMili) {
this.timeStampInMili = timeStampInMili;
}
/**
* @param timeZoneShiftHours
*/
public void setTimeZoneShiftHours(long timeZoneShiftHours) {
this.timeZoneShiftSec = timeZoneShiftHours * secInHour ;
}
/**
* @return
*/
public long getParentCycleIndex() {
return parentCycleIndex;
}
/**
* @param timeStamp
*/
private void monthOfYearCycleIndex(long timeStamp) {
//go up to year beginning
secToYearBeginning(timeStamp);
//month index
long remSec = timeStamp - secToYear;
daysInMonth[1] = year % 4 == 0 ? 29 : 28;
long secIntoYear = 0;
for (int i = 0; i < 12; ++i) {
secIntoYear += daysInMonth[i] + secInDay;
if (secIntoYear > remSec) {
cycleIndex = i - 1;
break;
}
}
parentCycleIndex = 0;
}
/**
* @param timeStamp
*/
private void weekOfYearCycleIndex(long timeStamp) {
//go up to year beginning
secToYearBeginning(timeStamp);
//week into beginning of year
long secToWeekYear = 0;
long week = secToYear / secInWeek;
long remainder = secToYear % secInWeek;
week += (remainder > 0 ? 1 : 0);
secToWeekYear = week * secInWeek;
//week index
cycleIndex = 0;
for (; secToWeekYear < timeStamp; ++cycleIndex) {
secToWeekYear += secInWeek;
}
parentCycleIndex = 0;
}
/**
* @param timeStamp
*/
private void secToYearBeginning(long timeStamp) {
//go upto year
secToYear = 0;
year = 1971;
long secInCurYear = 0;
for (; secToYear < timeStamp; ++year) {
secToYear += secInYear;
secInCurYear = secInYear;
if (year % 4 == 0) {
secToYear += secInDay;
secInCurYear += secInDay;
}
}
//back up to beginning of year
secToYear -= secInCurYear;
}
}