/** * */ package com.trendrr.oss; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.TimeZone; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.trendrr.oss.concurrent.LazyInit; /** * * * @author dustin * */ public enum Timeframe { MILLISECONDS("MILLI"), SECONDS("S"), MINUTES("MIN"), HOURS("HR"), DAYS("D"), WEEKS("W"), MONTHS("MO"), YEARS("Y"); protected static Log log = LogFactory.getLog(Timeframe.class); protected String abbrev; Timeframe(String abbrev) { this.abbrev = abbrev; } // /** // * returns all timeframe(end) that appear within the current // * // * dates >= start // * dates < end (now by default) // * // * will return [start] if start and end fall on the same timeframe // * // * @param start // * @param end // * @return // */ // public List<Date> range(Date start, Date end) { // List<Date> dates = new ArrayList<Date>(); // if (start == null) // return dates; // // // if (end == null) // end = new Date(); // if (start.after(end)) // return dates; // // Date d = this.end(start); // while(d.before(end)) { // dates.add(d); // d = this.add(d, 1); // } // if (dates.isEmpty()) { // dates.add(d); // } // // return dates; // } public boolean same(Date d1, Date d2) { return this.toTrendrrEpoch(d1) == this.toTrendrrEpoch(d2); } public static final Date trendrrEpoch = IsoDateUtil.parse("2000-01-01T05:00:00Z"); private static final Date epochWeekStart = IsoDateUtil.parse("1999-12-26T05:00:00.00Z"); /** * Returns the java TimeUnit. Returns null for Weeks, months, years. * @return */ public TimeUnit getTimeUnit() { switch (this) { case MILLISECONDS : return TimeUnit.MILLISECONDS; case SECONDS: return TimeUnit.SECONDS; case MINUTES: return TimeUnit.MINUTES; case HOURS: return TimeUnit.HOURS; case DAYS: return TimeUnit.DAYS; } return null; } /** * trendrr epoch is the number of elements since jan 1 2000. * * This value makes it easy to implement rolling timeframes without having to * worry about consistent date time parsing (and the representation is much smaller). * * Many people would shun this, but we have found it extremely useful. * * Note that seconds and millis return a Long, everything else returns an Int. * * @param date * @return */ public Number toTrendrrEpoch(Date date) { if (date == null) return -1; long millis = date.getTime() - trendrrEpoch.getTime(); if (this == MILLISECONDS) { return millis; } long seconds = (millis / 1000); if (this == SECONDS) { return seconds; } int minutes = (int)(seconds / 60); if (this == MINUTES) { return minutes; } int hours = (int)(minutes / 60); if (this == HOURS) { return hours; } if (TimeZone.getTimeZone("America/New_York").inDaylightTime(date)) { //add an hour for the date math below. hours++; } int days = (int)(hours / 24); if (this == DAYS) { return days; } if (this == WEEKS) { millis = date.getTime() - epochWeekStart.getTime(); seconds = (int)(millis / 1000); minutes = (int)(seconds / 60); hours = (int)(minutes / 60); if (TimeZone.getTimeZone("America/New_York").inDaylightTime(date)) { //add an hour for the date math below. hours++; } days = (int)(hours / 24); return (int)(days / 7); } if (this == MONTHS) { //get the year. Calendar c = Calendar.getInstance(TimeZone.getTimeZone("America/New_York")); c.setTime(trendrrEpoch); Calendar d = Calendar.getInstance(TimeZone.getTimeZone("America/New_York")); d.setTime(date); int years = d.get(Calendar.YEAR) - c.get(Calendar.YEAR); return (years*12) + d.get(Calendar.MONTH); } if (this == YEARS) { //get the year. Calendar c = Calendar.getInstance(TimeZone.getTimeZone("America/New_York")); c.setTime(trendrrEpoch); Calendar d = Calendar.getInstance(TimeZone.getTimeZone("America/New_York")); d.setTime(date); return d.get(Calendar.YEAR) - c.get(Calendar.YEAR); } return -1; } public Date fromTrendrrEpoch(Number val) { if (this == WEEKS) return this.add(epochWeekStart, val.intValue()); //TODO: could be a long? return this.add(trendrrEpoch, val.intValue()); } /* * End and start do not work as there is no consistent way to alter a timezone in a Calendar * then change any fields (try it! it just doesn't work). * * This is easily implemented in Joda Time but Id rather not add a dependancy. * */ // /** // * returns the first second of the given timeframe. millisecond is // * always 0 // * // * when timezone matters (i.e. DAYS) then end is given in America/New_York. // * // * // * @param date // * @param frame // * @return // */ // public Date start(Date date) { // if(this == MILLISECONDS){ // return date; // } // if (this == SECONDS) { // Calendar c = Calendar.getInstance(); // c.setTime(date); // c.set(Calendar.MILLISECOND, 0); // return c.getTime(); // } // if (this == MINUTES) { // Calendar c = Calendar.getInstance(); // c.setTime(date); // c.set(Calendar.MILLISECOND, 0); // c.set(Calendar.SECOND, 0); // return c.getTime(); // } // // int epoch = this.toTrendrrEpoch(date); // Date d = this.fromTrendrrEpoch(epoch); // // return d; // } // // /** // * returns the last second of the given timeframe. millisecond is // * always 0 // * // * when timezone matters (i.e. DAYS) then end is given in America/New_York. // * // * // * @param date // * @param frame // * @return // */ // public Date end(Date date) { // if (this == MILLISECONDS) { // return date; // } // if (this == SECONDS) { // return date; // } // // // Date d = this.add(date, 1); // d = this.start(d); // return TimeFrame.SECONDS.add(d, -1); // } public Date add(Date date, int amount) { Calendar cal = Calendar.getInstance(); cal.setTime(date); if (this == MILLISECONDS) { cal.add(Calendar.MILLISECOND, amount); return cal.getTime(); } if (this == SECONDS) { cal.add(Calendar.SECOND, amount); return cal.getTime(); } if (this == MINUTES) { cal.add(Calendar.MINUTE, amount); return cal.getTime(); } if (this == HOURS) { cal.add(Calendar.HOUR_OF_DAY, amount); return cal.getTime(); } if (this == DAYS) { cal.add(Calendar.DATE, amount); return cal.getTime(); } if (this == WEEKS) { cal.add(Calendar.WEEK_OF_YEAR, amount); return cal.getTime(); } if (this == MONTHS) { cal.add(Calendar.MONTH, amount); return cal.getTime(); } if (this == YEARS) { cal.add(Calendar.YEAR, amount); return cal.getTime(); } return null; } public Date subtract(Date date, int amount) { return add(date, -amount); } /** * gets a timeframe instance based on the start and end times. * * basically the timeframe will be the largest timeframe without going over * * * * * @param start * @param end * @return */ public static Timeframe instance(Date start, Date end) { long millis = Math.abs(end.getTime() - start.getTime()); long amount = 1000; if (millis < amount) { return Timeframe.MILLISECONDS; } amount *= 60; if (millis < amount) { return Timeframe.SECONDS; } amount *= 60; if (millis < amount) { return Timeframe.MINUTES; } amount *= 24; if (millis < amount) { return Timeframe.HOURS; } amount *= 7; if (millis < amount) { return Timeframe.DAYS; } amount *= 4; if (millis < amount) { return Timeframe.WEEKS; } amount *= 12; if (millis < amount) { return Timeframe.MONTHS; } return Timeframe.YEARS; } public static Timeframe instance(String str) { String s = str.toLowerCase().trim(); if (s.startsWith("mil")) { return Timeframe.MILLISECONDS; } if (s.startsWith("s")) { return Timeframe.SECONDS; } if (s.startsWith("min")) { return Timeframe.MINUTES; } if (s.startsWith("h")) { return Timeframe.HOURS; } if (s.startsWith("d")) { return Timeframe.DAYS; } if (s.startsWith("w")) { return Timeframe.WEEKS; } if (s.startsWith("mo")) { return Timeframe.MONTHS; } if (s.startsWith("y")) { return Timeframe.YEARS; } return null; } /** * returns the shortest string that will parse back into an appropriate timeframe. * @return */ public String abbreviation() { return this.abbrev; } public int compare(Timeframe frame) { Date tmp = new Date(); Date d1 = this.add(tmp, 1); Date d2 = frame.add(tmp, 1); return d1.compareTo(d2); } public static void sort(List<Timeframe> timeframes) { Collections.sort(timeframes, new Comparator<Timeframe>() { @Override public int compare(Timeframe o1, Timeframe o2) { Date tmp = new Date(); Date d1 = o1.add(tmp, 1); Date d2 = o2.add(tmp, 1); return d1.compareTo(d2); } }); } }