/**Copyright (c) 2015, Sana
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Sana nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.sana.util;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Hours;
import org.joda.time.Minutes;
import org.joda.time.Months;
import org.joda.time.Weeks;
import org.joda.time.Years;
/**
* Convenience wrappers around common calculations.
*
* @author Sana Development Team
*/
/**
*
*/
public class Functions {
public static final int MILLISECONDS = Calendar.MILLISECOND;
public static final int SECONDS = Calendar.SECOND;
public static final int MINUTES = Calendar.MINUTE;
public static final int HOURS = Calendar.HOUR;
public static final int DAYS = Calendar.DAY_OF_YEAR;
public static final int WEEKS = Calendar.WEEK_OF_YEAR;
public static final int MONTHS = Calendar.MONTH;
public static final int YEARS = Calendar.YEAR;
/**
* Convenience wrapper around {@link java.util.Calendar#get(int) get(int)}
* to determine whether the specified field value of the first argument
* should be considered to occur before the second. Allowed field values
* are:
* <pre>
* {@link #MILLISECONDS}
* {@link #SECONDS}
* {@link #MINUTES}
* {@link #HOURS}
* {@link #DAYS}
* {@link #WEEKS}
* {@link #MONTHS}
* {@link #YEARS}
* </pre>
*
* @param arg1
* @param arg2
* @param field The {@link java.util.Calendar Calendar} field to compare
* @return <code>true</code> if <code>arg1</code> is before
* <code>arg2</code>
* @throws NullPointerException If <code>arg1</code> or <code>arg2</code>
* is <code>null</code>.
* @throws IllegalArgumentException If <code>field</code> is invalid.
*/
protected static boolean isBefore(Calendar arg1, Calendar arg2, int field) {
// Argument validation
if (arg1 == null)
throw new NullPointerException("arg1 is null");
if (arg2 == null)
throw new NullPointerException("arg2 is null");
boolean result = false;
switch (field) {
case MILLISECONDS:
case SECONDS:
case MINUTES:
case HOURS:
case DAYS:
case WEEKS:
case MONTHS:
case YEARS:
// TODO add additional comparators
result = (arg1.get(field) < arg2.get(field));
break;
default:
throw new IllegalArgumentException("Invalid Calendar units: "
+ field + " See Functions.difference(Calendar,Calendar)"
+ " for allowed values");
}
return result;
}
/**
* Convenience method for calculating the years between a
* {@link java.util.Date Date} and the current system time.
*
* @param val
* @return The number of years between the date and now.
* @throws NullPointerException If <code>val</code> is <code>null</code>.
*/
public static long age(Date val) {
return period(new Date(), val, YEARS);
}
/**
* Convenience method for calculating the days between two
* {@link java.util.Date Date} objects.
*
* @param arg1
* @param arg2
* @return The number of dayss between the two dates.
* @throws NullPointerException If <code>arg1</code> or <code>arg2</code>
* is <code>null</code>.
*/
public static long days(Date arg1, Date arg2) {
return period(arg1, arg2, DAYS);
}
/**
* Convenience method for calculating the weeks between two
* {@link java.util.Date Date} objects.
*
* @param arg1
* @param arg2
* @return The number of weeks between the two dates.
* @throws NullPointerException If <code>arg1</code> or <code>arg2</code>
* is <code>null</code>.
*/
public static long weeks(Date arg1, Date arg2) {
return period(arg1, arg2, WEEKS);
}
/**
* Convenience method for calculating the months between two
* {@link java.util.Date Date} objects.
*
* @param arg1
* @param arg2
* @return The number of months between the two dates.
* @throws NullPointerException If <code>arg1</code> or <code>arg2</code>
* is <code>null</code>.
*/
public static long months(Date arg1, Date arg2) {
return period(arg1, arg2, MONTHS);
}
/**
* Convenience method for calculating the years between two
* {@link java.util.Date Date} objects.
*
* @param arg1
* @param arg2
* @return The number of years between the two dates.
* @throws NullPointerException If <code>arg1</code> or <code>arg2</code>
* is <code>null</code>.
*/
public static long years(Date arg1, Date arg2) {
return period(arg1, arg2, YEARS);
}
/**
* Calculates the time between two {@link java.util.Date Date} objects
* using {@link java.util.Date#getTime() Date.getTime()}.
*
* @param arg1 The value to use as the minuend.
* @param arg2 The value to use as the subtrahend.
* @return The milliseconds between the arguments.
* @throws NullPointerException If <code>arg1</code> or <code>arg2</code>
* is <code>null</code>.
*/
public static long difference(Date arg1, Date arg2) {
// Argument validation
if (arg1 == null) throw new NullPointerException("null minuend");
if (arg2 == null) throw new NullPointerException("null subtrahend");
// Do calculation
return arg1.getTime() - arg2.getTime();
}
/**
* Calculates the milliseconds between two {@link java.util.Calendar Calendar}
* objects. Equivalent to a call to
* {@link #difference(Calendar, Calendar, int) difference(Calendar, Calendar, int)}
* with the units specified as {@link #MILLISECONDS}.
*
* @param arg1 The value to use as the minuend.
* @param arg2 The value to use as the subtrahend.
* @return The milliseconds between the arguments.
* @throws NullPointerException If <code>arg1</code> or <code>arg2</code>
* is null.
*/
public static long difference(Calendar arg1, Calendar arg2) {
return difference(arg1,arg2,MILLISECONDS);
}
/**
* Calculates the difference between two {@link java.util.Calendar Calendar}
* objects and returns the {@link java.lang.Math#floor(double) floor} of
* the result in the specified <code>units</code>. The units value must be
* one of:
* <pre>
* {@link #MILLISECONDS}
* {@link #SECONDS}
* {@link #MINUTES}
* {@link #HOURS}
* {@link #DAYS}
* {@link #WEEKS}
* {@link #MONTHS}
* {@link #YEARS}
* </pre>
*
* @param arg1 The value to use as the minuend.
* @param arg2 The value to use as the subtrahend.
* @param units The {@link java.util.Calendar Calendar} field to use for
* expressing the difference.
* @return The difference between the arguments in the specified units.
* @throws NullPointerException If <code>arg1</code> or <code>arg2</code>
* is null.
*/
public static long difference(Calendar arg1, Calendar arg2, int units) {
// Argument validation
if (arg1 == null) throw new NullPointerException("null minuend");
if (arg2 == null) throw new NullPointerException("null subtrahend");
// Do calculation
long minuend;
long subtrahend;
long delta;
double mod = 1.0;
// Compute difference using units as Calendar field
switch(units) {
// First several we chain together the multiplier
case WEEKS:
mod = mod*7;
case DAYS:
mod = mod*24;
case HOURS:
mod = mod*60;
case MINUTES:
mod = mod*60;
case SECONDS:
mod = mod*1000;
case MILLISECONDS:
delta = arg1.getTimeInMillis() - arg2.getTimeInMillis();
delta = (long) Math.floor(delta/mod);
break;
case MONTHS:
case YEARS:
minuend = arg1.get(Calendar.YEAR);
subtrahend = arg2.get(Calendar.YEAR);
delta = minuend - subtrahend;
if(isBefore(arg1,arg2,Calendar.DAY_OF_YEAR)) delta--;
break;
default:
throw new IllegalArgumentException("Invalid Calendar units: "
+ units + " See Functions.difference(Calendar,Calendar)"
+ " for allowed values");
}
return delta;
}
/**
* Calculates the value of the difference between two
* {@link java.lang.Number Number} objects as a <code>double</code>.
*
* @param arg1 The value to use as the minuend.
* @param arg2 The value to use as the subtrahend.
* @return The <code>double</code> value of <code>arg1 - arg2</code>
* @throws NullPointerException If <code>arg1</code> or <code>arg2</code>
* is null.
*/
public static double difference(Number arg1, Number arg2) {
// Argument validation
if (arg1 == null) throw new NullPointerException("null minuend");
if (arg2 == null) throw new NullPointerException("null subtrahend");
// Do calculation
Double val1 = (arg1 == null) ? 0 : arg1.doubleValue();
Double val2 = (arg2 == null) ? 0 : arg2.doubleValue();
return val1 - val2;
}
/**
* Calculates the sum of an array {@link java.lang.Number Number} objects
* as a <code>double</code>.
*
* @param args The values to sum.
* @return The sum of the parameters as a <code>double</code>
* @throws NullPointerException If <code>args</code> is null.
* @throws IllegalArgumentException If length of <code>args</code> is zero.
*/
public static double sum(Number... args) {
// Argument validation
if (args == null)
throw new NullPointerException("Can not compute sum of null");
if (args.length == 0)
throw new IllegalArgumentException(
"Can not compute sum of zero values");
// Do calculation
Double result = Double.valueOf(0);
for (Number arg : args) {
result += arg.doubleValue();
}
return result;
}
/**
* Calculates the sum of an array {@link java.lang.Number Number} objects
* as a <code>double</code>.
*
* @param args The values to sum.
* @return The sum of the parameters as a <code>double</code>
* @throws NullPointerException If <code>args</code> is null.
* @throws IllegalArgumentException If length of <code>args</code> is zero.
*/
public static double sum(Collection<? extends Number> args) {
// Argument validation
if (args == null)
throw new NullPointerException("Can not compute sum of null");
if (args.size() == 0)
throw new IllegalArgumentException(
"Can not compute sum of zero values");
// Do calculation
Double result = Double.valueOf(0);
for (Number arg : args) {
result += arg.doubleValue();
}
return result;
}
/**
* Calculates the mean value, as a <code>double</code> of one or more
* {@link java.lang.Number Number} objects using the {@link #sum(Number...)
* sum(args)} as the dividend and length of <code></code>.
*
* @param args
* The values used to calculate the mean
* @return
* @throws NullPointerException
* If <code>args</code> is null.
* @throws IllegalArgumentException
* if length of <code>args</code> is zero.
*/
public static double mean(Number... args) {
// Argument validation
if (args == null)
throw new NullPointerException("Can not compute mean of null");
if (args.length == 0)
throw new IllegalArgumentException(
"Can not compute mean of zero values");
// Do calculation
return sum(args) / args.length;
}
/**
* Calculates the mean value, as a <code>double</code> of one or more
* {@link java.lang.Number Number} objects using the {@link #sum(Number...)
* sum(args)} as the dividend and length of <code></code>.
*
* @param args
* The values used to calculate the mean
* @return
* @throws NullPointerException
* If <code>args</code> is null.
* @throws IllegalArgumentException
* if length of <code>args</code> is zero.
*/
public static double mean(Collection<? extends Number> args) {
// Argument validation
if (args == null)
throw new NullPointerException("Can not compute mean of null");
if (args.size() == 0)
throw new IllegalArgumentException(
"Can not compute mean of zero values");
// Do calculation
return sum(args) / args.size();
}
/**
* Calculates the difference between two times, given as long values, and
* returns the period between them in the specified <code>units</code>. The
* units value must be one of:
* <pre>
* {@link #MILLISECONDS}
* {@link #SECONDS}
* {@link #MINUTES}
* {@link #HOURS}
* {@link #DAYS}
* {@link #WEEKS}
* {@link #MONTHS}
* {@link #YEARS}
* </pre>
* All values will be returned as the absolute value of the difference.
*
* @param arg1 The value to use as the minuend.
* @param arg2 The value to use as the subtrahend.
* @param units The time units to use for expressing the difference.
* @return The long value of the difference between the arguments in the
* specified units.
*/
public static long period(long arg1, long arg2, int units) {
long delta = arg1 - arg2;
DateTime start = new DateTime(arg1);
DateTime end = new DateTime(arg2);
// Compute delta into appropriate units
switch(units) {
case YEARS:
delta = Years.yearsBetween(start, end).getYears();
break;
case MONTHS:
delta = Months.monthsBetween(start, end).getMonths();
break;
case WEEKS:
delta = Weeks.weeksBetween(start, end).getWeeks();
break;
case DAYS:
delta = Days.daysBetween(start, end).getDays();
break;
case HOURS:
delta = Hours.hoursBetween(start, end).getHours();
break;
case MINUTES:
delta = Minutes.minutesBetween(start, end).getMinutes();
break;
case SECONDS:
delta = Double.valueOf(Math.floor(delta/1000.0)).longValue();
break;
case MILLISECONDS:
// Here for completeness but already calculated
break;
default:
throw new IllegalArgumentException("Invalid units: "
+ units + " See Functions.difference(Calendar,Calendar)"
+ " for allowed values");
}
return Math.abs(delta);
}
/**
* Calculates the difference between two {@link java.util.Calendar Calendar}
* objects and returns the period between them in the specified
* <code>units</code>. The units value must be one of:
* <pre>
* {@link #MILLISECONDS}
* {@link #SECONDS}
* {@link #MINUTES}
* {@link #HOURS}
* {@link #DAYS}
* {@link #WEEKS}
* {@link #MONTHS}
* {@link #YEARS}
* </pre>
*
* @param arg1 The value to use as the minuend.
* @param arg2 The value to use as the subtrahend.
* @param units The time units to use for expressing the difference.
* @return The long value of the period between the two
* {@link java.util.Calendar Calendar} objects.
* @throws NullPointerException If <code>arg1</code> or <code>arg2</code>
* is null.
*/
public static long period(Calendar arg1, Calendar arg2, int units) {
// Argument validation
if (arg1 == null) throw new NullPointerException("null minuend");
if (arg2 == null) throw new NullPointerException("null subtrahend");
return period(arg1.getTimeInMillis(),arg2.getTimeInMillis(),units);
}
/**
* Calculates the difference between two {@link java.util.Date Date}
* objects and returns the period between them in the specified
* <code>units</code>. The units value must be one of:
* <pre>
* {@link #MILLISECONDS}
* {@link #SECONDS}
* {@link #MINUTES}
* {@link #HOURS}
* {@link #DAYS}
* {@link #WEEKS}
* {@link #MONTHS}
* {@link #YEARS}
* </pre>
*
* @param arg1 The value to use as the minuend.
* @param arg2 The value to use as the subtrahend.
* @param units The time units to use for expressing the difference.
* @return The long value of the period between the two
* {@link java.util.Date Date} objects.
* @throws NullPointerException If <code>arg1</code> or <code>arg2</code>
* is null.
*/
public static long period(Date arg1, Date arg2, int units) {
// Argument validation
if (arg1 == null) throw new NullPointerException("null minuend");
if (arg2 == null) throw new NullPointerException("null subtrahend");
return period(arg1.getTime(),arg2.getTime(),units);
}
/**
* Returns the the first day of the year whose age would correspond to now
* minus a number of years.
* @param age The number of years from now. Positive age parameter is interpreted as years
* prior.
* @return A new date object as the first day of the first month n years from now.
*/
public static Date dateFromAge(int age){
DateTime dateTime = new DateTime();
Date date = dateTime.minusYears(age).withMonthOfYear(1).withDayOfMonth(1).toDate();
return date;
}
}