package timeflow.data.time;
|
|
import java.util.*;
|
import java.text.*;
|
|
public class TimeUnit
|
{
|
public static final TimeUnit YEAR = new TimeUnit("Years", Calendar.YEAR, 365 * 24 * 60 * 60 * 1000L, "yyyy", "yyyy");
|
public static final TimeUnit MONTH = new TimeUnit("Months", Calendar.MONTH, 30 * 24 * 60 * 60 * 1000L, "MMM", "MMM yyyy");
|
public static final TimeUnit WEEK = new TimeUnit("Weeks", Calendar.WEEK_OF_YEAR, 7 * 24 * 60 * 60 * 1000L, "d", "MMM d yyyy");
|
public static final TimeUnit DAY = new TimeUnit("Days", Calendar.DAY_OF_MONTH, 24 * 60 * 60 * 1000L, "d", "MMM d yyyy");
|
public static final TimeUnit DAY_OF_WEEK = new TimeUnit("Days", Calendar.DAY_OF_WEEK, 24 * 60 * 60 * 1000L, "d", "MMM d yyyy");
|
public static final TimeUnit HOUR = new TimeUnit("Hours", Calendar.HOUR_OF_DAY, 60 * 60 * 1000L, "HH:mm", "MMM d yyyy HH:mm");
|
public static final TimeUnit MINUTE = new TimeUnit("Minutes", Calendar.MINUTE, 60 * 1000L, ":mm", "MMM d yyyy HH:mm");
|
public static final TimeUnit SECOND = new TimeUnit("Seconds", Calendar.SECOND, 1000L, ":ss", "MMM d yyyy HH:mm:ss");
|
public static final TimeUnit REALTIME = new TimeUnit("Realtime", Calendar.MILLISECOND, 1L, ".SS", "HH:mm:ss.SS");
|
|
public static final TimeUnit DECADE = multipleYears(10);
|
public static final TimeUnit CENTURY = multipleYears(100);
|
private static final double DAY_SIZE = 24 * 60 * 60 * 1000L;
|
private int quantity;
|
private long roughSize;
|
private SimpleDateFormat format, fullFormat;
|
private String name;
|
private int calendarCode;
|
|
private TimeUnit()
|
{
|
}
|
|
private TimeUnit(String name, int calendarCode, long roughSize, String formatPattern, String fullFormatPattern)
|
{
|
this.name = name;
|
this.calendarCode = calendarCode;
|
this.roughSize = roughSize;
|
format = new SimpleDateFormat(formatPattern);
|
fullFormat = new SimpleDateFormat(fullFormatPattern);
|
quantity = 1;
|
}
|
|
public String toString()
|
{
|
return "[TimeUnit: " + name + "]";
|
}
|
|
public static TimeUnit multipleYears(int numYears)
|
{
|
TimeUnit t = new TimeUnit();
|
t.name = numYears + " Years";
|
t.calendarCode = Calendar.YEAR;
|
t.roughSize = YEAR.roughSize * numYears;
|
t.format = YEAR.format;
|
t.fullFormat = YEAR.fullFormat;
|
t.quantity = numYears;
|
return t;
|
}
|
|
public static TimeUnit multipleWeeks(int num)
|
{
|
TimeUnit t = new TimeUnit();
|
t.name = num + " Weeks";
|
t.calendarCode = Calendar.WEEK_OF_YEAR;
|
t.roughSize = WEEK.roughSize * num;
|
t.format = WEEK.format;
|
t.fullFormat = WEEK.fullFormat;
|
t.quantity = num;
|
return t;
|
}
|
|
public TimeUnit times(int quantity)
|
{
|
TimeUnit t = new TimeUnit();
|
t.name = quantity + " " + this.name;
|
t.calendarCode = this.calendarCode;
|
t.roughSize = this.roughSize * quantity;
|
t.format = this.format;
|
t.fullFormat = this.fullFormat;
|
t.quantity = quantity;
|
return t;
|
|
}
|
|
public int numUnitsIn(TimeUnit u)
|
{
|
return (int) Math.round(u.getRoughSize() / (double) getRoughSize());
|
}
|
|
public boolean isDayOrLess()
|
{
|
return roughSize <= 24 * 60 * 60 * 1000L;
|
}
|
|
public RoughTime roundDown(long timestamp)
|
{
|
return round(timestamp, false);
|
}
|
|
public RoughTime roundUp(long timestamp)
|
{
|
return round(timestamp, true);
|
}
|
private static final int[] calendarUnits =
|
{
|
Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR
|
};
|
|
public RoughTime round(long timestamp, boolean up)
|
{
|
Calendar c = TimeUtils.cal(timestamp);
|
|
if (calendarCode == Calendar.WEEK_OF_YEAR)
|
{
|
c.set(Calendar.DAY_OF_WEEK, c.getMinimum(Calendar.DAY_OF_WEEK));
|
} else
|
{
|
|
// set to minimum all fields of finer granularity.
|
int roundingCode = calendarCode;
|
if (calendarCode == Calendar.WEEK_OF_YEAR || calendarCode == Calendar.DAY_OF_WEEK)
|
{
|
roundingCode = Calendar.DAY_OF_MONTH;
|
}
|
for (int i = 0; i < calendarUnits.length; i++)
|
{
|
if (calendarUnits[i] == roundingCode)
|
{
|
break;
|
}
|
if (i == calendarUnits.length - 1)
|
{
|
//throw new IllegalArgumentException("Unsupported Calendar Unit: " + calendarCode);
|
}
|
c.set(calendarUnits[i], c.getMinimum(calendarUnits[i]));
|
}
|
if (quantity > 1)
|
{
|
c.set(calendarCode, quantity * (c.get(calendarCode) / quantity));
|
}
|
}
|
|
// if rounding up, then add a unit at current granularity.
|
if (up)
|
{
|
c.add(calendarCode, quantity);
|
}
|
|
return new RoughTime(c.getTimeInMillis(), this);
|
}
|
|
public int get(long timestamp)
|
{
|
Calendar c = TimeUtils.cal(timestamp);
|
int n = c.get(calendarCode);
|
return quantity == 1 ? n : n % quantity;
|
}
|
|
public void addTo(RoughTime r)
|
{
|
addTo(r, 1);
|
}
|
|
public void addTo(RoughTime r, int times)
|
{
|
Calendar c = TimeUtils.cal(r.getTime());
|
c.add(calendarCode, quantity * times);
|
r.setTime(c.getTimeInMillis());
|
}
|
|
// Finding the difference between two dates, in a given unit of time,
|
// is much subtler than you'd think! And annoyingly, the Calendar class does not do
|
// this for you, even though it actually "knows" how to do so since it
|
// can add fields.
|
//
|
// The most vexing problem is dealing with daylight savings time,
|
// which means that one day a year has 23 hours and one day has 25 hours.
|
// We also have to handle the fact that months and years aren't constant lengths.
|
//
|
// Rather than write all this ourselves, in this code we
|
// use the Calendar class to do the heavy lifting.
|
public long difference(long x, long y)
|
{
|
// If this is not one of the hard cases,
|
// just divide the timespan by the length of time unit.
|
// Note that we're not worrying about hours and daylight savings time.
|
if (calendarCode != Calendar.YEAR && calendarCode != Calendar.MONTH
|
&& calendarCode != Calendar.DAY_OF_MONTH && calendarCode != Calendar.DAY_OF_WEEK
|
&& calendarCode != Calendar.WEEK_OF_YEAR)
|
{
|
return (x - y) / roughSize;
|
}
|
|
Calendar c1 = TimeUtils.cal(x), c2 = TimeUtils.cal(y);
|
int diff = 0;
|
switch (calendarCode)
|
{
|
case Calendar.YEAR:
|
return (c1.get(Calendar.YEAR) - c2.get(Calendar.YEAR)) / quantity;
|
|
case Calendar.MONTH:
|
diff = 12 * (c1.get(Calendar.YEAR) - c2.get(Calendar.YEAR))
|
+ c1.get(Calendar.MONTH) - c2.get(Calendar.MONTH);
|
return diff / quantity;
|
|
case Calendar.DAY_OF_MONTH:
|
case Calendar.DAY_OF_WEEK:
|
case Calendar.DAY_OF_YEAR:
|
case Calendar.WEEK_OF_MONTH:
|
case Calendar.WEEK_OF_YEAR:
|
// This is ugly, but believe me, it beats the alternative methods :-)
|
// We use the Calendar class's knowledge of daylight savings time.
|
// and also the fact that if we calculate this naively, then we aren't going
|
// to be off by more than one in either direction.
|
int naive = (int) Math.round((x - y) / (double) roughSize);
|
c2.add(calendarCode, naive * quantity);
|
if (c1.get(calendarCode) == c2.get(calendarCode))
|
{
|
return naive / quantity;
|
}
|
c2.add(calendarCode, quantity);
|
if (c1.get(calendarCode) == c2.get(calendarCode))
|
{
|
return naive / quantity + 1;
|
}
|
return naive / quantity - 1;
|
}
|
throw new IllegalArgumentException("Unexpected calendar code: " + calendarCode);
|
}
|
|
public long approxNumInRange(long start, long end)
|
{
|
return 1 + (end - start) / roughSize;
|
}
|
|
public long getRoughSize()
|
{
|
return roughSize;
|
}
|
|
public String format(Date date)
|
{
|
return format.format(date);
|
}
|
|
public String formatFull(Date date)
|
{
|
return fullFormat.format(date);
|
}
|
|
public String formatFull(long timestamp)
|
{
|
return fullFormat.format(new Date(timestamp));
|
}
|
|
public String getName()
|
{
|
return name;
|
}
|
}
|