blob: 1fcb9ee915a4fa6c21a81d65a5ae305f40a16893 [file] [log] [blame]
/* Copyright (c) 2001-2010, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb.types;
import java.math.BigDecimal;
import org.hsqldb.Session;
import org.hsqldb.Tokens;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.IntKeyIntValueHashMap;
/**
* Common elements for Type instances for DATETIME and INTERVAL.<p>
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since 1.9.0
*/
public abstract class DTIType extends Type {
public static final byte[] yearToSecondSeparators = {
'-', '-', ' ', ':', ':', '.'
};
public static final int[] yearToSecondFactors = {
12, 1, 24 * 60 * 60, 60 * 60, 60, 1, 0
};
// static final int[] hourToSecondFactors = {
// 60 * 60, 60, 1, 0
// };
public static final int[] yearToSecondLimits = {
0, 12, 0, 24, 60, 60, 1000000000
};
public static final int INTERVAL_MONTH_INDEX = 1;
public static final int INTERVAL_FRACTION_PART_INDEX = 6;
public static final long[] precisionLimits = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
1000000000, 10000000000L, 100000000000L, 1000000000000L
};
public static final int[] precisionFactors = {
100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1
};
public static final int[] nanoScaleFactors = {
1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10,
1
};
public static final int timezoneSecondsLimit = 14 * 60 * 60;
static final int[] intervalParts = {
Types.SQL_INTERVAL_YEAR, Types.SQL_INTERVAL_MONTH,
Types.SQL_INTERVAL_DAY, Types.SQL_INTERVAL_HOUR,
Types.SQL_INTERVAL_MINUTE, Types.SQL_INTERVAL_SECOND
};
static final int[][] intervalTypes = {
{
Types.SQL_INTERVAL_YEAR, Types.SQL_INTERVAL_YEAR_TO_MONTH, 0, 0, 0,
0
}, {
0, Types.SQL_INTERVAL_MONTH, 0, 0, 0, 0
}, {
0, 0, Types.SQL_INTERVAL_DAY, Types.SQL_INTERVAL_DAY_TO_HOUR,
Types.SQL_INTERVAL_DAY_TO_MINUTE, Types.SQL_INTERVAL_DAY_TO_SECOND
}, {
0, 0, 0, Types.SQL_INTERVAL_HOUR,
Types.SQL_INTERVAL_HOUR_TO_MINUTE,
Types.SQL_INTERVAL_HOUR_TO_SECOND
}, {
0, 0, 0, 0, Types.SQL_INTERVAL_MINUTE,
Types.SQL_INTERVAL_MINUTE_TO_SECOND
}, {
0, 0, 0, 0, 0, Types.SQL_INTERVAL_SECOND
},
};
static final IntKeyIntValueHashMap intervalIndexMap =
new IntKeyIntValueHashMap();
static {
intervalIndexMap.put(Types.SQL_INTERVAL_YEAR, 0);
intervalIndexMap.put(Types.SQL_INTERVAL_MONTH, 1);
intervalIndexMap.put(Types.SQL_INTERVAL_DAY, 2);
intervalIndexMap.put(Types.SQL_INTERVAL_HOUR, 3);
intervalIndexMap.put(Types.SQL_INTERVAL_MINUTE, 4);
intervalIndexMap.put(Types.SQL_INTERVAL_SECOND, 5);
}
public static final int TIMEZONE_HOUR = Types.SQL_TYPE_NUMBER_LIMIT + 1;
public static final int TIMEZONE_MINUTE = Types.SQL_TYPE_NUMBER_LIMIT + 2;
public static final int DAY_OF_WEEK = Types.SQL_TYPE_NUMBER_LIMIT + 3;
public static final int DAY_OF_MONTH = Types.SQL_TYPE_NUMBER_LIMIT + 4;
public static final int DAY_OF_YEAR = Types.SQL_TYPE_NUMBER_LIMIT + 5;
public static final int WEEK_OF_YEAR = Types.SQL_TYPE_NUMBER_LIMIT + 6;
public static final int QUARTER = Types.SQL_TYPE_NUMBER_LIMIT + 7;
public static final int DAY_NAME = Types.SQL_TYPE_NUMBER_LIMIT + 8;
public static final int MONTH_NAME = Types.SQL_TYPE_NUMBER_LIMIT + 9;
public static final int SECONDS_MIDNIGHT = Types.SQL_TYPE_NUMBER_LIMIT
+ 10;
//
public final int startIntervalType;
public final int endIntervalType;
//
public final int startPartIndex;
public final int endPartIndex;
protected DTIType(int typeGroup, int type, long precision, int scale,
int startIntervalType, int endIntervalType) {
super(typeGroup, type, precision, scale);
this.startIntervalType = startIntervalType;
this.endIntervalType = endIntervalType;
startPartIndex = intervalIndexMap.get(startIntervalType);
endPartIndex = intervalIndexMap.get(endIntervalType);
}
protected DTIType(int typeGroup, int type, long precision, int scale) {
super(typeGroup, type, precision, scale);
switch (type) {
case Types.SQL_DATE :
startIntervalType = Types.SQL_INTERVAL_YEAR;
endIntervalType = Types.SQL_INTERVAL_DAY;
break;
case Types.SQL_TIME :
case Types.SQL_TIME_WITH_TIME_ZONE :
startIntervalType = Types.SQL_INTERVAL_HOUR;
endIntervalType = Types.SQL_INTERVAL_SECOND;
break;
case Types.SQL_TIMESTAMP :
case Types.SQL_TIMESTAMP_WITH_TIME_ZONE :
startIntervalType = Types.SQL_INTERVAL_YEAR;
endIntervalType = Types.SQL_INTERVAL_SECOND;
break;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "DTIType");
}
startPartIndex = intervalIndexMap.get(startIntervalType);
endPartIndex = intervalIndexMap.get(endIntervalType);
}
String intervalSecondToString(long seconds, int nanos, boolean signed) {
StringBuffer sb = new StringBuffer(64);
if (seconds < 0) {
seconds = -seconds;
sb.append('-');
} else if (signed) {
sb.append('+');
}
for (int i = startPartIndex; i <= endPartIndex; i++) {
int factor = DTIType.yearToSecondFactors[i];
long part = seconds / factor;
if (i == startPartIndex) {
int startDigits = precision == 0 ? 2
: (int) precision;
int zeros = (int) startDigits - getPrecisionExponent(part);
/*
for (int j = 0; j < zeros; j++) {
buffer.append('0');
}
*/
} else if (part < 10) {
sb.append('0');
}
sb.append(part);
seconds %= factor;
if (i < endPartIndex) {
sb.append((char) DTIType.yearToSecondSeparators[i]);
}
}
if (scale != 0) {
sb.append((char) DTIType
.yearToSecondSeparators[DTIType.INTERVAL_FRACTION_PART_INDEX - 1]);
}
if (nanos < 0) {
nanos = -nanos;
}
for (int i = 0; i < scale; i++) {
int digit = nanos / DTIType.precisionFactors[i];
nanos -= digit * DTIType.precisionFactors[i];
sb.append(digit);
}
return sb.toString();
}
public int getStartIntervalType() {
return startIntervalType;
}
public int getEndIntervalType() {
return endIntervalType;
}
public Type getExtractType(int part) {
switch (part) {
case DAY_NAME :
case MONTH_NAME :
case QUARTER :
case DAY_OF_MONTH :
case DAY_OF_YEAR :
case DAY_OF_WEEK :
case WEEK_OF_YEAR :
if (!isDateTimeType()
|| startIntervalType != Types.SQL_INTERVAL_YEAR) {
throw Error.error(ErrorCode.X_42561);
}
if (part == DAY_NAME || part == MONTH_NAME) {
return Type.SQL_VARCHAR;
} else {
return Type.SQL_INTEGER;
}
case Types.SQL_INTERVAL_SECOND :
if (part == startIntervalType) {
if (scale != 0) {
return new NumberType(Types.SQL_DECIMAL,
precision + scale, scale);
}
} else if (part == endIntervalType) {
if (scale != 0) {
return new NumberType(Types.SQL_DECIMAL,
maxIntervalPrecision + scale,
scale);
}
}
// fall through
case Types.SQL_INTERVAL_YEAR :
case Types.SQL_INTERVAL_MONTH :
case Types.SQL_INTERVAL_DAY :
case Types.SQL_INTERVAL_HOUR :
case Types.SQL_INTERVAL_MINUTE :
if (part < startIntervalType || part > endIntervalType) {
throw Error.error(ErrorCode.X_42561);
}
return Type.SQL_INTEGER;
case SECONDS_MIDNIGHT :
if (!isDateTimeType()
|| endIntervalType < Types.SQL_INTERVAL_SECOND) {
throw Error.error(ErrorCode.X_42561);
}
return Type.SQL_INTEGER;
case TIMEZONE_HOUR :
case TIMEZONE_MINUTE :
if (typeCode != Types.SQL_TIMESTAMP_WITH_TIME_ZONE
&& typeCode != Types.SQL_TIME_WITH_TIME_ZONE) {
throw Error.error(ErrorCode.X_42561);
}
return Type.SQL_INTEGER;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "DTIType");
}
}
public static int normaliseFraction(int fraction, int precision) {
return (fraction / nanoScaleFactors[precision])
* nanoScaleFactors[precision];
}
static int getPrecisionExponent(long value) {
int i = 1;
for (; i < precisionLimits.length; i++) {
if (value < precisionLimits[i]) {
break;
}
}
return i;
}
public static int getFieldNameTypeForToken(int token) {
switch (token) {
case Tokens.YEAR :
return Types.SQL_INTERVAL_YEAR;
case Tokens.MONTH :
return Types.SQL_INTERVAL_MONTH;
case Tokens.DAY :
return Types.SQL_INTERVAL_DAY;
case Tokens.HOUR :
return Types.SQL_INTERVAL_HOUR;
case Tokens.MINUTE :
return Types.SQL_INTERVAL_MINUTE;
case Tokens.SECOND :
return Types.SQL_INTERVAL_SECOND;
case Tokens.TIMEZONE_HOUR :
return TIMEZONE_HOUR;
case Tokens.TIMEZONE_MINUTE :
return TIMEZONE_MINUTE;
case Tokens.DAY_NAME :
return DAY_NAME;
case Tokens.MONTH_NAME :
return MONTH_NAME;
case Tokens.QUARTER :
return QUARTER;
case Tokens.DAY_OF_MONTH :
return DAY_OF_MONTH;
case Tokens.DAY_OF_WEEK :
return DAY_OF_WEEK;
case Tokens.DAY_OF_YEAR :
return DAY_OF_YEAR;
case Tokens.WEEK_OF_YEAR :
return WEEK_OF_YEAR;
case Tokens.SECONDS_MIDNIGHT :
return SECONDS_MIDNIGHT;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "DTIType");
}
}
public static String getFieldNameTokenForType(int type) {
switch (type) {
case Types.SQL_INTERVAL_YEAR :
return Tokens.T_YEAR;
case Types.SQL_INTERVAL_MONTH :
return Tokens.T_MONTH;
case Types.SQL_INTERVAL_DAY :
return Tokens.T_DAY;
case Types.SQL_INTERVAL_HOUR :
return Tokens.T_HOUR;
case Types.SQL_INTERVAL_MINUTE :
return Tokens.T_MINUTE;
case Types.SQL_INTERVAL_SECOND :
return Tokens.T_SECOND;
case TIMEZONE_HOUR :
return Tokens.T_TIMEZONE_HOUR;
case TIMEZONE_MINUTE :
return Tokens.T_TIMEZONE_MINUTE;
case DAY_NAME :
return Tokens.T_DAY_NAME;
case MONTH_NAME :
return Tokens.T_MONTH_NAME;
case QUARTER :
return Tokens.T_QUARTER;
case DAY_OF_MONTH :
return Tokens.T_DAY_OF_MONTH;
case DAY_OF_WEEK :
return Tokens.T_DAY_OF_WEEK;
case DAY_OF_YEAR :
return Tokens.T_DAY_OF_YEAR;
case WEEK_OF_YEAR :
return Tokens.T_WEEK_OF_YEAR;
case SECONDS_MIDNIGHT :
return Tokens.T_SECONDS_MIDNIGHT;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "DTIType");
}
}
public static boolean isValidDatetimeRange(Type a, Type b) {
if (!a.isDateTimeType()) {
return false;
}
if (b.isDateTimeType()) {
if ((a.typeCode == Types.SQL_TIME && b.typeCode == Types.SQL_DATE)
|| (a.typeCode == Types.SQL_DATE
&& b.typeCode == Types.SQL_TIME)) {
return false;
}
return true;
}
if (b.isIntervalType()) {
return ((DateTimeType) a).canAdd((IntervalType) b);
}
return false;
}
public static final int defaultTimeFractionPrecision = 0;
public static final int defaultTimestampFractionPrecision = 6;
public static final int defaultIntervalPrecision = 2;
public static final int defaultIntervalFractionPrecision = 6;
public static final int maxIntervalPrecision = 9;
public static final int maxFractionPrecision = 9;
public static final int limitNanoseconds = 1000000000;
abstract public int getPart(Session session, Object dateTime, int part);
abstract public BigDecimal getSecondPart(Object dateTime);
BigDecimal getSecondPart(long seconds, long nanos) {
seconds *= DTIType.precisionLimits[scale];
seconds += nanos / DTIType.nanoScaleFactors[scale];
return BigDecimal.valueOf(seconds, scale);
}
}