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.util.Calendar; 022import java.util.HashMap; 023import java.util.Locale; 024import java.util.Map; 025import java.util.TimeZone; 026 027import lucee.runtime.PageContext; 028import lucee.runtime.PageContextImpl; 029import lucee.runtime.engine.ThreadLocalPageContext; 030import lucee.runtime.op.Caster; 031import lucee.runtime.type.dt.DateTime; 032 033public class JREDateTimeUtil extends DateTimeUtil { 034 035 private static CalendarThreadLocal _calendar=new CalendarThreadLocal(); 036 private static CalendarThreadLocal calendar=new CalendarThreadLocal(); 037 private static LocaleCalendarThreadLocal _localeCalendar=new LocaleCalendarThreadLocal(); 038 private static LocaleCalendarThreadLocal localeCalendar=new LocaleCalendarThreadLocal(); 039 040 //Calendar string; 041 042 JREDateTimeUtil() { 043 044 } 045 046 long _toTime(TimeZone tz, int year, int month, int day, int hour,int minute, int second, int milliSecond) { 047 if(tz==null)tz=ThreadLocalPageContext.getTimeZone(tz); 048 Calendar time = _getThreadCalendar(tz); 049 time.set(year,month-1,day,hour,minute,second); 050 time.set(Calendar.MILLISECOND,milliSecond); 051 return time.getTimeInMillis(); 052 } 053 054 private static int _get(TimeZone tz, DateTime dt, int field) { 055 Calendar c = _getThreadCalendar(tz); 056 c.setTimeInMillis(dt.getTime()); 057 return c.get(field); 058 } 059 060 061 private static int _get(Locale l,TimeZone tz, DateTime dt, int field) { 062 Calendar c = _getThreadCalendar(l,tz); 063 c.setTimeInMillis(dt.getTime()); 064 return c.get(field); 065 } 066 067 @Override 068 public int getYear(TimeZone tz, DateTime dt) { 069 return _get(tz,dt,Calendar.YEAR); 070 } 071 072 @Override 073 public int getMonth(TimeZone tz, DateTime dt) { 074 return _get(tz,dt,Calendar.MONTH)+1; 075 } 076 077 @Override 078 public int getDay(TimeZone tz, DateTime dt) { 079 return _get(tz,dt,Calendar.DAY_OF_MONTH); 080 } 081 082 @Override 083 public int getHour(TimeZone tz, DateTime dt) { 084 return _get(tz,dt,Calendar.HOUR_OF_DAY); 085 } 086 087 @Override 088 public int getMinute(TimeZone tz, DateTime dt) { 089 return _get(tz,dt,Calendar.MINUTE); 090 } 091 092 @Override 093 public int getSecond(TimeZone tz, DateTime dt) { 094 return _get(tz,dt,Calendar.SECOND); 095 } 096 097 @Override 098 public int getMilliSecond(TimeZone tz, DateTime dt) { 099 return _get(tz,dt,Calendar.MILLISECOND); 100 } 101 102 @Override 103 public synchronized int getDayOfYear(Locale locale,TimeZone tz, DateTime dt) { 104 return _get(locale,tz,dt,Calendar.DAY_OF_YEAR); 105 } 106 107 @Override 108 public synchronized int getDayOfWeek(Locale locale,TimeZone tz, DateTime dt) { 109 return _get(locale,tz,dt,Calendar.DAY_OF_WEEK); 110 } 111 112 @Override 113 public synchronized int getFirstDayOfMonth(TimeZone tz, DateTime dt) { 114 Calendar c = _getThreadCalendar(tz); 115 c.setTimeInMillis(dt.getTime()); 116 c.set(Calendar.DATE,1); 117 return c.get(Calendar.DAY_OF_YEAR); 118 } 119 120 @Override 121 public synchronized int getWeekOfYear(Locale locale,TimeZone tz, DateTime dt) { 122 123 Calendar c=_getThreadCalendar(locale,tz); 124 c.setTimeInMillis(dt.getTime()); 125 int week=c.get(Calendar.WEEK_OF_YEAR); 126 127 if(week==1 && c.get(Calendar.MONTH)==Calendar.DECEMBER) { 128 if(isLeapYear(c.get(Calendar.YEAR)) && c.get(Calendar.DAY_OF_WEEK)==1){ 129 return 54; 130 } 131 return 53; 132 } 133 return week; 134 } 135 136 public synchronized long getMilliSecondsInDay(TimeZone tz,long time) { 137 Calendar c = _getThreadCalendar(tz); 138 c.setTimeInMillis(time); 139 return (c.get(Calendar.HOUR_OF_DAY)*3600000)+ 140 (c.get(Calendar.MINUTE)*60000)+ 141 (c.get(Calendar.SECOND)*1000)+ 142 (c.get(Calendar.MILLISECOND)); 143 } 144 145 public synchronized int getDaysInMonth(TimeZone tz, DateTime dt) { 146 Calendar c = _getThreadCalendar(tz); 147 c.setTimeInMillis(dt.getTime()); 148 return daysInMonth(c.get(Calendar.YEAR), c.get(Calendar.MONTH)+1); 149 } 150 151 152 public String toString(DateTime dt, TimeZone tz) { 153 PageContext pc=ThreadLocalPageContext.get(); 154 Calendar c = _getThreadCalendar(tz); 155 c.setTimeInMillis(dt.getTime()); 156 //"HH:mm:ss" 157 StringBuilder sb=new StringBuilder(); 158 159 sb.append("{ts '"); 160 toString(sb,c.get(Calendar.YEAR),4); 161 sb.append("-"); 162 toString(sb,c.get(Calendar.MONTH)+1,2); 163 sb.append("-"); 164 toString(sb,c.get(Calendar.DATE),2); 165 sb.append(" "); 166 toString(sb,c.get(Calendar.HOUR_OF_DAY),2); 167 sb.append(":"); 168 toString(sb,c.get(Calendar.MINUTE),2); 169 sb.append(":"); 170 toString(sb,c.get(Calendar.SECOND),2); 171 172 if(pc instanceof PageContextImpl) { 173 if(((PageContextImpl)pc).getTimestampWithTSOffset()) addTimeZoneOffset(c,sb); 174 } 175 sb.append("'}"); 176 177 return sb.toString(); 178 } 179 180 private void addTimeZoneOffset(Calendar c, StringBuilder sb) { 181 int min = (c.get(Calendar.ZONE_OFFSET)+c.get(Calendar.DST_OFFSET))/60000; 182 char op; 183 if(min<0) { 184 op='-'; 185 min=min-min-min; 186 } 187 else op='+'; 188 189 int hours=min/60; 190 min=min-(hours*60); 191 sb.append(op); 192 toString(sb,hours,2); 193 sb.append(':'); 194 toString(sb,min,2); 195 } 196 197 public static Calendar newInstance(TimeZone tz,Locale l) { 198 if(tz==null)tz=ThreadLocalPageContext.getTimeZone(); 199 return Calendar.getInstance(tz,l); 200 } 201 202 /** 203 * important:this function returns always the same instance for a specific thread, 204 * so make sure only use one thread calendar instance at time. 205 * @return calendar instance 206 */ 207 public static Calendar getThreadCalendar(){ 208 Calendar c = calendar.get(); 209 c.clear(); 210 return c; 211 } 212 213 /** 214 * important:this function returns always the same instance for a specific thread, 215 * so make sure only use one thread calendar instance at time. 216 * @return calendar instance 217 */ 218 public static Calendar getThreadCalendar(TimeZone tz){ 219 Calendar c = calendar.get(); 220 c.clear(); 221 if(tz==null)tz=ThreadLocalPageContext.getTimeZone(); 222 c.setTimeZone(tz); 223 return c; 224 } 225 226 /** 227 * important:this function returns always the same instance for a specific thread, 228 * so make sure only use one thread calendar instance at time. 229 * @return calendar instance 230 */ 231 public static Calendar getThreadCalendar(Locale l,TimeZone tz){ 232 if(tz==null)tz=ThreadLocalPageContext.getTimeZone(); 233 Calendar c = localeCalendar.get(tz,l); 234 c.setTimeZone(tz); 235 return c; 236 } 237 238 239 240 /* 241 * internally we use a other instance to avoid conflicts 242 */ 243 private static Calendar _getThreadCalendar(TimeZone tz){ 244 Calendar c = _calendar.get(); 245 c.clear(); 246 if(tz==null)tz=ThreadLocalPageContext.getTimeZone(); 247 c.setTimeZone(tz); 248 return c; 249 } 250 251 /* 252 * internally we use a other instance to avoid conflicts 253 */ 254 private static Calendar _getThreadCalendar(Locale l,TimeZone tz){ 255 Calendar c = _localeCalendar.get(tz,l); 256 if(tz==null)tz=ThreadLocalPageContext.getTimeZone(); 257 c.setTimeZone(tz); 258 return c; 259 } 260 261 262 /*public static Calendar newInstance(Locale l) { 263 Calendar c=Calendar.getInstance(l); 264 return c; 265 }*/ 266 267 static void toString(StringBuilder sb,int i, int amount) { 268 String str = Caster.toString(i); 269 270 amount = amount - str.length(); 271 while( amount-- > 0 ){ 272 sb.append( '0'); 273 } 274 sb.append(str); 275 } 276} 277 278 279class CalendarThreadLocal extends ThreadLocal<Calendar> { 280 protected synchronized Calendar initialValue() { 281 return Calendar.getInstance(); 282 } 283} 284 285 286class LocaleCalendarThreadLocal extends ThreadLocal<Map<String,Calendar>> { 287 protected synchronized Map<String,Calendar> initialValue() { 288 return new HashMap<String, Calendar>(); 289 } 290 291 public Calendar get(TimeZone tz,Locale l) { 292 Map<String, Calendar> map = get(); 293 Calendar c = map.get(l+":"+tz); 294 if(c==null) { 295 c=JREDateTimeUtil.newInstance(tz,l); 296 map.put(l+":"+tz, c); 297 } 298 else c.clear(); 299 return c; 300 } 301}