/* This file is part of Cyclos (www.cyclos.org). A project of the Social Trade Organisation (www.socialtrade.org). Cyclos is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Cyclos is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Cyclos; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.strohalm.cyclos.utils; import java.io.Serializable; import java.util.Calendar; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.time.DateUtils; /** * Defines a time period, with a number and a field. It represents an amount of time in the specified unit, i.e.: 5 DAYS. * @author luis */ public class TimePeriod implements Serializable, Cloneable { /** * A given time field * @author luis */ public static enum Field implements IntValuedEnum { MILLIS(Calendar.MILLISECOND), SECONDS(Calendar.SECOND), MINUTES(Calendar.MINUTE), HOURS(Calendar.HOUR_OF_DAY, Calendar.HOUR), DAYS(Calendar.DATE), WEEKS(Calendar.WEEK_OF_YEAR), MONTHS(Calendar.MONTH), YEARS(Calendar.YEAR); public static Field findByCalendarField(final int value) { for (final Field field : values()) { if (ArrayUtils.contains(field.calendarValues, value)) { return field; } } return null; } private int[] calendarValues; private Field(final int calendarValue) { calendarValues = new int[] { calendarValue }; } private Field(final int... calendarValues) { this.calendarValues = calendarValues; } public int getCalendarValue() { return calendarValues[0]; } @Override public int getValue() { return getCalendarValue(); } } /** * A time period of 1 month */ public static final TimePeriod ONE_MONTH = new TimePeriod(1, Field.MONTHS); /** * A time period of 1 day */ public static final TimePeriod ONE_DAY = new TimePeriod(1, Field.DAYS); private static final long serialVersionUID = 859616150477032565L; private int number; private Field field; public TimePeriod() { } public TimePeriod(final int number, final Field field) { this.number = number; this.field = field == null ? Field.DAYS : field; } /** * Return a new calendar adding this time period */ public Calendar add(final Calendar date) { if (date == null) { return null; } if (!isValid()) { return date; } final Calendar ret = (Calendar) date.clone(); ret.add(field.getCalendarValue(), number); return ret; } @Override public TimePeriod clone() { try { return (TimePeriod) super.clone(); } catch (final CloneNotSupportedException e) { return null; } } /** * Returns the full period which includes the given date */ public Period currentPeriod(final Calendar date) { final Calendar start = previousPeriod(date).getEnd(); start.add(Calendar.SECOND, 1); return periodStartingAt(start); } @Override public boolean equals(final Object obj) { if (!(obj instanceof TimePeriod)) { return false; } final TimePeriod tp = (TimePeriod) obj; return new EqualsBuilder().append(number, tp.number).append(field, tp.field).isEquals(); } public Field getField() { return field; } public int getNumber() { return number; } public float getValueIn(final Field field) { final Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(0L); final Calendar cal2 = add(cal); final float millisDiff = cal2.getTimeInMillis() - cal.getTimeInMillis(); switch (field) { case MILLIS: return millisDiff; case SECONDS: return millisDiff / DateUtils.MILLIS_PER_SECOND; case MINUTES: return millisDiff / DateUtils.MILLIS_PER_MINUTE; case HOURS: return millisDiff / DateUtils.MILLIS_PER_HOUR; case DAYS: return millisDiff / DateUtils.MILLIS_PER_DAY; case WEEKS: return millisDiff / (DateUtils.MILLIS_PER_DAY * 7); case MONTHS: return millisDiff / (DateUtils.MILLIS_PER_DAY * 30); case YEARS: return millisDiff / (DateUtils.MILLIS_PER_DAY * 365); } return 0F; } @Override public int hashCode() { return new HashCodeBuilder().append(number).append(field).toHashCode(); } public boolean isValid() { return number > 0 && field != null; } /** * Returns a period ending at the given date (not including it), with this size */ public Period periodEndingAt(Calendar endDate) { endDate = (Calendar) endDate.clone(); final Calendar beginDate = remove(endDate); endDate.add(Calendar.SECOND, -1); return Period.between(beginDate, endDate); } /** * Returns a period starting at the given date, with this size */ public Period periodStartingAt(final Calendar beginDate) { final Calendar endDate = add(beginDate); endDate.add(Calendar.SECOND, -1); return Period.between(beginDate, endDate); } /** * Returns the previous full period that does not include the given date. Example: date = 2007-02-15, field = month, number = 2 returns a period * of 2 months from 2006-12-01 to 2007-01-31 */ public Period previousPeriod(final Calendar date) { Calendar end; if (field == Field.WEEKS) { // Weeks are not supported on DateUtils.truncate, so, go back to the last monday, and get weeks before end = DateHelper.truncate(date); while (end.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) { end.add(Calendar.DATE, -1); } } else { end = DateUtils.truncate(date, field.getCalendarValue()); } return periodEndingAt(end); } /** * Return a new calendar removing this time period */ public Calendar remove(final Calendar date) { if (date == null) { return null; } if (!isValid()) { return date; } final Calendar ret = (Calendar) date.clone(); ret.add(field.getCalendarValue(), -number); return ret; } public void setField(final Field field) { this.field = field; } public void setNumber(final int number) { this.number = number; } @Override public String toString() { return number + " " + field; } }