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