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}