--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.iapi.types.SQLTime\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.iapi.types;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.services.io.ArrayInputStream;\r
+\r
+import org.apache.derby.iapi.services.context.ContextService;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.services.io.StoredFormatIds;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.db.DatabaseContext;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.TypeId;\r
+\r
+import org.apache.derby.iapi.types.DateTimeDataValue;\r
+import org.apache.derby.iapi.types.NumberDataValue;\r
+\r
+import org.apache.derby.iapi.types.DataType;\r
+import org.apache.derby.iapi.services.i18n.LocaleFinder;\r
+import org.apache.derby.iapi.services.cache.ClassSize;\r
+import org.apache.derby.iapi.util.StringUtil;\r
+\r
+import java.sql.Date;\r
+import java.sql.Time;\r
+import java.sql.Timestamp;\r
+import java.sql.Types;\r
+import java.sql.PreparedStatement;\r
+\r
+import java.util.Calendar;\r
+import java.util.GregorianCalendar;\r
+\r
+import java.io.ObjectOutput;\r
+import java.io.ObjectInput;\r
+import java.io.IOException;\r
+\r
+import java.sql.ResultSet;\r
+import java.sql.SQLException;\r
+\r
+import java.text.DateFormat;\r
+import java.text.ParseException;\r
+\r
+/**\r
+ * This contains an instance of a SQL Time\r
+ * Our current implementation doesn't implement time precision so the fractional\r
+ * seconds portion of the time is always 0. The default when no time precision\r
+ * is specified is 0 fractional seconds. A SQL Time without timezone information\r
+ * is assumed to be in the local time zone. The local time is stored as is\r
+ * and doesn't change if the timezone changes. This is in conformance with the\r
+ * SQL99 standard. The SQL92 standard indicates that the time is in GMT and\r
+ * changes with the timezone. The SQL99 standard clarifies this to allow time without\r
+ * timezoned to be stored as the local time.\r
+ * <p>\r
+ * Time is stored as two ints. The first int represents hour, minute, second \r
+ * and the second represents fractional seconds (currently 0 since we don't support\r
+ * time precision)\r
+ * encodedTime = -1 indicates null\r
+ *\r
+ * PERFORMANCE OPTIMIZATION:\r
+ * The java.sql.Time object is only instantiated on demand for performance\r
+ * reasons.\r
+ */\r
+\r
+public final class SQLTime extends DataType\r
+ implements DateTimeDataValue\r
+{\r
+\r
+ private int encodedTime;\r
+ private int encodedTimeFraction; //currently always 0 since we don't\r
+ //support time precision\r
+\r
+ // The cached value.toString()\r
+ private String valueString;\r
+\r
+ /*\r
+ ** DataValueDescriptor interface\r
+ ** (mostly implemented in DataType)\r
+ */\r
+\r
+ private static final int BASE_MEMORY_USAGE = ClassSize.estimateBaseFromCatalog( SQLTime.class);\r
+\r
+ public int estimateMemoryUsage()\r
+ {\r
+ return BASE_MEMORY_USAGE + ClassSize.estimateMemoryUsage( valueString);\r
+ } // end of estimateMemoryUsage\r
+\r
+ public String getString()\r
+ {\r
+ if (!isNull())\r
+ {\r
+ if (valueString == null)\r
+ {\r
+ valueString = encodedTimeToString(encodedTime);\r
+ }\r
+ return valueString;\r
+ }\r
+ else\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (valueString != null)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "valueString expected to be null, not " +\r
+ valueString);\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+ }\r
+\r
+ int getEncodedTime()\r
+ {\r
+ return encodedTime;\r
+ }\r
+\r
+ /**\r
+ * Convert a SQL TIME to a JDBC java.sql.Timestamp.\r
+ * \r
+ * Behaviour is to set the date portion of the Timestamp\r
+ * to the actual current date, which may not match the\r
+ * SQL CURRENT DATE, which remains fixed for the lifetime\r
+ * of a SQL statement. JDBC drivers (especially network client drivers)\r
+ * could not be expected to fetch the CURRENT_DATE SQL value\r
+ * on every query that involved a TIME value, so the current\r
+ * date as seen by the JDBC client was picked as the logical behaviour.\r
+ * See DERBY-1811.\r
+ */\r
+ public Timestamp getTimestamp( Calendar cal)\r
+ {\r
+ if (isNull())\r
+ return null;\r
+ else\r
+ {\r
+ if( cal == null)\r
+ {\r
+ // Calendar initialized to current date and time.\r
+ cal = new GregorianCalendar(); \r
+ }\r
+ else\r
+ {\r
+ cal.clear();\r
+ // Set Calendar to current date and time\r
+ // to pick up the current date. Time portion\r
+ // will be overridden by this value's time.\r
+ cal.setTimeInMillis(System.currentTimeMillis());\r
+ }\r
+ \r
+ SQLTime.setTimeInCalendar(cal, encodedTime);\r
+ \r
+ // Derby's resolution for the TIME type is only seconds.\r
+ cal.set(Calendar.MILLISECOND, 0);\r
+ \r
+ return new Timestamp(cal.getTimeInMillis());\r
+ }\r
+ }\r
+\r
+ public Object getObject()\r
+ {\r
+ return getTime( (Calendar) null);\r
+ }\r
+ \r
+ public int getLength()\r
+ {\r
+ return 8;\r
+ }\r
+\r
+ /* this is for DataType's error generator */\r
+ public String getTypeName()\r
+ {\r
+ return "TIME";\r
+ }\r
+\r
+\r
+ /*\r
+ * Storable interface, implies Externalizable, TypedFormat\r
+ */\r
+\r
+ /**\r
+ Return my format identifier.\r
+\r
+ @see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId\r
+ */\r
+ public int getTypeFormatId() {\r
+ return StoredFormatIds.SQL_TIME_ID;\r
+ }\r
+\r
+ /** \r
+ @exception IOException error writing data\r
+\r
+ */\r
+ public void writeExternal(ObjectOutput out) throws IOException {\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isNull(), "writeExternal() is not supposed to be called for null values.");\r
+\r
+ out.writeInt(encodedTime);\r
+ out.writeInt(encodedTimeFraction);\r
+ }\r
+\r
+ /**\r
+ * @see java.io.Externalizable#readExternal\r
+ *\r
+ * @exception IOException Thrown on error reading the object\r
+ */\r
+ public void readExternal(ObjectInput in) throws IOException\r
+ {\r
+ encodedTime = in.readInt();\r
+ encodedTimeFraction = in.readInt();\r
+ // reset cached values\r
+ valueString = null;\r
+ }\r
+ public void readExternalFromArray(ArrayInputStream in) throws IOException\r
+ {\r
+ encodedTime = in.readInt();\r
+ encodedTimeFraction = in.readInt();\r
+ // reset cached values\r
+ valueString = null;\r
+ }\r
+\r
+ /*\r
+ * DataValueDescriptor interface\r
+ */\r
+\r
+ /** @see DataValueDescriptor#getClone */\r
+ public DataValueDescriptor getClone()\r
+ {\r
+ // Call constructor with all of our info\r
+ return new SQLTime(encodedTime, encodedTimeFraction);\r
+ }\r
+\r
+ /**\r
+ * @see DataValueDescriptor#getNewNull\r
+ */\r
+ public DataValueDescriptor getNewNull()\r
+ {\r
+ return new SQLTime();\r
+ }\r
+ /**\r
+ * @see org.apache.derby.iapi.services.io.Storable#restoreToNull\r
+ *\r
+ */\r
+\r
+ public void restoreToNull()\r
+ {\r
+ encodedTime = -1;\r
+ encodedTimeFraction = 0;\r
+\r
+ // clear cached valueString\r
+ valueString = null;\r
+ }\r
+\r
+ /*\r
+ * DataValueDescriptor interface\r
+ */\r
+\r
+ /** \r
+ * @see DataValueDescriptor#setValueFromResultSet \r
+ *\r
+ * @exception SQLException Thrown on error\r
+ */\r
+ public void setValueFromResultSet(ResultSet resultSet, int colNumber,\r
+ boolean isNullable)\r
+ throws SQLException, StandardException\r
+ {\r
+ restoreToNull();\r
+ encodedTime = computeEncodedTime(resultSet.getTime(colNumber));\r
+ //need to set encodedTimeFraction when we implement time precision\r
+ }\r
+\r
+ /**\r
+ * Orderable interface\r
+ *\r
+ *\r
+ * @see org.apache.derby.iapi.types.Orderable\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ public int compare(DataValueDescriptor other)\r
+ throws StandardException\r
+ {\r
+ /* Use compare method from dominant type, negating result\r
+ * to reflect flipping of sides.\r
+ */\r
+ if (typePrecedence() < other.typePrecedence())\r
+ {\r
+ return - (other.compare(this));\r
+ }\r
+\r
+ boolean thisNull, otherNull;\r
+\r
+ thisNull = this.isNull();\r
+ otherNull = other.isNull();\r
+\r
+ /*\r
+ * thisNull otherNull return\r
+ * T T 0 (this == other)\r
+ * F T -1 (this < other)\r
+ * T F 1 (this > other)\r
+ */\r
+ if (thisNull || otherNull)\r
+ {\r
+ if (!thisNull) // otherNull must be true\r
+ return -1;\r
+ if (!otherNull) // thisNull must be true\r
+ return 1;\r
+ return 0;\r
+ }\r
+\r
+ /*\r
+ Neither are null compare them \r
+ */\r
+\r
+ int comparison;\r
+\r
+ /* get the comparison time values */\r
+ int otherEncodedTime = 0;\r
+\r
+ /* if the argument is another Time look up the value\r
+ * we have already taken care of Null\r
+ * ignoring encodedTimeFraction for now since it is always 0\r
+ * - need to change this when we support TIME(precision)\r
+ */\r
+ if (other instanceof SQLTime)\r
+ {\r
+ otherEncodedTime=((SQLTime)other).encodedTime;\r
+ }\r
+ else \r
+ {\r
+ /* O.K. have to do it the hard way and calculate the numeric value\r
+ * from the value\r
+ */\r
+ otherEncodedTime = computeEncodedTime(other.getTime( (Calendar) null));\r
+ }\r
+ if (encodedTime < otherEncodedTime)\r
+ comparison = -1;\r
+ else if (encodedTime > otherEncodedTime)\r
+ comparison = 1;\r
+ else\r
+ comparison = 0;\r
+\r
+ return comparison;\r
+ }\r
+\r
+ /**\r
+ @exception StandardException thrown on error\r
+ */\r
+ public boolean compare(int op,\r
+ DataValueDescriptor other,\r
+ boolean orderedNulls,\r
+ boolean unknownRV)\r
+ throws StandardException\r
+ {\r
+ if (!orderedNulls) // nulls are unordered\r
+ {\r
+ if (this.isNull() || other.isNull())\r
+ return unknownRV;\r
+ }\r
+\r
+ /* Do the comparison */\r
+ return super.compare(op, other, orderedNulls, unknownRV);\r
+ }\r
+\r
+ /*\r
+ ** Class interface\r
+ */\r
+\r
+ /*\r
+ ** Constructors\r
+ */\r
+\r
+ /** no-arg constructor required by Formattable */\r
+ public SQLTime() \r
+ { \r
+ encodedTime = -1; //null value\r
+ }\r
+\r
+ public SQLTime(Time value) throws StandardException\r
+ {\r
+ parseTime(value);\r
+ }\r
+\r
+ private void parseTime(java.util.Date value) throws StandardException\r
+ {\r
+ encodedTime = computeEncodedTime(value);\r
+ }\r
+\r
+ private SQLTime(int encodedTime, int encodedTimeFraction) {\r
+ this.encodedTime = encodedTime;\r
+ this.encodedTimeFraction = encodedTimeFraction;\r
+ }\r
+\r
+\r
+ /**\r
+ * Construct a time from a string. The allowed time formats are:\r
+ *<ol>\r
+ *<li>old ISO and IBM European standard: hh.mm[.ss]\r
+ *<li>IBM USA standard: hh[:mm] {AM | PM}\r
+ *<li>JIS & current ISO: hh:mm[:ss]\r
+ *</ol>\r
+ * \r
+ * @exception Standard exception if the syntax is invalid or the value is out of range.\r
+ */\r
+ public SQLTime( String timeStr, boolean isJdbcEscape, LocaleFinder localeFinder)\r
+ throws StandardException\r
+ {\r
+ parseTime( timeStr, isJdbcEscape, localeFinder, (Calendar) null);\r
+ }\r
+ \r
+ /**\r
+ * Construct a time from a string. The allowed time formats are:\r
+ *<ol>\r
+ *<li>old ISO and IBM European standard: hh.mm[.ss]\r
+ *<li>IBM USA standard: hh[:mm] {AM | PM}\r
+ *<li>JIS & current ISO: hh:mm[:ss]\r
+ *</ol>\r
+ * \r
+ * @exception Standard exception if the syntax is invalid or the value is out of range.\r
+ */\r
+ public SQLTime( String timeStr, boolean isJdbcEscape, LocaleFinder localeFinder, Calendar cal)\r
+ throws StandardException\r
+ {\r
+ parseTime( timeStr, isJdbcEscape, localeFinder, cal);\r
+ }\r
+\r
+ private static final char IBM_EUR_SEPARATOR = '.';\r
+ private static final char[] IBM_EUR_SEPARATOR_OR_END = {IBM_EUR_SEPARATOR, (char) 0};\r
+ static final char JIS_SEPARATOR = ':';\r
+ private static final char[] US_OR_JIS_MINUTE_END = {JIS_SEPARATOR, ' ', (char) 0};\r
+ private static final char[] ANY_SEPARATOR = { '.', ':', ' '};\r
+ private static final String[] AM_PM = {"AM", "PM"};\r
+ private static final char[] END_OF_STRING = {(char) 0};\r
+ \r
+ private void parseTime( String timeStr, boolean isJdbcEscape, LocaleFinder localeFinder, Calendar cal)\r
+ throws StandardException\r
+ {\r
+ boolean validSyntax = true;\r
+ DateTimeParser parser = new DateTimeParser( timeStr);\r
+ StandardException thrownSE = null;\r
+ int hour = 0;\r
+ int minute = 0;\r
+ int second = 0;\r
+ int amPm = -1;\r
+ try\r
+ {\r
+ if( parser.nextSeparator() == SQLTimestamp.DATE_SEPARATOR)\r
+ {\r
+ encodedTime = SQLTimestamp.parseDateOrTimestamp( parser, true)[1];\r
+ valueString = parser.getTrimmedString();\r
+ return;\r
+ }\r
+ hour = parser.parseInt( 2, true, ANY_SEPARATOR, false);\r
+ switch( parser.getCurrentSeparator())\r
+ {\r
+ case IBM_EUR_SEPARATOR:\r
+ if( isJdbcEscape)\r
+ {\r
+ validSyntax = false;\r
+ break;\r
+ }\r
+ minute = parser.parseInt( 2, false, IBM_EUR_SEPARATOR_OR_END, false);\r
+ if( parser.getCurrentSeparator() == IBM_EUR_SEPARATOR)\r
+ second = parser.parseInt( 2, false, END_OF_STRING, false);\r
+ break;\r
+\r
+ case ':':\r
+ // IBM USA or JIS (new ISO)\r
+ minute = parser.parseInt( 2, false, US_OR_JIS_MINUTE_END, false);\r
+ switch( parser.getCurrentSeparator())\r
+ {\r
+ case ' ':\r
+ // IBM USA with minutes\r
+ if( isJdbcEscape)\r
+ {\r
+ validSyntax = false;\r
+ break;\r
+ }\r
+ amPm = parser.parseChoice( AM_PM);\r
+ parser.checkEnd();\r
+ break;\r
+\r
+ case JIS_SEPARATOR:\r
+ second = parser.parseInt( 2, false, END_OF_STRING, false);\r
+ break;\r
+\r
+ // default is end of string, meaning that the seconds part is zero.\r
+ }\r
+ break;\r
+\r
+ case ' ':\r
+ // IBM USA with minutes omitted\r
+ if( isJdbcEscape)\r
+ {\r
+ validSyntax = false;\r
+ break;\r
+ }\r
+ amPm = parser.parseChoice( AM_PM);\r
+ break;\r
+\r
+ default:\r
+ validSyntax = false;\r
+ }\r
+ }\r
+ catch( StandardException se)\r
+ {\r
+ validSyntax = false;\r
+ thrownSE = se;\r
+ }\r
+ if( validSyntax)\r
+ {\r
+ if( amPm == 0) // AM\r
+ {\r
+ if( hour == 12)\r
+ {\r
+ if( minute == 0 && second == 0)\r
+ hour = 24;\r
+ else\r
+ hour = 0;\r
+ }\r
+ else if( hour > 12)\r
+ throw StandardException.newException( SQLState.LANG_DATE_RANGE_EXCEPTION);\r
+ }\r
+ else if( amPm == 1) // PM\r
+ {\r
+ if( hour < 12)\r
+ hour += 12;\r
+ else if( hour > 12)\r
+ throw StandardException.newException( SQLState.LANG_DATE_RANGE_EXCEPTION);\r
+ }\r
+ valueString = parser.checkEnd();\r
+ encodedTime = computeEncodedTime( hour, minute, second);\r
+ }\r
+ else\r
+ {\r
+ // See if it is a localized time or timestamp\r
+ timeStr = StringUtil.trimTrailing( timeStr);\r
+ DateFormat timeFormat = null;\r
+ if(localeFinder == null)\r
+ timeFormat = DateFormat.getTimeInstance();\r
+ else if( cal == null)\r
+ timeFormat = localeFinder.getTimeFormat();\r
+ else\r
+ timeFormat = (DateFormat) localeFinder.getTimeFormat().clone();\r
+ if( cal != null)\r
+ timeFormat.setCalendar( cal);\r
+ try\r
+ {\r
+ encodedTime = computeEncodedTime( timeFormat.parse( timeStr), cal);\r
+ }\r
+ catch( ParseException pe)\r
+ {\r
+ // Maybe it is a localized timestamp\r
+ try\r
+ {\r
+ encodedTime = SQLTimestamp.parseLocalTimestamp( timeStr, localeFinder, cal)[1];\r
+ }\r
+ catch( ParseException pe2)\r
+ {\r
+ if( thrownSE != null)\r
+ throw thrownSE;\r
+ throw StandardException.newException( SQLState.LANG_DATE_SYNTAX_EXCEPTION);\r
+ }\r
+ }\r
+ valueString = timeStr;\r
+ }\r
+ } // end of parseTime\r
+\r
+ /**\r
+ * Set the value from a correctly typed Time object.\r
+ * @throws StandardException \r
+ */\r
+ void setObject(Object theValue) throws StandardException\r
+ {\r
+ setValue((Time) theValue);\r
+ }\r
+ \r
+ protected void setFrom(DataValueDescriptor theValue) throws StandardException {\r
+\r
+ if (theValue instanceof SQLTime) {\r
+ restoreToNull();\r
+\r
+ SQLTime tvst = (SQLTime) theValue;\r
+ encodedTime = tvst.encodedTime;\r
+ encodedTimeFraction = tvst.encodedTimeFraction;\r
+\r
+ }\r
+ else\r
+ {\r
+ Calendar cal = new GregorianCalendar();\r
+ setValue(theValue.getTime( cal), cal);\r
+ }\r
+ }\r
+\r
+ /**\r
+ @see DateTimeDataValue#setValue\r
+\r
+ @exception StandardException thrown on failure.\r
+ */\r
+ public void setValue(Time value, Calendar cal) throws StandardException\r
+ {\r
+ restoreToNull();\r
+ encodedTime = computeEncodedTime(value, cal);\r
+ }\r
+\r
+ /**\r
+ @see DateTimeDataValue#setValue\r
+\r
+ @exception StandardException thrown on failure.\r
+ */\r
+ public void setValue(Timestamp value, Calendar cal) throws StandardException\r
+ {\r
+ restoreToNull();\r
+ encodedTime = computeEncodedTime(value, cal);\r
+ }\r
+\r
+\r
+ public void setValue(String theValue)\r
+ throws StandardException\r
+ {\r
+ restoreToNull();\r
+ if (theValue != null)\r
+ {\r
+ DatabaseContext databaseContext = (DatabaseContext) ContextService.getContext(DatabaseContext.CONTEXT_ID);\r
+ parseTime( theValue,\r
+ false,\r
+ (databaseContext == null) ? null : databaseContext.getDatabase(),\r
+ (Calendar) null);\r
+ }\r
+ }\r
+\r
+ /*\r
+ ** SQL Operators\r
+ */\r
+\r
+ NumberDataValue nullValueInt() {\r
+ return new SQLInteger();\r
+ }\r
+\r
+ /**\r
+ * @see DateTimeDataValue#getYear\r
+ * \r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public NumberDataValue getYear(NumberDataValue result)\r
+ throws StandardException\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, \r
+ "getYear", "Time");\r
+ }\r
+\r
+ /**\r
+ * @see DateTimeDataValue#getMonth\r
+ * \r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public NumberDataValue getMonth(NumberDataValue result)\r
+ throws StandardException\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, \r
+ "getMonth", "Time");\r
+ }\r
+\r
+ /**\r
+ * @see DateTimeDataValue#getDate\r
+ * \r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public NumberDataValue getDate(NumberDataValue result)\r
+ throws StandardException\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, \r
+ "getDate", "Time");\r
+ }\r
+\r
+ /**\r
+ * @see DateTimeDataValue#getHours\r
+ * \r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public NumberDataValue getHours(NumberDataValue result)\r
+ throws StandardException\r
+ {\r
+ if (isNull()) {\r
+ return nullValueInt();\r
+ } else { \r
+ return SQLDate.setSource(getHour(encodedTime), result);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see DateTimeDataValue#getMinutes\r
+ * \r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public NumberDataValue getMinutes(NumberDataValue result)\r
+ throws StandardException\r
+ {\r
+ if (isNull()) {\r
+ return nullValueInt();\r
+ } else { \r
+ return SQLDate.setSource(getMinute(encodedTime), result);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see DateTimeDataValue#getSeconds\r
+ * \r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public NumberDataValue getSeconds(NumberDataValue result)\r
+ throws StandardException\r
+ {\r
+ if (isNull()) {\r
+ return nullValueInt();\r
+ } else { \r
+ return SQLDate.setSource(getSecond(encodedTime), result);\r
+ }\r
+ }\r
+\r
+ /*\r
+ ** String display of value\r
+ */\r
+\r
+ public String toString()\r
+ {\r
+ if (isNull())\r
+ {\r
+ return "NULL";\r
+ }\r
+ else\r
+ {\r
+ return getTime( (Calendar) null).toString();\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Hash code\r
+ */\r
+ public int hashCode()\r
+ {\r
+ if (isNull())\r
+ {\r
+ return 0;\r
+ }\r
+ // add 1 since 0 represents a valid time\r
+ return encodedTime + encodedTimeFraction + 1;\r
+\r
+ }\r
+\r
+ /** @see DataValueDescriptor#typePrecedence */\r
+ public int typePrecedence()\r
+ {\r
+ return TypeId.TIME_PRECEDENCE;\r
+ }\r
+\r
+ /**\r
+ * Check if the value is null. \r
+ *\r
+ * @return Whether or not value is logically null.\r
+ */\r
+ public final boolean isNull()\r
+ {\r
+ return (encodedTime == -1);\r
+ }\r
+\r
+ /**\r
+ * Get the time value \r
+ * Since this is a JDBC object we use the JDBC definition\r
+ * we use the JDBC definition, see JDBC API Tutorial and Reference\r
+ * section 47.3.12\r
+ * Date is set to Jan. 1, 1970\r
+ *\r
+ * @return The localized time value.\r
+ */\r
+ public Time getTime(java.util.Calendar cal)\r
+ {\r
+ if (isNull())\r
+ return null;\r
+ \r
+ // Derby's SQL TIME type only has second resolution\r
+ // so pass in 0 for nano-seconds\r
+ return getTime(cal, encodedTime, 0);\r
+ }\r
+ \r
+ /**\r
+ * Set the time portion of a date-time value into\r
+ * the passed in Calendar object from its encodedTime\r
+ * value. Note that this is only the time down\r
+ * to a resolution of one second. Only the HOUR_OF_DAY,\r
+ * MINUTE and SECOND fields are modified. The remaining\r
+ * state of the Calendar is not modified.\r
+ */\r
+ static void setTimeInCalendar(Calendar cal, int encodedTime)\r
+ {\r
+ cal.set(Calendar.HOUR_OF_DAY, getHour(encodedTime));\r
+ cal.set(Calendar.MINUTE, getMinute(encodedTime));\r
+ cal.set(Calendar.SECOND, getSecond(encodedTime)); \r
+ }\r
+ \r
+ /**\r
+ * Get a java.sql.Time object from an encoded time\r
+ * and nano-second value. As required by JDBC the\r
+ * date component of the Time object will be set to\r
+ * Jan. 1, 1970\r
+ * @param cal Calendar to use for conversion\r
+ * @param encodedTime Derby encoded time value\r
+ * @param nanos number of nano-seconds.\r
+ * @return Valid Time object.\r
+ */\r
+ static Time getTime(Calendar cal, int encodedTime, int nanos)\r
+ {\r
+ if( cal == null)\r
+ cal = new GregorianCalendar();\r
+ \r
+ cal.clear();\r
+ \r
+ cal.set(1970, Calendar.JANUARY, 1);\r
+\r
+ SQLTime.setTimeInCalendar(cal, encodedTime);\r
+\r
+ cal.set(Calendar.MILLISECOND, nanos/1000000);\r
+ \r
+ return new Time(cal.getTimeInMillis());\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Get the encoded hour value (may be different than hour value for\r
+ * current timezone if value encoded in a different timezone)\r
+ *\r
+ * @return hour value\r
+ */\r
+ protected static int getHour(int encodedTime)\r
+ {\r
+ return (encodedTime >>> 16) & 0xff;\r
+ }\r
+ /**\r
+ * Get the encoded minute value (may be different than the minute value for\r
+ * current timezone if value encoded in a different timezone)\r
+ *\r
+ * @return minute value\r
+ */\r
+ protected static int getMinute(int encodedTime)\r
+ {\r
+ return ((encodedTime >>> 8) & 0xff);\r
+ }\r
+ /**\r
+ * Get the encoded second value (may be different than the second value for\r
+ * current timezone if value encoded in a different timezone)\r
+ *\r
+ * @return second value\r
+ */\r
+ protected static int getSecond(int encodedTime)\r
+ {\r
+ return (encodedTime & 0xff);\r
+ }\r
+ /**\r
+ * Calculate the encoded time from a Calendar object\r
+ * encoded time is hour << 16 + min << 8 + sec\r
+ * this function is also used by SQLTimestamp \r
+ *\r
+ * @param cal calendar with time set\r
+ * @return encoded time\r
+ *\r
+ * @exception StandardException if the time is not in the DB2 range\r
+ */\r
+ static int computeEncodedTime(Calendar cal) throws StandardException\r
+ {\r
+ return computeEncodedTime(cal.get(Calendar.HOUR_OF_DAY),\r
+ cal.get(Calendar.MINUTE),\r
+ cal.get(Calendar.SECOND));\r
+ }\r
+\r
+ static int computeEncodedTime( int hour, int minute, int second) throws StandardException\r
+ {\r
+ if( hour == 24)\r
+ {\r
+ if( minute != 0 || second != 0)\r
+ throw StandardException.newException( SQLState.LANG_DATE_RANGE_EXCEPTION);\r
+ }\r
+ else if( hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59)\r
+ throw StandardException.newException( SQLState.LANG_DATE_RANGE_EXCEPTION);\r
+\r
+ return (hour << 16) + (minute << 8) + second;\r
+ }\r
+\r
+ /**\r
+ * Convert a time to a JDBC escape format string\r
+ *\r
+ * @param hour\r
+ * @param minute\r
+ * @param second\r
+ * @param sb The resulting string is appended to this StringBuffer\r
+ */\r
+ static void timeToString( int hour, int minute, int second, StringBuffer sb)\r
+ {\r
+ String hourStr = Integer.toString( hour);\r
+ String minStr = Integer.toString( minute);\r
+ String secondStr = Integer.toString( second);\r
+ if (hourStr.length() == 1)\r
+ sb.append("0");\r
+ sb.append( hourStr);\r
+ sb.append( JIS_SEPARATOR);\r
+ if (minStr.length() == 1)\r
+ sb.append("0");\r
+ sb.append(minStr);\r
+ sb.append( JIS_SEPARATOR);\r
+ if (secondStr.length() == 1)\r
+ sb.append("0");\r
+ sb.append(secondStr);\r
+ } // end of timeToString\r
+\r
+ /**\r
+ * Get the String version from the encodedTime.\r
+ *\r
+ * @return string value.\r
+ */\r
+ protected static String encodedTimeToString(int encodedTime)\r
+ {\r
+ StringBuffer vstr = new StringBuffer();\r
+ timeToString( SQLTime.getHour(encodedTime), SQLTime.getMinute(encodedTime), SQLTime.getSecond(encodedTime), vstr);\r
+ return vstr.toString();\r
+ }\r
+\r
+ // International Support\r
+\r
+ /**\r
+ * International version of getString(). Overrides getNationalString\r
+ * in DataType for date, time, and timestamp.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ protected String getNationalString(LocaleFinder localeFinder) throws StandardException\r
+ {\r
+ if (isNull())\r
+ {\r
+ return getString();\r
+ }\r
+\r
+ return localeFinder.getTimeFormat().format(getTime( (Calendar) null));\r
+ }\r
+\r
+ /**\r
+ * Compute encoded time value\r
+ * Time is represented by hour << 16 + minute << 8 + seconds\r
+ */\r
+ private int computeEncodedTime(java.util.Date value) throws StandardException\r
+ {\r
+ return computeEncodedTime( value, (Calendar) null);\r
+ }\r
+\r
+ static int computeEncodedTime(java.util.Date value, Calendar currentCal) throws StandardException\r
+ {\r
+ if (value == null)\r
+ return -1;\r
+ if( currentCal == null)\r
+ currentCal = new GregorianCalendar();\r
+ currentCal.setTime(value);\r
+ return computeEncodedTime(currentCal);\r
+ }\r
+\r
+ /** Adding this method to ensure that super class' setInto method doesn't get called\r
+ * that leads to the violation of JDBC spec( untyped nulls ) when batching is turned on.\r
+ */\r
+ public void setInto(PreparedStatement ps, int position) throws SQLException, StandardException {\r
+\r
+ ps.setTime(position, getTime((Calendar) null));\r
+ }\r
+\r
+\r
+ /**\r
+ * Add a number of intervals to a datetime value. Implements the JDBC escape TIMESTAMPADD function.\r
+ *\r
+ * @param intervalType One of FRAC_SECOND_INTERVAL, SECOND_INTERVAL, MINUTE_INTERVAL, HOUR_INTERVAL,\r
+ * DAY_INTERVAL, WEEK_INTERVAL, MONTH_INTERVAL, QUARTER_INTERVAL, or YEAR_INTERVAL\r
+ * @param intervalCount The number of intervals to add\r
+ * @param currentDate Used to convert time to timestamp\r
+ * @param resultHolder If non-null a DateTimeDataValue that can be used to hold the result. If null then\r
+ * generate a new holder\r
+ *\r
+ * @return startTime + intervalCount intervals, as a timestamp\r
+ *\r
+ * @exception StandardException\r
+ */\r
+ public DateTimeDataValue timestampAdd( int intervalType,\r
+ NumberDataValue intervalCount,\r
+ java.sql.Date currentDate,\r
+ DateTimeDataValue resultHolder)\r
+ throws StandardException\r
+ {\r
+ return toTimestamp( currentDate).timestampAdd( intervalType, intervalCount, currentDate, resultHolder);\r
+ }\r
+\r
+ private SQLTimestamp toTimestamp(java.sql.Date currentDate) throws StandardException\r
+ {\r
+ return new SQLTimestamp( SQLDate.computeEncodedDate( currentDate, (Calendar) null),\r
+ getEncodedTime(),\r
+ 0 /* nanoseconds */);\r
+ }\r
+ \r
+ /**\r
+ * Finds the difference between two datetime values as a number of intervals. Implements the JDBC\r
+ * TIMESTAMPDIFF escape function.\r
+ *\r
+ * @param intervalType One of FRAC_SECOND_INTERVAL, SECOND_INTERVAL, MINUTE_INTERVAL, HOUR_INTERVAL,\r
+ * DAY_INTERVAL, WEEK_INTERVAL, MONTH_INTERVAL, QUARTER_INTERVAL, or YEAR_INTERVAL\r
+ * @param time1\r
+ * @param currentDate Used to convert time to timestamp\r
+ * @param resultHolder If non-null a NumberDataValue that can be used to hold the result. If null then\r
+ * generate a new holder\r
+ *\r
+ * @return the number of intervals by which this datetime is greater than time1\r
+ *\r
+ * @exception StandardException\r
+ */\r
+ public NumberDataValue timestampDiff( int intervalType,\r
+ DateTimeDataValue time1,\r
+ java.sql.Date currentDate,\r
+ NumberDataValue resultHolder)\r
+ throws StandardException\r
+ {\r
+ return toTimestamp( currentDate ).timestampDiff( intervalType, time1, currentDate, resultHolder);\r
+ }\r
+}\r
+\r