package org.magenta.random;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Ticker;
import com.google.common.collect.Range;
/**
* Helper class for {@link Date} generation.
*
* @author ngagnon
*
*/
public class RandomDate {
private static final Range<Date> ALL = Range.all();
private static final Range<Date> FROM_1970_TO_2070 = Range.closed(new Date(0), new Date(1000L * 60 * 60 * 24 * 365 * 100));
private RandomLong longs;
private long resolutionInMillis;
private Range<Date> constraint;
private Ticker ticker;
/**
* Constructs an instance using a resolution of 1 millis and a date range
* between 1970 and 2070.
*
* @param longs
* from which moment will be randomly selected.
*/
public RandomDate(RandomLong longs) {
this(1L, FROM_1970_TO_2070, longs, Ticker.systemTicker());
}
/**
* Full constructor.
*
* @param resolutionInMillis
* resolution of the generated time.
* @param constraint
* the date range from which to pick a date
* @param longs
* the random longs from which moments will be randomly selected.
* @param ticker
* a ticker to read the current time
*/
public RandomDate(long resolutionInMillis, Range<Date> constraint, RandomLong longs, Ticker ticker) {
this.resolutionInMillis = resolutionInMillis;
this.constraint = constraint;
this.longs = longs;
this.ticker = ticker;
}
/**
* Randomly select a date included in this {@link RandomDate} defined range.
*
* @return a randomly generated date
*/
public Date any() {
return any(ALL);
}
/**
* Randomly select a date within the specified <code>range</code> that
* intersects with this {@link RandomDate} defined range.
*
* @param range
* a range
* @return a randomly generated date
*/
public Date any(Range<Date> range) {
Range<Date> r = range.intersection(constraint);
if (r.isEmpty()) {
throw new IllegalStateException(String.format(
"The intersection of the passed in range %s and this class constrained range %s result in a empty range", range, constraint));
}
Range<Long> dateRangeInLong;
if (r.hasLowerBound() && r.hasUpperBound()) {
dateRangeInLong = Range.range(r.lowerEndpoint().getTime(), r.lowerBoundType(), r.upperEndpoint().getTime(), r.upperBoundType());
} else if (r.hasLowerBound()) {
dateRangeInLong = Range.downTo(r.lowerEndpoint().getTime(), r.lowerBoundType());
} else if (r.hasUpperBound()) {
dateRangeInLong = Range.upTo(r.upperEndpoint().getTime(), r.upperBoundType());
} else {
dateRangeInLong = Range.all();
}
long dateInTime = longs.resolution(resolutionInMillis).any(dateRangeInLong);
Date randomDate = new Date(dateInTime);
return randomDate;
}
/**
* Randomly select a date in the future (a moment after now) within the
* specified <code>period</code> of <code>unit</code>.
*
* @param period
* the period from which to select a date
* @param unit
* the unit of the period
* @return a randomly generated date
*/
public Date anyInTheFuture(long period, TimeUnit unit) {
Date today = new Date(TimeUnit.MILLISECONDS.convert(ticker.read(), TimeUnit.NANOSECONDS));
Date after = new Date(today.getTime() + (TimeUnit.MILLISECONDS.convert(period, unit)));
Range<Date> range = Range.closed(today, after);
return any(range);
}
/**
* Randomly select a date in the past (a moment after now) within the
* specified <code>period</code> of <code>unit</code>.
*
* @param period
* the period from which to select a date
* @param unit
* the unit of the period
* @return a randomly generated date
*/
public Date anyInThePast(long period, TimeUnit unit) {
Date today = new Date(TimeUnit.MILLISECONDS.convert(ticker.read(), TimeUnit.NANOSECONDS));
Date before = new Date(today.getTime() - (TimeUnit.MILLISECONDS.convert(period, unit)));
Range<Date> range = Range.closed(before, today);
return any(range);
}
/**
* Randomly select a date after a specified moment within the specified
* <code>period</code> of <code>unit</code>.
*
* @param from
* the initial moment
* @param period
* the period from which to select a date
* @param unit
* the unit of the period
* @return a randomly generated date
*/
public Date anyInTheNext(Date from, int period, TimeUnit unit) {
Date after = new Date(from.getTime() + (TimeUnit.MILLISECONDS.convert(period, unit)));
Range<Date> range = Range.closed(from, after);
return any(range);
}
/**
* Randomly select a date before a specified moment within the specified
* <code>period</code> of <code>unit</code>.
*
* @param to
* the final moment
* @param period
* the period from which to select a date
* @param unit
* the unit of the period
* @return a randomly generated date
*/
public Date anyInTheLast(Date to, int period, TimeUnit unit) {
Date before = new Date(to.getTime() - (TimeUnit.MILLISECONDS.convert(period, unit)));
Range<Date> range = Range.closed(before, to);
return any(range);
}
/**
* The resolution in milliseconds of the time to generate. E.G. a value of
* "1000" will generate random date that are separated by a minimum of 1
* second.
*
* @param resolutionInMillis the resolution
* @return this instance
*/
public RandomDate resolution(long resolutionInMillis) {
return new RandomDate(resolutionInMillis, constraint, longs, ticker);
}
/**
* Constraint this instant to the specified range. The real range will in fact be an intersection of the current range and the one specified.
*
* @param constraint the range
* @return this instance
*/
public RandomDate constraint(Range<Date> constraint) {
return new RandomDate(resolutionInMillis, this.constraint.intersection(constraint), longs, ticker);
}
}