package com.platinumsolutions; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; /** * Class to specify a time interval. *

* Useful for specifing "expiration dates", where you * may want to change an expiration date from * a number of days to a number of months to a number * of years. *

* If you hard code a "number of days" * check in your code, and the requirement changes to expire a value * in 1 year, then you can't just specify 365 days, because * you have to take leap years into account. *

* Another example is if a requirement changes from "expire in 90 days" * to "expire in 3 months". These things are not the same. * Three months from March 1st is June 1st, but 90 days from * March 1st is May 30th. *

* When using this class, changing an expiration value is as easy * as changing the line: *

 *   TimeInterval ti = new TimeInterval(90,Units.DAY);
 * 
* To: *
 *   TimeInterval ti = new TimeInterval(3,Units.MONTH);
 * 
*

* To do a date comparison using a TimeInterval, use the * static method "dateDiff". * * @author Christopher Pierce * */ public class TimeInterval { /** * Enumeration of the valid Units you can use * for a time interval. * Values are: *

*/ public static enum Units { YEAR, MONTH, DAY, HOUR, MINUTE, SECOND } private int interval; private Units units; /** * Simple Constructor. * * @param interval The duration of the interval. * @param units The units of measure for the duration. */ public TimeInterval(int interval, Units units) { this.interval = interval; this.units = units; } /** * Convenience constructor for * setting the interval as a string. *

* Calls the "setIntervalAsString" method. *

* * @see #setIntervalAsString(java.lang.String) * * @param intervalStr The Interval expressed as a String value. */ public TimeInterval(String intervalStr) { this.setIntervalAsString(intervalStr); } /** * The Duration of the interval. * The actual significance of this value * is dependent upon the Units. * * @return Numeric part of the duration. */ public int getInterval() { return interval; } /** * The Duration of the interval. * The actual significance of this value * is dependent upon the Units. * * @param interval Numeric part of the duration. */ public void setInterval(int interval) { this.interval = interval; } /** * The Unit of Measure for the interval. * Specifies how to interpret the interval * value (days, years, etc.) * * @return An instance of the enum Units. */ public Units getUnits() { return units; } /** * The Unit of Measure for the interval. * Specifies how to interpret the interval * value (days, years, etc.) * * @param units An instance of the enum Units. */ public void setUnits(Units units) { this.units = units; } /** * Convenience method to set the interval as a string. *

* Useful for specifing time intervals in configuration * files, so you can specify something like: *

     * some.timeinterval=5 days
     * other.timeinterval=1 year
     * 
* You would then pass the the string "5 days" or * "1 year" into this method. *

* The intervalStr value should be an integer, followed * by a a unit of measurement name. Putting white space(s) * between the integer an unit is optional. *

* Below are the valid values for the the unit of measurement names. * All value checks are case insensitive except where noted. *

     * YEAR = "year", "years", "y"
     * MONTH = "month", "months", "M" (The "M" is a case sensitive check)
     * DAY = "day", "days", "d"
     * HOUR = "hour", "hours", "h"
     * MINUTE = "minute", "minutes", "m" (The "m" is a case sensitive check)
     * SECONDS = "second", "seconds", "s"
     * 
* *

* Some example values: *

     * 1 year, 2 years, 3 y, 4y, 5years
     * 1 month, 2 months, 3 M, 4M, 5months
     * 1 day, 2 days, 3 d, 4d, 5days
     * 1 hour, 2 hours, 3 h, 4h, 5hours
     * 1 minute, 2 minutes, 3 m, 4m, 5minutes
     * 1 second, 2 seconds, 3 s, 4s, 5seconds
     * 
* * @param intervalStr The Interval expressed as a String value. */ public void setIntervalAsString(String intervalStr) { if(intervalStr == null) { throw new IllegalArgumentException("Null value passed to TimeInterval constructor"); } intervalStr = intervalStr.trim(); StringBuffer numberPart = new StringBuffer(); char[] intervalCharArray = intervalStr.toCharArray(); int idx=0; for(idx=0; idx < intervalCharArray.length; ++idx) { if(Character.isDigit(intervalCharArray[idx])) { numberPart.append(intervalCharArray[idx]); } else { break; } } try { this.interval = Integer.valueOf(numberPart.toString()); } catch(NumberFormatException ex) { throw new IllegalArgumentException("Invalid integer passed to TimeInterval constructor",ex); } if(idx >= intervalStr.length()) { // no units where specified, default to days this.units = Units.DAY; } else { // lets be robust about how units is specified. // These are the valid values // (all values are case insesitive except where noted): // YEAR = "year", "years", "y" // MONTH = "month", "months", "M" (case sensitive) // DAY = "day", "days", "d" // HOUR = "hour", "hours", "h" // MINUTE = "minute", "minutes", "m" (case sensive) // SECOND = "second", "seconds", "s" intervalStr = intervalStr.substring(idx).trim(); char firstChar = intervalStr.charAt(0); intervalStr = intervalStr.toLowerCase(); if(intervalStr.startsWith("y")) { this.units = Units.YEAR; } else if(intervalStr.startsWith("d")) { this.units = Units.DAY; } else if(intervalStr.startsWith("h")) { this.units = Units.HOUR; } else if(intervalStr.startsWith("s")) { this.units = Units.SECOND; } else if(intervalStr.startsWith("month") || (!intervalStr.startsWith("minute") && firstChar == 'M')) { this.units = Units.MONTH; } else if(intervalStr.startsWith("minute") || (!intervalStr.startsWith("month") && firstChar == 'm')) { this.units = Units.MINUTE; } else { throw new IllegalArgumentException("Invalid interval passed to TimeInterval constructor"); } } } /** * Computes the difference between two dates expressed * in the specified units. *

* e.g. Given the two dates: *

     * date1 = 2005-01-01
     * date2 = 2005-01-05
     * 
* * This method when called with a Units of "DAY" will return * a value of 4. *
     *    TimeInterval.dateDiff(date1, date2, Units.DAY) == 4
     * 
* If the second date is less than the first date, than a negative * nuber will be returned. *
     *    TimeInterval.dateDiff(date2, date1, Units.DAY) == - 4
     * 
*

* NOTE: A 1 year diference is defined as two dates with the * same month and day, but whose years differ by one. So: *

     *    "March 5th, 2005" - "March 5th, 2004" = 1 year
     *    "March 4th, 2005" - "March 5th, 2004" = 0 years
     * 
* NOTE: A 1 month diference is defined as two dates with the * same day, but whose months differ by one. So: *
     *    "April 5th, 2005" - "March 5th, 2005" = 1 month
     *    "April 4th, 2005" - "March 5th, 2005" = 0 months
     * 
* NOTE: There is no "last day of month" check, meaning that * subtracting the 31st in a 31 day month from the 30th of a 30 day * month does not equal 1 month. *
     *    "November 30th, 2005" - "October 31st, 2005" = 0 months!
     *    "December 1st, 2005" - "October 31st, 2005" = 1 month
     * 
* * NOTE: When getting the difference between two dates using * units of YEAR, MONTH, or DAY, the time part of the date is ignored. *

* Generally this method will be used with a TimeInterval object. * For example, If I want to make sure that a date is no more * than 10 days in the future, I would code the following: *

     * public boolean tenDayCheck(Date futureDate) {
     * 
     *     TimeInterval ti = new TimeInterval(10,Units.DAY);
     *     
     *     return (TimeInterval.dateDiff(new Date(),
     *                                  futureDate,
     *                                  ti.getUnits())
     *             < ti.getInterval());
     *      
     * }
     * 
* * @param date1 The earlier date. * @param date2 The later date. * @param units The units to use * @return The difference bewteen the two dates expressed in * the specified units (and negative if date 2 was before date 1). */ public static long dateDiff(Date date1, Date date2, Units units) { Calendar cal1 = Calendar.getInstance(TimeZone.getTimeZone("GMT")); cal1.setTime(date1); Calendar cal2 = Calendar.getInstance(TimeZone.getTimeZone("GMT")); cal2.setTime(date2); switch(units) { case YEAR: case MONTH: case DAY: cal1.set(Calendar.HOUR_OF_DAY,0); cal2.set(Calendar.HOUR_OF_DAY,0); cal1.set(Calendar.MINUTE,0); cal2.set(Calendar.MINUTE,0); cal1.set(Calendar.SECOND,0); cal2.set(Calendar.SECOND,0); default: cal1.set(Calendar.MILLISECOND,0); cal2.set(Calendar.MILLISECOND,0); } long dateDif = 0; switch(units) { case YEAR: case MONTH: dateDif = 0; if(!cal2.equals(cal1)) { Calendar calLowDate = cal1; Calendar calHighDate = cal2; long sign = 1; if(cal2.before(cal1)) { sign = -1; calLowDate = cal2; calHighDate = cal1; } long yearDif = calHighDate.get(Calendar.YEAR) - calLowDate.get(Calendar.YEAR); long monthDif = calHighDate.get(Calendar.MONTH) - calLowDate.get(Calendar.MONTH); long dayDif = calHighDate.get(Calendar.DAY_OF_MONTH) - calLowDate.get(Calendar.DAY_OF_MONTH); switch(units) { case YEAR: dateDif = yearDif; if(dateDif > 0) { if(monthDif >= 0 && dayDif < 0) { monthDif -= 1; } if(monthDif < 0) { dateDif -= 1; } } break; case MONTH: dateDif = (yearDif*12) + monthDif; if(dateDif > 0 && dayDif < 0) { dateDif -= 1; } break; } dateDif = sign * (dateDif); } break; case DAY: dateDif = cal2.getTimeInMillis() - cal1.getTimeInMillis(); dateDif = ((dateDif)/86400000); //convert milliseconds to days break; case HOUR: dateDif = cal2.getTimeInMillis() - cal1.getTimeInMillis(); dateDif = ((dateDif)/3600000); //convert milliseconds to hours break; case MINUTE: dateDif = cal2.getTimeInMillis() - cal1.getTimeInMillis(); dateDif = ((dateDif)/60000); //convert milliseconds to minutes break; default: dateDif = cal2.getTimeInMillis() - cal1.getTimeInMillis(); dateDif = ((dateDif)/1000); //convert milliseconds to seconds break; } return dateDif; } }