001    // TODO Time constructor muss auch noch entfernt werden und durch DateUtil methode ersetzen
002    package railo.runtime.op.date;
003    
004    import java.text.DateFormat;
005    import java.text.ParsePosition;
006    import java.util.Calendar;
007    import java.util.Date;
008    import java.util.Locale;
009    import java.util.TimeZone;
010    
011    import railo.commons.date.DateTimeUtil;
012    import railo.commons.date.JREDateTimeUtil;
013    import railo.commons.date.TimeZoneConstants;
014    import railo.commons.i18n.FormatUtil;
015    import railo.commons.lang.StringUtil;
016    import railo.runtime.Component;
017    import railo.runtime.engine.ThreadLocalPageContext;
018    import railo.runtime.exp.ExpressionException;
019    import railo.runtime.exp.PageException;
020    import railo.runtime.op.Castable;
021    import railo.runtime.op.Caster;
022    import railo.runtime.op.Decision;
023    import railo.runtime.type.ObjectWrap;
024    import railo.runtime.type.dt.DateTime;
025    import railo.runtime.type.dt.DateTimeImpl;
026    import railo.runtime.type.dt.Time;
027    import railo.runtime.type.dt.TimeImpl;
028    
029    /**
030     * Class to cast Strings to Date Objects
031     */
032    public final class DateCaster {
033    
034            private static final Object NULL = new Object();
035    
036            //private static short MODE_DAY_STR=1;
037            //private static short MODE_MONTH_STR=2;
038            //private static short MODE_NONE=4;
039            private static long DEFAULT_VALUE=Long.MIN_VALUE;
040            
041            private static DateTimeUtil util=DateTimeUtil.getInstance();
042            public static boolean classicStyle=false;
043            
044            /**     
045             * converts a Object to a DateTime Object (Advanced but slower)
046             * @param o Object to Convert
047             * @param timezone
048             * @return Date Time Object
049             * @throws PageException
050             */
051            public static DateTime toDateAdvanced(Object o,TimeZone timezone) throws PageException {
052                    if(o instanceof Date)           {
053                            if(o instanceof DateTime) return (DateTime)o;
054                            return new DateTimeImpl((Date)o);
055                    }
056                    else if(o instanceof Castable)  return ((Castable)o).castToDateTime();
057                    else if(o instanceof String)    {
058                        DateTime dt=toDateAdvanced((String)o,timezone,null);
059                        if(dt==null)
060                                    throw new ExpressionException("can't cast ["+o+"] to date value");
061                        return dt;
062                    }
063                    else if(o instanceof Number)            return util.toDateTime(((Number)o).doubleValue());
064                    else if(o instanceof ObjectWrap) return toDateAdvanced(((ObjectWrap)o).getEmbededObject(),timezone);
065                    else if(o instanceof Calendar){
066                            
067                            return new DateTimeImpl((Calendar)o);
068                    }
069                    throw new ExpressionException("can't cast ["+Caster.toClassName(o)+"] to date value");
070            }
071            
072            /**
073             * converts a Object to a DateTime Object (Advanced but slower)
074             * @param str String to Convert
075             * @param timezone
076             * @return Date Time Object
077             * @throws PageException
078             */
079            public static DateTime toDateAdvanced(String str,TimeZone timezone) throws PageException {
080                    DateTime dt=toDateAdvanced(str,timezone,null);
081                if(dt==null)
082                            throw new ExpressionException("can't cast ["+str+"] to date value");
083                return dt;
084            }
085    
086            /**
087             * converts a Object to a DateTime Object (Advanced but slower), returns null if invalid string
088             * @param o Object to Convert
089             * @param timeZone
090             * @param defaultValue 
091             * @return Date Time Object
092             */
093            public static DateTime toDateAdvanced(Object o,TimeZone timeZone, DateTime defaultValue) {
094                    if(o instanceof DateTime)               return (DateTime)o;
095                    else if(o instanceof Date)              return new DateTimeImpl((Date)o);
096                    else if(o instanceof Castable)  {
097                        return ((Castable)o).castToDateTime(defaultValue);
098                    }
099                    else if(o instanceof String)    return toDateAdvanced(o.toString(),timeZone,defaultValue);
100                    else if(o instanceof Number)    return util.toDateTime(((Number)o).doubleValue());
101                    else if(o instanceof Calendar){
102                            return new DateTimeImpl((Calendar)o);
103                    }
104                    else if(o instanceof ObjectWrap) return toDateAdvanced(((ObjectWrap)o).getEmbededObject(defaultValue),timeZone,defaultValue);
105                    return defaultValue;
106            }
107    
108            /**
109             * converts a String to a DateTime Object (Advanced but slower), returns null if invalid string
110             * @param str String to convert
111             * @param timeZone
112             * @param defaultValue 
113             * @return Date Time Object
114             */
115            public static DateTime toDateAdvanced(String str,boolean alsoNumbers,TimeZone timeZone, DateTime defaultValue) {
116                    str=str.trim();
117                    if(StringUtil.isEmpty(str)) return defaultValue;
118                    timeZone=ThreadLocalPageContext.getTimeZone(timeZone);
119                    DateTime dt=toDateSimple(str,alsoNumbers,true,timeZone,defaultValue);
120                    if(dt==null) {  
121                    DateFormat[] formats = FormatUtil.getCFMLFormats(timeZone, true);
122                        synchronized(formats){
123                            Date d;
124                            ParsePosition pp=new ParsePosition(0);
125                            for(int i=0;i<formats.length;i++) {
126                                            //try {
127                                                    pp.setErrorIndex(-1);
128                                                    pp.setIndex(0);
129                                                    d = formats[i].parse(str,pp);
130                                                    if (pp.getIndex() == 0 || d==null || pp.getIndex()<str.length()) continue;   
131                                                    dt= new DateTimeImpl(d.getTime(),false);
132                                                    return dt;
133                                            //}catch (ParseException e) {}
134                                    }
135                    }
136                        dt=toDateTime(Locale.US, str, timeZone,defaultValue, false);
137                }
138                return dt;
139            }
140            
141    
142        
143        /**
144         * parse a string to a Datetime Object
145         * @param locale 
146         * @param str String representation of a locale Date
147         * @param tz
148         * @return DateTime Object
149         * @throws PageException 
150         */
151        public static DateTime toDateTime(Locale locale,String str, TimeZone tz,boolean useCommomDateParserAsWell) throws PageException {
152            
153            DateTime dt=toDateTime(locale, str, tz,null,useCommomDateParserAsWell);
154            if(dt==null){
155                    /* FUTURE 4.1
156                    String prefix=locale.getLanguage()+"-"+locale.getCountry()+"-";
157                    throw new ExpressionException("can't cast ["+str+"] to date value",
158                                    "to add custom formats for "+LocaleFactory.toString(locale)+
159                                    ", create/extend on of the following files ["+prefix+"datetime.df (for date time formats), "+prefix+"date.df (for date formats) or "+prefix+"time.df (for time formats)] in the following directory [<context>/railo/locales]."+
160                                    "");
161                    */
162                    throw new ExpressionException("can't cast ["+str+"] to date value");
163            }
164            return dt;
165        }
166            
167        /**
168         * parse a string to a Datetime Object, returns null if can't convert
169         * @param locale 
170         * @param str String representation of a locale Date
171         * @param tz
172         * @param defaultValue 
173         * @return datetime object
174         */
175        public synchronized static DateTime toDateTime(Locale locale,String str, TimeZone tz, DateTime defaultValue,boolean useCommomDateParserAsWell) {
176            str=str.trim();
177            tz=ThreadLocalPageContext.getTimeZone(tz);
178            DateFormat[] df;
179    
180            // get Calendar
181            Calendar c=JREDateTimeUtil.getCalendar(locale);
182            //synchronized(c){
183                    
184                    // datetime
185                    ParsePosition pp=new ParsePosition(0);
186                    df=FormatUtil.getDateTimeFormats(locale,tz,false);//dfc[FORMATS_DATE_TIME];
187                    Date d;
188                    //print.e(locale.getDisplayName(Locale.ENGLISH));
189                    for(int i=0;i<df.length;i++) {
190                            //print.e(df[i].format(new Date()));
191                            pp.setErrorIndex(-1);
192                                    pp.setIndex(0);
193                                    //try {
194                    df[i].setTimeZone(tz);
195                    d = df[i].parse(str,pp);
196                    if (pp.getIndex() == 0 || d==null || pp.getIndex()<str.length()) continue;   
197                                    
198                    synchronized(c) {
199                            optimzeDate(c,tz,d);
200                            return new DateTimeImpl(c.getTime());
201                    }
202                        //}catch (ParseException e) {}
203                    }
204                    // date
205                    df=FormatUtil.getDateFormats(locale,tz,false);//dfc[FORMATS_DATE];
206                    //print.e(locale.getDisplayName(Locale.ENGLISH));
207                    for(int i=0;i<df.length;i++) {
208                            //print.e(df[i].format(new Date()));
209                            pp.setErrorIndex(-1);
210                                    pp.setIndex(0);
211                                    //try {
212                    df[i].setTimeZone(tz);
213                                    d=df[i].parse(str,pp);
214                    if (pp.getIndex() == 0 || d==null || pp.getIndex()<str.length()) continue;   
215                    
216                                    synchronized(c) {
217                            optimzeDate(c,tz,d);
218                            return new DateTimeImpl(c.getTime());
219                    }
220                                    //}catch (ParseException e) {}
221                    }
222                    
223                    // time
224                    df=FormatUtil.getTimeFormats(locale,tz,false);//dfc[FORMATS_TIME];
225                    //print.e(locale.getDisplayName(Locale.ENGLISH));
226                    for(int i=0;i<df.length;i++) {
227                            //print.e(df[i].format(new Date()));
228                            pp.setErrorIndex(-1);
229                                    pp.setIndex(0);
230                                    //try {
231                    df[i].setTimeZone(tz);
232                    d=df[i].parse(str,pp);
233                    if (pp.getIndex() == 0 || d==null || pp.getIndex()<str.length()) continue;   
234                    synchronized(c) {
235                            c.setTimeZone(tz);
236                            c.setTime(d);
237                            c.set(Calendar.YEAR,1899);
238                            c.set(Calendar.MONTH,11);
239                            c.set(Calendar.DAY_OF_MONTH,30);
240                            c.setTimeZone(tz);
241                    }
242                    return new DateTimeImpl(c.getTime());
243                        //}catch (ParseException e) {}
244                    }
245            //}
246            if(useCommomDateParserAsWell)return DateCaster.toDateSimple(str, false,true, tz, defaultValue);
247            return defaultValue;
248        }
249        
250        private static void optimzeDate(Calendar c, TimeZone tz, Date d) {
251            c.setTimeZone(tz);
252            c.setTime(d);
253            int year=c.get(Calendar.YEAR);
254            if(year<40) c.set(Calendar.YEAR,2000+year);
255            else if(year<100) c.set(Calendar.YEAR,1900+year);
256        }
257            
258            
259            
260            public static DateTime toDateAdvanced(String str, TimeZone timeZone, DateTime defaultValue) {
261                return toDateAdvanced(str, true, timeZone,defaultValue);
262            }
263            
264            /**
265             * converts a boolean to a DateTime Object
266             * @param b boolean to Convert
267             * @param timeZone 
268             * @return coverted Date Time Object
269             */
270            public static DateTime toDateSimple(boolean b, TimeZone timeZone) {
271                    return toDateSimple(b?1L:0L, timeZone);
272            }
273    
274            /**
275             * converts a char to a DateTime Object
276             * @param c char to Convert
277             * @param timeZone
278             * @return coverted Date Time Object
279             */
280            public static DateTime toDateSimple(char c, TimeZone timeZone) {
281                    return toDateSimple((long)c, timeZone);
282            }
283    
284            /**
285             * converts a double to a DateTime Object
286             * @param d double to Convert
287             * @param timeZone
288             * @return coverted Date Time Object
289             */
290            public static DateTime toDateSimple(double d, TimeZone timeZone) {
291                    return toDateSimple((long)d, timeZone);
292            }
293            
294            /* *
295             * converts a double to a DateTime Object
296             * @param d double to Convert
297             * @param timeZone
298             * @return coverted Date Time Object
299             * /
300            public static DateTime toDateSimple(long l, TimeZone timezone) {
301                    return new DateTimeImpl(l,false);
302            }*/
303    
304            /**
305             * converts a Object to a DateTime Object, returns null if invalid string
306             * @param o Object to Convert
307             * @param timeZone
308             * @return coverted Date Time Object
309             * @throws PageException
310             */
311            public static DateTime toDateSimple(Object o, boolean alsoNumbers,boolean alsoMonthString, TimeZone timeZone) throws PageException {
312                    if(o instanceof DateTime)               return (DateTime)o;
313                    else if(o instanceof Date)              return new DateTimeImpl((Date)o);
314                    else if(o instanceof Castable)  return ((Castable)o).castToDateTime();
315                    else if(o instanceof String)    return toDateSimple(o.toString(),alsoNumbers,alsoMonthString, timeZone);
316                    else if(o instanceof Number)            return util.toDateTime(((Number)o).doubleValue());
317                    else if(o instanceof Calendar)          return new DateTimeImpl((Calendar)o);
318                    else if(o instanceof ObjectWrap) return toDateSimple(((ObjectWrap)o).getEmbededObject(),alsoNumbers,alsoMonthString,timeZone);
319                    else if(o instanceof Calendar){
320                            return new DateTimeImpl((Calendar)o);
321                    }
322                    
323                    if(o instanceof Component)
324                            throw new ExpressionException("can't cast component ["+((Component)o).getAbsName()+"] to date value");
325                    
326                    throw new ExpressionException("can't cast ["+Caster.toTypeName(o)+"] to date value");
327            }
328            
329            public static DateTime toDateSimple(Object o, boolean alsoNumbers,boolean alsoMonthString, TimeZone timeZone, DateTime defaultValue) {
330                    if(o instanceof DateTime)               return (DateTime)o;
331                    else if(o instanceof Date)              return new DateTimeImpl((Date)o);
332                    else if(o instanceof Castable)  return ((Castable)o).castToDateTime(defaultValue);
333                    else if(o instanceof String)    return toDateSimple(o.toString(),alsoNumbers,alsoMonthString, timeZone,defaultValue);
334                    else if(o instanceof Number)            return util.toDateTime(((Number)o).doubleValue());
335                    else if(o instanceof Calendar)          return new DateTimeImpl((Calendar)o);
336                    else if(o instanceof ObjectWrap) {
337                            Object eo = ((ObjectWrap)o).getEmbededObject(NULL);
338                            if(eo==NULL) return defaultValue;
339                            return toDateSimple(eo,alsoNumbers,alsoMonthString,timeZone,defaultValue);
340                    }
341                    else if(o instanceof Calendar){
342                            return new DateTimeImpl((Calendar)o);
343                    }
344                    return defaultValue;
345            }
346            
347            /**
348             * converts a Object to a DateTime Object, returns null if invalid string
349             * @param str String to Convert
350             * @param timeZone
351             * @return coverted Date Time Object
352             * @throws PageException
353             */
354            public static DateTime toDateSimple(String str, TimeZone timeZone) throws PageException {
355                     DateTime dt=toDateSimple(str,true,true, timeZone,null);
356                     if(dt==null)
357                             throw new ExpressionException("can't cast ["+str+"] to date value");
358                     return dt;
359            }
360    
361            /**
362             * converts a Object to a Time Object, returns null if invalid string
363             * @param o Object to Convert
364             * @return coverted Date Time Object
365             * @throws PageException
366             */
367            public static Time toTime(TimeZone timeZone,Object o) throws PageException {
368                if(o instanceof Time)               return (Time)o;
369                else if(o instanceof Date)          return new TimeImpl((Date)o);
370                    else if(o instanceof Castable)  return new TimeImpl(((Castable)o).castToDateTime());
371                    else if(o instanceof String)    {
372                        Time dt=toTime(timeZone,o.toString(),null);
373                        if(dt==null)
374                                    throw new ExpressionException("can't cast ["+o+"] to time value");
375                        return dt;
376                    }
377                    else if(o instanceof ObjectWrap) return toTime(timeZone,((ObjectWrap)o).getEmbededObject());
378                    else if(o instanceof Calendar){
379                            // TODO check timezone offset
380                            return new TimeImpl(((Calendar)o).getTimeInMillis(),false);
381                    }
382                    throw new ExpressionException("can't cast ["+Caster.toClassName(o)+"] to time value");
383            }
384             
385             public static DateTime toDateAdvanced(Object o,boolean alsoNumbers, TimeZone timeZone, DateTime defaultValue) {
386            return _toDateAdvanced(o, alsoNumbers, timeZone, defaultValue, true);
387             }
388             
389             private static DateTime _toDateAdvanced(Object o,boolean alsoNumbers, TimeZone timeZone, DateTime defaultValue, boolean advanced) {
390            if(o instanceof DateTime)       return (DateTime)o;
391            else if(o instanceof Date)      return new DateTimeImpl((Date)o);
392            else if(o instanceof Castable)  {
393                return ((Castable)o).castToDateTime(defaultValue);
394            }
395            else if(o instanceof String)    {
396                    if(advanced)return toDateAdvanced(o.toString(),alsoNumbers, timeZone,defaultValue);
397                    return toDateSimple(o.toString(),alsoNumbers,true, timeZone,defaultValue);
398            }
399            else if(alsoNumbers && o instanceof Number)     return util.toDateTime(((Number)o).doubleValue());
400            else if(o instanceof ObjectWrap) {
401                    return _toDateAdvanced(((ObjectWrap)o).getEmbededObject(defaultValue),alsoNumbers,timeZone,defaultValue,advanced);
402            }
403            else if(o instanceof Calendar){
404                            return new DateTimeImpl((Calendar)o);
405                    }
406                    return defaultValue;
407        }
408                
409        
410    
411        /**
412         * converts a Object to a DateTime Object, returns null if invalid string
413         * @param str Stringt to Convert
414         * @param alsoNumbers
415         * @param timeZone
416         * @return coverted Date Time Object
417         * @throws PageException  
418         */
419        public static DateTime toDateSimple(String str,boolean alsoNumbers,boolean alsoMonthString, TimeZone timeZone) throws PageException {
420            DateTime dt = toDateSimple(str,alsoNumbers,alsoMonthString,timeZone,null);
421            if(dt==null) throw new ExpressionException("can't cast value to a Date Object");
422            return dt;
423        }
424        
425        public static DateTime toDateAdvanced(Object o,boolean alsoNumbers, TimeZone timeZone) throws PageException {
426            DateTime dt = toDateAdvanced(o,alsoNumbers,timeZone,null);
427            if(dt==null) throw new ExpressionException("can't cast value to a Date Object");
428            return dt;
429        }
430        
431        
432            
433            /**
434             * converts a String to a Time Object, returns null if invalid string
435             * @param str String to convert
436             * @param defaultValue 
437             * @return Time Object
438             * @throws  
439             */
440            public static Time toTime(TimeZone timeZone,String str, Time defaultValue)  {
441                    
442                    if(str==null || str.length()<3) {
443                        return defaultValue;
444                    }
445                    DateString ds=new DateString(str);
446            // Timestamp
447                    if(ds.isCurrent('{') && ds.isLast('}')) {
448                        
449                            // Time
450                            // "^\\{t '([0-9]{1,2}):([0-9]{1,2}):([0-9]{2})'\\}$"
451                            if(ds.fwIfNext('t')) {
452                                        
453                                        // Time
454                                                    if(!(ds.fwIfNext(' ') && ds.fwIfNext('\'')))return defaultValue;
455                                                    ds.next();
456                                            // hour
457                                                    int hour=ds.readDigits();
458                                                    if(hour==-1) return defaultValue;
459                                                    
460                                                    if(!ds.fwIfCurrent(':'))return defaultValue;
461                                            
462                                            // minute
463                                                    int minute=ds.readDigits();
464                                                    if(minute==-1) return defaultValue;
465                                                    
466                                                    if(!ds.fwIfCurrent(':'))return defaultValue;
467                                            
468                                            // second
469                                                    int second=ds.readDigits();
470                                                    if(second==-1) return defaultValue;
471                                                    
472                                                    if(!(ds.fwIfCurrent('\'') && ds.fwIfCurrent('}')))return defaultValue;
473                                                    
474                                                    if(ds.isAfterLast()){
475                                                            long time=util.toTime(timeZone,1899,12,30,hour,minute,second,0,DEFAULT_VALUE);
476                                                            if(time==DEFAULT_VALUE)return defaultValue;
477                                                            return new TimeImpl(time,false);
478                                                    }
479                                                    return defaultValue;
480                                    
481                                                    
482                            }
483                            return defaultValue;
484                    }
485            // Time start with int
486                    /*else if(ds.isDigit()) {
487                        char sec=ds.charAt(1);
488                        char third=ds.charAt(2);
489                        // 16.10.2004 (02:15)?
490                            if(sec==':' || third==':') {
491                                    // hour
492                                    int hour=ds.readDigits();
493                                    if(hour==-1) return defaultValue;
494                                    
495                                    if(!ds.fwIfCurrent(':'))return defaultValue;
496                                    
497                                    // minutes
498                                    int minutes=ds.readDigits();
499                                    if(minutes==-1) return defaultValue;
500                                    
501                                    if(ds.isAfterLast()) {
502                                            long time=util.toTime(timeZone,1899,12,30,hour,minutes,0,0,DEFAULT_VALUE);
503                                            if(time==DEFAULT_VALUE) return defaultValue;
504                                            
505                                            return new TimeImpl(time,false);
506                                    }
507                                    //else if(!ds.fwIfCurrent(':'))return null;
508                                    else if(!ds.fwIfCurrent(':')) {
509                                        if(!ds.fwIfCurrent(' '))return defaultValue;
510                                        
511                                        if(ds.fwIfCurrent('a') || ds.fwIfCurrent('A'))      {
512                                            if(ds.fwIfCurrent('m') || ds.fwIfCurrent('M')) {
513                                                if(ds.isAfterLast()) {
514                                                    long time=util.toTime(timeZone,1899,12,30,hour,minutes,0,0,DEFAULT_VALUE);
515                                                                    if(time==DEFAULT_VALUE) return defaultValue;
516                                                    return new TimeImpl(time,false);
517                                                }
518                                            }
519                                            return defaultValue;
520                                        }
521                                        else if(ds.fwIfCurrent('p') || ds.fwIfCurrent('P')) {
522                                            if(ds.fwIfCurrent('m') || ds.fwIfCurrent('M')) {
523                                                if(ds.isAfterLast()) {
524                                                    long time=util.toTime(timeZone,1899,12,30,hour<13?hour+12:hour,minutes,0,0,DEFAULT_VALUE);
525                                                                    if(time==DEFAULT_VALUE) return defaultValue;
526                                                                    return new TimeImpl(time,false);
527                                                }
528                                            }
529                                            return defaultValue;
530                                        }
531                                        return defaultValue;
532                                    }
533                                    
534                                    
535                                    // seconds
536                                    int seconds=ds.readDigits();
537                                    if(seconds==-1) return defaultValue;
538                                    
539                                    if(ds.isAfterLast()) {
540                                            long time=util.toTime(timeZone,1899,12,30,hour,minutes,seconds,0,DEFAULT_VALUE);
541                                            if(time==DEFAULT_VALUE) return defaultValue;
542                                            return new TimeImpl(time,false);
543                                    }
544                                                                    
545                        }
546                    }*/
547                    
548                    // TODO bessere impl
549                    ds.reset();
550                    DateTime rtn = parseTime(timeZone, new int[]{1899,12,30}, ds, defaultValue,-1);
551                    if(rtn==defaultValue) return defaultValue;
552                    return new TimeImpl(rtn);
553                    
554                    
555                    
556                    //return defaultValue;
557            }
558    
559            
560            
561            /**
562             * converts a String to a DateTime Object, returns null if invalid string
563             * @param str String to convert
564             * @param alsoNumbers
565             * @param alsoMonthString allow that the month is a english name
566             * @param timeZone
567             * @param defaultValue 
568             * @return Date Time Object
569             */     
570            private static DateTime parseDateTime(String str,DateString ds,boolean alsoNumbers,boolean alsoMonthString,TimeZone timeZone, DateTime defaultValue) {
571                    int month=0;
572                    int first=ds.readDigits();
573                    // first
574                    if(first==-1) {
575                            if(!alsoMonthString) return defaultValue;
576                            first=ds.readMonthString();
577                            if(first==-1)return defaultValue;
578                            month=1;
579                    }
580                    
581                    if(ds.isAfterLast()) return month==1?defaultValue:toNumberDate(str,alsoNumbers,defaultValue);
582                    
583                    char del=ds.current();
584                    if(del!='.' && del!='/' && del!='-' && del!=' ' && del!='\t') {
585                            if(ds.fwIfCurrent(':')){
586                                    return parseTime(timeZone, new int[]{1899,12,30}, ds, defaultValue,first);
587                            }
588                            return defaultValue;
589                    }
590                    ds.next();
591                    ds.removeWhitespace();
592                    
593                    // second
594                    int second=ds.readDigits();
595                    if(second==-1){
596                            if(!alsoMonthString) return defaultValue;
597                            second=ds.readMonthString();
598                            if(second==-1)return defaultValue;
599                            month=2;
600                    }
601                    
602                    if(ds.isAfterLast()) {
603                            return toDate(month,timeZone,first,second,defaultValue);
604                    }
605                    
606                    char del2=ds.current();
607                    if(del!=del2) {
608                            ds.fwIfCurrent(' ');
609                            ds.fwIfCurrent('T');
610                            ds.fwIfCurrent(' ');
611                            return parseTime(timeZone,_toDate(timeZone,month, first, second),ds,defaultValue,-1);
612                    }
613                    ds.next();
614                    ds.removeWhitespace();
615                    
616                    
617                    
618                    int third=ds.readDigits();
619                    if(third==-1){
620                            return defaultValue;
621                    }
622                    
623                    if(ds.isAfterLast()) {
624                            if(classicStyle() && del=='.')return toDate(month,timeZone,second,first,third,defaultValue);
625                            return toDate(month,timeZone,first,second,third,defaultValue);
626                    }
627                    ds.fwIfCurrent(' ');
628                    ds.fwIfCurrent('T');
629                    ds.fwIfCurrent(' ');
630                    if(classicStyle() && del=='.')return parseTime(timeZone,_toDate(month, second,first,third),ds,defaultValue,-1);
631                    return parseTime(timeZone,_toDate(month, first, second,third),ds,defaultValue,-1);
632                    
633                    
634            }
635            
636            private static boolean classicStyle() {
637                    return classicStyle;
638            }
639    
640            private static DateTime parseTime(TimeZone timeZone,int[] date, DateString ds,DateTime defaultValue, int hours) {
641                    if(date==null)return defaultValue;
642                    
643    
644                    ds.removeWhitespace();
645                    
646                    // hour
647                    boolean next=false;
648                    if(hours==-1){
649                            ds.removeWhitespace();
650                            hours=ds.readDigits();
651                            ds.removeWhitespace();
652                            if(hours==-1) {
653                                    return parseOffset(ds,timeZone,date,0,0,0,0,defaultValue);
654                            }
655                    }
656                    else next=true;
657                    
658                    int minutes=0;
659                    if(next || ds.fwIfCurrent(':')){
660                            ds.removeWhitespace();
661                            minutes=ds.readDigits();
662                            ds.removeWhitespace();
663                            if(minutes==-1) return defaultValue;
664                    }
665                    
666    
667                    int seconds=0;
668                    if(ds.fwIfCurrent(':')){
669                            ds.removeWhitespace();
670                            seconds=ds.readDigits();
671                            ds.removeWhitespace();
672                            if(seconds==-1) return defaultValue;
673                    }
674                    
675                    int msSeconds=0;
676                    if(ds.fwIfCurrent('.')){
677                            ds.removeWhitespace();
678                            msSeconds=ds.readDigits();
679                            ds.removeWhitespace();
680                            if(msSeconds==-1) return defaultValue;
681                    }
682                    
683                    if(ds.isAfterLast()){
684                            return DateTimeUtil.getInstance().toDateTime(timeZone, date[0], date[1], date[2], hours, minutes, seconds, msSeconds, defaultValue);
685                    }
686                    ds.fwIfCurrent(' ');
687                    
688                    if(ds.fwIfCurrent('a') || ds.fwIfCurrent('A'))  {
689                            if(!ds.fwIfCurrent('m'))ds.fwIfCurrent('M');
690                    if(ds.isAfterLast()) 
691                        return DateTimeUtil.getInstance().toDateTime(timeZone, date[0], date[1], date[2], 
692                                    hours<12?hours:hours-12, minutes, seconds, msSeconds, defaultValue);
693                    return defaultValue;
694                }
695                else if(ds.fwIfCurrent('p') || ds.fwIfCurrent('P')) {
696                    if(!ds.fwIfCurrent('m'))ds.fwIfCurrent('M');
697                    if(hours>24) return defaultValue;
698                    if(ds.isAfterLast()) 
699                            return DateTimeUtil.getInstance().toDateTime(timeZone, date[0], date[1], date[2], 
700                                            hours<12?hours+12:hours, minutes, seconds, msSeconds,defaultValue);
701                    return defaultValue;
702                }
703                    
704                    ds.fwIfCurrent(' ');
705                    return parseOffset(ds,timeZone,date,hours,minutes,seconds,msSeconds,defaultValue);
706                
707            }
708    
709            private static DateTime parseOffset(DateString ds, TimeZone timeZone, int[] date,int hours, int minutes, int seconds, int msSeconds,DateTime defaultValue) {
710                    if(ds.isLast() && (ds.fwIfCurrent('Z') ||  ds.fwIfCurrent('z'))) {
711                            return util.toDateTime(TimeZoneConstants.UTC, date[0], date[1], date[2], hours, minutes, seconds, msSeconds,defaultValue);
712                    }
713                    else if(ds.fwIfCurrent('+')){
714                    DateTime rtn = util.toDateTime(timeZone, date[0], date[1], date[2], hours, minutes, seconds, msSeconds,defaultValue);
715                    if(rtn==defaultValue) return rtn;
716                    return readOffset(true,timeZone,rtn,date[0], date[1], date[2], hours, minutes, seconds, msSeconds,ds,defaultValue);
717                }
718                else if(ds.fwIfCurrent('-')){
719                    DateTime rtn = util.toDateTime(timeZone, date[0], date[1], date[2], hours, minutes, seconds, msSeconds, defaultValue);
720                    if(rtn==defaultValue) return rtn;
721                    return readOffset(false,timeZone,rtn,date[0], date[1], date[2], hours, minutes, seconds, msSeconds,ds,defaultValue);
722                }
723                    return defaultValue;
724            }
725    
726            private static DateTime toDate(int month, TimeZone timeZone,int first, int second, DateTime defaultValue) {
727                    int[] d = _toDate(timeZone,month, first, second);
728                    if(d==null)return defaultValue;
729                    return util.toDateTime(timeZone, d[0], d[1], d[2],0,0,0,0,defaultValue);
730            }
731            
732            private static int[] _toDate(TimeZone tz,int month, int first, int second) {
733                    int YEAR=year(tz);
734                    if(first<=12 && month<2){
735                            if(util.daysInMonth(YEAR, first)>=second)
736                                    return new int[]{YEAR, first, second};
737                            return new int[]{util.toYear(second), first, 1};
738                    }
739                    // first>12
740                    if(second<=12){
741                            if(util.daysInMonth(YEAR, second)>=first)
742                                    return new int[]{YEAR, second, first};
743                            return new int[]{util.toYear(first), second, 1};
744                    }
745                    return null;
746            }
747            private static int year(TimeZone tz) {
748                    return util.getYear(ThreadLocalPageContext.getTimeZone(tz),new DateTimeImpl());
749            }
750    
751            private static DateTime toDate(int month, TimeZone timeZone,int first, int second, int third, DateTime defaultValue) {
752                    int[] d = _toDate(month, first, second, third);
753                    if(d==null) return defaultValue;
754                    return util.toDateTime(timeZone, d[0], d[1], d[2], 0, 0, 0,0,defaultValue);
755            }
756                    
757            private static int[] _toDate(int month, int first, int second, int third) {
758                    if(first<=12){
759                            if(month==2)
760                                    return new int[]{util.toYear(third), second, first};
761                            if(util.daysInMonth(util.toYear(third), first)>=second)
762                                    return new int[]{util.toYear(third), first, second};
763                            return null;
764                    }
765                    if(second>12)return null;
766                    if(month==2){
767                            int tmp=first;
768                            first=third;
769                            third=tmp;
770                    }
771                    if(util.daysInMonth(util.toYear(first), second)<third) {
772                            if(util.daysInMonth(util.toYear(third), second)>=first)
773                                    return new int[]{util.toYear(third), second, first};
774                            return null;
775                    }
776                    return new int[]{util.toYear(first), second, third};
777            }
778            
779            /**
780             * converts the given string to a date following simple and fast parsing rules (no international formats)
781             * @param str
782             * @param alsoNumbers
783             * @param alsoMonthString allow that the month is defined as english word (jan,janauary ...)
784             * @param timeZone
785             * @param defaultValue
786             * @return
787             */
788            public static DateTime toDateSimple(String str,boolean alsoNumbers,boolean alsoMonthString, TimeZone timeZone, DateTime defaultValue) {
789                    str=StringUtil.trim(str,"");
790                    DateString ds=new DateString(str);
791                    
792            // Timestamp
793                    if(ds.isCurrent('{') && ds.isLast('}')) {
794                            return _toDateSimpleTS(ds,timeZone,defaultValue);
795                    }
796                    DateTime res = parseDateTime(str,ds,alsoNumbers,alsoMonthString,timeZone,defaultValue);
797                    if(alsoNumbers && res==defaultValue && Decision.isNumeric(str)) {
798                    double dbl = Caster.toDoubleValue(str,Double.NaN);
799                    if(Decision.isValid(dbl))return util.toDateTime(dbl);
800                }
801                    return res;
802            }
803    
804            private static DateTime _toDateSimpleTS(DateString ds, TimeZone timeZone, DateTime defaultValue) {
805                    // Date
806                    // "^\\{d '([0-9]{2,4})-([0-9]{1,2})-([0-9]{1,2})'\\}$"
807                    if(ds.fwIfNext('d')) {
808                            if(!(ds.fwIfNext(' ') && ds.fwIfNext('\'')))return defaultValue;
809                            ds.next();
810                    // year
811                            int year=ds.readDigits();
812                            if(year==-1) return defaultValue;
813                            
814                            if(!ds.fwIfCurrent('-'))return defaultValue;
815                    
816                    // month
817                            int month=ds.readDigits();
818                            if(month==-1) return defaultValue;
819                            
820                            if(!ds.fwIfCurrent('-'))return defaultValue;
821                    
822                    // day
823                            int day=ds.readDigits();
824                            if(day==-1) return defaultValue;
825                            
826                            if(!(ds.fwIfCurrent('\'') && ds.fwIfCurrent('}')))return defaultValue;
827                            
828                            if(ds.isAfterLast()) return util.toDateTime(timeZone,year, month, day, 0, 0, 0, 0, defaultValue);//new DateTimeImpl(year,month,day);
829                            
830                            return defaultValue;
831                            
832                    }
833                    
834                    // DateTime
835                    // "^\\{ts '([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2}) ([0-9]{2}):([0-9]{1,2}):([0-9]{2})'\\}$"
836                    else if(ds.fwIfNext('t')) {
837                            if(!(ds.fwIfNext('s') && ds.fwIfNext(' ') && ds.fwIfNext('\''))) {
838                                    
839                                // Time
840                                            if(!(ds.fwIfNext(' ') && ds.fwIfNext('\'')))return defaultValue;
841                                            ds.next();
842                                    // hour
843                                            int hour=ds.readDigits();
844                                            if(hour==-1) return defaultValue;
845                                            
846                                            if(!ds.fwIfCurrent(':'))return defaultValue;
847                                    
848                                    // minute
849                                            int minute=ds.readDigits();
850                                            if(minute==-1) return defaultValue;
851                                            
852                                            if(!ds.fwIfCurrent(':'))return defaultValue;
853                                    
854                                    // second
855                                            int second=ds.readDigits();
856                                            if(second==-1) return defaultValue;
857                                    
858    
859                       // Milli Second
860                            int millis=0;
861                            
862                            if(ds.fwIfCurrent('.')){
863                                millis=ds.readDigits();
864                            }
865                                            
866                                            
867                                            if(!(ds.fwIfCurrent('\'') && ds.fwIfCurrent('}')))return defaultValue;
868                                            
869                                            
870                                            
871                                            if(ds.isAfterLast()){
872                                                    long time=util.toTime(timeZone,1899,12,30,hour,minute,second,millis,DEFAULT_VALUE);
873                                                    if(time==DEFAULT_VALUE)return defaultValue;
874                                                    return new TimeImpl(time,false);
875                                            }
876                                            return defaultValue;
877                            }
878                            ds.next();
879                    // year
880                            int year=ds.readDigits();
881                            if(year==-1) return defaultValue;
882                            
883                            if(!ds.fwIfCurrent('-'))return defaultValue;
884                    
885                    // month
886                            int month=ds.readDigits();
887                            if(month==-1) return defaultValue;
888                            
889                            if(!ds.fwIfCurrent('-'))return defaultValue;
890                    
891                    // day
892                            int day=ds.readDigits();
893                            if(day==-1) return defaultValue;
894                            
895                            if(!ds.fwIfCurrent(' '))return defaultValue;
896                    
897                    // hour
898                            int hour=ds.readDigits();
899                            if(hour==-1) return defaultValue;
900                            
901                            if(!ds.fwIfCurrent(':'))return defaultValue;
902                    
903                    // minute
904                            int minute=ds.readDigits();
905                            if(minute==-1) return defaultValue;
906                            
907                            if(!ds.fwIfCurrent(':'))return defaultValue;
908                    
909                    // second
910                            int second=ds.readDigits();
911                            if(second==-1) return defaultValue;
912                            
913    
914    
915           // Milli Second
916                int millis=0;
917                
918                if(ds.fwIfCurrent('.')){
919                    millis=ds.readDigits();
920                }
921                
922                if(!(ds.fwIfCurrent('\'') && ds.fwIfCurrent('}')))return defaultValue;
923                if(ds.isAfterLast())return util.toDateTime(timeZone,year, month, day,hour,minute,second,millis,defaultValue);//new DateTimeImpl(year,month,day,hour,minute,second);
924                            return defaultValue;
925                            
926                    }
927                    else return defaultValue;
928            }
929    
930            private static DateTime toNumberDate(String str,boolean alsoNumbers, DateTime defaultValue) {
931                if(!alsoNumbers) return defaultValue;
932                    double dbl = Caster.toDoubleValue(str,Double.NaN);
933                    if(Decision.isValid(dbl))return util.toDateTime(dbl);
934                return defaultValue;
935            }
936    
937        /**
938             * reads a offset definition at the end of a date string
939         * @param timeZone
940             * @param dt previous parsed date Object
941             * @param ds DateString to parse
942         * @param defaultValue 
943             * @return date Object with offset
944         */
945        private static DateTime readOffset(boolean isPlus,TimeZone timeZone,DateTime dt,int years, int months, int days, int hours, int minutes, int seconds, int milliSeconds, DateString ds,DateTime defaultValue) {
946        //timeZone=ThreadLocalPageContext.getTimeZone(timeZone);
947        if(timeZone==null) return defaultValue;
948            // HOUR
949            int hourLength=ds.getPos();
950            int hour=ds.readDigits();
951            hourLength=ds.getPos()-hourLength;
952            if(hour==-1) return defaultValue;
953            
954            // MINUTE
955            int minute=0;
956            if(!ds.isAfterLast()) {
957                    if(!ds.fwIfCurrent(':'))return defaultValue;            
958                    minute=ds.readDigits();
959                    if(minute==-1) return defaultValue;
960                    
961            }
962            else if(hourLength>2){
963                    int h=hour/100;
964                    minute=hour-(h*100);
965                    hour=h;
966            }
967    
968            if(hour>12) return defaultValue;
969            if(minute>59) return defaultValue;
970            if(hour==12 && minute>0) return defaultValue;
971            
972            long offset = hour*60L*60L*1000L;
973            offset+=minute*60*1000;
974            
975            if(ds.isAfterLast()) {
976                    long time= util.toTime(TimeZoneConstants.UTC, years, months, days, hours, minutes, seconds, milliSeconds, 0);
977            
978                    if(isPlus)time-=offset;
979                    else time+=offset;
980                    return new DateTimeImpl(time,false);
981            }
982            return defaultValue;
983        }
984        
985        public static String toUSDate(Object o, TimeZone timeZone) throws PageException {
986            if(Decision.isUSDate(o)) return Caster.toString(o);
987            DateTime date = DateCaster.toDateAdvanced(o, timeZone);
988            return new railo.runtime.format.DateFormat(Locale.US).format(date,"mm/dd/yyyy");
989        }
990        
991        public static String toEuroDate(Object o, TimeZone timeZone) throws PageException {
992            if(Decision.isEuroDate(o)) return Caster.toString(o);
993            DateTime date = DateCaster.toDateAdvanced(o, timeZone);
994            return new railo.runtime.format.DateFormat(Locale.US).format(date,"dd.mm.yyyy");
995        }
996    
997            public static String toShortTime(long time) {
998                    return Long.toString(time/1000,36);
999            }
1000            
1001            public static long fromShortTime(String str) {
1002                    return Long.parseLong(str,36)*1000;
1003            }
1004    }