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.runtime.format;
020
021import java.util.Calendar;
022import java.util.Date;
023import java.util.Locale;
024import java.util.TimeZone;
025
026import lucee.commons.date.JREDateTimeUtil;
027import lucee.commons.lang.StringUtil;
028import lucee.runtime.op.Caster;
029
030public final class DateTimeFormat extends BaseFormat implements Format {
031        
032        //private final Calendar calendar;
033        
034        /**
035         * constructor of the class
036         * @param locale
037         */
038        public DateTimeFormat(Locale locale) {
039                super(locale);
040                //calendar=JREDateTimeUtil.newInstance(locale);
041        }
042        
043
044        /**
045         * formats a date to a cfml date format (short)
046         * @param date
047         * @return formated date
048         */
049        public String format(Date date) {
050                return format(date,"medium");
051        }
052        
053        /**
054         * formats a date to a cfml date format
055         * @param date
056         * @param mask
057         * @return formated date as string
058         */
059        public String format(Date date,String mask) {
060                return format(date,mask,null);
061        }
062        public String format(Date date,String mask, TimeZone tz) {
063                Calendar calendar = JREDateTimeUtil.getThreadCalendar(getLocale(),tz);
064                calendar.setTimeInMillis(date.getTime());
065                
066
067                String lcMask=StringUtil.toLowerCase(mask);
068                if(lcMask.equals("short"))                      return getAsString(calendar,java.text.DateFormat.SHORT,tz);
069                else if(lcMask.equals("medium"))                return getAsString(calendar,java.text.DateFormat.MEDIUM,tz);
070                else if(lcMask.equals("long"))          return getAsString(calendar,java.text.DateFormat.LONG,tz);
071                else if(lcMask.equals("full"))          return getAsString(calendar,java.text.DateFormat.FULL,tz);
072                
073                int len=mask.length();
074                int pos=0;
075                if(len==0) return "";
076                
077                StringBuilder formated=new StringBuilder();
078                
079                
080                
081                for(;pos<len;pos++) {
082                        char c=mask.charAt(pos);
083                        char next=(len>pos+1)?mask.charAt(pos+1):(char)0;
084                        switch(c) {
085
086                        // d: Day of month. Digits; no leading zero for single-digit days 
087                        // dd: Day of month. Digits; leading zero for single-digit days 
088                        // ddd: Day of week, abbreviation 
089                        // dddd: Day of week. Full name 
090                                case 'd':
091                                case 'D':
092                                        char next2=(len>pos+2)?mask.charAt(pos+2):(char)0;
093                                        char next3=(len>pos+3)?mask.charAt(pos+3):(char)0;
094                                        
095                                        int day=calendar.get(Calendar.DATE);
096                                        if(next=='d' || next=='D') {
097                                                if(next2=='d' || next2=='D') {
098                                                        if(next3=='d' || next3=='D') {
099                                                                formated.append(getDayOfWeekAsString(calendar.get(Calendar.DAY_OF_WEEK)));
100                                                                pos+=3;
101                                                        }
102                                                        else {
103                                                                formated.append(getDayOfWeekShortAsString(calendar.get(Calendar.DAY_OF_WEEK)));
104                                                                pos+=2;                                                         
105                                                        }
106                                                }
107                                                else {
108                                                        formated.append(day<10?"0"+day:""+day);
109                                                        pos++;
110                                                }
111                                        }
112                                        else {
113                                                formated.append(day);
114                                        }                                       
115                                break;
116
117                        // m: Month. Digits; no leading zero for single-digit months 
118                        // mm: Month. Digits; leading zero for single-digit months 
119                        // mmm: Month. abbreviation (if appropriate) 
120                        // mmmm: Month. Full name 
121                                case 'm':
122                                case 'M':
123                                        char next_2=(len>pos+2)?mask.charAt(pos+2):(char)0;
124                                        char next_3=(len>pos+3)?mask.charAt(pos+3):(char)0;
125                                        
126                                        int month=calendar.get(Calendar.MONTH)+1;
127                                        if(next=='m' || next=='M') {
128                                                if(next_2=='m' || next_2=='M') {
129                                                        if(next_3=='m' || next_3=='M') {
130                                                                formated.append(getMonthAsString(month));
131                                                                pos+=3;
132                                                        }
133                                                        else {
134                                                                formated.append(getMonthShortAsString(month));
135                                                                pos+=2;                                                         
136                                                        }
137                                                }
138                                                else {
139                                                        formated.append(month<10?"0"+month:""+month);
140                                                        pos++;
141                                                }
142                                        }
143                                        else {
144                                                formated.append(month);
145                                        }                                       
146                                break;
147
148                        // y: Year. Last two digits; no leading zero for years less than 10 
149                        // yy: Year. Last two digits; leading zero for years less than 10 
150                        // yyyy: Year. Four digits 
151                                case 'y':
152                                case 'Y':
153                                        char next__2=(len>pos+2)?mask.charAt(pos+2):(char)0;
154                                        char next__3=(len>pos+3)?mask.charAt(pos+3):(char)0;
155                                        
156                                        int year4=calendar.get(Calendar.YEAR);
157                                        int year2=year4%100;
158                                        if(next=='y' || next=='Y') {
159                                                if((next__2=='y' || next__2=='Y') && (next__3=='y' || next__3=='Y')) {
160                                                        formated.append(year4);
161                                                        pos+=3;
162                                                }
163                                                else {
164                                                        formated.append(year2<10?"0"+year2:""+year2);
165                                                        pos++;
166                                                }
167                                        }
168                                        else {
169                                                formated.append(year2);
170                                        }                                       
171                                break;
172                                
173
174
175                                // h: Hours; no leading zero for single-digit hours (12-hour clock) 
176                                // hh: Hours; leading zero for single-digit hours. (12-hour clock) 
177                                        case 'h':
178                                                int hour1=calendar.get(Calendar.HOUR_OF_DAY);
179                                                if(hour1==0)hour1=12;
180                                                if(hour1>12)hour1=hour1-12;
181                                                if(next=='h') {
182                                                        formated.append(hour1<10?"0"+hour1:""+hour1);
183                                                        pos++;
184                                                }
185                                                else {
186                                                        formated.append(hour1);
187                                                }                                       
188                                        break;
189
190                                // H: Hours; no leading zero for single-digit hours (24-hour clock) 
191                                // HH: Hours; leading zero for single-digit hours (24-hour clock) 
192                                        case 'H':
193                                                int hour2=calendar.get(Calendar.HOUR_OF_DAY);
194                                                if(next=='H') {
195                                                        formated.append(hour2<10?"0"+hour2:""+hour2);
196                                                        pos++;
197                                                }
198                                                else {
199                                                        formated.append(hour2);
200                                                }                                       
201                                        break;
202
203                                // n: Minutes; no leading zero for single-digit minutes 
204                                // nn: Minutes; leading zero for single-digit minutes 
205                                        case 'N':
206                                        case 'n':
207                                                int minute=calendar.get(Calendar.MINUTE);
208                                                if(next=='N' || next=='n') {
209                                                        formated.append(minute<10?"0"+minute:""+minute);
210                                                        pos++;
211                                                }
212                                                else {
213                                                        formated.append(minute);
214                                                }                                       
215                                        break;
216
217                                // s: Seconds; no leading zero for single-digit seconds 
218                                // ss: Seconds; leading zero for single-digit seconds 
219                                        case 's':
220                                        case 'S':
221                                                int second=calendar.get(Calendar.SECOND);
222                                                if(next=='S' || next=='s') {
223                                                        formated.append(second<10?"0"+second:""+second);
224                                                        pos++;
225                                                }
226                                                else {
227                                                        formated.append(second);
228                                                }                                       
229                                        break;
230
231                                // l: Milliseconds 
232                                        case 'l':
233                                        case 'L':
234                                                char nextnext=(len>pos+2)?mask.charAt(pos+2):(char)0;
235
236                                                String millis=Caster.toString(calendar.get(Calendar.MILLISECOND));
237                                                if(next=='L' || next=='l') {
238                                                        if(millis.length()==1)millis="0"+millis;
239                                                        pos++;
240                                                }
241                                                if(nextnext=='L' || nextnext=='l') {
242                                                        if(millis.length()==2)millis="0"+millis;
243                                                        pos++;
244                                                }
245                                                formated.append(millis);        
246                                                
247                                                
248                                                
249                                        break;
250
251                                // t: One-character time marker string, such as A or P. 
252                                // tt: Multiple-character time marker string, such as AM or PM 
253                                        case 't':
254                                        case 'T':
255                                                boolean isAm=calendar.get(Calendar.HOUR_OF_DAY)<12;
256                                                if(next=='T' || next=='t') {
257                                                        formated.append(isAm?"AM":"PM");
258                                                        pos++;
259                                                }
260                                                else {
261                                                        formated.append(isAm?"A":"P");
262                                                }                                       
263                                        break;
264                                        case 'z':
265                                        case 'Z':
266                                                // count next z and jump to last z (max 6)
267                                                int start=pos;
268                                                while((pos+1)<len && Character.toLowerCase(mask.charAt(pos+1))=='z'){
269                                                        pos++;
270                                                        if(pos-start>4)break;
271                                                }
272                                                if(pos-start>2)formated.append(tz.getDisplayName(getLocale()));      
273                                                else formated.append(tz.getID());       
274                                                
275                                        break;
276                                
277                                
278                                
279                        // Otherwise
280                                default:
281                                        formated.append(c);
282                        }
283                }
284                return formated.toString();
285        }
286        
287
288        private String getAsString(Calendar c,int style, TimeZone tz) {
289                java.text.DateFormat df = java.text.DateFormat.getDateTimeInstance(style,style,getLocale());
290                df.setTimeZone(tz);
291                return df.format(c.getTime());  
292        }
293}