KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > iapi > types > SQLTimestamp


1 /*
2
3    Derby - Class org.apache.derby.iapi.types.SQLTimestamp
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.iapi.types;
23
24 import org.apache.derby.iapi.reference.SQLState;
25
26 import org.apache.derby.iapi.services.io.ArrayInputStream;
27
28 import org.apache.derby.iapi.error.StandardException;
29 import org.apache.derby.iapi.db.DatabaseContext;
30
31 import org.apache.derby.iapi.types.DataValueDescriptor;
32 import org.apache.derby.iapi.types.TypeId;
33
34 import org.apache.derby.iapi.types.NumberDataValue;
35 import org.apache.derby.iapi.types.DateTimeDataValue;
36
37 import org.apache.derby.iapi.services.io.StoredFormatIds;
38 import org.apache.derby.iapi.services.context.ContextService;
39  
40 import org.apache.derby.iapi.services.sanity.SanityManager;
41 import org.apache.derby.iapi.types.DataType;
42 import org.apache.derby.iapi.services.i18n.LocaleFinder;
43 import org.apache.derby.iapi.services.cache.ClassSize;
44 import org.apache.derby.iapi.util.StringUtil;
45 import org.apache.derby.iapi.util.ReuseFactory;
46
47 import org.apache.derby.iapi.types.SQLDouble;
48 import org.apache.derby.iapi.types.SQLTime;
49
50 import java.sql.Date JavaDoc;
51 import java.sql.Time JavaDoc;
52 import java.sql.Timestamp JavaDoc;
53 import java.sql.Types JavaDoc;
54 import java.sql.ResultSet JavaDoc;
55 import java.sql.SQLException JavaDoc;
56 import java.sql.PreparedStatement JavaDoc;
57
58 import java.util.Calendar JavaDoc;
59 import java.util.GregorianCalendar JavaDoc;
60
61 import java.io.ObjectOutput JavaDoc;
62 import java.io.ObjectInput JavaDoc;
63 import java.io.IOException JavaDoc;
64
65 import java.text.DateFormat JavaDoc;
66 import java.text.ParseException JavaDoc;
67
68 /**
69  * This contains an instance of a SQL Timestamp object.
70  * <p>
71  * SQLTimestamp is stored in 3 ints - an encoded date, an encoded time and
72  * nanoseconds
73  * encodedDate = 0 indicates a null WSCTimestamp
74  *
75  * SQLTimestamp is similar to SQLTimestamp, but it does conserves space by not keeping a GregorianCalendar object
76  *
77  * PERFORMANCE OPTIMIZATION:
78  * We only instantiate the value field when required due to the overhead of the
79  * Date methods.
80  * Thus, use isNull() instead of "value == null" and
81  * getTimestamp() instead of using value directly.
82  */

83
84 public final class SQLTimestamp extends DataType
85                         implements DateTimeDataValue
86 {
87
88     static final int MAX_FRACTION_DIGITS = 6; // Only microsecond resolution on conversion to/from strings
89
static final int FRACTION_TO_NANO = 1000; // 10**(9 - MAX_FRACTION_DIGITS)
90

91     static final int ONE_BILLION = 1000000000;
92     
93     private int encodedDate;
94     private int encodedTime;
95     private int nanos;
96
97     // The cached value.toString()
98
private String JavaDoc valueString;
99
100     /*
101     ** DataValueDescriptor interface
102     ** (mostly implemented in DataType)
103     */

104
105     private static final int BASE_MEMORY_USAGE = ClassSize.estimateBaseFromCatalog( SQLTimestamp.class);
106
107     public int estimateMemoryUsage()
108     {
109         int sz = BASE_MEMORY_USAGE + ClassSize.estimateMemoryUsage( valueString);
110         return sz;
111     } // end of estimateMemoryUsage
112

113     public String JavaDoc getString()
114     {
115         if (!isNull())
116         {
117             if (valueString == null)
118             {
119                 valueString = getTimestamp((Calendar JavaDoc) null).toString();
120                 /* The java.sql.Timestamp.toString() method is supposed to return a string in
121                  * the JDBC escape format. However the JDK 1.3 libraries truncate leading zeros from
122                  * the year. This is not acceptable to DB2. So add leading zeros if necessary.
123                  */

124                 int separatorIdx = valueString.indexOf( '-');
125                 if( separatorIdx >= 0 && separatorIdx < 4)
126                 {
127                     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
128                     for( ; separatorIdx < 4; separatorIdx++)
129                         sb.append('0');
130                     sb.append( valueString);
131                     valueString = sb.toString();
132                 }
133             }
134
135             return valueString;
136         }
137         else
138         {
139             if (SanityManager.DEBUG)
140             {
141                 if (valueString != null)
142                 {
143                     SanityManager.THROWASSERT(
144                         "valueString expected to be null, not " +
145                         valueString);
146                 }
147             }
148             return null;
149         }
150     }
151
152
153     /**
154         getDate returns the date portion of the timestamp
155         Time is set to 00:00:00.0
156         Since Date is a JDBC object we use the JDBC definition
157         for the time portion. See JDBC API Tutorial, 47.3.12.
158
159         @exception StandardException thrown on failure
160      */

161     public Date JavaDoc getDate( Calendar JavaDoc cal) throws StandardException
162     {
163         if (isNull())
164             return null;
165         return newDate(cal);
166     }
167
168     private Date JavaDoc newDate(java.util.Calendar JavaDoc cal) throws StandardException
169     {
170         if( cal == null)
171             cal = new GregorianCalendar JavaDoc();
172         cal.clear();
173         cal.set(Calendar.YEAR, SQLDate.getYear(encodedDate) );
174         cal.set(Calendar.MONTH, SQLDate.getMonth(encodedDate)-1);
175         cal.set(Calendar.DATE, SQLDate.getDay(encodedDate) );
176         cal.set(Calendar.HOUR_OF_DAY, 0);
177         cal.set(Calendar.MINUTE, 0);
178         cal.set(Calendar.SECOND, 0);
179         cal.set(Calendar.MILLISECOND, 0);
180         return new Date JavaDoc(cal.getTime().getTime());
181     }
182
183     /**
184         getTime returns the time portion of the timestamp
185         Date is set to 1970-01-01
186         Since Time is a JDBC object we use the JDBC definition
187         for the date portion. See JDBC API Tutorial, 47.3.12.
188         @exception StandardException thrown on failure
189      */

190     public Time getTime( Calendar JavaDoc cal) throws StandardException
191     {
192         if (isNull())
193             return null;
194         return newTime(cal);
195     }
196
197     private Time newTime(java.util.Calendar JavaDoc cal) throws StandardException
198     {
199         if( cal == null)
200             cal = new GregorianCalendar JavaDoc();
201         cal.clear();
202         cal.set(Calendar.YEAR, 1970);
203         cal.set(Calendar.MONTH, Calendar.JANUARY);
204         cal.set(Calendar.DATE, 1);
205         cal.set(Calendar.HOUR_OF_DAY, SQLTime.getHour(encodedTime));
206         cal.set(Calendar.MINUTE, SQLTime.getMinute(encodedTime));
207         cal.set(Calendar.SECOND, SQLTime.getSecond(encodedTime));
208         cal.set(Calendar.MILLISECOND, (int)(nanos/1000000));
209         return new Time(cal.getTime().getTime());
210     }
211
212     public Object JavaDoc getObject()
213     {
214         return getTimestamp((Calendar JavaDoc) null);
215     }
216         
217     /* get storage length */
218     public int getLength()
219     {
220         return 12;
221     }
222
223     /* this is for DataType's error generator */
224     public String JavaDoc getTypeName()
225     {
226         return "TIMESTAMP";
227     }
228
229     /*
230      * Storable interface, implies Externalizable, TypedFormat
231      */

232
233     /**
234         Return my format identifier.
235
236         @see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId
237     */

238     public int getTypeFormatId() {
239         return StoredFormatIds.SQL_TIMESTAMP_ID;
240     }
241
242     /**
243         @exception IOException error writing data
244
245     */

246     public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc {
247
248         if (SanityManager.DEBUG)
249             SanityManager.ASSERT(!isNull(), "writeExternal() is not supposed to be called for null values.");
250
251         /*
252         ** Timestamp is written out 3 ints, encoded date, encoded time, and
253         ** nanoseconds
254         */

255         out.writeInt(encodedDate);
256         out.writeInt(encodedTime);
257         out.writeInt(nanos);
258     }
259
260     /**
261      * @see java.io.Externalizable#readExternal
262      *
263      * @exception IOException Thrown on error reading the object
264      */

265     public void readExternal(ObjectInput JavaDoc in) throws IOException JavaDoc
266     {
267         encodedDate = in.readInt();
268         encodedTime = in.readInt();
269         nanos = in.readInt();
270         // reset cached values
271
valueString = null;
272     }
273     public void readExternalFromArray(ArrayInputStream in) throws IOException JavaDoc
274     {
275         encodedDate = in.readInt();
276         encodedTime = in.readInt();
277         nanos = in.readInt();
278         // reset cached values
279
valueString = null;
280     }
281
282     /*
283      * DataValueDescriptor interface
284      */

285
286     /** @see DataValueDescriptor#getClone */
287     public DataValueDescriptor getClone()
288     {
289         // Call constructor with all of our info
290
return new SQLTimestamp(encodedDate, encodedTime, nanos);
291     }
292
293     /**
294      * @see DataValueDescriptor#getNewNull
295      */

296     public DataValueDescriptor getNewNull()
297     {
298         return new SQLTimestamp();
299     }
300     /**
301      * @see org.apache.derby.iapi.services.io.Storable#restoreToNull
302      *
303      */

304     public void restoreToNull()
305     {
306         // clear numeric representation
307
encodedDate = 0;
308         encodedTime = 0;
309         nanos = 0;
310
311         // clear cached valueString
312
valueString = null;
313     }
314
315     /*
316      * DataValueDescriptor interface
317      */

318
319     /**
320      * @see DataValueDescriptor#setValueFromResultSet
321      *
322      * @exception SQLException Thrown on error
323      */

324     public void setValueFromResultSet(ResultSet JavaDoc resultSet, int colNumber,
325                                       boolean isNullable)
326         throws SQLException JavaDoc, StandardException
327     {
328             setValue(resultSet.getTimestamp(colNumber), (Calendar JavaDoc) null);
329     }
330
331     public int compare(DataValueDescriptor other)
332         throws StandardException
333     {
334         /* Use compare method from dominant type, negating result
335          * to reflect flipping of sides.
336          */

337         if (typePrecedence() < other.typePrecedence())
338         {
339             return - (other.compare(this));
340         }
341
342         boolean thisNull, otherNull;
343
344         thisNull = this.isNull();
345         otherNull = other.isNull();
346
347         /*
348          * thisNull otherNull return
349          * T T 0 (this == other)
350          * F T -1 (this < other)
351          * T F 1 (this > other)
352          */

353         if (thisNull || otherNull)
354         {
355             if (!thisNull) // otherNull must be true
356
return -1;
357             if (!otherNull) // thisNull must be true
358
return 1;
359             return 0;
360         }
361
362         /*
363             Neither are null compare them
364          */

365
366         int comparison;
367         /* get the comparison date values */
368         int otherEncodedDate = 0;
369         int otherEncodedTime = 0;
370         int otherNanos = 0;
371
372         /* if the argument is another SQLTimestamp, look up the value
373          */

374         if (other instanceof SQLTimestamp)
375         {
376             SQLTimestamp st = (SQLTimestamp)other;
377             otherEncodedDate= st.encodedDate;
378             otherEncodedTime= st.encodedTime;
379             otherNanos= st.nanos;
380         }
381         else
382         {
383             /* O.K. have to do it the hard way and calculate the numeric value
384              * from the value
385              */

386             Calendar JavaDoc cal = new GregorianCalendar JavaDoc();
387             Timestamp JavaDoc otherts = other.getTimestamp(cal);
388             otherEncodedDate = SQLTimestamp.computeEncodedDate(otherts, cal);
389             otherEncodedTime = SQLTimestamp.computeEncodedTime(otherts, cal);
390             otherNanos = otherts.getNanos();
391         }
392         if (encodedDate < otherEncodedDate)
393             comparison = -1;
394         else if (encodedDate > otherEncodedDate)
395             comparison = 1;
396         else if (encodedTime < otherEncodedTime)
397             comparison = -1;
398         else if (encodedTime > otherEncodedTime)
399             comparison = 1;
400         else if (nanos < otherNanos)
401             comparison = -1;
402         else if (nanos > otherNanos)
403             comparison = 1;
404         else
405             comparison = 0;
406
407         return comparison;
408     }
409
410     /**
411         @exception StandardException thrown on error
412      */

413     public boolean compare(int op,
414                            DataValueDescriptor other,
415                            boolean orderedNulls,
416                            boolean unknownRV)
417         throws StandardException
418     {
419         if (!orderedNulls) // nulls are unordered
420
{
421             if (this.isNull() || ((DataValueDescriptor)other).isNull())
422                 return unknownRV;
423         }
424
425         /* Do the comparison */
426         return super.compare(op, other, orderedNulls, unknownRV);
427     }
428
429     /*
430     ** Class interface
431     */

432
433     /*
434     ** Constructors
435     */

436
437     /** no-arg constructor required by Formattable */
438     public SQLTimestamp() { }
439
440
441     public SQLTimestamp(Timestamp JavaDoc value) throws StandardException
442     {
443         setValue(value, (Calendar JavaDoc) null);
444     }
445
446     SQLTimestamp(int encodedDate, int encodedTime, int nanos) {
447
448         this.encodedDate = encodedDate;
449         this.encodedTime = encodedTime;
450         this.nanos = nanos;
451     }
452
453     public SQLTimestamp( DataValueDescriptor date, DataValueDescriptor time) throws StandardException
454     {
455         Calendar JavaDoc cal = null;
456         if( date == null || date.isNull()
457             || time == null || time.isNull())
458             return;
459         if( date instanceof SQLDate)
460         {
461             SQLDate sqlDate = (SQLDate) date;
462             encodedDate = sqlDate.getEncodedDate();
463         }
464         else
465         {
466             cal = new GregorianCalendar JavaDoc();
467             encodedDate = computeEncodedDate( date.getDate( cal), cal);
468         }
469         if( time instanceof SQLTime)
470         {
471             SQLTime sqlTime = (SQLTime) time;
472             encodedTime = sqlTime.getEncodedTime();
473         }
474         else
475         {
476             if( cal == null)
477                 cal = new GregorianCalendar JavaDoc();
478             encodedTime = computeEncodedTime( time.getTime( cal), cal);
479         }
480     }
481
482     /**
483      * Construct a timestamp from a string. The allowed formats are:
484      *<ol>
485      *<li>JDBC escape: yyyy-mm-dd hh:mm:ss[.fffff]
486      *<li>IBM: yyyy-mm-dd-hh.mm.ss[.nnnnnn]
487      *</ol>
488      * The format is specified by a parameter to the constructor. Leading zeroes may be omitted from the month, day,
489      * and hour part of the timestamp. The microsecond part may be omitted or truncated.
490      */

491     public SQLTimestamp( String JavaDoc timestampStr, boolean isJDBCEscape, LocaleFinder localeFinder)
492         throws StandardException
493     {
494         parseTimestamp( timestampStr, isJDBCEscape,localeFinder, (Calendar JavaDoc) null);
495     }
496     
497     /**
498      * Construct a timestamp from a string. The allowed formats are:
499      *<ol>
500      *<li>JDBC escape: yyyy-mm-dd hh:mm:ss[.fffff]
501      *<li>IBM: yyyy-mm-dd-hh.mm.ss[.nnnnnn]
502      *</ol>
503      * The format is specified by a parameter to the constructor. Leading zeroes may be omitted from the month, day,
504      * and hour part of the timestamp. The microsecond part may be omitted or truncated.
505      */

506     public SQLTimestamp( String JavaDoc timestampStr, boolean isJDBCEscape, LocaleFinder localeFinder, Calendar JavaDoc cal)
507         throws StandardException
508     {
509         parseTimestamp( timestampStr, isJDBCEscape, localeFinder, cal);
510     }
511
512     static final char DATE_SEPARATOR = '-';
513     private static final char[] DATE_SEPARATORS = { DATE_SEPARATOR};
514     private static final char IBM_DATE_TIME_SEPARATOR = '-';
515     private static final char ODBC_DATE_TIME_SEPARATOR = ' ';
516     private static final char[] DATE_TIME_SEPARATORS = {IBM_DATE_TIME_SEPARATOR, ODBC_DATE_TIME_SEPARATOR};
517     private static final char[] DATE_TIME_SEPARATORS_OR_END
518     = {IBM_DATE_TIME_SEPARATOR, ODBC_DATE_TIME_SEPARATOR, (char) 0};
519     private static final char IBM_TIME_SEPARATOR = '.';
520     private static final char ODBC_TIME_SEPARATOR = ':';
521     private static final char[] TIME_SEPARATORS = {IBM_TIME_SEPARATOR, ODBC_TIME_SEPARATOR};
522     private static final char[] TIME_SEPARATORS_OR_END = {IBM_TIME_SEPARATOR, ODBC_TIME_SEPARATOR, (char) 0};
523     private static final char[] END_OF_STRING = {(char) 0};
524     
525     private void parseTimestamp( String JavaDoc timestampStr, boolean isJDBCEscape, LocaleFinder localeFinder, Calendar JavaDoc cal)
526         throws StandardException
527     {
528         StandardException thrownSE = null;
529         DateTimeParser parser = new DateTimeParser( timestampStr);
530         try
531         {
532             int[] dateTimeNano = parseDateOrTimestamp( parser, true);
533             encodedDate = dateTimeNano[0];
534             encodedTime = dateTimeNano[1];
535             nanos = dateTimeNano[2];
536             valueString = parser.getTrimmedString();
537             return;
538         }
539         catch( StandardException se)
540         {
541             thrownSE = se;
542         }
543         // see if it is a localized timestamp
544
try
545         {
546             timestampStr = StringUtil.trimTrailing( timestampStr);
547             int[] dateAndTime = parseLocalTimestamp( timestampStr, localeFinder, cal);
548             encodedDate = dateAndTime[0];
549             encodedTime = dateAndTime[1];
550             valueString = timestampStr;
551             return;
552         }
553         catch( ParseException JavaDoc pe){}
554         catch( StandardException se){}
555         if( thrownSE != null)
556             throw thrownSE;
557         throw StandardException.newException( SQLState.LANG_DATE_SYNTAX_EXCEPTION);
558     } // end of parseTimestamp
559

560     /**
561      * Parse a localized timestamp.
562      *
563      * @param str the timestamp string, with trailing blanks removed.
564      * @param localeFinder
565      *
566      * @return a {encodedDate, encodedTime} array.
567      *
568      * @exception ParseException If the string is not a valid timestamp.
569      */

570     static int[] parseLocalTimestamp( String JavaDoc str, LocaleFinder localeFinder, Calendar JavaDoc cal)
571         throws StandardException, ParseException JavaDoc
572     {
573         DateFormat JavaDoc timestampFormat = null;
574         if(localeFinder == null)
575             timestampFormat = DateFormat.getDateTimeInstance();
576         else if( cal == null)
577             timestampFormat = localeFinder.getTimestampFormat();
578         else
579             timestampFormat = (DateFormat JavaDoc) localeFinder.getTimestampFormat().clone();
580         if( cal == null)
581             cal = new GregorianCalendar JavaDoc();
582         else
583             timestampFormat.setCalendar( cal);
584         java.util.Date JavaDoc date = timestampFormat.parse( str);
585             
586         return new int[] { computeEncodedDate( date, cal), computeEncodedTime( date, cal)};
587     } // end of parseLocalTimestamp
588

589     /**
590      * Parse a timestamp or a date. DB2 allows timestamps to be used as dates or times. So
591      * date('2004-04-15-16.15.32') is valid, as is date('2004-04-15').
592      *
593      * This method does not handle localized timestamps.
594      *
595      * @param parser a DateTimeParser initialized with a string.
596      * @param timeRequired If true then an error will be thrown if the time is missing. If false then the time may
597      * be omitted.
598      *
599      * @return {encodedDate, encodedTime, nanosecond} array.
600      *
601      * @exception StandardException if the syntax is incorrect for an IBM standard timestamp.
602      */

603     static int[] parseDateOrTimestamp( DateTimeParser parser, boolean timeRequired)
604         throws StandardException
605     {
606         int year = parser.parseInt( 4, false, DATE_SEPARATORS, false);
607         int month = parser.parseInt( 2, true, DATE_SEPARATORS, false);
608         int day = parser.parseInt( 2, true, timeRequired ? DATE_TIME_SEPARATORS : DATE_TIME_SEPARATORS_OR_END, false);
609         int hour = 0;
610         int minute = 0;
611         int second = 0;
612         int nano = 0;
613         if( parser.getCurrentSeparator() != 0)
614         {
615             char timeSeparator = (parser.getCurrentSeparator() == ODBC_DATE_TIME_SEPARATOR)
616               ? ODBC_TIME_SEPARATOR : IBM_TIME_SEPARATOR;
617             hour = parser.parseInt( 2, true, TIME_SEPARATORS, false);
618             if( timeSeparator == parser.getCurrentSeparator())
619             {
620                 minute = parser.parseInt( 2, false, TIME_SEPARATORS, false);
621                 if( timeSeparator == parser.getCurrentSeparator())
622                 {
623                     second = parser.parseInt( 2, false, TIME_SEPARATORS_OR_END, false);
624                     if( parser.getCurrentSeparator() == '.')
625                         nano = parser.parseInt( MAX_FRACTION_DIGITS, true, END_OF_STRING, true)*FRACTION_TO_NANO;
626                 }
627             }
628         }
629         parser.checkEnd();
630         return new int[] { SQLDate.computeEncodedDate( year, month, day),
631                            SQLTime.computeEncodedTime( hour,minute,second),
632                            nano};
633     } // end of parseDateOrTimestamp
634

635     /**
636      * Set the value from a correctly typed Timestamp object.
637      * @throws StandardException
638      */

639     void setObject(Object JavaDoc theValue) throws StandardException
640     {
641         setValue((Timestamp JavaDoc) theValue);
642     }
643     
644     protected void setFrom(DataValueDescriptor theValue) throws StandardException {
645
646         if (theValue instanceof SQLTimestamp) {
647             restoreToNull();
648             SQLTimestamp tvst = (SQLTimestamp) theValue;
649             encodedDate = tvst.encodedDate;
650             encodedTime = tvst.encodedTime;
651             nanos = tvst.nanos;
652         }
653         else
654         {
655             Calendar JavaDoc cal = new GregorianCalendar JavaDoc();
656             setValue(theValue.getTimestamp( cal), cal);
657         }
658     }
659
660     /**
661         @see DateTimeDataValue#setValue
662         When converting from a date to a timestamp, time is set to 00:00:00.0
663
664      */

665     public void setValue(Date JavaDoc value, Calendar JavaDoc cal) throws StandardException
666     {
667         restoreToNull();
668         if( value != null)
669         {
670             if( cal == null)
671                 cal = new GregorianCalendar JavaDoc();
672             encodedDate = computeEncodedDate(value, cal);
673         }
674         /* encodedTime and nanos are already set to zero by restoreToNull() */
675     }
676
677     /**
678         @see DateTimeDataValue#setValue
679
680      */

681     public void setValue(Time value, Calendar JavaDoc cal) throws StandardException
682     {
683         restoreToNull();
684         if (value != null)
685         {
686             /*
687             ** Create a new timestamp with today's date,
688             ** and 'value' time.
689             **
690             ** We create a new calendar to get today's date
691             */

692             Calendar JavaDoc today = GregorianCalendar.getInstance();
693             encodedDate = SQLDate.computeEncodedDate(today);
694             if( cal == null)
695                 cal = today;
696             encodedTime = computeEncodedTime(value, cal);
697         }
698     }
699
700     /**
701         @see DateTimeDataValue#setValue
702
703      */

704     public void setValue(Timestamp JavaDoc value, Calendar JavaDoc cal)
705         throws StandardException
706     {
707         restoreToNull();
708         setNumericTimestamp(value, cal);
709     }
710
711
712     public void setValue(String JavaDoc theValue)
713         throws StandardException
714     {
715         restoreToNull();
716
717         if (theValue != null)
718         {
719             DatabaseContext databaseContext = (DatabaseContext) ContextService.getContext(DatabaseContext.CONTEXT_ID);
720             parseTimestamp( theValue,
721                             false,
722                             (databaseContext == null) ? null : databaseContext.getDatabase(),
723                             (Calendar JavaDoc) null);
724         }
725         /* restoreToNull will have already set the encoded date to 0 (null value) */
726     }
727
728     /*
729     ** SQL Operators
730     */

731
732
733     /**
734      * @see DateTimeDataValue#getYear
735      *
736      * @exception StandardException Thrown on error
737      */

738     public NumberDataValue getYear(NumberDataValue result)
739                             throws StandardException
740     {
741         if (SanityManager.DEBUG)
742         {
743             SanityManager.ASSERT(!isNull(), "getYear called on a null");
744         }
745         return SQLDate.setSource(SQLDate.getYear(encodedDate), result);
746     }
747
748     /**
749      * @see DateTimeDataValue#getMonth
750      *
751      * @exception StandardException Thrown on error
752      */

753     public NumberDataValue getMonth(NumberDataValue result)
754                             throws StandardException
755     {
756         if (SanityManager.DEBUG)
757         {
758             SanityManager.ASSERT(!isNull(), "getMonth called on a null");
759         }
760         return SQLDate.setSource(SQLDate.getMonth(encodedDate), result);
761     }
762
763     /**
764      * @see DateTimeDataValue#getDate
765      *
766      * @exception StandardException Thrown on error
767      */

768     public NumberDataValue getDate(NumberDataValue result)
769                             throws StandardException
770     {
771         if (SanityManager.DEBUG)
772         {
773             SanityManager.ASSERT(!isNull(), "getDate called on a null");
774         }
775         return SQLDate.setSource(SQLDate.getDay(encodedDate), result);
776     }
777
778     /**
779      * @see DateTimeDataValue#getHours
780      *
781      * @exception StandardException Thrown on error
782      */

783     public NumberDataValue getHours(NumberDataValue result)
784                             throws StandardException
785     {
786         if (SanityManager.DEBUG)
787         {
788             SanityManager.ASSERT(!isNull(), "getHours called on a null");
789         }
790         return SQLDate.setSource(SQLTime.getHour(encodedTime), result);
791     }
792
793     /**
794      * @see DateTimeDataValue#getMinutes
795      *
796      * @exception StandardException Thrown on error
797      */

798     public NumberDataValue getMinutes(NumberDataValue result)
799                             throws StandardException
800     {
801         if (SanityManager.DEBUG)
802         {
803             SanityManager.ASSERT(!isNull(), "getMinute called on a null");
804         }
805         return SQLDate.setSource(SQLTime.getMinute(encodedTime), result);
806     }
807
808     /**
809      * @see DateTimeDataValue#getSeconds
810      *
811      * @exception StandardException Thrown on error
812      */

813     public NumberDataValue getSeconds(NumberDataValue source)
814                             throws StandardException
815     {
816         if (SanityManager.DEBUG)
817         {
818             SanityManager.ASSERT(!isNull(), "getSeconds called on a null");
819             SanityManager.ASSERT(source == null || source instanceof SQLDouble,
820         "getSeconds for a timestamp was given a source other than a SQLDouble");
821         }
822         NumberDataValue result;
823
824         if (source != null)
825             result = source;
826         else
827             result = new SQLDouble();
828
829         result.setValue((double)(SQLTime.getSecond(encodedTime))
830                 + ((double)nanos)/1.0e9);
831
832         return result;
833     }
834
835     /*
836     ** String display of value
837     */

838
839     public String JavaDoc toString()
840     {
841         if (isNull())
842         {
843             return "NULL";
844         }
845         else
846         {
847             return getTimestamp( (Calendar JavaDoc) null).toString();
848         }
849     }
850
851     /*
852      * Hash code
853      */

854     public int hashCode()
855     {
856         if (isNull())
857         {
858             return 0;
859         }
860         
861         return encodedDate + encodedTime + nanos; //since 0 is null
862

863     }
864
865     /** @see DataValueDescriptor#typePrecedence */
866     public int typePrecedence()
867     {
868         return TypeId.TIMESTAMP_PRECEDENCE;
869     }
870
871     /**
872      * Check if the value is null. encodedDate value of 0 is null
873      *
874      * @return Whether or not value is logically null.
875      */

876     public final boolean isNull()
877     {
878         return (encodedDate == 0);
879     }
880
881     /**
882      * Get the value field. We instantiate the field
883      * on demand.
884      *
885      * @return The value field.
886      */

887     public Timestamp JavaDoc getTimestamp(java.util.Calendar JavaDoc cal)
888     {
889         if (isNull())
890             return null;
891         return newTimestamp(cal);
892     }
893
894     private Timestamp JavaDoc newTimestamp(Calendar JavaDoc currentCal)
895     {
896         if( currentCal == null)
897             currentCal = new GregorianCalendar JavaDoc();
898         setCalendar( currentCal);
899         Timestamp JavaDoc t = new Timestamp JavaDoc(currentCal.getTime().getTime());
900         t.setNanos(nanos);
901         return t;
902     }
903
904     private void setCalendar( Calendar JavaDoc cal)
905     {
906         cal.clear();
907         cal.set(Calendar.YEAR, SQLDate.getYear(encodedDate));
908         /* Note calendar month is zero based so we subtract 1*/
909         cal.set(Calendar.MONTH, (SQLDate.getMonth(encodedDate)-1));
910         cal.set(Calendar.DATE, SQLDate.getDay(encodedDate));
911         cal.set(Calendar.HOUR_OF_DAY, SQLTime.getHour(encodedTime));
912         cal.set(Calendar.MINUTE, SQLTime.getMinute(encodedTime));
913         cal.set(Calendar.SECOND, SQLTime.getSecond(encodedTime));
914         cal.set(Calendar.MILLISECOND, 0);
915     } // end of setCalendar
916

917     /**
918      * Set the encoded values for the timestamp
919      *
920      */

921     private void setNumericTimestamp(Timestamp JavaDoc value, Calendar JavaDoc cal) throws StandardException
922     {
923         if (SanityManager.DEBUG)
924         {
925             SanityManager.ASSERT(isNull(), "setNumericTimestamp called when already set");
926         }
927         if (value != null)
928         {
929             if( cal == null)
930                 cal = new GregorianCalendar JavaDoc();
931             encodedDate = computeEncodedDate(value, cal);
932             encodedTime = computeEncodedTime(value, cal);
933             nanos = value.getNanos();
934         }
935         /* encoded date should already be 0 for null */
936     }
937
938     // International Support
939

940     /**
941      * International version of getString(). Overrides getNationalString
942      * in DataType for date, time, and timestamp.
943      *
944      * @exception StandardException Thrown on error
945      */

946     protected String JavaDoc getNationalString(LocaleFinder localeFinder) throws StandardException
947     {
948         if (isNull())
949         {
950             return getString();
951         }
952
953
954         return localeFinder.getTimestampFormat().format(getTimestamp((Calendar JavaDoc) null));
955     }
956
957     /**
958         computeEncodedDate sets the date in a Calendar object
959         and then uses the SQLDate function to compute an encoded date
960         The encoded date is
961             year << 16 + month << 8 + date
962         @param value the value to convert
963         @return the encodedDate
964
965      */

966     private static int computeEncodedDate(java.util.Date JavaDoc value, Calendar JavaDoc currentCal) throws StandardException
967     {
968         if (value == null)
969             return 0;
970
971         currentCal.setTime(value);
972         return SQLDate.computeEncodedDate(currentCal);
973     }
974     /**
975         computeEncodedTime extracts the hour, minute and seconds from
976         a java.util.Date value and encodes them as
977             hour << 16 + minute << 8 + second
978         using the SQLTime function for encoding the data
979         @param value the value to convert
980         @return the encodedTime
981
982      */

983     private static int computeEncodedTime(java.util.Date JavaDoc value, Calendar JavaDoc currentCal) throws StandardException
984     {
985         currentCal.setTime(value);
986         return SQLTime.computeEncodedTime(currentCal);
987     }
988
989     
990     public void setInto(PreparedStatement JavaDoc ps, int position) throws SQLException JavaDoc, StandardException {
991
992                   ps.setTimestamp(position, getTimestamp((Calendar JavaDoc) null));
993      }
994
995     /**
996      * Compute the SQL timestamp function.
997      *
998      * @exception StandardException
999      */

1000    public static DateTimeDataValue computeTimestampFunction( DataValueDescriptor operand,
1001                                                              DataValueFactory dvf) throws StandardException
1002    {
1003        try
1004        {
1005            if( operand.isNull())
1006                return new SQLTimestamp();
1007            if( operand instanceof SQLTimestamp)
1008                return (SQLTimestamp) operand.getClone();
1009
1010            String JavaDoc str = operand.getString();
1011            if( str.length() == 14)
1012            {
1013                int year = parseDateTimeInteger( str, 0, 4);
1014                int month = parseDateTimeInteger( str, 4, 2);
1015                int day = parseDateTimeInteger( str, 6, 2);
1016                int hour = parseDateTimeInteger( str, 8, 2);
1017                int minute = parseDateTimeInteger( str, 10, 2);
1018                int second = parseDateTimeInteger( str, 12, 2);
1019                return new SQLTimestamp( SQLDate.computeEncodedDate( year, month, day),
1020                                         SQLTime.computeEncodedTime( hour,minute,second),
1021                                         0);
1022            }
1023            // else use the standard cast
1024
return dvf.getTimestampValue( str, false);
1025        }
1026        catch( StandardException se)
1027        {
1028            if( SQLState.LANG_DATE_SYNTAX_EXCEPTION.startsWith( se.getSQLState()))
1029                throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT,
1030                                                      operand.getString(), "timestamp");
1031            throw se;
1032        }
1033    } // end of computeTimestampFunction
1034

1035    static int parseDateTimeInteger( String JavaDoc str, int start, int ndigits) throws StandardException
1036    {
1037        int end = start + ndigits;
1038        int retVal = 0;
1039        for( int i = start; i < end; i++)
1040        {
1041            char c = str.charAt( i);
1042            if( !Character.isDigit( c))
1043                throw StandardException.newException( SQLState.LANG_DATE_SYNTAX_EXCEPTION);
1044            retVal = 10*retVal + Character.digit( c, 10);
1045        }
1046        return retVal;
1047    } // end of parseDateTimeInteger
1048

1049    /**
1050     * Add a number of intervals to a datetime value. Implements the JDBC escape TIMESTAMPADD function.
1051     *
1052     * @param intervalType One of FRAC_SECOND_INTERVAL, SECOND_INTERVAL, MINUTE_INTERVAL, HOUR_INTERVAL,
1053     * DAY_INTERVAL, WEEK_INTERVAL, MONTH_INTERVAL, QUARTER_INTERVAL, or YEAR_INTERVAL
1054     * @param count The number of intervals to add
1055     * @param currentDate Used to convert time to timestamp
1056     * @param resultHolder If non-null a DateTimeDataValue that can be used to hold the result. If null then
1057     * generate a new holder
1058     *
1059     * @return startTime + intervalCount intervals, as a timestamp
1060     *
1061     * @exception StandardException
1062     */

1063    public DateTimeDataValue timestampAdd( int intervalType,
1064                                           NumberDataValue count,
1065                                           java.sql.Date JavaDoc currentDate,
1066                                           DateTimeDataValue resultHolder)
1067        throws StandardException
1068    {
1069        if( resultHolder == null)
1070            resultHolder = new SQLTimestamp();
1071        SQLTimestamp tsResult = (SQLTimestamp) resultHolder;
1072        if( isNull() || count.isNull())
1073        {
1074            tsResult.restoreToNull();
1075            return resultHolder;
1076        }
1077        tsResult.setFrom( this);
1078        int intervalCount = count.getInt();
1079        
1080        switch( intervalType)
1081        {
1082        case FRAC_SECOND_INTERVAL:
1083            // The interval is nanoseconds. Do the computation in long to avoid overflow.
1084
long nanos = this.nanos + intervalCount;
1085            if( nanos >= 0 && nanos < ONE_BILLION)
1086                tsResult.nanos = (int) nanos;
1087            else
1088            {
1089                int secondsInc = (int)(nanos/ONE_BILLION);
1090                if( nanos >= 0)
1091                    tsResult.nanos = (int) (nanos % ONE_BILLION);
1092                else
1093                {
1094                    secondsInc--;
1095                    nanos -= secondsInc * (long)ONE_BILLION; // 0 <= nanos < ONE_BILLION
1096
tsResult.nanos = (int) nanos;
1097                }
1098                addInternal( Calendar.SECOND, secondsInc, tsResult);
1099            }
1100            break;
1101
1102        case SECOND_INTERVAL:
1103            addInternal( Calendar.SECOND, intervalCount, tsResult);
1104            break;
1105
1106        case MINUTE_INTERVAL:
1107            addInternal( Calendar.MINUTE, intervalCount, tsResult);
1108            break;
1109
1110        case HOUR_INTERVAL:
1111            addInternal( Calendar.HOUR, intervalCount, tsResult);
1112            break;
1113
1114        case DAY_INTERVAL:
1115            addInternal( Calendar.DATE, intervalCount, tsResult);
1116            break;
1117
1118        case WEEK_INTERVAL:
1119            addInternal( Calendar.DATE, intervalCount*7, tsResult);
1120            break;
1121
1122        case MONTH_INTERVAL:
1123            addInternal( Calendar.MONTH, intervalCount, tsResult);
1124            break;
1125
1126        case QUARTER_INTERVAL:
1127            addInternal( Calendar.MONTH, intervalCount*3, tsResult);
1128            break;
1129
1130        case YEAR_INTERVAL:
1131            addInternal( Calendar.YEAR, intervalCount, tsResult);
1132            break;
1133
1134        default:
1135            throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT,
1136                                                  ReuseFactory.getInteger( intervalType),
1137                                                  "TIMESTAMPADD");
1138        }
1139        return tsResult;
1140    } // end of timestampAdd
1141

1142    private void addInternal( int calIntervalType, int count, SQLTimestamp tsResult) throws StandardException
1143    {
1144        Calendar JavaDoc cal = new GregorianCalendar JavaDoc();
1145        setCalendar( cal);
1146        try
1147        {
1148            cal.add( calIntervalType, count);
1149            tsResult.encodedTime = SQLTime.computeEncodedTime( cal);
1150            tsResult.encodedDate = SQLDate.computeEncodedDate( cal);
1151        }
1152        catch( StandardException se)
1153        {
1154            String JavaDoc state = se.getSQLState();
1155            if( state != null && state.length() > 0 && SQLState.LANG_DATE_RANGE_EXCEPTION.startsWith( state))
1156            {
1157                throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "TIMESTAMP");
1158            }
1159            throw se;
1160        }
1161    } // end of addInternal
1162

1163    /**
1164     * Finds the difference between two datetime values as a number of intervals. Implements the JDBC
1165     * TIMESTAMPDIFF escape function.
1166     *
1167     * @param intervalType One of FRAC_SECOND_INTERVAL, SECOND_INTERVAL, MINUTE_INTERVAL, HOUR_INTERVAL,
1168     * DAY_INTERVAL, WEEK_INTERVAL, MONTH_INTERVAL, QUARTER_INTERVAL, or YEAR_INTERVAL
1169     * @param time1
1170     * @param currentDate Used to convert time to timestamp
1171     * @param resultHolder If non-null a NumberDataValue that can be used to hold the result. If null then
1172     * generate a new holder
1173     *
1174     * @return the number of intervals by which this datetime is greater than time1
1175     *
1176     * @exception StandardException
1177     */

1178    public NumberDataValue timestampDiff( int intervalType,
1179                                          DateTimeDataValue time1,
1180                                          java.sql.Date JavaDoc currentDate,
1181                                          NumberDataValue resultHolder)
1182        throws StandardException
1183    {
1184        if( resultHolder == null)
1185            resultHolder = new SQLInteger();
1186 
1187       if( isNull() || time1.isNull())
1188        {
1189            resultHolder.setToNull();
1190            return resultHolder;
1191        }
1192        
1193        SQLTimestamp ts1 = promote( time1, currentDate);
1194
1195        /* Years, months, and quarters are difficult because their lengths are not constant.
1196         * The other intervals are relatively easy (because we ignore leap seconds).
1197         */

1198        Calendar JavaDoc cal = new GregorianCalendar JavaDoc();
1199        setCalendar( cal);
1200        long thisInSeconds = cal.getTime().getTime()/1000;
1201        ts1.setCalendar( cal);
1202        long ts1InSeconds = cal.getTime().getTime()/1000;
1203        long secondsDiff = thisInSeconds - ts1InSeconds;
1204        int nanosDiff = nanos - ts1.nanos;
1205        // Normalize secondsDiff and nanosDiff so that they are both <= 0 or both >= 0.
1206
if( nanosDiff < 0 && secondsDiff > 0)
1207        {
1208            secondsDiff--;
1209            nanosDiff += ONE_BILLION;
1210        }
1211        else if( nanosDiff > 0 && secondsDiff < 0)
1212        {
1213            secondsDiff++;
1214            nanosDiff -= ONE_BILLION;
1215        }
1216        long ldiff = 0;
1217        
1218        switch( intervalType)
1219        {
1220        case FRAC_SECOND_INTERVAL:
1221            if( secondsDiff > Integer.MAX_VALUE/ONE_BILLION || secondsDiff < Integer.MIN_VALUE/ONE_BILLION)
1222                throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "INTEGER");
1223            ldiff = secondsDiff*ONE_BILLION + nanosDiff;
1224            break;
1225            
1226        case SECOND_INTERVAL:
1227            ldiff = secondsDiff;
1228            break;
1229            
1230        case MINUTE_INTERVAL:
1231            ldiff = secondsDiff/60;
1232            break;
1233
1234        case HOUR_INTERVAL:
1235            ldiff = secondsDiff/(60*60);
1236            break;
1237            
1238        case DAY_INTERVAL:
1239            ldiff = secondsDiff/(24*60*60);
1240            break;
1241            
1242        case WEEK_INTERVAL:
1243            ldiff = secondsDiff/(7*24*60*60);
1244            break;
1245
1246        case QUARTER_INTERVAL:
1247        case MONTH_INTERVAL:
1248            // Make a conservative guess and increment until we overshoot.
1249
if( Math.abs( secondsDiff) > 366*24*60*60) // Certainly more than a year
1250
ldiff = 12*(secondsDiff/(366*24*60*60));
1251            else
1252                ldiff = secondsDiff/(31*24*60*60);
1253            if( secondsDiff >= 0)
1254            {
1255                if (ldiff >= Integer.MAX_VALUE)
1256                    throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "INTEGER");
1257                // cal holds the time for time1
1258
cal.add( Calendar.MONTH, (int) (ldiff + 1));
1259                for(;;)
1260                {
1261                    if( cal.getTime().getTime()/1000 > thisInSeconds)
1262                        break;
1263                    cal.add( Calendar.MONTH, 1);
1264                    ldiff++;
1265                }
1266            }
1267            else
1268            {
1269                if (ldiff <= Integer.MIN_VALUE)
1270                    throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "INTEGER");
1271                // cal holds the time for time1
1272
cal.add( Calendar.MONTH, (int) (ldiff - 1));
1273                for(;;)
1274                {
1275                    if( cal.getTime().getTime()/1000 < thisInSeconds)
1276                        break;
1277                    cal.add( Calendar.MONTH, -1);
1278                    ldiff--;
1279                }
1280            }
1281            if( intervalType == QUARTER_INTERVAL)
1282                ldiff = ldiff/3;
1283            break;
1284
1285        case YEAR_INTERVAL:
1286            // Make a conservative guess and increment until we overshoot.
1287
ldiff = secondsDiff/(366*24*60*60);
1288            if( secondsDiff >= 0)
1289            {
1290                if (ldiff >= Integer.MAX_VALUE)
1291                    throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "INTEGER");
1292                // cal holds the time for time1
1293
cal.add( Calendar.YEAR, (int) (ldiff + 1));
1294                for(;;)
1295                {
1296                    if( cal.getTime().getTime()/1000 > thisInSeconds)
1297                        break;
1298                    cal.add( Calendar.YEAR, 1);
1299                    ldiff++;
1300                }
1301            }
1302            else
1303            {
1304                if (ldiff <= Integer.MIN_VALUE)
1305                    throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "INTEGER");
1306                // cal holds the time for time1
1307
cal.add( Calendar.YEAR, (int) (ldiff - 1));
1308                for(;;)
1309                {
1310                    if( cal.getTime().getTime()/1000 < thisInSeconds)
1311                        break;
1312                    cal.add( Calendar.YEAR, -1);
1313                    ldiff--;
1314                }
1315            }
1316            break;
1317
1318        default:
1319            throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT,
1320                                                  ReuseFactory.getInteger( intervalType),
1321                                                  "TIMESTAMPDIFF");
1322        }
1323        if (ldiff > Integer.MAX_VALUE || ldiff < Integer.MIN_VALUE)
1324            throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "INTEGER");
1325        resultHolder.setValue( (int) ldiff);
1326        return resultHolder;
1327    } // end of timestampDiff
1328

1329    /**
1330     * Promotes a DateTimeDataValue to a timestamp.
1331     *
1332     *
1333     * @return the corresponding timestamp, using the current date if datetime is a time,
1334     * or time 00:00:00 if datetime is a date.
1335     *
1336     * @exception StandardException
1337     */

1338    static SQLTimestamp promote( DateTimeDataValue dateTime, java.sql.Date JavaDoc currentDate) throws StandardException
1339    {
1340        if( dateTime instanceof SQLTimestamp)
1341            return (SQLTimestamp) dateTime;
1342        else if( dateTime instanceof SQLTime)
1343            return new SQLTimestamp( SQLDate.computeEncodedDate( currentDate, (Calendar JavaDoc) null),
1344                                    ((SQLTime) dateTime).getEncodedTime(),
1345                                    0 /* nanoseconds */);
1346        else if( dateTime instanceof SQLDate)
1347            return new SQLTimestamp( ((SQLDate) dateTime).getEncodedDate(), 0, 0);
1348        else
1349            return new SQLTimestamp( dateTime.getTimestamp( new GregorianCalendar JavaDoc()));
1350    } // end of promote
1351
}
1352
Popular Tags