package marubinotto.util.time; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.net.URL; import java.text.DateFormat; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import marubinotto.util.Assert; import marubinotto.util.message.MessageSource; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.time.FastDateFormat; /** * marubinotto.util.time.DateTime */ public final class DateTime implements Serializable, Comparable<DateTime> { private long time; private Calendar timeAsCalendar; private DateFormat formatter = DateFormat.getDateTimeInstance(); public static final String CURRENT_TIME_FILE = "current-time"; public static final String CURRENT_TIME_FILE_FORMAT = "yyyy-MM-dd HH:mm:ss.S"; private static DateTime sf_currentTime; public static DateTime getCurrentTime() { if (sf_currentTime != null) { return sf_currentTime; } DateTime currentTime = getCurrentTimeFromFile(); if (currentTime != null) { return currentTime; } else { return new DateTime(System.currentTimeMillis()); } } public static void clearCurrentTimeFile() throws IOException { File currentTimeFile = getCurrentTimeFile(); if (currentTimeFile != null) { FileUtils.writeStringToFile(currentTimeFile, ""); } } public static File getCurrentTimeFile() { URL currentTimeResource = DateTime.class.getClassLoader().getResource( CURRENT_TIME_FILE); if (currentTimeResource == null) { return null; } File currentTimeFile = FileUtils.toFile(currentTimeResource); if (!currentTimeFile.isFile()) { return null; } return currentTimeFile; } private static DateTime getCurrentTimeFromFile() { File currentTimeFile = getCurrentTimeFile(); if (currentTimeFile == null) { return null; } String currentTime; try { currentTime = FileUtils.readFileToString(currentTimeFile); } catch (IOException e) { return null; } if (currentTime == null || currentTime.trim().equals("")) { return null; } return new DateTime(currentTime, CURRENT_TIME_FILE_FORMAT); } public static void setCurrentTimeForTest(DateTime currentTime) { sf_currentTime = currentTime; } public static void setCurrentTimeForTest(int year, int month, int dayOfMonth) { setCurrentTimeForTest(new DateTime(year, month, dayOfMonth)); } public static Date date(int year, int month, int dayOfMonth) { return new DateTime(year, month, dayOfMonth).toDate(); } public static void setCurrentTimeForTest(int year, int month, int dayOfMonth, int hourOfDay, int minute, int second) { setCurrentTimeForTest(new DateTime(year, month, dayOfMonth, hourOfDay, minute, second)); } public DateTime(long time) { initializeTime(time); } public DateTime(Date date) { Assert.Arg.notNull(date, "date"); initializeTime(date.getTime()); } public DateTime(Calendar calendar) { Assert.Arg.notNull(calendar, "calendar"); initializeTime(calendar.getTime().getTime()); } public DateTime(DateTime dateTime) { Assert.Arg.notNull(dateTime, "dateTime"); initializeTime(dateTime.time); } public static Calendar createCalendar(int year, int month, int dayOfMonth) { Calendar calendar = Calendar.getInstance(); calendar.setLenient(false); calendar.clear(); calendar.set(year, month - 1, dayOfMonth); return calendar; } public DateTime(int year, int month, int dayOfMonth) { this.timeAsCalendar = createCalendar(year, month, dayOfMonth); this.time = this.timeAsCalendar.getTime().getTime(); } public static Calendar createCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute, int second) { Calendar calendar = Calendar.getInstance(); calendar.setLenient(false); calendar.clear(); calendar.set(year, month - 1, dayOfMonth, hourOfDay, minute, second); return calendar; } public DateTime(int year, int month, int dayOfMonth, int hourOfDay, int minute, int second) { this.timeAsCalendar = createCalendar(year, month, dayOfMonth, hourOfDay, minute, second); this.time = this.timeAsCalendar.getTime().getTime(); } public DateTime(String source, String pattern) { Assert.Arg.notNull(source, "source"); Assert.Arg.notNull(pattern, "pattern"); applyPattern(pattern); Date date = this.formatter.parse(source, new ParsePosition(0)); if (date == null) { throw new DateFormatException("Illegal pattern \"" + pattern + "\" for: " + source); } initializeTime(date.getTime()); } public DateTime(String source, String[] patterns) { Assert.Arg.notNull(source, "source"); Assert.Arg.notNull(patterns, "patterns"); Date date = null; for (int i = 0; i < patterns.length; i++) { applyPattern(patterns[i]); date = this.formatter.parse(source, new ParsePosition(0)); if (date != null) { break; } } if (date == null) { throw new IllegalArgumentException("Illegal patterns for: " + source); } initializeTime(date.getTime()); } public long getTime() { return this.time; } public Date toDate() { return new Date(this.time); } public Calendar toCalendar() { return (Calendar) this.timeAsCalendar.clone(); } public int compareTo(DateTime another) { long thisTime = getTime(); long anotherTime = another.getTime(); return (thisTime < anotherTime ? -1 : (thisTime == anotherTime ? 0 : 1)); } public synchronized String toString() { return this.formatter.format(toDate()); } public boolean equals(Object obj) { if (!(obj instanceof DateTime)) { return false; } long thisTime = getTime(); long anotherTime = ((DateTime) obj).getTime(); return thisTime == anotherTime; } public int hashCode() { long ht = getTime(); return (int) ht ^ (int) (ht >> 32); } // Time fields public int getYear() { return this.timeAsCalendar.get(Calendar.YEAR); } public int getMonth() { return this.timeAsCalendar.get(Calendar.MONTH) + 1; } public int getDayOfMonth() { return this.timeAsCalendar.get(Calendar.DAY_OF_MONTH); } public int getDayOfWeek() { return this.timeAsCalendar.get(Calendar.DAY_OF_WEEK); } public int getHourOfDay() { return this.timeAsCalendar.get(Calendar.HOUR_OF_DAY); } public int getMinute() { return this.timeAsCalendar.get(Calendar.MINUTE); } public int getSecond() { return this.timeAsCalendar.get(Calendar.SECOND); } public int getMillisecond() { return this.timeAsCalendar.get(Calendar.MILLISECOND); } // Utilities /** * <ul> * <li>yyyy-MM-dd HH:mm:ss</li> * <li>yyyy.MM.dd G 'at' hh:mm:ss a zzz</li> * </ul> */ public synchronized String format(String pattern) { Assert.Arg.notNull(pattern, "pattern"); applyPattern(pattern); return this.formatter.format(toDate()); } public static FastDateFormat ISO8601 = FastDateFormat .getInstance("yyyy-MM-dd'T'HH:mm:ss.SZZ"); public String formatAsISO8601() { return ISO8601.format(toDate()); } public boolean after(DateTime when) { return this.time > when.getTime(); } public boolean before(DateTime when) { return this.time < when.getTime(); } public boolean isSameMonth(DateTime other) { if (getYear() != other.getYear()) return false; if (getMonth() != other.getMonth()) return false; return true; } public boolean isSameDay(DateTime other) { if (!isSameMonth(other)) return false; if (getDayOfMonth() != other.getDayOfMonth()) return false; return true; } public boolean isToday() { return isSameDay(DateTime.getCurrentTime()); } public boolean isLastDayOfMonth() { return getDayOfMonth() == this.timeAsCalendar .getActualMaximum(Calendar.DAY_OF_MONTH); } public DateTime addMonths(int months) { Calendar calendar = toCalendar(); calendar.add(Calendar.MONTH, months); return new DateTime(calendar); } public DateTime addDays(int days) { Calendar calendar = toCalendar(); calendar.add(Calendar.DAY_OF_MONTH, days); return new DateTime(calendar); } public DateTime addHours(int hours) { Calendar calendar = toCalendar(); calendar.add(Calendar.HOUR_OF_DAY, hours); return new DateTime(calendar); } public DateTime addMinutes(int minutes) { Calendar calendar = toCalendar(); calendar.add(Calendar.MINUTE, minutes); return new DateTime(calendar); } public DateTime addMilliseconds(int milliseconds) { Calendar calendar = toCalendar(); calendar.add(Calendar.MILLISECOND, milliseconds); return new DateTime(calendar); } public Month toMonth() { return new Month(this); } public DateTime getStartInstantOfDay() { return new DateTime(getYear(), getMonth(), getDayOfMonth()); } public DateTime getEndInstantOfDay() { DateTime firstInstant = getStartInstantOfDay(); Calendar calendar = firstInstant.toCalendar(); calendar.add(Calendar.DAY_OF_MONTH, 1); calendar.add(Calendar.MILLISECOND, -1); return new DateTime(calendar); } public Interval toDayInterval() { return new Interval(getStartInstantOfDay(), getEndInstantOfDay()); } public String getRelativeDescription(MessageSource messageSource) { Assert.Arg.notNull(messageSource, "messageSource"); DateTime now = DateTime.getCurrentTime(); if (after(now)) return "future time"; Interval interval = new Interval(this, now); if (interval.getTime() < Duration.Unit.MINUTE.getValue()) return messageSource.getMessage("ago-seconds"); if (interval.getTime() < 2 * Duration.Unit.MINUTE.getValue()) return messageSource.getMessage("ago-one-minute"); if (interval.getTime() < Duration.Unit.HOUR.getValue()) { int minutes = new Double(interval.getTime(Duration.Unit.MINUTE)) .intValue(); return messageSource.getMessage("ago-minutes", minutes); } if (interval.getTime() < 2 * Duration.Unit.HOUR.getValue()) return messageSource.getMessage("ago-one-hour"); if (interval.getTime() < Duration.Unit.DAY.getValue()) { int hours = new Double(interval.getTime(Duration.Unit.HOUR)).intValue(); return messageSource.getMessage("ago-hours", hours); } if (interval.getTime() < 2 * Duration.Unit.DAY.getValue()) return messageSource.getMessage("ago-one-day"); if (interval.getTime() < Duration.Unit.MONTH.getValue()) { int days = new Double(interval.getTime(Duration.Unit.DAY)).intValue(); return messageSource.getMessage("ago-days", days); } if (interval.getTime() < 2 * Duration.Unit.MONTH.getValue()) return messageSource.getMessage("ago-one-month"); if (interval.getTime() < Duration.Unit.YEAR.getValue()) { int month = new Double(interval.getTime(Duration.Unit.MONTH)).intValue(); return messageSource.getMessage("ago-months", month); } int years = new Double(interval.getTime(Duration.Unit.YEAR)).intValue(); return years <= 1 ? messageSource.getMessage("ago-one-year") : messageSource.getMessage("ago-years", years); } // Internals private void initializeTime(long time) { this.time = time; this.timeAsCalendar = Calendar.getInstance(); this.timeAsCalendar.setTimeInMillis(this.time); } private void applyPattern(String pattern) { if (this.formatter instanceof SimpleDateFormat) { ((SimpleDateFormat) this.formatter).applyPattern(pattern); } } }