Source code: org/apache/derby/iapi/types/SQLDate.java
1 /*
2
3 Derby - Class org.apache.derby.iapi.types.SQLDate
4
5 Copyright 2001, 2004 The Apache Software Foundation or its licensors, as applicable.
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18
19 */
20
21 package org.apache.derby.iapi.types;
22
23 import org.apache.derby.iapi.types.SQLInteger;
24
25 import org.apache.derby.iapi.reference.SQLState;
26
27 import org.apache.derby.iapi.services.io.ArrayInputStream;
28
29 import org.apache.derby.iapi.error.StandardException;
30
31 import org.apache.derby.iapi.db.DatabaseContext;
32 import org.apache.derby.iapi.types.DataValueDescriptor;
33 import org.apache.derby.iapi.types.TypeId;
34
35 import org.apache.derby.iapi.types.NumberDataValue;
36 import org.apache.derby.iapi.types.DateTimeDataValue;
37
38 import org.apache.derby.iapi.services.context.ContextService;
39
40 import org.apache.derby.iapi.services.io.StoredFormatIds;
41
42 import org.apache.derby.iapi.services.sanity.SanityManager;
43
44 import org.apache.derby.iapi.types.DataType;
45
46 import org.apache.derby.iapi.services.cache.ClassSize;
47 import org.apache.derby.iapi.services.i18n.LocaleFinder;
48 import org.apache.derby.iapi.util.StringUtil;
49
50 import java.sql.Date;
51 import java.sql.Time;
52 import java.sql.Timestamp;
53 import java.sql.Types;
54 import java.sql.PreparedStatement;
55
56 import java.util.Calendar;
57 import java.util.GregorianCalendar;
58
59 import java.io.ObjectOutput;
60 import java.io.ObjectInput;
61 import java.io.IOException;
62
63 import java.sql.ResultSet;
64 import java.sql.SQLException;
65
66 import java.text.DateFormat;
67 import java.text.ParseException;
68
69 /**
70 * This contains an instance of a SQL Date.
71 * <p>
72 * The date is stored as int (year << 16 + month << 8 + day)
73 * Null is represented by an encodedDate value of 0.
74 * Some of the static methods in this class are also used by SQLTime and SQLTimestamp
75 * so check those classes if you change the date encoding
76 *
77 * PERFORMANCE OPTIMIZATION:
78 * The java.sql.Date object is only instantiated when needed
79 * do to the overhead of Date.valueOf(), etc. methods.
80 */
81
82 public final class SQLDate extends DataType
83 implements DateTimeDataValue
84 {
85
86 private int encodedDate; //year << 16 + month << 8 + day
87
88 // The cached value.toString()
89 private String valueString;
90
91 private static final int BASE_MEMORY_USAGE = ClassSize.estimateBaseFromCatalog( SQLDate.class);
92
93 public int estimateMemoryUsage()
94 {
95 return BASE_MEMORY_USAGE + ClassSize.estimateMemoryUsage( valueString);
96 } // end of estimateMemoryUsage
97
98 int getEncodedDate()
99 {
100 return encodedDate;
101 }
102
103 /*
104 ** DataValueDescriptor interface
105 ** (mostly implemented in DataType)
106 */
107
108 public String getString()
109 {
110 //format is [yyy]y-mm-dd e.g. 1-01-01, 9999-99-99
111 if (!isNull())
112 {
113 if (valueString == null)
114 {
115 valueString = encodedDateToString(encodedDate);
116 }
117 return valueString;
118 }
119 else
120 {
121 if (SanityManager.DEBUG)
122 {
123 if (valueString != null)
124 {
125 SanityManager.THROWASSERT(
126 "valueString expected to be null, not " +
127 valueString);
128 }
129 }
130 return null;
131 }
132 }
133
134 /**
135 getTimestamp returns a timestamp with the date value
136 time is set to 00:00:00.0
137 */
138 public Timestamp getTimestamp( Calendar cal)
139 {
140 if (isNull())
141 {
142 return null;
143 }
144 else
145 // date is converted to a timestamp filling the time in with 00:00:00
146 return newTimestamp(cal);
147 }
148
149 private long getTimeInMillis( Calendar cal)
150 {
151 if( cal == null)
152 cal = new GregorianCalendar();
153 cal.clear();
154 cal.set( getYear( encodedDate), getMonth( encodedDate)-1, getDay( encodedDate));
155 return cal.getTime().getTime();
156 }
157
158 private Timestamp newTimestamp(java.util.Calendar cal)
159 {
160 return new Timestamp(getTimeInMillis( cal));
161 }
162
163 /**
164 getObject returns the date value
165
166 */
167 public Object getObject()
168 {
169 return getDate( (Calendar) null);
170 }
171
172 public int getLength()
173 {
174 return 4;
175 }
176
177 /* this is for DataType's error generator */
178 public String getTypeName()
179 {
180 return "DATE";
181 }
182
183 /*
184 * Storable interface, implies Externalizable, TypedFormat
185 */
186
187 /**
188 Return my format identifier.
189
190 @see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId
191 */
192 public int getTypeFormatId() {
193 return StoredFormatIds.SQL_DATE_ID;
194 }
195
196 /**
197 @exception IOException error writing data
198
199 */
200 public void writeExternal(ObjectOutput out) throws IOException {
201
202 if (SanityManager.DEBUG)
203 SanityManager.ASSERT(!isNull(), "writeExternal() is not supposed to be called for null values.");
204
205 out.writeInt(encodedDate);
206 }
207
208 /**
209 * @see java.io.Externalizable#readExternal
210 *
211 * @exception IOException Thrown on error reading the object
212 */
213 public void readExternal(ObjectInput in) throws IOException
214 {
215 encodedDate = in.readInt();
216
217 // reset cached string values
218 valueString = null;
219 }
220 public void readExternalFromArray(ArrayInputStream in) throws IOException
221 {
222 encodedDate = in.readInt();
223
224 // reset cached string values
225 valueString = null;
226 }
227
228 /*
229 * DataValueDescriptor interface
230 */
231
232 /** @see DataValueDescriptor#getClone */
233 public DataValueDescriptor getClone()
234 {
235 // Call constructor with all of our info
236 return new SQLDate(encodedDate);
237 }
238
239 /**
240 * @see DataValueDescriptor#getNewNull
241 */
242 public DataValueDescriptor getNewNull()
243 {
244 return new SQLDate();
245 }
246 /**
247 * @see org.apache.derby.iapi.services.io.Storable#restoreToNull
248 *
249 */
250
251 public void restoreToNull()
252 {
253 // clear encodedDate
254 encodedDate = 0;
255
256 // clear cached valueString
257 valueString = null;
258 }
259
260 /*
261 * DataValueDescriptor interface
262 */
263
264 /**
265 * @see DataValueDescriptor#setValueFromResultSet
266 *
267 * @exception SQLException Thrown on error
268 */
269 public void setValueFromResultSet(ResultSet resultSet, int colNumber,
270 boolean isNullable)
271 throws SQLException, StandardException
272 {
273 setValue(resultSet.getDate(colNumber), (Calendar) null);
274 }
275
276 /**
277 * Orderable interface
278 *
279 *
280 * @see org.apache.derby.iapi.types.Orderable
281 *
282 * @exception StandardException thrown on failure
283 */
284 public int compare(DataValueDescriptor other)
285 throws StandardException
286 {
287 /* Use compare method from dominant type, negating result
288 * to reflect flipping of sides.
289 */
290 if (typePrecedence() < other.typePrecedence())
291 {
292 return - (other.compare(this));
293 }
294
295
296 boolean thisNull, otherNull;
297
298 thisNull = this.isNull();
299 otherNull = other.isNull();
300
301 /*
302 * thisNull otherNull return
303 * T T 0 (this == other)
304 * F T -1 (this < other)
305 * T F 1 (this > other)
306 */
307 if (thisNull || otherNull)
308 {
309 if (!thisNull) // otherNull must be true
310 return -1;
311 if (!otherNull) // thisNull must be true
312 return 1;
313 return 0;
314 }
315
316 /*
317 Neither are null compare them
318 */
319
320 int comparison;
321 /* get the comparison date values */
322 int otherVal = 0;
323
324 /* if the argument is another SQLDate
325 * get the encodedDate
326 */
327 if (other instanceof SQLDate)
328 {
329 otherVal = ((SQLDate)other).encodedDate;
330 }
331 else
332 {
333 /* O.K. have to do it the hard way and calculate the numeric value
334 * from the value
335 */
336 otherVal = SQLDate.computeEncodedDate(other.getDate(new GregorianCalendar()));
337 }
338 if (encodedDate > otherVal)
339 comparison = 1;
340 else if (encodedDate < otherVal)
341 comparison = -1;
342 else
343 comparison = 0;
344
345 return comparison;
346 }
347
348 /**
349 @exception StandardException thrown on error
350 */
351 public boolean compare(int op,
352 DataValueDescriptor other,
353 boolean orderedNulls,
354 boolean unknownRV)
355 throws StandardException
356 {
357 if (!orderedNulls) // nulls are unordered
358 {
359 if (this.isNull() || other.isNull())
360 return unknownRV;
361 }
362
363 /* Do the comparison */
364 return super.compare(op, other, orderedNulls, unknownRV);
365 }
366
367 /*
368 ** Class interface
369 */
370
371 /*
372 ** Constructors
373 */
374
375 /** no-arg constructor required by Formattable */
376 public SQLDate() {
377 }
378
379 public SQLDate(Date value) throws StandardException
380 {
381 parseDate(value);
382 }
383
384 private void parseDate( java.util.Date value) throws StandardException
385 {
386 encodedDate = computeEncodedDate(value);
387 }
388
389 private SQLDate(int encodedDate) {
390 this.encodedDate = encodedDate;
391 }
392
393 /**
394 * Construct a date from a string. The allowed date formats are:
395 *<ol>
396 *<li>ISO: yyyy-mm-dd
397 *<li>IBM USA standard: mm/dd/yyyy
398 *<li>IBM European standard: dd.mm.yyyy
399 *</ol>
400 * Trailing blanks may be included; leading zeros may be omitted from the month and day portions.
401 *
402 * @param dateStr
403 * @param isJdbcEscape if true then only the JDBC date escape syntax is allowed
404 * @param localeFinder
405 *
406 * @return the internal DataValueDescriptor for the value
407 *
408 * @exception Standard exception if the syntax is invalid or the value is out of range.
409 */
410 public SQLDate( String dateStr, boolean isJdbcEscape, LocaleFinder localeFinder)
411 throws StandardException
412 {
413 parseDate( dateStr, isJdbcEscape, localeFinder, (Calendar) null);
414 }
415
416 /**
417 * Construct a date from a string. The allowed date formats are:
418 *<ol>
419 *<li>ISO: yyyy-mm-dd
420 *<li>IBM USA standard: mm/dd/yyyy
421 *<li>IBM European standard: dd.mm.yyyy
422 *</ol>
423 * Trailing blanks may be included; leading zeros may be omitted from the month and day portions.
424 *
425 * @param dateStr
426 * @param isJdbcEscape if true then only the JDBC date escape syntax is allowed
427 * @param localeFinder
428 *
429 * @return the internal DataValueDescriptor for the value
430 *
431 * @exception Standard exception if the syntax is invalid or the value is out of range.
432 */
433 public SQLDate( String dateStr, boolean isJdbcEscape, LocaleFinder localeFinder, Calendar cal)
434 throws StandardException
435 {
436 parseDate( dateStr, isJdbcEscape, localeFinder, cal);
437 }
438
439 static final char ISO_SEPARATOR = '-';
440 private static final char[] ISO_SEPARATOR_ONLY = {ISO_SEPARATOR};
441 private static final char IBM_USA_SEPARATOR = '/';
442 private static final char[] IBM_USA_SEPARATOR_ONLY = {IBM_USA_SEPARATOR};
443 private static final char IBM_EUR_SEPARATOR = '.';
444 private static final char[] IBM_EUR_SEPARATOR_ONLY = {IBM_EUR_SEPARATOR};
445 private static final char[] END_OF_STRING = {(char) 0};
446
447 private void parseDate( String dateStr, boolean isJdbcEscape, LocaleFinder localeFinder, Calendar cal)
448 throws StandardException
449 {
450 boolean validSyntax = true;
451 DateTimeParser parser = new DateTimeParser( dateStr);
452 int year = 0;
453 int month = 0;
454 int day = 0;
455 StandardException thrownSE = null;
456
457 try
458 {
459 switch( parser.nextSeparator())
460 {
461 case ISO_SEPARATOR:
462 encodedDate = SQLTimestamp.parseDateOrTimestamp( parser, false)[0];
463 valueString = parser.getTrimmedString();
464 return;
465
466 case IBM_USA_SEPARATOR:
467 if( isJdbcEscape)
468 {
469 validSyntax = false;
470 break;
471 }
472 month = parser.parseInt( 2, true, IBM_USA_SEPARATOR_ONLY, false);
473 day = parser.parseInt( 2, true, IBM_USA_SEPARATOR_ONLY, false);
474 year = parser.parseInt( 4, false, END_OF_STRING, false);
475 break;
476
477 case IBM_EUR_SEPARATOR:
478 if( isJdbcEscape)
479 {
480 validSyntax = false;
481 break;
482 }
483 day = parser.parseInt( 2, true, IBM_EUR_SEPARATOR_ONLY, false);
484 month = parser.parseInt( 2, true, IBM_EUR_SEPARATOR_ONLY, false);
485 year = parser.parseInt( 4, false, END_OF_STRING, false);
486 break;
487
488 default:
489 validSyntax = false;
490 }
491 }
492 catch( StandardException se)
493 {
494 validSyntax = false;
495 thrownSE = se;
496 }
497 if( validSyntax)
498 {
499 valueString = parser.checkEnd();
500 encodedDate = computeEncodedDate( year, month, day);
501 }
502 else
503 {
504 // See if it is a localized date or timestamp.
505 dateStr = StringUtil.trimTrailing( dateStr);
506 DateFormat dateFormat = null;
507 if( localeFinder == null)
508 dateFormat = DateFormat.getDateInstance();
509 else if( cal == null)
510 dateFormat = localeFinder.getDateFormat();
511 else
512 dateFormat = (DateFormat) localeFinder.getDateFormat().clone();
513 if( cal != null)
514 dateFormat.setCalendar( cal);
515 try
516 {
517 encodedDate = computeEncodedDate( dateFormat.parse( dateStr), cal);
518 }
519 catch( ParseException pe)
520 {
521 // Maybe it is a localized timestamp
522 try
523 {
524 encodedDate = SQLTimestamp.parseLocalTimestamp( dateStr, localeFinder, cal)[0];
525 }
526 catch( ParseException pe2)
527 {
528 if( thrownSE != null)
529 throw thrownSE;
530 throw StandardException.newException( SQLState.LANG_DATE_SYNTAX_EXCEPTION);
531 }
532 }
533 valueString = dateStr;
534 }
535 } // end of parseDate
536
537 public void setValue(Object theValue) throws StandardException
538 {
539 if (theValue == null)
540 {
541 setToNull();
542 }
543 else if (theValue instanceof Date)
544 {
545 setValue((Date)theValue, (Calendar) null);
546 }
547 else if (theValue instanceof Timestamp)
548 {
549 setValue((Timestamp)theValue, (Calendar) null);
550 }
551 else
552 {
553 genericSetObject(theValue);
554 }
555
556 }
557
558 protected void setFrom(DataValueDescriptor theValue) throws StandardException {
559
560 // Same format means same type SQLDate
561 if (theValue instanceof SQLDate) {
562 restoreToNull();
563 encodedDate = ((SQLDate) theValue).encodedDate;
564 }
565 else
566 {
567 Calendar cal = new GregorianCalendar();
568 setValue(theValue.getDate( cal), cal);
569 }
570 }
571
572 /**
573 @see DateTimeDataValue#setValue
574
575 */
576 public void setValue(Date value, Calendar cal) throws StandardException
577 {
578 restoreToNull();
579 encodedDate = computeEncodedDate((java.util.Date) value, cal);
580 }
581
582 /**
583 @see DateTimeDataValue#setValue
584
585 */
586 public void setValue(Timestamp value, Calendar cal) throws StandardException
587 {
588 restoreToNull();
589 encodedDate = computeEncodedDate((java.util.Date) value, cal);
590 }
591
592
593 public void setValue(String theValue)
594 throws StandardException
595 {
596 restoreToNull();
597
598 if (theValue != null)
599 {
600 DatabaseContext databaseContext = (DatabaseContext) ContextService.getContext(DatabaseContext.CONTEXT_ID);
601 parseDate( theValue,
602 false,
603 (databaseContext == null) ? null : databaseContext.getDatabase(),
604 (Calendar) null);
605 }
606 }
607
608 /*
609 ** SQL Operators
610 */
611
612 /**
613 * @see DateTimeDataValue#getYear
614 *
615 * @exception StandardException Thrown on error
616 */
617 public NumberDataValue getYear(NumberDataValue result)
618 throws StandardException
619 {
620 if (SanityManager.DEBUG)
621 {
622 SanityManager.ASSERT(!isNull(), "getYear called on a null");
623 }
624 return SQLDate.setSource(getYear(encodedDate), result);
625 }
626
627 /**
628 * @see DateTimeDataValue#getMonth
629 *
630 * @exception StandardException Thrown on error
631 */
632 public NumberDataValue getMonth(NumberDataValue result)
633 throws StandardException
634 {
635 if (SanityManager.DEBUG)
636 {
637 SanityManager.ASSERT(!isNull(), "getMonth called on a null");
638 }
639 return SQLDate.setSource(getMonth(encodedDate), result);
640 }
641
642 /**
643 * @see DateTimeDataValue#getDate
644 *
645 * @exception StandardException Thrown on error
646 */
647 public NumberDataValue getDate(NumberDataValue result)
648 throws StandardException
649 {
650 if (SanityManager.DEBUG)
651 {
652 SanityManager.ASSERT(!isNull(), "getDate called on a null");
653 }
654 return SQLDate.setSource(getDay(encodedDate), result);
655 }
656
657 /**
658 * @see DateTimeDataValue#getHours
659 *
660 * @exception StandardException Thrown on error
661 */
662 public NumberDataValue getHours(NumberDataValue result)
663 throws StandardException
664 {
665 if (SanityManager.DEBUG)
666 {
667 SanityManager.ASSERT(!isNull(), "getHours called on null.");
668 }
669 throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE,
670 "getHours", "Date");
671 }
672
673 /**
674 * @see DateTimeDataValue#getMinutes
675 *
676 * @exception StandardException Thrown on error
677 */
678 public NumberDataValue getMinutes(NumberDataValue result)
679 throws StandardException
680 {
681 if (SanityManager.DEBUG)
682 {
683 SanityManager.ASSERT(!isNull(), "getMinutes called on null.");
684 }
685 throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE,
686 "getMinutes", "Date");
687 }
688
689 /**
690 * @see DateTimeDataValue#getSeconds
691 *
692 * @exception StandardException Thrown on error
693 */
694 public NumberDataValue getSeconds(NumberDataValue result)
695 throws StandardException
696 {
697 if (SanityManager.DEBUG)
698 {
699 SanityManager.ASSERT(!isNull(), "getSeconds called on null.");
700 }
701 throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE,
702 "getSeconds", "Date");
703 }
704
705 /*
706 ** String display of value
707 */
708
709 public String toString()
710 {
711 if (isNull())
712 {
713 return "NULL";
714 }
715 else
716 {
717 return getDate( (Calendar) null).toString();
718 }
719 }
720
721 /*
722 * Hash code
723 */
724 public int hashCode()
725 {
726 return encodedDate;
727 }
728
729 /** @see DataValueDescriptor#typePrecedence */
730 public int typePrecedence()
731 {
732 return TypeId.DATE_PRECEDENCE;
733 }
734
735 /**
736 * Check if the value is null.
737 * encodedDate is 0 if the value is null
738 *
739 * @return Whether or not value is logically null.
740 */
741 public final boolean isNull()
742 {
743 return (encodedDate == 0);
744 }
745
746 /**
747 * Get the value field. We instantiate the field
748 * on demand.
749 *
750 * @return The value field.
751 */
752 public Date getDate( Calendar cal)
753 {
754 if (encodedDate != 0)
755 return new Date( getTimeInMillis( cal));
756
757 return null;
758 }
759
760 /**
761 * Get the year from the encodedDate.
762 *
763 * @param encodedDate the encoded date
764 * @return year value.
765 */
766 static int getYear(int encodedDate)
767 {
768 return (encodedDate >>> 16);
769 }
770
771 /**
772 * Get the month from the encodedDate.
773 *
774 * @param encodedDate the encoded date
775 * @return month value.
776 */
777 static int getMonth(int encodedDate)
778 {
779 return ((encodedDate >>> 8) & 0x00ff);
780 }
781
782 /**
783 * Get the day from the encodedDate.
784 *
785 * @param encodedDate the encoded date
786 * @return day value.
787 */
788 static int getDay(int encodedDate)
789 {
790 return (encodedDate & 0x00ff);
791 }
792 /**
793 * computeEncodedDate extracts the year, month and date from
794 * a Calendar value and encodes them as
795 * year << 16 + month << 8 + date
796 * Use this function will help to remember to add 1 to month
797 * which is 0 based in the Calendar class
798 * @param value the Calendar
799 * @return the encodedDate
800 *
801 * @exception StandardException if the value is out of the DB2 date range
802 */
803 static int computeEncodedDate(Calendar cal) throws StandardException
804 {
805 return computeEncodedDate(cal.get(Calendar.YEAR),
806 cal.get(Calendar.MONTH) + 1,
807 cal.get(Calendar.DATE));
808 }
809
810 static int computeEncodedDate( int y, int m, int d) throws StandardException
811 {
812 int maxDay = 31;
813 switch( m)
814 {
815 case 4:
816 case 6:
817 case 9:
818 case 11:
819 maxDay = 30;
820 break;
821
822 case 2:
823 // leap years are every 4 years except for century years not divisble by 400.
824 maxDay = ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) ? 29 : 28;
825 break;
826 }
827 if( y < 1 || y > 9999
828 || m < 1 || m > 12
829 || d < 1 || d > maxDay)
830 throw StandardException.newException( SQLState.LANG_DATE_RANGE_EXCEPTION);
831 return (y << 16) + (m << 8) + d;
832 }
833
834 /**
835 * Convert a date to the JDBC representation and append it to a string buffer.
836 *
837 * @param year
838 * @param month 1 based (January == 1)
839 * @param day
840 * @param sb The string representation is appended to this StringBuffer
841 */
842 static void dateToString( int year, int month, int day, StringBuffer sb)
843 {
844 String yearStr = Integer.toString( year);
845 for( int i = yearStr.length(); i < 4; i++)
846 sb.append( '0');
847 sb.append(yearStr);
848 sb.append(ISO_SEPARATOR);
849
850 String monthStr = Integer.toString( month);
851 String dayStr = Integer.toString( day);
852 if (monthStr.length() == 1)
853 sb.append('0');
854 sb.append(monthStr);
855 sb.append(ISO_SEPARATOR);
856 if (dayStr.length() == 1)
857 sb.append('0');
858 sb.append(dayStr);
859 } // end of dateToString
860
861 /**
862 * Get the String version from the encodedDate.
863 *
864 * @return string value.
865 */
866 static String encodedDateToString(int encodedDate)
867 {
868 StringBuffer vstr = new StringBuffer();
869 dateToString( getYear(encodedDate), getMonth(encodedDate), getDay(encodedDate), vstr);
870 return vstr.toString();
871 }
872
873 // International Support
874
875 /**
876 * International version of getString(). Overrides getNationalString
877 * in DataType for date, time, and timestamp.
878 *
879 * @exception StandardException Thrown on error
880 */
881 protected String getNationalString(LocaleFinder localeFinder) throws StandardException
882 {
883 if (isNull())
884 {
885 return getString();
886 }
887
888 return localeFinder.getDateFormat().format(getDate(new GregorianCalendar()));
889 }
890
891 /**
892 This helper routine tests the nullability of various parameters
893 and sets up the result appropriately.
894
895 If source is null, a new NumberDataValue is built.
896
897 @exception StandardException Thrown on error
898 */
899 static NumberDataValue setSource(int value,
900 NumberDataValue source)
901 throws StandardException {
902 /*
903 ** NOTE: Most extract operations return int, so the generation of
904 ** a SQLInteger is here. Those extract operations that return
905 ** something other than int must allocate the source NumberDataValue
906 ** themselves, so that we do not allocate a SQLInteger here.
907 */
908 if (source == null)
909 source = new SQLInteger();
910
911 source.setValue(value);
912
913 return source;
914 }
915 /**
916 * Compute the encoded date given a date
917 *
918 */
919 private static int computeEncodedDate(java.util.Date value) throws StandardException
920 {
921 return computeEncodedDate( value, null);
922 }
923
924 static int computeEncodedDate(java.util.Date value, Calendar currentCal) throws StandardException
925 {
926 if (value == null)
927 return 0; //encoded dates have a 0 value for null
928 if( currentCal == null)
929 currentCal = new GregorianCalendar();
930 currentCal.setTime(value);
931 return SQLDate.computeEncodedDate(currentCal);
932 }
933
934
935 /**
936 * Implement the date SQL function: construct a SQL date from a string, number, or timestamp.
937 *
938 * @param operand Must be a date or a string convertible to a date.
939 * @param dvf the DataValueFactory
940 *
941 * @exception StandardException standard error policy
942 */
943 public static DateTimeDataValue computeDateFunction( DataValueDescriptor operand,
944 DataValueFactory dvf) throws StandardException
945 {
946 try
947 {
948 if( operand.isNull())
949 return new SQLDate();
950 if( operand instanceof SQLDate)
951 return (SQLDate) operand.getClone();
952
953 if( operand instanceof SQLTimestamp)
954 {
955 DateTimeDataValue retVal = new SQLDate();
956 retVal.setValue( operand);
957 return retVal;
958 }
959 if( operand instanceof NumberDataValue)
960 {
961 int daysSinceEpoch = operand.getInt();
962 if( daysSinceEpoch <= 0 || daysSinceEpoch > 3652059)
963 throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT,
964 operand.getString(), "date");
965 Calendar cal = new GregorianCalendar( 1970, 0, 1, 12, 0, 0);
966 cal.add( Calendar.DATE, daysSinceEpoch - 1);
967 return new SQLDate( computeEncodedDate( cal.get( Calendar.YEAR),
968 cal.get( Calendar.MONTH) + 1,
969 cal.get( Calendar.DATE)));
970 }
971 String str = operand.getString();
972 if( str.length() == 7)
973 {
974 // yyyyddd where ddd is the day of the year
975 int year = SQLTimestamp.parseDateTimeInteger( str, 0, 4);
976 int dayOfYear = SQLTimestamp.parseDateTimeInteger( str, 4, 3);
977 if( dayOfYear < 1 || dayOfYear > 366)
978 throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT,
979 operand.getString(), "date");
980 Calendar cal = new GregorianCalendar( year, 0, 1, 2, 0, 0);
981 cal.add( Calendar.DAY_OF_YEAR, dayOfYear - 1);
982 int y = cal.get( Calendar.YEAR);
983 if( y != year)
984 throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT,
985 operand.getString(), "date");
986 return new SQLDate( computeEncodedDate( year,
987 cal.get( Calendar.MONTH) + 1,
988 cal.get( Calendar.DATE)));
989 }
990 // Else use the standard cast.
991 return dvf.getDateValue( str, false);
992 }
993 catch( StandardException se)
994 {
995 if( SQLState.LANG_DATE_SYNTAX_EXCEPTION.startsWith( se.getSQLState()))
996 throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT,
997 operand.getString(), "date");
998 throw se;
999 }
1000 } // end of computeDateFunction
1001
1002 /** Adding this method to ensure that super class' setInto method doesn't get called
1003 * that leads to the violation of JDBC spec( untyped nulls ) when batching is turned on.
1004 */
1005 public void setInto(PreparedStatement ps, int position) throws SQLException, StandardException {
1006
1007 ps.setDate(position, getDate((Calendar) null));
1008 }
1009
1010
1011 /**
1012 * Add a number of intervals to a datetime value. Implements the JDBC escape TIMESTAMPADD function.
1013 *
1014 * @param intervalType One of FRAC_SECOND_INTERVAL, SECOND_INTERVAL, MINUTE_INTERVAL, HOUR_INTERVAL,
1015 * DAY_INTERVAL, WEEK_INTERVAL, MONTH_INTERVAL, QUARTER_INTERVAL, or YEAR_INTERVAL
1016 * @param intervalCount The number of intervals to add
1017 * @param currentDate Used to convert time to timestamp
1018 * @param resultHolder If non-null a DateTimeDataValue that can be used to hold the result. If null then
1019 * generate a new holder
1020 *
1021 * @return startTime + intervalCount intervals, as a timestamp
1022 *
1023 * @exception StandardException
1024 */
1025 public DateTimeDataValue timestampAdd( int intervalType,
1026 NumberDataValue intervalCount,
1027 java.sql.Date currentDate,
1028 DateTimeDataValue resultHolder)
1029 throws StandardException
1030 {
1031 return toTimestamp().timestampAdd( intervalType, intervalCount, currentDate, resultHolder);
1032 }
1033
1034 private SQLTimestamp toTimestamp() throws StandardException
1035 {
1036 return new SQLTimestamp( getEncodedDate(), 0, 0);
1037 }
1038
1039 /**
1040 * Finds the difference between two datetime values as a number of intervals. Implements the JDBC
1041 * TIMESTAMPDIFF escape function.
1042 *
1043 * @param intervalType One of FRAC_SECOND_INTERVAL, SECOND_INTERVAL, MINUTE_INTERVAL, HOUR_INTERVAL,
1044 * DAY_INTERVAL, WEEK_INTERVAL, MONTH_INTERVAL, QUARTER_INTERVAL, or YEAR_INTERVAL
1045 * @param time1
1046 * @param currentDate Used to convert time to timestamp
1047 * @param resultHolder If non-null a NumberDataValue that can be used to hold the result. If null then
1048 * generate a new holder
1049 *
1050 * @return the number of intervals by which this datetime is greater than time1
1051 *
1052 * @exception StandardException
1053 */
1054 public NumberDataValue timestampDiff( int intervalType,
1055 DateTimeDataValue time1,
1056 java.sql.Date currentDate,
1057 NumberDataValue resultHolder)
1058 throws StandardException
1059 {
1060 return toTimestamp().timestampDiff( intervalType, time1, currentDate, resultHolder);
1061 }
1062}