--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.iapi.types.SQLDate\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.types.SQLInteger;\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.error.StandardException;\r
+\r
+import org.apache.derby.iapi.db.DatabaseContext;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.TypeId;\r
+\r
+import org.apache.derby.iapi.types.NumberDataValue;\r
+import org.apache.derby.iapi.types.DateTimeDataValue;\r
+\r
+import org.apache.derby.iapi.services.context.ContextService;\r
+\r
+import org.apache.derby.iapi.services.io.StoredFormatIds;\r
+ \r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.types.DataType;\r
+\r
+import org.apache.derby.iapi.services.cache.ClassSize;\r
+import org.apache.derby.iapi.services.i18n.LocaleFinder;\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 Date.\r
+ * <p>\r
+ * The date is stored as int (year << 16 + month << 8 + day)\r
+ * Null is represented by an encodedDate value of 0.\r
+ * Some of the static methods in this class are also used by SQLTime and SQLTimestamp\r
+ * so check those classes if you change the date encoding\r
+ *\r
+ * PERFORMANCE OPTIMIZATION:\r
+ * The java.sql.Date object is only instantiated when needed\r
+ * do to the overhead of Date.valueOf(), etc. methods.\r
+ */\r
+\r
+public final class SQLDate extends DataType\r
+ implements DateTimeDataValue\r
+{\r
+\r
+ private int encodedDate; //year << 16 + month << 8 + day\r
+\r
+ // The cached value.toString()\r
+ private String valueString;\r
+\r
+ private static final int BASE_MEMORY_USAGE = ClassSize.estimateBaseFromCatalog( SQLDate.class);\r
+\r
+ public int estimateMemoryUsage()\r
+ {\r
+ return BASE_MEMORY_USAGE + ClassSize.estimateMemoryUsage( valueString);\r
+ } // end of estimateMemoryUsage\r
+\r
+ int getEncodedDate()\r
+ {\r
+ return encodedDate;\r
+ }\r
+ \r
+ /*\r
+ ** DataValueDescriptor interface\r
+ ** (mostly implemented in DataType)\r
+ */\r
+\r
+ public String getString()\r
+ {\r
+ //format is [yyy]y-mm-dd e.g. 1-01-01, 9999-99-99\r
+ if (!isNull())\r
+ {\r
+ if (valueString == null)\r
+ {\r
+ valueString = encodedDateToString(encodedDate);\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
+ /**\r
+ getTimestamp returns a timestamp with the date value \r
+ time is set to 00:00:00.0\r
+ */\r
+ public Timestamp getTimestamp( Calendar cal) \r
+ {\r
+ if (isNull())\r
+ {\r
+ return null;\r
+ }\r
+ \r
+ return new Timestamp(getTimeInMillis(cal));\r
+ }\r
+\r
+ /**\r
+ * Convert the date into a milli-seconds since the epoch\r
+ * with the time set to 00:00 based upon the passed in Calendar.\r
+ */\r
+ private long getTimeInMillis(Calendar cal)\r
+ {\r
+ if( cal == null)\r
+ cal = new GregorianCalendar();\r
+ cal.clear();\r
+ \r
+ SQLDate.setDateInCalendar(cal, encodedDate);\r
+ \r
+ return cal.getTimeInMillis();\r
+ }\r
+ \r
+ /**\r
+ * Set the date portion of a date-time value into\r
+ * the passed in Calendar object from its encodedDate\r
+ * value. Only the YEAR, MONTH and DAY_OF_MONTH\r
+ * fields are modified. The remaining\r
+ * state of the Calendar is not modified.\r
+ */\r
+ static void setDateInCalendar(Calendar cal, int encodedDate)\r
+ {\r
+ // Note Calendar uses 0 for January, Derby uses 1.\r
+ cal.set(getYear(encodedDate),\r
+ getMonth(encodedDate)-1, getDay(encodedDate)); \r
+ }\r
+ \r
+ /**\r
+ getObject returns the date value\r
+\r
+ */\r
+ public Object getObject()\r
+ {\r
+ return getDate( (Calendar) null);\r
+ }\r
+ \r
+ public int getLength()\r
+ {\r
+ return 4;\r
+ }\r
+\r
+ /* this is for DataType's error generator */\r
+ public String getTypeName()\r
+ {\r
+ return "DATE";\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_DATE_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(encodedDate);\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
+ encodedDate = in.readInt();\r
+\r
+ // reset cached string values\r
+ valueString = null;\r
+ }\r
+ public void readExternalFromArray(ArrayInputStream in) throws IOException\r
+ {\r
+ encodedDate = in.readInt();\r
+\r
+ // reset cached string 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 SQLDate(encodedDate);\r
+ }\r
+\r
+ /**\r
+ * @see DataValueDescriptor#getNewNull\r
+ */\r
+ public DataValueDescriptor getNewNull()\r
+ {\r
+ return new SQLDate();\r
+ }\r
+ /**\r
+ * @see org.apache.derby.iapi.services.io.Storable#restoreToNull\r
+ *\r
+ */\r
+\r
+ public void restoreToNull()\r
+ {\r
+ // clear encodedDate\r
+ encodedDate = 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
+ setValue(resultSet.getDate(colNumber), (Calendar) null);\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
+\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
+ /* get the comparison date values */\r
+ int otherVal = 0;\r
+\r
+ /* if the argument is another SQLDate\r
+ * get the encodedDate\r
+ */\r
+ if (other instanceof SQLDate)\r
+ {\r
+ otherVal = ((SQLDate)other).encodedDate; \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
+ otherVal = SQLDate.computeEncodedDate(other.getDate(new GregorianCalendar()));\r
+ }\r
+ if (encodedDate > otherVal)\r
+ comparison = 1;\r
+ else if (encodedDate < otherVal)\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 SQLDate() {\r
+ }\r
+\r
+ public SQLDate(Date value) throws StandardException\r
+ {\r
+ parseDate(value);\r
+ }\r
+ \r
+ private void parseDate( java.util.Date value) throws StandardException\r
+ {\r
+ encodedDate = computeEncodedDate(value);\r
+ }\r
+\r
+ private SQLDate(int encodedDate) {\r
+ this.encodedDate = encodedDate;\r
+ }\r
+\r
+ /**\r
+ * Construct a date from a string. The allowed date formats are:\r
+ *<ol>\r
+ *<li>ISO: yyyy-mm-dd\r
+ *<li>IBM USA standard: mm/dd/yyyy\r
+ *<li>IBM European standard: dd.mm.yyyy\r
+ *</ol>\r
+ * Trailing blanks may be included; leading zeros may be omitted from the month and day portions.\r
+ *\r
+ * @param dateStr\r
+ * @param isJdbcEscape if true then only the JDBC date escape syntax is allowed\r
+ * @param localeFinder\r
+ *\r
+ * @exception Standard exception if the syntax is invalid or the value is out of range.\r
+ */\r
+ public SQLDate( String dateStr, boolean isJdbcEscape, LocaleFinder localeFinder)\r
+ throws StandardException\r
+ {\r
+ parseDate( dateStr, isJdbcEscape, localeFinder, (Calendar) null);\r
+ }\r
+\r
+ /**\r
+ * Construct a date from a string. The allowed date formats are:\r
+ *<ol>\r
+ *<li>ISO: yyyy-mm-dd\r
+ *<li>IBM USA standard: mm/dd/yyyy\r
+ *<li>IBM European standard: dd.mm.yyyy\r
+ *</ol>\r
+ * Trailing blanks may be included; leading zeros may be omitted from the month and day portions.\r
+ *\r
+ * @param dateStr\r
+ * @param isJdbcEscape if true then only the JDBC date escape syntax is allowed\r
+ * @param localeFinder\r
+ *\r
+ * @exception Standard exception if the syntax is invalid or the value is out of range.\r
+ */\r
+ public SQLDate( String dateStr, boolean isJdbcEscape, LocaleFinder localeFinder, Calendar cal)\r
+ throws StandardException\r
+ {\r
+ parseDate( dateStr, isJdbcEscape, localeFinder, cal);\r
+ }\r
+\r
+ static final char ISO_SEPARATOR = '-';\r
+ private static final char[] ISO_SEPARATOR_ONLY = {ISO_SEPARATOR};\r
+ private static final char IBM_USA_SEPARATOR = '/';\r
+ private static final char[] IBM_USA_SEPARATOR_ONLY = {IBM_USA_SEPARATOR};\r
+ private static final char IBM_EUR_SEPARATOR = '.';\r
+ private static final char[] IBM_EUR_SEPARATOR_ONLY = {IBM_EUR_SEPARATOR};\r
+ private static final char[] END_OF_STRING = {(char) 0};\r
+ \r
+ private void parseDate( String dateStr, boolean isJdbcEscape, LocaleFinder localeFinder, Calendar cal)\r
+ throws StandardException\r
+ {\r
+ boolean validSyntax = true;\r
+ DateTimeParser parser = new DateTimeParser( dateStr);\r
+ int year = 0;\r
+ int month = 0;\r
+ int day = 0;\r
+ StandardException thrownSE = null;\r
+\r
+ try\r
+ {\r
+ switch( parser.nextSeparator())\r
+ {\r
+ case ISO_SEPARATOR:\r
+ encodedDate = SQLTimestamp.parseDateOrTimestamp( parser, false)[0];\r
+ valueString = parser.getTrimmedString();\r
+ return;\r
+\r
+ case IBM_USA_SEPARATOR:\r
+ if( isJdbcEscape)\r
+ {\r
+ validSyntax = false;\r
+ break;\r
+ }\r
+ month = parser.parseInt( 2, true, IBM_USA_SEPARATOR_ONLY, false);\r
+ day = parser.parseInt( 2, true, IBM_USA_SEPARATOR_ONLY, false);\r
+ year = parser.parseInt( 4, false, END_OF_STRING, false);\r
+ break;\r
+\r
+ case IBM_EUR_SEPARATOR:\r
+ if( isJdbcEscape)\r
+ {\r
+ validSyntax = false;\r
+ break;\r
+ }\r
+ day = parser.parseInt( 2, true, IBM_EUR_SEPARATOR_ONLY, false);\r
+ month = parser.parseInt( 2, true, IBM_EUR_SEPARATOR_ONLY, false);\r
+ year = parser.parseInt( 4, false, END_OF_STRING, false);\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
+ valueString = parser.checkEnd();\r
+ encodedDate = computeEncodedDate( year, month, day);\r
+ }\r
+ else\r
+ {\r
+ // See if it is a localized date or timestamp.\r
+ dateStr = StringUtil.trimTrailing( dateStr);\r
+ DateFormat dateFormat = null;\r
+ if( localeFinder == null)\r
+ dateFormat = DateFormat.getDateInstance();\r
+ else if( cal == null)\r
+ dateFormat = localeFinder.getDateFormat();\r
+ else\r
+ dateFormat = (DateFormat) localeFinder.getDateFormat().clone();\r
+ if( cal != null)\r
+ dateFormat.setCalendar( cal);\r
+ try\r
+ {\r
+ encodedDate = computeEncodedDate( dateFormat.parse( dateStr), cal);\r
+ }\r
+ catch( ParseException pe)\r
+ {\r
+ // Maybe it is a localized timestamp\r
+ try\r
+ {\r
+ encodedDate = SQLTimestamp.parseLocalTimestamp( dateStr, localeFinder, cal)[0];\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 = dateStr;\r
+ }\r
+ } // end of parseDate\r
+\r
+ /**\r
+ * Set the value from a correctly typed Date object.\r
+ * @throws StandardException \r
+ */\r
+ void setObject(Object theValue) throws StandardException\r
+ {\r
+ setValue((Date) theValue);\r
+ }\r
+\r
+ protected void setFrom(DataValueDescriptor theValue) throws StandardException {\r
+\r
+ // Same format means same type SQLDate\r
+ if (theValue instanceof SQLDate) {\r
+ restoreToNull();\r
+ encodedDate = ((SQLDate) theValue).encodedDate;\r
+ }\r
+ else\r
+ {\r
+ Calendar cal = new GregorianCalendar();\r
+ setValue(theValue.getDate( cal), cal);\r
+ }\r
+ }\r
+\r
+ /**\r
+ @see DateTimeDataValue#setValue\r
+\r
+ */\r
+ public void setValue(Date value, Calendar cal) throws StandardException\r
+ {\r
+ restoreToNull();\r
+ encodedDate = computeEncodedDate((java.util.Date) value, cal);\r
+ }\r
+\r
+ /**\r
+ @see DateTimeDataValue#setValue\r
+\r
+ */\r
+ public void setValue(Timestamp value, Calendar cal) throws StandardException\r
+ {\r
+ restoreToNull();\r
+ encodedDate = computeEncodedDate((java.util.Date) value, cal);\r
+ }\r
+\r
+\r
+ public void setValue(String theValue)\r
+ throws StandardException\r
+ {\r
+ restoreToNull();\r
+\r
+ if (theValue != null)\r
+ {\r
+ DatabaseContext databaseContext = (DatabaseContext) ContextService.getContext(DatabaseContext.CONTEXT_ID);\r
+ parseDate( 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
+ /**\r
+ * @see DateTimeDataValue#getYear\r
+ * \r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public NumberDataValue getYear(NumberDataValue result)\r
+ throws StandardException\r
+ {\r
+ if (isNull()) {\r
+ return nullValueInt();\r
+ } else { \r
+ return SQLDate.setSource(getYear(encodedDate), result);\r
+ }\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
+ if (isNull()) {\r
+ return nullValueInt();\r
+ } else {\r
+ return SQLDate.setSource(getMonth(encodedDate), result);\r
+ }\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
+ if (isNull()) {\r
+ return nullValueInt();\r
+ } else {\r
+ return SQLDate.setSource(getDay(encodedDate), result);\r
+ }\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
+ throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, \r
+ "getHours", "Date");\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
+ throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, \r
+ "getMinutes", "Date");\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
+ throw StandardException.newException(SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, \r
+ "getSeconds", "Date");\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 getDate( (Calendar) null).toString();\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Hash code\r
+ */\r
+ public int hashCode()\r
+ {\r
+ return encodedDate;\r
+ }\r
+\r
+ /** @see DataValueDescriptor#typePrecedence */\r
+ public int typePrecedence()\r
+ {\r
+ return TypeId.DATE_PRECEDENCE;\r
+ }\r
+\r
+ /**\r
+ * Check if the value is null. \r
+ * encodedDate is 0 if the value is null\r
+ *\r
+ * @return Whether or not value is logically null.\r
+ */\r
+ public final boolean isNull()\r
+ {\r
+ return (encodedDate == 0);\r
+ }\r
+\r
+ /**\r
+ * Get the value field. We instantiate the field\r
+ * on demand.\r
+ *\r
+ * @return The value field.\r
+ */\r
+ public Date getDate( Calendar cal)\r
+ {\r
+ if (isNull())\r
+ return null;\r
+ \r
+ return new Date(getTimeInMillis(cal));\r
+ }\r
+\r
+ /**\r
+ * Get the year from the encodedDate.\r
+ *\r
+ * @param encodedDate the encoded date\r
+ * @return year value.\r
+ */\r
+ static int getYear(int encodedDate)\r
+ {\r
+ return (encodedDate >>> 16);\r
+ }\r
+\r
+ /**\r
+ * Get the month from the encodedDate,\r
+ * January is one.\r
+ *\r
+ * @param encodedDate the encoded date\r
+ * @return month value.\r
+ */\r
+ static int getMonth(int encodedDate)\r
+ {\r
+ return ((encodedDate >>> 8) & 0x00ff);\r
+ }\r
+\r
+ /**\r
+ * Get the day from the encodedDate.\r
+ *\r
+ * @param encodedDate the encoded date\r
+ * @return day value.\r
+ */\r
+ static int getDay(int encodedDate)\r
+ {\r
+ return (encodedDate & 0x00ff);\r
+ }\r
+ /**\r
+ * computeEncodedDate extracts the year, month and date from\r
+ * a Calendar value and encodes them as\r
+ * year << 16 + month << 8 + date\r
+ * Use this function will help to remember to add 1 to month\r
+ * which is 0 based in the Calendar class\r
+ * @param cal the Calendar \r
+ * @return the encodedDate\r
+ *\r
+ * @exception StandardException if the value is out of the DB2 date range\r
+ */\r
+ static int computeEncodedDate(Calendar cal) throws StandardException\r
+ {\r
+ return computeEncodedDate(cal.get(Calendar.YEAR),\r
+ cal.get(Calendar.MONTH) + 1,\r
+ cal.get(Calendar.DATE));\r
+ }\r
+\r
+ static int computeEncodedDate( int y, int m, int d) throws StandardException\r
+ {\r
+ int maxDay = 31;\r
+ switch( m)\r
+ {\r
+ case 4:\r
+ case 6:\r
+ case 9:\r
+ case 11:\r
+ maxDay = 30;\r
+ break;\r
+ \r
+ case 2:\r
+ // leap years are every 4 years except for century years not divisble by 400.\r
+ maxDay = ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) ? 29 : 28;\r
+ break;\r
+ }\r
+ if( y < 1 || y > 9999\r
+ || m < 1 || m > 12\r
+ || d < 1 || d > maxDay)\r
+ throw StandardException.newException( SQLState.LANG_DATE_RANGE_EXCEPTION);\r
+ return (y << 16) + (m << 8) + d;\r
+ }\r
+\r
+ /**\r
+ * Convert a date to the JDBC representation and append it to a string buffer.\r
+ *\r
+ * @param year\r
+ * @param month 1 based (January == 1)\r
+ * @param day\r
+ * @param sb The string representation is appended to this StringBuffer\r
+ */\r
+ static void dateToString( int year, int month, int day, StringBuffer sb)\r
+ {\r
+ String yearStr = Integer.toString( year);\r
+ for( int i = yearStr.length(); i < 4; i++)\r
+ sb.append( '0');\r
+ sb.append(yearStr);\r
+ sb.append(ISO_SEPARATOR);\r
+\r
+ String monthStr = Integer.toString( month);\r
+ String dayStr = Integer.toString( day);\r
+ if (monthStr.length() == 1)\r
+ sb.append('0');\r
+ sb.append(monthStr);\r
+ sb.append(ISO_SEPARATOR);\r
+ if (dayStr.length() == 1)\r
+ sb.append('0');\r
+ sb.append(dayStr);\r
+ } // end of dateToString\r
+ \r
+ /**\r
+ * Get the String version from the encodedDate.\r
+ *\r
+ * @return string value.\r
+ */\r
+ static String encodedDateToString(int encodedDate)\r
+ {\r
+ StringBuffer vstr = new StringBuffer();\r
+ dateToString( getYear(encodedDate), getMonth(encodedDate), getDay(encodedDate), 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.getDateFormat().format(getDate(new GregorianCalendar()));\r
+ }\r
+\r
+ /**\r
+ This helper routine tests the nullability of various parameters\r
+ and sets up the result appropriately.\r
+\r
+ If source is null, a new NumberDataValue is built. \r
+\r
+ @exception StandardException Thrown on error\r
+ */\r
+ static NumberDataValue setSource(int value,\r
+ NumberDataValue source)\r
+ throws StandardException {\r
+ /*\r
+ ** NOTE: Most extract operations return int, so the generation of\r
+ ** a SQLInteger is here. Those extract operations that return\r
+ ** something other than int must allocate the source NumberDataValue\r
+ ** themselves, so that we do not allocate a SQLInteger here.\r
+ */\r
+ if (source == null)\r
+ source = new SQLInteger();\r
+\r
+ source.setValue(value);\r
+\r
+ return source;\r
+ }\r
+ /**\r
+ * Compute the encoded date given a date\r
+ *\r
+ */\r
+ private static int computeEncodedDate(java.util.Date value) throws StandardException\r
+ {\r
+ return computeEncodedDate( value, null);\r
+ }\r
+\r
+ static int computeEncodedDate(java.util.Date value, Calendar currentCal) throws StandardException\r
+ {\r
+ if (value == null)\r
+ return 0; //encoded dates have a 0 value for null\r
+ if( currentCal == null)\r
+ currentCal = new GregorianCalendar();\r
+ currentCal.setTime(value);\r
+ return SQLDate.computeEncodedDate(currentCal);\r
+ }\r
+\r
+\r
+ /**\r
+ * Implement the date SQL function: construct a SQL date from a string, number, or timestamp.\r
+ *\r
+ * @param operand Must be a date or a string convertible to a date.\r
+ * @param dvf the DataValueFactory\r
+ *\r
+ * @exception StandardException standard error policy\r
+ */\r
+ public static DateTimeDataValue computeDateFunction( DataValueDescriptor operand,\r
+ DataValueFactory dvf) throws StandardException\r
+ {\r
+ try\r
+ {\r
+ if( operand.isNull())\r
+ return new SQLDate();\r
+ if( operand instanceof SQLDate)\r
+ return (SQLDate) operand.getClone();\r
+\r
+ if( operand instanceof SQLTimestamp)\r
+ {\r
+ DateTimeDataValue retVal = new SQLDate();\r
+ retVal.setValue( operand);\r
+ return retVal;\r
+ }\r
+ if( operand instanceof NumberDataValue)\r
+ {\r
+ int daysSinceEpoch = operand.getInt();\r
+ if( daysSinceEpoch <= 0 || daysSinceEpoch > 3652059)\r
+ throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT,\r
+ operand.getString(), "date");\r
+ Calendar cal = new GregorianCalendar( 1970, 0, 1, 12, 0, 0);\r
+ cal.add( Calendar.DATE, daysSinceEpoch - 1);\r
+ return new SQLDate( computeEncodedDate( cal.get( Calendar.YEAR),\r
+ cal.get( Calendar.MONTH) + 1,\r
+ cal.get( Calendar.DATE)));\r
+ }\r
+ String str = operand.getString();\r
+ if( str.length() == 7)\r
+ {\r
+ // yyyyddd where ddd is the day of the year\r
+ int year = SQLTimestamp.parseDateTimeInteger( str, 0, 4);\r
+ int dayOfYear = SQLTimestamp.parseDateTimeInteger( str, 4, 3);\r
+ if( dayOfYear < 1 || dayOfYear > 366)\r
+ throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT,\r
+ operand.getString(), "date");\r
+ Calendar cal = new GregorianCalendar( year, 0, 1, 2, 0, 0);\r
+ cal.add( Calendar.DAY_OF_YEAR, dayOfYear - 1);\r
+ int y = cal.get( Calendar.YEAR);\r
+ if( y != year)\r
+ throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT,\r
+ operand.getString(), "date");\r
+ return new SQLDate( computeEncodedDate( year,\r
+ cal.get( Calendar.MONTH) + 1,\r
+ cal.get( Calendar.DATE)));\r
+ }\r
+ // Else use the standard cast.\r
+ return dvf.getDateValue( str, false);\r
+ }\r
+ catch( StandardException se)\r
+ {\r
+ if( SQLState.LANG_DATE_SYNTAX_EXCEPTION.startsWith( se.getSQLState()))\r
+ throw StandardException.newException( SQLState.LANG_INVALID_FUNCTION_ARGUMENT,\r
+ operand.getString(), "date");\r
+ throw se;\r
+ }\r
+ } // end of computeDateFunction\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.setDate(position, getDate((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().timestampAdd( intervalType, intervalCount, currentDate, resultHolder);\r
+ }\r
+\r
+ private SQLTimestamp toTimestamp() throws StandardException\r
+ {\r
+ return new SQLTimestamp( getEncodedDate(), 0, 0);\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().timestampDiff( intervalType, time1, currentDate, resultHolder);\r
+ }\r
+}\r