001: /*
002:
003: Derby - Class org.apache.derby.iapi.types.SQLTime
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.iapi.types;
023:
024: import org.apache.derby.iapi.reference.SQLState;
025:
026: import org.apache.derby.iapi.services.io.ArrayInputStream;
027:
028: import org.apache.derby.iapi.services.context.ContextService;
029:
030: import org.apache.derby.iapi.services.sanity.SanityManager;
031: import org.apache.derby.iapi.services.io.StoredFormatIds;
032:
033: import org.apache.derby.iapi.error.StandardException;
034:
035: import org.apache.derby.iapi.db.DatabaseContext;
036:
037: import org.apache.derby.iapi.types.DataValueDescriptor;
038: import org.apache.derby.iapi.types.TypeId;
039:
040: import org.apache.derby.iapi.types.DateTimeDataValue;
041: import org.apache.derby.iapi.types.NumberDataValue;
042:
043: import org.apache.derby.iapi.types.DataType;
044: import org.apache.derby.iapi.services.i18n.LocaleFinder;
045: import org.apache.derby.iapi.services.cache.ClassSize;
046: import org.apache.derby.iapi.util.StringUtil;
047:
048: import java.sql.Date;
049: import java.sql.Time;
050: import java.sql.Timestamp;
051: import java.sql.Types;
052: import java.sql.PreparedStatement;
053:
054: import java.util.Calendar;
055: import java.util.GregorianCalendar;
056:
057: import java.io.ObjectOutput;
058: import java.io.ObjectInput;
059: import java.io.IOException;
060:
061: import java.sql.ResultSet;
062: import java.sql.SQLException;
063:
064: import java.text.DateFormat;
065: import java.text.ParseException;
066:
067: /**
068: * This contains an instance of a SQL Time
069: * Our current implementation doesn't implement time precision so the fractional
070: * seconds portion of the time is always 0. The default when no time precision
071: * is specified is 0 fractional seconds. A SQL Time without timezone information
072: * is assumed to be in the local time zone. The local time is stored as is
073: * and doesn't change if the timezone changes. This is in conformance with the
074: * SQL99 standard. The SQL92 standard indicates that the time is in GMT and
075: * changes with the timezone. The SQL99 standard clarifies this to allow time without
076: * timezoned to be stored as the local time.
077: * <p>
078: * Time is stored as two ints. The first int represents hour, minute, second
079: * and the second represents fractional seconds (currently 0 since we don't support
080: * time precision)
081: * encodedTime = -1 indicates null
082: *
083: * PERFORMANCE OPTIMIZATION:
084: * The java.sql.Time object is only instantiated on demand for performance
085: * reasons.
086: */
087:
088: public final class SQLTime extends DataType implements
089: DateTimeDataValue {
090:
091: private int encodedTime;
092: private int encodedTimeFraction; //currently always 0 since we don't
093: //support time precision
094:
095: // The cached value.toString()
096: private String valueString;
097:
098: /*
099: ** DataValueDescriptor interface
100: ** (mostly implemented in DataType)
101: */
102:
103: private static final int BASE_MEMORY_USAGE = ClassSize
104: .estimateBaseFromCatalog(SQLTime.class);
105:
106: public int estimateMemoryUsage() {
107: return BASE_MEMORY_USAGE
108: + ClassSize.estimateMemoryUsage(valueString);
109: } // end of estimateMemoryUsage
110:
111: public String getString() {
112: if (!isNull()) {
113: if (valueString == null) {
114: valueString = encodedTimeToString(encodedTime);
115: }
116: return valueString;
117: } else {
118: if (SanityManager.DEBUG) {
119: if (valueString != null) {
120: SanityManager
121: .THROWASSERT("valueString expected to be null, not "
122: + valueString);
123: }
124: }
125: return null;
126: }
127: }
128:
129: int getEncodedTime() {
130: return encodedTime;
131: }
132:
133: /**
134: * Convert a SQL TIME to a JDBC java.sql.Timestamp.
135: *
136: * Behaviour is to set the date portion of the Timestamp
137: * to the actual current date, which may not match the
138: * SQL CURRENT DATE, which remains fixed for the lifetime
139: * of a SQL statement. JDBC drivers (especially network client drivers)
140: * could not be expected to fetch the CURRENT_DATE SQL value
141: * on every query that involved a TIME value, so the current
142: * date as seen by the JDBC client was picked as the logical behaviour.
143: * See DERBY-1811.
144: */
145: public Timestamp getTimestamp(Calendar cal) {
146: if (isNull())
147: return null;
148: else {
149: if (cal == null) {
150: // Calendar initialized to current date and time.
151: cal = new GregorianCalendar();
152: } else {
153: cal.clear();
154: // Set Calendar to current date and time.
155: cal.setTime(new Date(System.currentTimeMillis()));
156: }
157:
158: cal.set(Calendar.HOUR_OF_DAY, getHour(encodedTime));
159: cal.set(Calendar.MINUTE, getMinute(encodedTime));
160: cal.set(Calendar.SECOND, getSecond(encodedTime));
161:
162: // Derby's resolution for the TIME type is only seconds.
163: cal.set(Calendar.MILLISECOND, 0);
164:
165: return new Timestamp(cal.getTime().getTime());
166: }
167: }
168:
169: public Object getObject() {
170: return getTime((Calendar) null);
171: }
172:
173: public int getLength() {
174: return 8;
175: }
176:
177: /* this is for DataType's error generator */
178: public String getTypeName() {
179: return "TIME";
180: }
181:
182: /*
183: * Storable interface, implies Externalizable, TypedFormat
184: */
185:
186: /**
187: Return my format identifier.
188:
189: @see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId
190: */
191: public int getTypeFormatId() {
192: return StoredFormatIds.SQL_TIME_ID;
193: }
194:
195: /**
196: @exception IOException error writing data
197:
198: */
199: public void writeExternal(ObjectOutput out) throws IOException {
200:
201: if (SanityManager.DEBUG)
202: SanityManager
203: .ASSERT(!isNull(),
204: "writeExternal() is not supposed to be called for null values.");
205:
206: out.writeInt(encodedTime);
207: out.writeInt(encodedTimeFraction);
208: }
209:
210: /**
211: * @see java.io.Externalizable#readExternal
212: *
213: * @exception IOException Thrown on error reading the object
214: */
215: public void readExternal(ObjectInput in) throws IOException {
216: encodedTime = in.readInt();
217: encodedTimeFraction = in.readInt();
218: // reset cached values
219: valueString = null;
220: }
221:
222: public void readExternalFromArray(ArrayInputStream in)
223: throws IOException {
224: encodedTime = in.readInt();
225: encodedTimeFraction = in.readInt();
226: // reset cached values
227: valueString = null;
228: }
229:
230: /*
231: * DataValueDescriptor interface
232: */
233:
234: /** @see DataValueDescriptor#getClone */
235: public DataValueDescriptor getClone() {
236: // Call constructor with all of our info
237: return new SQLTime(encodedTime, encodedTimeFraction);
238: }
239:
240: /**
241: * @see DataValueDescriptor#getNewNull
242: */
243: public DataValueDescriptor getNewNull() {
244: return new SQLTime();
245: }
246:
247: /**
248: * @see org.apache.derby.iapi.services.io.Storable#restoreToNull
249: *
250: */
251:
252: public void restoreToNull() {
253: encodedTime = -1;
254: encodedTimeFraction = 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,
270: int colNumber, boolean isNullable) throws SQLException,
271: StandardException {
272: restoreToNull();
273: encodedTime = computeEncodedTime(resultSet.getTime(colNumber));
274: //need to set encodedTimeFraction when we implement time precision
275: }
276:
277: /**
278: * Orderable interface
279: *
280: *
281: * @see org.apache.derby.iapi.types.Orderable
282: *
283: * @exception StandardException thrown on failure
284: */
285: public int compare(DataValueDescriptor other)
286: throws StandardException {
287: /* Use compare method from dominant type, negating result
288: * to reflect flipping of sides.
289: */
290: if (typePrecedence() < other.typePrecedence()) {
291: return -(other.compare(this ));
292: }
293:
294: boolean this Null, otherNull;
295:
296: this Null = this .isNull();
297: otherNull = other.isNull();
298:
299: /*
300: * thisNull otherNull return
301: * T T 0 (this == other)
302: * F T -1 (this < other)
303: * T F 1 (this > other)
304: */
305: if (this Null || otherNull) {
306: if (!this Null) // otherNull must be true
307: return -1;
308: if (!otherNull) // thisNull must be true
309: return 1;
310: return 0;
311: }
312:
313: /*
314: Neither are null compare them
315: */
316:
317: int comparison;
318:
319: /* get the comparison time values */
320: int otherEncodedTime = 0;
321:
322: /* if the argument is another Time look up the value
323: * we have already taken care of Null
324: * ignoring encodedTimeFraction for now since it is always 0
325: * - need to change this when we support TIME(precision)
326: */
327: if (other instanceof SQLTime) {
328: otherEncodedTime = ((SQLTime) other).encodedTime;
329: } else {
330: /* O.K. have to do it the hard way and calculate the numeric value
331: * from the value
332: */
333: otherEncodedTime = computeEncodedTime(other
334: .getTime((Calendar) null));
335: }
336: if (encodedTime < otherEncodedTime)
337: comparison = -1;
338: else if (encodedTime > otherEncodedTime)
339: comparison = 1;
340: else
341: comparison = 0;
342:
343: return comparison;
344: }
345:
346: /**
347: @exception StandardException thrown on error
348: */
349: public boolean compare(int op, DataValueDescriptor other,
350: boolean orderedNulls, boolean unknownRV)
351: throws StandardException {
352: if (!orderedNulls) // nulls are unordered
353: {
354: if (this .isNull() || other.isNull())
355: return unknownRV;
356: }
357:
358: /* Do the comparison */
359: return super .compare(op, other, orderedNulls, unknownRV);
360: }
361:
362: /*
363: ** Class interface
364: */
365:
366: /*
367: ** Constructors
368: */
369:
370: /** no-arg constructor required by Formattable */
371: public SQLTime() {
372: encodedTime = -1; //null value
373: }
374:
375: public SQLTime(Time value) throws StandardException {
376: parseTime(value);
377: }
378:
379: private void parseTime(java.util.Date value)
380: throws StandardException {
381: encodedTime = computeEncodedTime(value);
382: }
383:
384: private SQLTime(int encodedTime, int encodedTimeFraction) {
385: this .encodedTime = encodedTime;
386: this .encodedTimeFraction = encodedTimeFraction;
387: }
388:
389: /**
390: * Construct a time from a string. The allowed time formats are:
391: *<ol>
392: *<li>old ISO and IBM European standard: hh.mm[.ss]
393: *<li>IBM USA standard: hh[:mm] {AM | PM}
394: *<li>JIS & current ISO: hh:mm[:ss]
395: *</ol>
396: *
397: * @exception Standard exception if the syntax is invalid or the value is out of range.
398: */
399: public SQLTime(String timeStr, boolean isJdbcEscape,
400: LocaleFinder localeFinder) throws StandardException {
401: parseTime(timeStr, isJdbcEscape, localeFinder, (Calendar) null);
402: }
403:
404: /**
405: * Construct a time from a string. The allowed time formats are:
406: *<ol>
407: *<li>old ISO and IBM European standard: hh.mm[.ss]
408: *<li>IBM USA standard: hh[:mm] {AM | PM}
409: *<li>JIS & current ISO: hh:mm[:ss]
410: *</ol>
411: *
412: * @exception Standard exception if the syntax is invalid or the value is out of range.
413: */
414: public SQLTime(String timeStr, boolean isJdbcEscape,
415: LocaleFinder localeFinder, Calendar cal)
416: throws StandardException {
417: parseTime(timeStr, isJdbcEscape, localeFinder, cal);
418: }
419:
420: private static final char IBM_EUR_SEPARATOR = '.';
421: private static final char[] IBM_EUR_SEPARATOR_OR_END = {
422: IBM_EUR_SEPARATOR, (char) 0 };
423: static final char JIS_SEPARATOR = ':';
424: private static final char[] US_OR_JIS_MINUTE_END = { JIS_SEPARATOR,
425: ' ', (char) 0 };
426: private static final char[] ANY_SEPARATOR = { '.', ':', ' ' };
427: private static final String[] AM_PM = { "AM", "PM" };
428: private static final char[] END_OF_STRING = { (char) 0 };
429:
430: private void parseTime(String timeStr, boolean isJdbcEscape,
431: LocaleFinder localeFinder, Calendar cal)
432: throws StandardException {
433: boolean validSyntax = true;
434: DateTimeParser parser = new DateTimeParser(timeStr);
435: StandardException thrownSE = null;
436: int hour = 0;
437: int minute = 0;
438: int second = 0;
439: int amPm = -1;
440: try {
441: if (parser.nextSeparator() == SQLTimestamp.DATE_SEPARATOR) {
442: encodedTime = SQLTimestamp.parseDateOrTimestamp(parser,
443: true)[1];
444: valueString = parser.getTrimmedString();
445: return;
446: }
447: hour = parser.parseInt(2, true, ANY_SEPARATOR, false);
448: switch (parser.getCurrentSeparator()) {
449: case IBM_EUR_SEPARATOR:
450: if (isJdbcEscape) {
451: validSyntax = false;
452: break;
453: }
454: minute = parser.parseInt(2, false,
455: IBM_EUR_SEPARATOR_OR_END, false);
456: if (parser.getCurrentSeparator() == IBM_EUR_SEPARATOR)
457: second = parser.parseInt(2, false, END_OF_STRING,
458: false);
459: break;
460:
461: case ':':
462: // IBM USA or JIS (new ISO)
463: minute = parser.parseInt(2, false,
464: US_OR_JIS_MINUTE_END, false);
465: switch (parser.getCurrentSeparator()) {
466: case ' ':
467: // IBM USA with minutes
468: if (isJdbcEscape) {
469: validSyntax = false;
470: break;
471: }
472: amPm = parser.parseChoice(AM_PM);
473: parser.checkEnd();
474: break;
475:
476: case JIS_SEPARATOR:
477: second = parser.parseInt(2, false, END_OF_STRING,
478: false);
479: break;
480:
481: // default is end of string, meaning that the seconds part is zero.
482: }
483: break;
484:
485: case ' ':
486: // IBM USA with minutes omitted
487: if (isJdbcEscape) {
488: validSyntax = false;
489: break;
490: }
491: amPm = parser.parseChoice(AM_PM);
492: break;
493:
494: default:
495: validSyntax = false;
496: }
497: } catch (StandardException se) {
498: validSyntax = false;
499: thrownSE = se;
500: }
501: if (validSyntax) {
502: if (amPm == 0) // AM
503: {
504: if (hour == 12) {
505: if (minute == 0 && second == 0)
506: hour = 24;
507: else
508: hour = 0;
509: } else if (hour > 12)
510: throw StandardException
511: .newException(SQLState.LANG_DATE_RANGE_EXCEPTION);
512: } else if (amPm == 1) // PM
513: {
514: if (hour < 12)
515: hour += 12;
516: else if (hour > 12)
517: throw StandardException
518: .newException(SQLState.LANG_DATE_RANGE_EXCEPTION);
519: }
520: valueString = parser.checkEnd();
521: encodedTime = computeEncodedTime(hour, minute, second);
522: } else {
523: // See if it is a localized time or timestamp
524: timeStr = StringUtil.trimTrailing(timeStr);
525: DateFormat timeFormat = null;
526: if (localeFinder == null)
527: timeFormat = DateFormat.getTimeInstance();
528: else if (cal == null)
529: timeFormat = localeFinder.getTimeFormat();
530: else
531: timeFormat = (DateFormat) localeFinder.getTimeFormat()
532: .clone();
533: if (cal != null)
534: timeFormat.setCalendar(cal);
535: try {
536: encodedTime = computeEncodedTime(timeFormat
537: .parse(timeStr), cal);
538: } catch (ParseException pe) {
539: // Maybe it is a localized timestamp
540: try {
541: encodedTime = SQLTimestamp.parseLocalTimestamp(
542: timeStr, localeFinder, cal)[1];
543: } catch (ParseException pe2) {
544: if (thrownSE != null)
545: throw thrownSE;
546: throw StandardException
547: .newException(SQLState.LANG_DATE_SYNTAX_EXCEPTION);
548: }
549: }
550: valueString = timeStr;
551: }
552: } // end of parseTime
553:
554: /**
555: * Set the value from a correctly typed Time object.
556: * @throws StandardException
557: */
558: void setObject(Object theValue) throws StandardException {
559: setValue((Time) theValue);
560: }
561:
562: protected void setFrom(DataValueDescriptor theValue)
563: throws StandardException {
564:
565: if (theValue instanceof SQLTime) {
566: restoreToNull();
567:
568: SQLTime tvst = (SQLTime) theValue;
569: encodedTime = tvst.encodedTime;
570: encodedTimeFraction = tvst.encodedTimeFraction;
571:
572: } else {
573: Calendar cal = new GregorianCalendar();
574: setValue(theValue.getTime(cal), cal);
575: }
576: }
577:
578: /**
579: @see DateTimeDataValue#setValue
580:
581: @exception StandardException thrown on failure.
582: */
583: public void setValue(Time value, Calendar cal)
584: throws StandardException {
585: restoreToNull();
586: encodedTime = computeEncodedTime(value, cal);
587: }
588:
589: /**
590: @see DateTimeDataValue#setValue
591:
592: @exception StandardException thrown on failure.
593: */
594: public void setValue(Timestamp value, Calendar cal)
595: throws StandardException {
596: restoreToNull();
597: encodedTime = computeEncodedTime(value, cal);
598: }
599:
600: public void setValue(String theValue) throws StandardException {
601: restoreToNull();
602: if (theValue != null) {
603: DatabaseContext databaseContext = (DatabaseContext) ContextService
604: .getContext(DatabaseContext.CONTEXT_ID);
605: parseTime(theValue, false, (databaseContext == null) ? null
606: : databaseContext.getDatabase(), (Calendar) null);
607: }
608: }
609:
610: /*
611: ** SQL Operators
612: */
613:
614: /**
615: * @see DateTimeDataValue#getYear
616: *
617: * @exception StandardException Thrown on error
618: */
619: public NumberDataValue getYear(NumberDataValue result)
620: throws StandardException {
621: if (SanityManager.DEBUG) {
622: SanityManager.ASSERT(!isNull(), "getYear called on null.");
623: }
624: throw StandardException.newException(
625: SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, "getYear",
626: "Time");
627: }
628:
629: /**
630: * @see DateTimeDataValue#getMonth
631: *
632: * @exception StandardException Thrown on error
633: */
634: public NumberDataValue getMonth(NumberDataValue result)
635: throws StandardException {
636: if (SanityManager.DEBUG) {
637: SanityManager.ASSERT(!isNull(), "getMonth called on null.");
638: }
639: throw StandardException.newException(
640: SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, "getMonth",
641: "Time");
642: }
643:
644: /**
645: * @see DateTimeDataValue#getDate
646: *
647: * @exception StandardException Thrown on error
648: */
649: public NumberDataValue getDate(NumberDataValue result)
650: throws StandardException {
651: if (SanityManager.DEBUG) {
652: SanityManager.ASSERT(!isNull(), "getDate called on null.");
653: }
654: throw StandardException.newException(
655: SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, "getDate",
656: "Time");
657: }
658:
659: /**
660: * @see DateTimeDataValue#getHours
661: *
662: * @exception StandardException Thrown on error
663: */
664: public NumberDataValue getHours(NumberDataValue result)
665: throws StandardException {
666: if (SanityManager.DEBUG) {
667: SanityManager.ASSERT(!isNull(), "getHours called on null");
668: }
669: return SQLDate.setSource(getHour(encodedTime), result);
670: }
671:
672: /**
673: * @see DateTimeDataValue#getMinutes
674: *
675: * @exception StandardException Thrown on error
676: */
677: public NumberDataValue getMinutes(NumberDataValue result)
678: throws StandardException {
679: if (SanityManager.DEBUG) {
680: SanityManager
681: .ASSERT(!isNull(), "getMinutes called on null");
682: }
683: return SQLDate.setSource(getMinute(encodedTime), result);
684: }
685:
686: /**
687: * @see DateTimeDataValue#getSeconds
688: *
689: * @exception StandardException Thrown on error
690: */
691: public NumberDataValue getSeconds(NumberDataValue result)
692: throws StandardException {
693: if (SanityManager.DEBUG) {
694: SanityManager
695: .ASSERT(!isNull(), "getMinutes called on null");
696: }
697: return SQLDate.setSource(getSecond(encodedTime), result);
698: }
699:
700: /*
701: ** String display of value
702: */
703:
704: public String toString() {
705: if (isNull()) {
706: return "NULL";
707: } else {
708: return getTime((Calendar) null).toString();
709: }
710: }
711:
712: /*
713: * Hash code
714: */
715: public int hashCode() {
716: if (isNull()) {
717: return 0;
718: }
719: // add 1 since 0 represents a valid time
720: return encodedTime + encodedTimeFraction + 1;
721:
722: }
723:
724: /** @see DataValueDescriptor#typePrecedence */
725: public int typePrecedence() {
726: return TypeId.TIME_PRECEDENCE;
727: }
728:
729: /**
730: * Check if the value is null.
731: *
732: * @return Whether or not value is logically null.
733: */
734: public final boolean isNull() {
735: return (encodedTime == -1);
736: }
737:
738: /**
739: * Get the time value
740: * Since this is a JDBC object we use the JDBC definition
741: * we use the JDBC definition, see JDBC API Tutorial and Reference
742: * section 47.3.12
743: * Date is set to Jan. 1, 1970
744: *
745: * @return The localized time value.
746: */
747: public Time getTime(java.util.Calendar cal) {
748: if (isNull())
749: return null;
750:
751: if (cal == null)
752: cal = new GregorianCalendar();
753:
754: cal.clear();
755: cal.set(Calendar.YEAR, 1970);
756: cal.set(Calendar.MONTH, Calendar.JANUARY);
757: cal.set(Calendar.DATE, 1);
758: cal.set(Calendar.HOUR_OF_DAY, getHour(encodedTime));
759: cal.set(Calendar.MINUTE, getMinute(encodedTime));
760: cal.set(Calendar.SECOND, getSecond(encodedTime));
761: cal.set(Calendar.MILLISECOND, 0); //only 0 fractional seconds currently
762: return new Time(cal.getTime().getTime());
763: }
764:
765: /**
766: * Get the encoded hour value (may be different than hour value for
767: * current timezone if value encoded in a different timezone)
768: *
769: * @return hour value
770: */
771: protected static int getHour(int encodedTime) {
772: return (encodedTime >>> 16) & 0xff;
773: }
774:
775: /**
776: * Get the encoded minute value (may be different than the minute value for
777: * current timezone if value encoded in a different timezone)
778: *
779: * @return minute value
780: */
781: protected static int getMinute(int encodedTime) {
782: return ((encodedTime >>> 8) & 0xff);
783: }
784:
785: /**
786: * Get the encoded second value (may be different than the second value for
787: * current timezone if value encoded in a different timezone)
788: *
789: * @return second value
790: */
791: protected static int getSecond(int encodedTime) {
792: return (encodedTime & 0xff);
793: }
794:
795: /**
796: * Calculate the encoded time from a Calendar object
797: * encoded time is hour << 16 + min << 8 + sec
798: * this function is also used by SQLTimestamp
799: *
800: * @param cal calendar with time set
801: * @return encoded time
802: *
803: * @exception StandardException if the time is not in the DB2 range
804: */
805: static int computeEncodedTime(Calendar cal)
806: throws StandardException {
807: return computeEncodedTime(cal.get(Calendar.HOUR_OF_DAY), cal
808: .get(Calendar.MINUTE), cal.get(Calendar.SECOND));
809: }
810:
811: static int computeEncodedTime(int hour, int minute, int second)
812: throws StandardException {
813: if (hour == 24) {
814: if (minute != 0 || second != 0)
815: throw StandardException
816: .newException(SQLState.LANG_DATE_RANGE_EXCEPTION);
817: } else if (hour < 0 || hour > 23 || minute < 0 || minute > 59
818: || second < 0 || second > 59)
819: throw StandardException
820: .newException(SQLState.LANG_DATE_RANGE_EXCEPTION);
821:
822: return (hour << 16) + (minute << 8) + second;
823: }
824:
825: /**
826: * Convert a time to a JDBC escape format string
827: *
828: * @param hour
829: * @param minute
830: * @param second
831: * @param sb The resulting string is appended to this StringBuffer
832: */
833: static void timeToString(int hour, int minute, int second,
834: StringBuffer sb) {
835: String hourStr = Integer.toString(hour);
836: String minStr = Integer.toString(minute);
837: String secondStr = Integer.toString(second);
838: if (hourStr.length() == 1)
839: sb.append("0");
840: sb.append(hourStr);
841: sb.append(JIS_SEPARATOR);
842: if (minStr.length() == 1)
843: sb.append("0");
844: sb.append(minStr);
845: sb.append(JIS_SEPARATOR);
846: if (secondStr.length() == 1)
847: sb.append("0");
848: sb.append(secondStr);
849: } // end of timeToString
850:
851: /**
852: * Get the String version from the encodedTime.
853: *
854: * @return string value.
855: */
856: protected static String encodedTimeToString(int encodedTime) {
857: StringBuffer vstr = new StringBuffer();
858: timeToString(SQLTime.getHour(encodedTime), SQLTime
859: .getMinute(encodedTime),
860: SQLTime.getSecond(encodedTime), vstr);
861: return vstr.toString();
862: }
863:
864: // International Support
865:
866: /**
867: * International version of getString(). Overrides getNationalString
868: * in DataType for date, time, and timestamp.
869: *
870: * @exception StandardException Thrown on error
871: */
872: protected String getNationalString(LocaleFinder localeFinder)
873: throws StandardException {
874: if (isNull()) {
875: return getString();
876: }
877:
878: return localeFinder.getTimeFormat().format(
879: getTime((Calendar) null));
880: }
881:
882: /**
883: * Compute encoded time value
884: * Time is represented by hour << 16 + minute << 8 + seconds
885: */
886: private int computeEncodedTime(java.util.Date value)
887: throws StandardException {
888: return computeEncodedTime(value, (Calendar) null);
889: }
890:
891: static int computeEncodedTime(java.util.Date value,
892: Calendar currentCal) throws StandardException {
893: if (value == null)
894: return -1;
895: if (currentCal == null)
896: currentCal = new GregorianCalendar();
897: currentCal.setTime(value);
898: return computeEncodedTime(currentCal);
899: }
900:
901: /** Adding this method to ensure that super class' setInto method doesn't get called
902: * that leads to the violation of JDBC spec( untyped nulls ) when batching is turned on.
903: */
904: public void setInto(PreparedStatement ps, int position)
905: throws SQLException, StandardException {
906:
907: ps.setTime(position, getTime((Calendar) null));
908: }
909:
910: /**
911: * Add a number of intervals to a datetime value. Implements the JDBC escape TIMESTAMPADD function.
912: *
913: * @param intervalType One of FRAC_SECOND_INTERVAL, SECOND_INTERVAL, MINUTE_INTERVAL, HOUR_INTERVAL,
914: * DAY_INTERVAL, WEEK_INTERVAL, MONTH_INTERVAL, QUARTER_INTERVAL, or YEAR_INTERVAL
915: * @param intervalCount The number of intervals to add
916: * @param currentDate Used to convert time to timestamp
917: * @param resultHolder If non-null a DateTimeDataValue that can be used to hold the result. If null then
918: * generate a new holder
919: *
920: * @return startTime + intervalCount intervals, as a timestamp
921: *
922: * @exception StandardException
923: */
924: public DateTimeDataValue timestampAdd(int intervalType,
925: NumberDataValue intervalCount, java.sql.Date currentDate,
926: DateTimeDataValue resultHolder) throws StandardException {
927: return toTimestamp(currentDate).timestampAdd(intervalType,
928: intervalCount, currentDate, resultHolder);
929: }
930:
931: private SQLTimestamp toTimestamp(java.sql.Date currentDate)
932: throws StandardException {
933: return new SQLTimestamp(SQLDate.computeEncodedDate(currentDate,
934: (Calendar) null), getEncodedTime(), 0 /* nanoseconds */);
935: }
936:
937: /**
938: * Finds the difference between two datetime values as a number of intervals. Implements the JDBC
939: * TIMESTAMPDIFF escape function.
940: *
941: * @param intervalType One of FRAC_SECOND_INTERVAL, SECOND_INTERVAL, MINUTE_INTERVAL, HOUR_INTERVAL,
942: * DAY_INTERVAL, WEEK_INTERVAL, MONTH_INTERVAL, QUARTER_INTERVAL, or YEAR_INTERVAL
943: * @param time1
944: * @param currentDate Used to convert time to timestamp
945: * @param resultHolder If non-null a NumberDataValue that can be used to hold the result. If null then
946: * generate a new holder
947: *
948: * @return the number of intervals by which this datetime is greater than time1
949: *
950: * @exception StandardException
951: */
952: public NumberDataValue timestampDiff(int intervalType,
953: DateTimeDataValue time1, java.sql.Date currentDate,
954: NumberDataValue resultHolder) throws StandardException {
955: return toTimestamp(currentDate).timestampDiff(intervalType,
956: time1, currentDate, resultHolder);
957: }
958: }
|