1 21 22 package org.apache.derby.iapi.types; 23 24 import org.apache.derby.iapi.types.SQLInteger; 25 26 import org.apache.derby.iapi.reference.SQLState; 27 28 import org.apache.derby.iapi.services.io.ArrayInputStream; 29 30 import org.apache.derby.iapi.error.StandardException; 31 32 import org.apache.derby.iapi.db.DatabaseContext; 33 import org.apache.derby.iapi.types.DataValueDescriptor; 34 import org.apache.derby.iapi.types.TypeId; 35 36 import org.apache.derby.iapi.types.NumberDataValue; 37 import org.apache.derby.iapi.types.DateTimeDataValue; 38 39 import org.apache.derby.iapi.services.context.ContextService; 40 41 import org.apache.derby.iapi.services.io.StoredFormatIds; 42 43 import org.apache.derby.iapi.services.sanity.SanityManager; 44 45 import org.apache.derby.iapi.types.DataType; 46 47 import org.apache.derby.iapi.services.cache.ClassSize; 48 import org.apache.derby.iapi.services.i18n.LocaleFinder; 49 import org.apache.derby.iapi.util.StringUtil; 50 51 import java.sql.Date ; 52 import java.sql.Time ; 53 import java.sql.Timestamp ; 54 import java.sql.Types ; 55 import java.sql.PreparedStatement ; 56 57 import java.util.Calendar ; 58 import java.util.GregorianCalendar ; 59 60 import java.io.ObjectOutput ; 61 import java.io.ObjectInput ; 62 import java.io.IOException ; 63 64 import java.sql.ResultSet ; 65 import java.sql.SQLException ; 66 67 import java.text.DateFormat ; 68 import java.text.ParseException ; 69 70 82 83 public final class SQLDate extends DataType 84 implements DateTimeDataValue 85 { 86 87 private int encodedDate; 89 private String valueString; 91 92 private static final int BASE_MEMORY_USAGE = ClassSize.estimateBaseFromCatalog( SQLDate.class); 93 94 public int estimateMemoryUsage() 95 { 96 return BASE_MEMORY_USAGE + ClassSize.estimateMemoryUsage( valueString); 97 } 99 int getEncodedDate() 100 { 101 return encodedDate; 102 } 103 104 108 109 public String getString() 110 { 111 if (!isNull()) 113 { 114 if (valueString == null) 115 { 116 valueString = encodedDateToString(encodedDate); 117 } 118 return valueString; 119 } 120 else 121 { 122 if (SanityManager.DEBUG) 123 { 124 if (valueString != null) 125 { 126 SanityManager.THROWASSERT( 127 "valueString expected to be null, not " + 128 valueString); 129 } 130 } 131 return null; 132 } 133 } 134 135 139 public Timestamp getTimestamp( Calendar cal) 140 { 141 if (isNull()) 142 { 143 return null; 144 } 145 else 146 return newTimestamp(cal); 148 } 149 150 private long getTimeInMillis( Calendar cal) 151 { 152 if( cal == null) 153 cal = new GregorianCalendar (); 154 cal.clear(); 155 cal.set( getYear( encodedDate), getMonth( encodedDate)-1, getDay( encodedDate)); 156 return cal.getTime().getTime(); 157 } 158 159 private Timestamp newTimestamp(java.util.Calendar cal) 160 { 161 return new Timestamp (getTimeInMillis( cal)); 162 } 163 164 168 public Object getObject() 169 { 170 return getDate( (Calendar ) null); 171 } 172 173 public int getLength() 174 { 175 return 4; 176 } 177 178 179 public String getTypeName() 180 { 181 return "DATE"; 182 } 183 184 187 188 193 public int getTypeFormatId() { 194 return StoredFormatIds.SQL_DATE_ID; 195 } 196 197 201 public void writeExternal(ObjectOutput out) throws IOException { 202 203 if (SanityManager.DEBUG) 204 SanityManager.ASSERT(!isNull(), "writeExternal() is not supposed to be called for null values."); 205 206 out.writeInt(encodedDate); 207 } 208 209 214 public void readExternal(ObjectInput in) throws IOException 215 { 216 encodedDate = in.readInt(); 217 218 valueString = null; 220 } 221 public void readExternalFromArray(ArrayInputStream in) throws IOException 222 { 223 encodedDate = in.readInt(); 224 225 valueString = null; 227 } 228 229 232 233 234 public DataValueDescriptor getClone() 235 { 236 return new SQLDate(encodedDate); 238 } 239 240 243 public DataValueDescriptor getNewNull() 244 { 245 return new SQLDate(); 246 } 247 251 252 public void restoreToNull() 253 { 254 encodedDate = 0; 256 257 valueString = null; 259 } 260 261 264 265 270 public void setValueFromResultSet(ResultSet resultSet, int colNumber, 271 boolean isNullable) 272 throws SQLException , StandardException 273 { 274 setValue(resultSet.getDate(colNumber), (Calendar ) null); 275 } 276 277 285 public int compare(DataValueDescriptor other) 286 throws StandardException 287 { 288 291 if (typePrecedence() < other.typePrecedence()) 292 { 293 return - (other.compare(this)); 294 } 295 296 297 boolean thisNull, otherNull; 298 299 thisNull = this.isNull(); 300 otherNull = other.isNull(); 301 302 308 if (thisNull || otherNull) 309 { 310 if (!thisNull) return -1; 312 if (!otherNull) return 1; 314 return 0; 315 } 316 317 320 321 int comparison; 322 323 int otherVal = 0; 324 325 328 if (other instanceof SQLDate) 329 { 330 otherVal = ((SQLDate)other).encodedDate; 331 } 332 else 333 { 334 337 otherVal = SQLDate.computeEncodedDate(other.getDate(new GregorianCalendar ())); 338 } 339 if (encodedDate > otherVal) 340 comparison = 1; 341 else if (encodedDate < otherVal) 342 comparison = -1; 343 else 344 comparison = 0; 345 346 return comparison; 347 } 348 349 352 public boolean compare(int op, 353 DataValueDescriptor other, 354 boolean orderedNulls, 355 boolean unknownRV) 356 throws StandardException 357 { 358 if (!orderedNulls) { 360 if (this.isNull() || other.isNull()) 361 return unknownRV; 362 } 363 364 365 return super.compare(op, other, orderedNulls, unknownRV); 366 } 367 368 371 372 375 376 377 public SQLDate() { 378 } 379 380 public SQLDate(Date value) throws StandardException 381 { 382 parseDate(value); 383 } 384 385 private void parseDate( java.util.Date value) throws StandardException 386 { 387 encodedDate = computeEncodedDate(value); 388 } 389 390 private SQLDate(int encodedDate) { 391 this.encodedDate = encodedDate; 392 } 393 394 409 public SQLDate( String dateStr, boolean isJdbcEscape, LocaleFinder localeFinder) 410 throws StandardException 411 { 412 parseDate( dateStr, isJdbcEscape, localeFinder, (Calendar ) null); 413 } 414 415 430 public SQLDate( String dateStr, boolean isJdbcEscape, LocaleFinder localeFinder, Calendar cal) 431 throws StandardException 432 { 433 parseDate( dateStr, isJdbcEscape, localeFinder, cal); 434 } 435 436 static final char ISO_SEPARATOR = '-'; 437 private static final char[] ISO_SEPARATOR_ONLY = {ISO_SEPARATOR}; 438 private static final char IBM_USA_SEPARATOR = '/'; 439 private static final char[] IBM_USA_SEPARATOR_ONLY = {IBM_USA_SEPARATOR}; 440 private static final char IBM_EUR_SEPARATOR = '.'; 441 private static final char[] IBM_EUR_SEPARATOR_ONLY = {IBM_EUR_SEPARATOR}; 442 private static final char[] END_OF_STRING = {(char) 0}; 443 444 private void parseDate( String dateStr, boolean isJdbcEscape, LocaleFinder localeFinder, Calendar cal) 445 throws StandardException 446 { 447 boolean validSyntax = true; 448 DateTimeParser parser = new DateTimeParser( dateStr); 449 int year = 0; 450 int month = 0; 451 int day = 0; 452 StandardException thrownSE = null; 453 454 try 455 { 456 switch( parser.nextSeparator()) 457 { 458 case ISO_SEPARATOR: 459 encodedDate = SQLTimestamp.parseDateOrTimestamp( parser, false)[0]; 460 valueString = parser.getTrimmedString(); 461 return; 462 463 case IBM_USA_SEPARATOR: 464 if( isJdbcEscape) 465 { 466 validSyntax = false; 467 break; 468 } 469 month = parser.parseInt( 2, true, IBM_USA_SEPARATOR_ONLY, false); 470 day = parser.parseInt( 2, true, IBM_USA_SEPARATOR_ONLY, false); 471 year = parser.parseInt( 4, false, END_OF_STRING, false); 472 break; 473 474 case IBM_EUR_SEPARATOR: 475 if( isJdbcEscape) 476 { 477 validSyntax = false; 478 break; 479 } 480 day = parser.parseInt( 2, true, IBM_EUR_SEPARATOR_ONLY, false); 481 month = parser.parseInt( 2, true, IBM_EUR_SEPARATOR_ONLY, false); 482 year = parser.parseInt( 4, false, END_OF_STRING, false); 483 break; 484 485 default: 486 validSyntax = false; 487 } 488 } 489 catch( StandardException se) 490 { 491 validSyntax = false; 492 thrownSE = se; 493 } 494 if( validSyntax) 495 { 496 valueString = parser.checkEnd(); 497 encodedDate = computeEncodedDate( year, month, day); 498 } 499 else 500 { 501 dateStr = StringUtil.trimTrailing( dateStr); 503 DateFormat dateFormat = null; 504 if( localeFinder == null) 505 dateFormat = DateFormat.getDateInstance(); 506 else if( cal == null) 507 dateFormat = localeFinder.getDateFormat(); 508 else 509 dateFormat = (DateFormat ) localeFinder.getDateFormat().clone(); 510 if( cal != null) 511 dateFormat.setCalendar( cal); 512 try 513 { 514 encodedDate = computeEncodedDate( dateFormat.parse( dateStr), cal); 515 } 516 catch( ParseException pe) 517 { 518 try 520 { 521 encodedDate = SQLTimestamp.parseLocalTimestamp( dateStr, localeFinder, cal)[0]; 522 } 523 catch( ParseException pe2) 524 { 525 if( thrownSE != null) 526 throw thrownSE; 527 throw StandardException.newException( SQLState.LANG_DATE_SYNTAX_EXCEPTION); 528 } 529 } 530 valueString = dateStr; 531 } 532 } 534 538 void setObject(Object theValue) throws StandardException 539 { 540 setValue((Date ) theValue); 541 } 542 543 protected void setFrom(DataValueDescriptor theValue) throws StandardException { 544 545 if (theValue instanceof SQLDate) { 547 restoreToNull(); 548 encodedDate = ((SQLDate) theValue).encodedDate; 549 } 550 else 551 { 552 Calendar cal = new GregorianCalendar (); 553 setValue(theValue.getDate( cal), cal); 554 } 555 } 556 557 561 public void setValue(Date value, Calendar cal) throws StandardException 562 { 563 restoreToNull(); 564 encodedDate = computeEncodedDate((java.util.Date ) value, cal); 565 } 566 567 571 public void setValue(Timestamp value, Calendar cal) throws StandardException 572 { 573 restoreToNull(); 574 encodedDate = computeEncodedDate((java.util.Date ) value, cal); 575 } 576 577 578 public void setValue(String theValue) 579 throws StandardException 580 { 581 restoreToNull(); 582 583 if (theValue != null) 584 { 585 DatabaseContext databaseContext = (DatabaseContext) ContextService.getContext(DatabaseContext.CONTEXT_ID); 586 parseDate( theValue, 587 false, 588 (databaseContext == null) ? null : databaseContext.getDatabase(), 589 (Calendar ) null); 590 } 591 } 592 593 596 597 602 public NumberDataValue getYear(NumberDataValue result) 603 throws StandardException 604 { 605 if (SanityManager.DEBUG) 606 { 607 SanityManager.ASSERT(!isNull(), "getYear called on a null"); 608 } 609 return SQLDate.setSource(getYear(encodedDate), result); 610 } 611 612 617 public NumberDataValue getMonth(NumberDataValue result) 618 throws StandardException 619 { 620 if (SanityManager.DEBUG) 621 { 622 SanityManager.ASSERT(!isNull(), "getMonth called on a null"); 623 } 624 return SQLDate.setSource(getMonth(encodedDate), result); 625 } 626 627 632 public NumberDataValue getDate(NumberDataValue result) 633 throws StandardException 634 { 635 if (SanityManager.DEBUG) 636 { 637 SanityManager.ASSERT(!isNull(), "getDate called on a null"); 638 } 639 return SQLDate.setSource(getDay(encodedDate), result); 640 } 641 642 647 public NumberDataValue getHours(NumberDataValue result) 648 throws StandardException 649 { 650 if (SanityManager.DEBUG) 651 { 652 SanityManager.ASSERT(!isNull(), "getHours called on null."); 653 } 654 throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, 655 "getHours", "Date"); 656 } 657 658 663 public NumberDataValue getMinutes(NumberDataValue result) 664 throws StandardException 665 { 666 if (SanityManager.DEBUG) 667 { 668 SanityManager.ASSERT(!isNull(), "getMinutes called on null."); 669 } 670 throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, 671 "getMinutes", "Date"); 672 } 673 674 679 public NumberDataValue getSeconds(NumberDataValue result) 680 throws StandardException 681 { 682 if (SanityManager.DEBUG) 683 { 684 SanityManager.ASSERT(!isNull(), "getSeconds called on null."); 685 } 686 throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, 687 "getSeconds", "Date"); 688 } 689 690 693 694 public String toString() 695 { 696 if (isNull()) 697 { 698 return "NULL"; 699 } 700 else 701 { 702 return getDate( (Calendar ) null).toString(); 703 } 704 } 705 706 709 public int hashCode() 710 { 711 return encodedDate; 712 } 713 714 715 public int typePrecedence() 716 { 717 return TypeId.DATE_PRECEDENCE; 718 } 719 720 726 public final boolean isNull() 727 { 728 return (encodedDate == 0); 729 } 730 731 737 public Date getDate( Calendar cal) 738 { 739 if (encodedDate != 0) 740 return new Date ( getTimeInMillis( cal)); 741 742 return null; 743 } 744 745 751 static int getYear(int encodedDate) 752 { 753 return (encodedDate >>> 16); 754 } 755 756 762 static int getMonth(int encodedDate) 763 { 764 return ((encodedDate >>> 8) & 0x00ff); 765 } 766 767 773 static int getDay(int encodedDate) 774 { 775 return (encodedDate & 0x00ff); 776 } 777 788 static int computeEncodedDate(Calendar cal) throws StandardException 789 { 790 return computeEncodedDate(cal.get(Calendar.YEAR), 791 cal.get(Calendar.MONTH) + 1, 792 cal.get(Calendar.DATE)); 793 } 794 795 static int computeEncodedDate( int y, int m, int d) throws StandardException 796 { 797 int maxDay = 31; 798 switch( m) 799 { 800 case 4: 801 case 6: 802 case 9: 803 case 11: 804 maxDay = 30; 805 break; 806 807 case 2: 808 maxDay = ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) ? 29 : 28; 810 break; 811 } 812 if( y < 1 || y > 9999 813 || m < 1 || m > 12 814 || d < 1 || d > maxDay) 815 throw StandardException.newException( SQLState.LANG_DATE_RANGE_EXCEPTION); 816 return (y << 16) + (m << 8) + d; 817 } 818 819 827 static void dateToString( int year, int month, int day, StringBuffer sb) 828 { 829 String yearStr = Integer.toString( year); 830 for( int i = yearStr.length(); i < 4; i++) 831 sb.append( '0'); 832 sb.append(yearStr); 833 sb.append(ISO_SEPARATOR); 834 835 String monthStr = Integer.toString( month); 836 String dayStr = Integer.toString( day); 837 if (monthStr.length() == 1) 838 sb.append('0'); 839 sb.append(monthStr); 840 sb.append(ISO_SEPARATOR); 841 if (dayStr.length() == 1) 842 sb.append('0'); 843 sb.append(dayStr); 844 } 846 851 static String encodedDateToString(int encodedDate) 852 { 853 StringBuffer vstr = new StringBuffer (); 854 dateToString( getYear(encodedDate), getMonth(encodedDate), getDay(encodedDate), vstr); 855 return vstr.toString(); 856 } 857 858 860 866 protected String getNationalString(LocaleFinder localeFinder) throws StandardException 867 { 868 if (isNull()) 869 { 870 return getString(); 871 } 872 873 return localeFinder.getDateFormat().format(getDate(new GregorianCalendar ())); 874 } 875 876 884 static NumberDataValue setSource(int value, 885 NumberDataValue source) 886 throws StandardException { 887 893 if (source == null) 894 source = new SQLInteger(); 895 896 source.setValue(value); 897 898 return source; 899 } 900 904 private static int computeEncodedDate(java.util.Date value) throws StandardException 905 { 906 return computeEncodedDate( value, null); 907 } 908 909 static int computeEncodedDate(java.util.Date value, Calendar currentCal) throws StandardException 910 { 911 if (value == null) 912 return 0; if( currentCal == null) 914 currentCal = new GregorianCalendar (); 915 currentCal.setTime(value); 916 return SQLDate.computeEncodedDate(currentCal); 917 } 918 919 920 928 public static DateTimeDataValue computeDateFunction( DataValueDescriptor operand, 929 DataValueFactory dvf) throws StandardException 930 { 931 try 932 { 933 if( operand.isNull()) 934 return new SQLDate(); 935 if( operand instanceof SQLDate) 936 return (SQLDate) operand.getClone(); 937 938 if( operand instanceof SQLTimestamp) 939 { 940 DateTimeDataValue retVal = new SQLDate(); 941 retVal.setValue( operand); 942 return retVal; 943 } 944 if( operand instanceof NumberDataValue) 945 { 946 int daysSinceEpoch = operand.getInt(); 947 if( daysSinceEpoch <= 0 || daysSinceEpoch > 3652059) 948 throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT, 949 operand.getString(), "date"); 950 Calendar cal = new GregorianCalendar ( 1970, 0, 1, 12, 0, 0); 951 cal.add( Calendar.DATE, daysSinceEpoch - 1); 952 return new SQLDate( computeEncodedDate( cal.get( Calendar.YEAR), 953 cal.get( Calendar.MONTH) + 1, 954 cal.get( Calendar.DATE))); 955 } 956 String str = operand.getString(); 957 if( str.length() == 7) 958 { 959 int year = SQLTimestamp.parseDateTimeInteger( str, 0, 4); 961 int dayOfYear = SQLTimestamp.parseDateTimeInteger( str, 4, 3); 962 if( dayOfYear < 1 || dayOfYear > 366) 963 throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT, 964 operand.getString(), "date"); 965 Calendar cal = new GregorianCalendar ( year, 0, 1, 2, 0, 0); 966 cal.add( Calendar.DAY_OF_YEAR, dayOfYear - 1); 967 int y = cal.get( Calendar.YEAR); 968 if( y != year) 969 throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT, 970 operand.getString(), "date"); 971 return new SQLDate( computeEncodedDate( year, 972 cal.get( Calendar.MONTH) + 1, 973 cal.get( Calendar.DATE))); 974 } 975 return dvf.getDateValue( str, false); 977 } 978 catch( StandardException se) 979 { 980 if( SQLState.LANG_DATE_SYNTAX_EXCEPTION.startsWith( se.getSQLState())) 981 throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT, 982 operand.getString(), "date"); 983 throw se; 984 } 985 } 987 990 public void setInto(PreparedStatement ps, int position) throws SQLException , StandardException { 991 992 ps.setDate(position, getDate((Calendar ) null)); 993 } 994 995 996 1010 public DateTimeDataValue timestampAdd( int intervalType, 1011 NumberDataValue intervalCount, 1012 java.sql.Date currentDate, 1013 DateTimeDataValue resultHolder) 1014 throws StandardException 1015 { 1016 return toTimestamp().timestampAdd( intervalType, intervalCount, currentDate, resultHolder); 1017 } 1018 1019 private SQLTimestamp toTimestamp() throws StandardException 1020 { 1021 return new SQLTimestamp( getEncodedDate(), 0, 0); 1022 } 1023 1024 1039 public NumberDataValue timestampDiff( int intervalType, 1040 DateTimeDataValue time1, 1041 java.sql.Date currentDate, 1042 NumberDataValue resultHolder) 1043 throws StandardException 1044 { 1045 return toTimestamp().timestampDiff( intervalType, time1, currentDate, resultHolder); 1046 } 1047} 1048 | Popular Tags |