001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.commons.date; 020 021import java.text.SimpleDateFormat; 022import java.util.Date; 023import java.util.Locale; 024import java.util.TimeZone; 025 026import lucee.commons.lang.StringUtil; 027import lucee.commons.lang.SystemOut; 028import lucee.runtime.engine.ThreadLocalPageContext; 029import lucee.runtime.exp.ExpressionException; 030import lucee.runtime.type.dt.DateTime; 031import lucee.runtime.type.dt.DateTimeImpl; 032 033public abstract class DateTimeUtil { 034 035 private final static SimpleDateFormat HTTP_TIME_STRING_FORMAT_OLD; 036 private final static SimpleDateFormat HTTP_TIME_STRING_FORMAT; 037 static { 038 HTTP_TIME_STRING_FORMAT_OLD = new SimpleDateFormat("EE, dd MMM yyyy HH:mm:ss zz",Locale.ENGLISH); 039 HTTP_TIME_STRING_FORMAT_OLD.setTimeZone(TimeZone.getTimeZone("GMT")); 040 041 HTTP_TIME_STRING_FORMAT = new SimpleDateFormat("EE, dd-MMM-yyyy HH:mm:ss zz",Locale.ENGLISH); 042 HTTP_TIME_STRING_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); 043 } 044 045 private static final double DAY_MILLIS = 86400000D; 046 private static final long CF_UNIX_OFFSET = 2209161600000L; 047 048 public static final int SECOND = 0; 049 public static final int MINUTE = 1; 050 public static final int HOUR = 2; 051 public static final int DAY = 3; 052 public static final int YEAR = 10; 053 public static final int MONTH = 11; 054 public static final int WEEK = 12; 055 public static final int QUARTER = 20; 056 public static final int MILLISECOND =30; 057 058 059 060 private static DateTimeUtil instance; 061 062 public static DateTimeUtil getInstance(){ 063 if(instance==null) { 064 instance=new JREDateTimeUtil(); 065 SystemOut.printDate("using JRE Date Library"); 066 } 067 return instance; 068 } 069 070 public DateTime toDateTime(TimeZone tz,int year, int month, int day, int hour, int minute, int second, int milliSecond) throws DateTimeException { 071 return new DateTimeImpl(toTime(tz,year, month, day, hour, minute, second,milliSecond),false); 072 } 073 074 public DateTime toDateTime(TimeZone tz,int year, int month, int day, int hour, int minute, int second, int milliSecond, DateTime defaultValue) { 075 long time = toTime(tz,year, month, day, hour, minute, second,milliSecond,Long.MIN_VALUE); 076 if(time==Long.MIN_VALUE) return defaultValue; 077 return new DateTimeImpl(time,false); 078 } 079 080 /** 081 * returns a date time instance by a number, the conversion from the double to 082 * date is o the base of the CFML rules. 083 * @param days double value to convert to a number 084 * @return DateTime Instance 085 */ 086 public DateTime toDateTime(double days) { 087 long utc=Math.round(days*DAY_MILLIS); 088 utc-=CF_UNIX_OFFSET; 089 utc-=getLocalTimeZoneOffset(utc); 090 return new DateTimeImpl(utc,false); 091 } 092 093 094 public long toTime(TimeZone tz,int year,int month,int day,int hour,int minute,int second,int milliSecond,long defaultValue){ 095 tz=ThreadLocalPageContext.getTimeZone(tz); 096 year=toYear(year); 097 098 if(month<1) return defaultValue; 099 if(month>12) return defaultValue; 100 if(day<1) return defaultValue; 101 if(hour<0) return defaultValue; 102 if(minute<0) return defaultValue; 103 if(second<0) return defaultValue; 104 if(milliSecond<0) return defaultValue; 105 if(hour>24) return defaultValue; 106 if(minute>59) return defaultValue; 107 if(second>59) return defaultValue; 108 109 if(daysInMonth(year, month)<day) return defaultValue; 110 111 return _toTime(tz, year, month, day, hour, minute, second, milliSecond); 112 } 113 114 public long toTime(TimeZone tz,int year,int month,int day,int hour,int minute,int second,int milliSecond) throws DateTimeException{ 115 tz=ThreadLocalPageContext.getTimeZone(tz); 116 year=toYear(year); 117 118 if(month<1) throw new DateTimeException("month number ["+month+"] must be at least 1"); 119 if(month>12) throw new DateTimeException("month number ["+month+"] can not be greater than 12"); 120 if(day<1) throw new DateTimeException("day number ["+day+"] must be at least 1"); 121 if(hour<0) throw new DateTimeException("hour number ["+hour+"] must be at least 0"); 122 if(minute<0) throw new DateTimeException("minute number ["+minute+"] must be at least 0"); 123 if(second<0) throw new DateTimeException("second number ["+second+"] must be at least 0"); 124 if(milliSecond<0)throw new DateTimeException("milli second number ["+milliSecond+"] must be at least 0"); 125 126 if(hour>24) throw new DateTimeException("hour number ["+hour+"] can not be greater than 24"); 127 if(minute>59) throw new DateTimeException("minute number ["+minute+"] can not be greater than 59"); 128 if(second>59) throw new DateTimeException("second number ["+second+"] can not be greater than 59"); 129 130 if(daysInMonth(year, month)<day) 131 throw new DateTimeException("day number ["+day+"] can not be greater than "+daysInMonth(year, month)+" when month is "+month+" and year "+year); 132 133 return _toTime(tz, year, month, day, hour, minute, second, milliSecond); 134 } 135 136 /** 137 * return how much days given month in given year has 138 * @param year 139 * @param month 140 * @return 141 */ 142 public int daysInMonth(int year,int month){ 143 switch(month) { 144 case 1: 145 case 3: 146 case 5: 147 case 7: 148 case 8: 149 case 10: 150 case 12: 151 return 31; 152 case 4: 153 case 6: 154 case 9: 155 case 11: 156 return 30; 157 case 2: 158 return isLeapYear(year)?29:28; 159 } 160 return -1; 161 } 162 163 /** 164 * translate 2 digit numbers to a year; for example 10 to 2010 or 50 to 1950 165 * @param year 166 * @return year matching number 167 */ 168 public int toYear(int year) { 169 if(year<100) { 170 if(year<30)year=year+=2000; 171 else year=year+=1900; 172 } 173 return year; 174 } 175 176 /** 177 * return if given is is a leap year or not 178 * @param year 179 * @return is leap year 180 */ 181 public boolean isLeapYear(int year) { 182 return ((year%4 == 0) && ((year%100 != 0) || (year%400 == 0))); 183 } 184 185 /** 186 * cast boolean value 187 * @param dateTime 188 * @return boolean value 189 * @throws ExpressionException 190 */ 191 public boolean toBooleanValue(DateTime dateTime) throws DateTimeException { 192 throw new DateTimeException("can't cast Date ["+DateTimeUtil.toHTTPTimeString(dateTime,false)+"] to boolean value"); 193 } 194 195 public double toDoubleValue(DateTime dateTime) { 196 return toDoubleValue(dateTime.getTime()); 197 } 198 199 public double toDoubleValue(long time) { 200 time+=getLocalTimeZoneOffset (time); 201 time+=CF_UNIX_OFFSET; 202 return time/DAY_MILLIS; 203 } 204 205 private static long getLocalTimeZoneOffset(long time){ 206 return ThreadLocalPageContext.getTimeZone().getOffset(time); 207 } 208 209 210 public long getMilliSecondsAdMidnight(TimeZone timeZone, long time) { 211 return time-getMilliSecondsInDay(timeZone, time); 212 } 213 214 abstract long _toTime(TimeZone tz,int year,int month,int day,int hour,int minute,int second,int milliSecond); 215 216 public abstract int getYear(TimeZone tz,lucee.runtime.type.dt.DateTime dt); 217 218 public abstract int getMonth(TimeZone tz,DateTime dt); 219 220 public abstract int getDay(TimeZone tz,DateTime dt); 221 222 public abstract int getHour(TimeZone tz,DateTime dt); 223 224 public abstract int getMinute(TimeZone tz,DateTime dt); 225 226 public abstract int getSecond(TimeZone tz,DateTime dt); 227 228 public abstract int getMilliSecond(TimeZone tz,DateTime dt); 229 230 public abstract long getMilliSecondsInDay(TimeZone tz, long time); 231 232 public abstract int getDaysInMonth(TimeZone tz,DateTime dt); 233 234 public abstract int getDayOfYear(Locale locale,TimeZone tz, DateTime dt); 235 236 public abstract int getDayOfWeek(Locale locale,TimeZone tz, DateTime dt); 237 238 public abstract int getWeekOfYear(Locale locale,TimeZone tz,DateTime dt); 239 public abstract int getFirstDayOfMonth(TimeZone tz, DateTime dt); 240 241 public abstract String toString(DateTime dt, TimeZone tz); 242 243 public static String toHTTPTimeString(long time, boolean oldFormat) { 244 return toHTTPTimeString(new Date(time),oldFormat); 245 } 246 247 /** 248 * converts a date to a http time String 249 * @param date date to convert 250 * @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"), 251 * in the "new" format, the date part is separated by "-" (like "EE, dd-MMM-yyyy HH:mm:ss zz") 252 * @return 253 */ 254 public static String toHTTPTimeString(Date date, boolean oldFormat) { 255 if(oldFormat) { 256 synchronized(HTTP_TIME_STRING_FORMAT_OLD){ 257 return StringUtil.replace(HTTP_TIME_STRING_FORMAT_OLD.format(date),"+00:00","",true); 258 } 259 } 260 synchronized(HTTP_TIME_STRING_FORMAT){ 261 return StringUtil.replace(HTTP_TIME_STRING_FORMAT.format(date),"+00:00","",true); 262 } 263 } 264 265 266}