package railo.commons.date; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import railo.commons.lang.StringUtil; import railo.commons.lang.SystemOut; import railo.runtime.engine.ThreadLocalPageContext; import railo.runtime.exp.ExpressionException; import railo.runtime.type.dt.DateTime; import railo.runtime.type.dt.DateTimeImpl; public abstract class DateTimeUtil { private final static SimpleDateFormat HTTP_TIME_STRING_FORMAT_OLD; private final static SimpleDateFormat HTTP_TIME_STRING_FORMAT; static { HTTP_TIME_STRING_FORMAT_OLD = new SimpleDateFormat("EE, dd MMM yyyy HH:mm:ss zz",Locale.ENGLISH); HTTP_TIME_STRING_FORMAT_OLD.setTimeZone(TimeZone.getTimeZone("GMT")); HTTP_TIME_STRING_FORMAT = new SimpleDateFormat("EE, dd-MMM-yyyy HH:mm:ss zz",Locale.ENGLISH); HTTP_TIME_STRING_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); } private static final double DAY_MILLIS = 86400000D; private static final long CF_UNIX_OFFSET = 2209161600000L; public static final int SECOND = 0; public static final int MINUTE = 1; public static final int HOUR = 2; public static final int DAY = 3; public static final int YEAR = 10; public static final int MONTH = 11; public static final int WEEK = 12; public static final int QUARTER = 20; public static final int MILLISECOND =30; private static DateTimeUtil instance; public static DateTimeUtil getInstance(){ if(instance==null) { // try to load Joda Date TimeUtil try{ instance=new JREDateTimeUtil(); SystemOut.printDate("using JRE Date Library"); } // when not available (jar) load Impl that is based on the JRE catch(Throwable t){ instance=new JREDateTimeUtil(); SystemOut.printDate("using JRE Date Library"); } } return instance; } public DateTime toDateTime(TimeZone tz,int year, int month, int day, int hour, int minute, int second, int milliSecond) throws DateTimeException { return new DateTimeImpl(toTime(tz,year, month, day, hour, minute, second,milliSecond),false); } public DateTime toDateTime(TimeZone tz,int year, int month, int day, int hour, int minute, int second, int milliSecond, DateTime defaultValue) { long time = toTime(tz,year, month, day, hour, minute, second,milliSecond,Long.MIN_VALUE); if(time==Long.MIN_VALUE) return defaultValue; return new DateTimeImpl(time,false); } /** * returns a date time instance by a number, the conversion from the double to * date is o the base of the CFML rules. * @param days double value to convert to a number * @return DateTime Instance */ public DateTime toDateTime(double days) { long utc=Math.round(days*DAY_MILLIS); utc-=CF_UNIX_OFFSET; utc-=getLocalTimeZoneOffset(utc); return new DateTimeImpl(utc,false); } public long toTime(TimeZone tz,int year,int month,int day,int hour,int minute,int second,int milliSecond,long defaultValue){ tz=ThreadLocalPageContext.getTimeZone(tz); year=toYear(year); if(month<1) return defaultValue; if(month>12) return defaultValue; if(day<1) return defaultValue; if(hour<0) return defaultValue; if(minute<0) return defaultValue; if(second<0) return defaultValue; if(milliSecond<0) return defaultValue; if(hour>24) return defaultValue; if(minute>59) return defaultValue; if(second>59) return defaultValue; if(daysInMonth(year, month)<day) return defaultValue; return _toTime(tz, year, month, day, hour, minute, second, milliSecond); } public long toTime(TimeZone tz,int year,int month,int day,int hour,int minute,int second,int milliSecond) throws DateTimeException{ tz=ThreadLocalPageContext.getTimeZone(tz); year=toYear(year); if(month<1) throw new DateTimeException("month number ["+month+"] must be at least 1"); if(month>12) throw new DateTimeException("month number ["+month+"] can not be greater than 12"); if(day<1) throw new DateTimeException("day number ["+day+"] must be at least 1"); if(hour<0) throw new DateTimeException("hour number ["+hour+"] must be at least 0"); if(minute<0) throw new DateTimeException("minute number ["+minute+"] must be at least 0"); if(second<0) throw new DateTimeException("second number ["+second+"] must be at least 0"); if(milliSecond<0)throw new DateTimeException("milli second number ["+milliSecond+"] must be at least 0"); if(hour>24) throw new DateTimeException("hour number ["+hour+"] can not be greater than 24"); if(minute>59) throw new DateTimeException("minute number ["+minute+"] can not be greater than 59"); if(second>59) throw new DateTimeException("second number ["+second+"] can not be greater than 59"); if(daysInMonth(year, month)<day) throw new DateTimeException("day number ["+day+"] can not be greater than "+daysInMonth(year, month)+" when month is "+month+" and year "+year); return _toTime(tz, year, month, day, hour, minute, second, milliSecond); } /** * return how much days given month in given year has * @param year * @param month * @return */ public int daysInMonth(int year,int month){ switch(month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31; case 4: case 6: case 9: case 11: return 30; case 2: return isLeapYear(year)?29:28; } return -1; } /** * translate 2 digit numbers to a year; for example 10 to 2010 or 50 to 1950 * @param year * @return year matching number */ public int toYear(int year) { if(year<100) { if(year<30)year=year+=2000; else year=year+=1900; } return year; } /** * return if given is is a leap year or not * @param year * @return is leap year */ public boolean isLeapYear(int year) { return ((year%4 == 0) && ((year%100 != 0) || (year%400 == 0))); } /** * cast boolean value * @param dateTime * @return boolean value * @throws ExpressionException */ public boolean toBooleanValue(DateTime dateTime) throws DateTimeException { throw new DateTimeException("can't cast Date ["+dateTime.toGMTString()+"] to boolean value"); } public double toDoubleValue(DateTime dateTime) { long utc = dateTime.getTime(); utc+=getLocalTimeZoneOffset (utc); utc+=CF_UNIX_OFFSET; return utc/DAY_MILLIS; } private static long getLocalTimeZoneOffset(long utc){ return ThreadLocalPageContext.getTimeZone().getOffset(utc); } public long getMilliSecondsAdMidnight(TimeZone timeZone, long time) { return time-getMilliSecondsInDay(timeZone, time); } abstract long _toTime(TimeZone tz,int year,int month,int day,int hour,int minute,int second,int milliSecond); public abstract int getYear(TimeZone tz,railo.runtime.type.dt.DateTime dt); public abstract int getMonth(TimeZone tz,DateTime dt); public abstract int getDay(TimeZone tz,DateTime dt); public abstract int getHour(TimeZone tz,DateTime dt); public abstract int getMinute(TimeZone tz,DateTime dt); public abstract int getSecond(TimeZone tz,DateTime dt); public abstract int getMilliSecond(TimeZone tz,DateTime dt); public abstract long getMilliSecondsInDay(TimeZone tz, long time); public abstract int getDaysInMonth(TimeZone tz,DateTime dt); public abstract int getDayOfYear(Locale locale,TimeZone tz, DateTime dt); public abstract int getDayOfWeek(Locale locale,TimeZone tz, DateTime dt); public abstract int getWeekOfYear(Locale locale,TimeZone tz,DateTime dt); public abstract int getFirstDayOfMonth(TimeZone tz, DateTime dt); public abstract String toString(DateTime dt, TimeZone tz); public static String toHTTPTimeString(long time, boolean oldFormat) { return toHTTPTimeString(new Date(time),oldFormat); } /** * converts a date to a http time String * @param date date to convert * @param oldFormat "old" in that context means the format support the existing functionality in CFML like the function getHTTPTimeString, in that format the date parts are separated by a space (like "EE, dd MMM yyyy HH:mm:ss zz"), * in the "new" format, the date part is separated by "-" (like "EE, dd-MMM-yyyy HH:mm:ss zz") * @return */ public static String toHTTPTimeString(Date date, boolean oldFormat) { if(oldFormat) { synchronized(HTTP_TIME_STRING_FORMAT_OLD){ return StringUtil.replace(HTTP_TIME_STRING_FORMAT_OLD.format(date),"+00:00","",true); } } synchronized(HTTP_TIME_STRING_FORMAT){ return StringUtil.replace(HTTP_TIME_STRING_FORMAT.format(date),"+00:00","",true); } } }