/*
* 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 com.addthis.hydra.data.filter.value;
import java.util.concurrent.TimeUnit;
import com.addthis.basis.util.JitterClock;
import com.addthis.bundle.util.ValueUtil;
import com.addthis.bundle.value.ValueArray;
import com.addthis.bundle.value.ValueFactory;
import com.addthis.bundle.value.ValueObject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import static com.google.common.base.Preconditions.checkArgument;
/**
* This {@link AbstractValueFilter ValueFilter} <span class="hydra-summary">returns one or more formatted time values</span>.
* <p/>
* <p>The input to this filter is assumed to be a date in Unix milliseconds.
* This behavior can be changed using the {@link #now now} or {@link #defaultNow defaultNow} fields.
* The output is an array of length {@link #rangeDays rangeDays} or {@link #rangeHours rangeHours}.
* For example if rangeDays is 10 then an array of length 10 is returned containing a formatted time
* string for the input day and each of the next nine days. To begin counting at 1 instead of 0,
* using the {@link #offsetHours offsetHours} or {@link #offsetDays offsetDays} field.
* <p/>
* <p>If the rangeDays and rangeHours parameters are not used then this filter
* returns a single string value. If rangeDays has the value N for N > 0, then
* this filter returns an array of N elements. Similarly an array is returned
* if rangeHours is used.</p>
* <p/>
* <p>Example:</p>
* <pre>
* // return yesterday's date as a string
* {time-range {offsetDays:-1, now:true, format:"YYMMdd"}}
*
* // return an array of yesterday's, today's, and tomorrow's dates
* {time-range {offsetDays:-1, rangeDays:3, now:true, format:"YYMMdd"}}
*
* // return an array containing the input date and the next two days
* {time-range {rangeDays:3, format:"YYMMdd"}}
* </pre>
*
* @user-reference
*/
public class ValueFilterTimeRange extends AbstractValueFilter {
/**
* The output format using the
* <a href="http://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat
* .html">DateTimeFormat</a>.
* This field is required.
*/
private final String format;
/**
* Offset the input time by this number of days. Default is 0.
*/
private final int offsetDays;
/**
* Offset the input time by this number of hours. Default is 0.
*/
private final int offsetHours;
/**
* The number of days to return. Cannot be used together in conjunction with rangeHours.
*/
private final int rangeDays;
/**
* The number of hours to return. Cannot be used together in conjunction with rangeDays.
*/
private final int rangeHours;
/**
* If true, use the current time when the input is 0. Default is false.
*/
private final boolean defaultNow;
/**
* If true, then ignore the input value and use the current time. Default is false.
*/
private final boolean now;
private final DateTimeFormatter date;
@JsonCreator
public ValueFilterTimeRange(@JsonProperty(value = "format", required = true) String format,
@JsonProperty("offsetDays") int offsetDays,
@JsonProperty("offsetHours") int offsetHours,
@JsonProperty("rangeDays") int rangeDays,
@JsonProperty("rangeHours") int rangeHours,
@JsonProperty("defaultNow") boolean defaultNow,
@JsonProperty("now") boolean now) {
checkArgument(!(rangeHours > 0 && rangeDays > 0), "rangeHours and rangeDays cannot both be > 0");
this.format = format;
this.offsetDays = offsetDays;
this.offsetHours = offsetHours;
this.rangeDays = rangeDays;
this.rangeHours = rangeHours;
this.defaultNow = defaultNow;
this.now = now;
this.date = DateTimeFormat.forPattern(format);
}
private ValueArray getTimeRange(long time, int range, TimeUnit timeUnit) {
ValueArray arr = ValueFactory.createArray(range);
for (int i = 0; i < range; i++) {
arr.add(ValueFactory.create(date.print(time + timeUnit.toMillis(i))));
}
return arr;
}
@Override
public ValueObject filterValue(ValueObject value) {
try {
long time = now ?
JitterClock.globalTime() :
ValueUtil.asNumberOrParseLong(value, 10).asLong().getLong();
if (time == 0 && defaultNow) {
time = JitterClock.globalTime();
}
if (offsetDays != 0) {
time += TimeUnit.DAYS.toMillis(offsetDays);
}
if (offsetHours != 0) {
time += TimeUnit.HOURS.toMillis(offsetHours);
}
if (rangeHours > 0) {
return getTimeRange(time, rangeHours, TimeUnit.HOURS);
}
if (rangeDays > 0) {
return getTimeRange(time, rangeDays, TimeUnit.DAYS);
}
return ValueFactory.create(date.print(time));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}