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);
}
}
}