提交 7224c100 authored 作者: Noel Grandin's avatar Noel Grandin

Experimental support for datatype TIMESTAMP WITH TIMEZONE

上级 86792ec9
......@@ -21,6 +21,8 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>Experimental support for datatype TIMESTAMP WITH TIMEZONE
</li>
<li>Add support for ALTER TABLE ... RENAME CONSTRAINT .. TO ...
</li>
<li>Add support for PostgreSQL ALTER TABLE ... RENAME COLUMN .. TO ...
......
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.api;
import java.sql.Timestamp;
/**
* Extends java.sql.Timestamp to add our time zone information.
*
*/
public class TimestampWithTimeZone extends Timestamp {
/**
* Time zone offset from UTC in minutes, range of -12hours to +12hours
*/
private final short timeZoneOffsetMins;
public TimestampWithTimeZone(long time_ms, int nanos, short timeZoneOffsetMins) {
super(time_ms);
setNanos(nanos);
this.timeZoneOffsetMins = timeZoneOffsetMins;
}
/**
* The timezone offset in minutes.
*
* @return the offset
*/
public short getTimeZoneOffsetMins() {
return timeZoneOffsetMins;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + timeZoneOffsetMins;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
TimestampWithTimeZone other = (TimestampWithTimeZone) obj;
if (timeZoneOffsetMins != other.timeZoneOffsetMins)
return false;
return true;
}
}
......@@ -4033,6 +4033,11 @@ public class Parser {
if (readIf("VARYING")) {
original += " VARYING";
}
} else if (readIf("TIMESTAMP")) {
if (readIf("WITH")) {
read("TIMEZONE");
original += " WITH TIMEZONE";
}
} else {
regular = true;
}
......
......@@ -28,7 +28,6 @@ import java.sql.Struct;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.engine.ConnectionInfo;
......
......@@ -45,6 +45,7 @@ import org.h2.value.ValueStringFixed;
import org.h2.value.ValueStringIgnoreCase;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
import org.h2.value.ValueTimestampUtc;
import org.h2.value.ValueUuid;
......@@ -283,6 +284,19 @@ public class ValueDataType implements DataType {
buff.put((byte) type).putVarLong(dateTimeValue);
break;
}
case Value.TIMESTAMP_TZ: {
ValueTimestampTimeZone ts = (ValueTimestampTimeZone) v;
long dateValue = ts.getDateValue();
long nanos = ts.getTimeNanos();
long millis = nanos / 1000000;
nanos -= millis * 1000000;
buff.put((byte) type).
putVarLong(dateValue).
putVarLong(millis).
putVarLong(nanos).
putVarInt(ts.getTimeZoneOffsetMins());
break;
}
case Value.JAVA_OBJECT: {
byte[] b = v.getBytesNoCopy();
buff.put((byte) type).
......@@ -492,6 +506,12 @@ public class ValueDataType implements DataType {
long dateTimeValue = readVarLong(buff);
return ValueTimestampUtc.fromNanos(dateTimeValue);
}
case Value.TIMESTAMP_TZ: {
long dateValue = readVarLong(buff);
long nanos = readVarLong(buff) * 1000000 + readVarLong(buff);
short tz = (short) readVarInt(buff);
return ValueTimestampTimeZone.fromDateValueAndNanos(dateValue, nanos, tz);
}
case Value.BYTES: {
int len = readVarInt(buff);
byte[] b = DataUtils.newBytes(len);
......
......@@ -49,6 +49,7 @@ import org.h2.value.ValueStringFixed;
import org.h2.value.ValueStringIgnoreCase;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
import org.h2.value.ValueTimestampUtc;
import org.h2.value.ValueUuid;
......@@ -544,6 +545,13 @@ public class Data {
writeVarLong(ts.getUtcDateTimeNanos());
break;
}
case Value.TIMESTAMP_TZ: {
ValueTimestampTimeZone ts = (ValueTimestampTimeZone) v;
writeByte((byte) type);
writeVarLong(ts.getDateValue());
writeVarLong(ts.getTimeNanos());
writeVarInt(ts.getTimeZoneOffsetMins());
}
case Value.GEOMETRY:
case Value.JAVA_OBJECT: {
writeByte((byte) type);
......@@ -781,6 +789,12 @@ public class Data {
case Value.TIMESTAMP_UTC: {
return ValueTimestampUtc.fromNanos(readVarLong());
}
case Value.TIMESTAMP_TZ: {
long dateValue = readVarLong();
long nanos = readVarLong();
short tz = (short)readVarInt();
return ValueTimestampTimeZone.fromDateValueAndNanos(dateValue, nanos, tz);
}
case Value.BYTES: {
int len = readVarInt();
byte[] b = DataUtils.newBytes(len);
......@@ -1033,6 +1047,14 @@ public class Data {
ValueTimestampUtc ts = (ValueTimestampUtc) v;
return 1 + getVarLongLen(ts.getUtcDateTimeNanos());
}
case Value.TIMESTAMP_TZ: {
ValueTimestampTimeZone ts = (ValueTimestampTimeZone) v;
long dateValue = ts.getDateValue();
long nanos = ts.getTimeNanos();
short tz = ts.getTimeZoneOffsetMins();
return 1 + getVarLongLen(dateValue) + getVarLongLen(nanos) +
getVarIntLen(tz);
}
case Value.GEOMETRY:
case Value.JAVA_OBJECT: {
byte[] b = v.getBytesNoCopy();
......
......@@ -31,6 +31,7 @@ import org.h2.value.ValueNull;
import org.h2.value.ValueString;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
import org.h2.value.ValueTimestampUtc;
import org.h2.value.ValueUuid;
......@@ -296,6 +297,8 @@ public class Column {
value = ValueTimestamp.fromMillis(session.getTransactionStart());
} else if (dt.type == Value.TIMESTAMP_UTC) {
value = ValueTimestampUtc.fromMillis(session.getTransactionStart());
} else if (dt.type == Value.TIMESTAMP_TZ) {
value = ValueTimestampTimeZone.fromMillis(session.getTransactionStart(), (short)0);
} else if (dt.type == Value.TIME) {
value = ValueTime.fromNanos(0);
} else if (dt.type == Value.DATE) {
......
......@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;
import org.h2.api.ErrorCode;
import org.h2.api.TimestampWithTimeZone;
import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
import org.h2.engine.SysProperties;
......@@ -321,6 +322,13 @@ public class DataType {
// 24 for ValueTimestampUtc, 32 for java.sql.Timestamp
56
);
add(Value.TIMESTAMP_TZ, Types.OTHER, "TimestampTimeZone",
createDate(ValueTimestampTimeZone.PRECISION, "TIMESTAMP_TZ",
ValueTimestampTimeZone.DEFAULT_SCALE, ValueTimestampTimeZone.DISPLAY_SIZE),
new String[]{"TIMESTAMP WITH TIMEZONE"},
// 26 for ValueTimestampUtc, 32 for java.sql.Timestamp
58
);
add(Value.BYTES, Types.VARBINARY, "Bytes",
createString(false),
new String[]{"VARBINARY"},
......@@ -551,6 +559,12 @@ public class DataType {
ValueTimestampUtc.fromMillisNanos(value.getTime(), value.getNanos());
break;
}
case Value.TIMESTAMP_TZ: {
TimestampWithTimeZone value = (TimestampWithTimeZone) rs.getTimestamp(columnIndex);
v = value == null ? (Value) ValueNull.INSTANCE :
ValueTimestampTimeZone.get(value);
break;
}
case Value.DECIMAL: {
BigDecimal value = rs.getBigDecimal(columnIndex);
v = value == null ? (Value) ValueNull.INSTANCE :
......@@ -726,6 +740,9 @@ public class DataType {
case Value.TIMESTAMP_UTC:
// "java.sql.Timestamp";
return Timestamp.class.getName();
case Value.TIMESTAMP_TZ:
// "org.h2.api.TimestampWithTimeZone";
return TimestampWithTimeZone.class.getName();
case Value.BYTES:
case Value.UUID:
// "[B", not "byte[]";
......
......@@ -392,6 +392,13 @@ public class Transfer {
writeLong(ts.getUtcDateTimeNanos());
break;
}
case Value.TIMESTAMP_TZ: {
ValueTimestampTimeZone ts = (ValueTimestampTimeZone) v;
writeLong(ts.getDateValue());
writeLong(ts.getTimeNanos());
writeInt(ts.getTimeZoneOffsetMins());
break;
}
case Value.DECIMAL:
writeString(v.getString());
break;
......@@ -581,6 +588,9 @@ public class Transfer {
case Value.TIMESTAMP_UTC: {
return ValueTimestampUtc.fromNanos(readLong());
}
case Value.TIMESTAMP_TZ: {
return ValueTimestampTimeZone.fromDateValueAndNanos(readLong(), readLong(), (short) readInt());
}
case Value.DECIMAL:
return ValueDecimal.get(new BigDecimal(readString()));
case Value.DOUBLE:
......
......@@ -162,11 +162,15 @@ public abstract class Value {
* The value type for TIMESTAMP UTC values.
*/
public static final int TIMESTAMP_UTC = 23;
/**
* The value type for TIMESTAMP WITH TIMEZONE values.
*/
public static final int TIMESTAMP_TZ = 24;
/**
* The number of value types.
*/
public static final int TYPE_COUNT = TIMESTAMP_UTC + 1;
public static final int TYPE_COUNT = TIMESTAMP_TZ + 1;
private static SoftReference<Value[]> softCache =
new SoftReference<Value[]>(null);
......@@ -304,6 +308,8 @@ public abstract class Value {
return 32;
case TIMESTAMP_UTC:
return 33;
case TIMESTAMP_TZ:
return 34;
case BYTES:
return 40;
case BLOB:
......@@ -548,6 +554,7 @@ public abstract class Value {
case DATE:
case TIMESTAMP:
case TIMESTAMP_UTC:
case TIMESTAMP_TZ:
case BYTES:
case JAVA_OBJECT:
case UUID:
......@@ -575,6 +582,9 @@ public abstract class Value {
return ValueByte.get(convertToByte(convertToLong(getFloat())));
case BYTES:
return ValueByte.get((byte) Integer.parseInt(getString(), 16));
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
break;
}
......@@ -597,6 +607,9 @@ public abstract class Value {
return ValueShort.get(convertToShort(convertToLong(getFloat())));
case BYTES:
return ValueShort.get((short) Integer.parseInt(getString(), 16));
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
break;
}
......@@ -619,6 +632,9 @@ public abstract class Value {
return ValueInt.get(convertToInt(convertToLong(getFloat())));
case BYTES:
return ValueInt.get((int) Long.parseLong(getString(), 16));
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
break;
}
......@@ -648,6 +664,9 @@ public abstract class Value {
}
case TIMESTAMP_UTC:
return ValueLong.get(getLong());
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
break;
}
......@@ -682,6 +701,9 @@ public abstract class Value {
// better rounding behavior than BigDecimal.valueOf(f)
return ValueDecimal.get(new BigDecimal(Float.toString(f)));
}
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
break;
}
......@@ -702,6 +724,9 @@ public abstract class Value {
return ValueDouble.get(getBigDecimal().doubleValue());
case FLOAT:
return ValueDouble.get(getFloat());
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
break;
}
......@@ -722,6 +747,9 @@ public abstract class Value {
return ValueFloat.get(getBigDecimal().floatValue());
case DOUBLE:
return ValueFloat.get((float) getDouble());
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
break;
}
......@@ -738,6 +766,9 @@ public abstract class Value {
case TIMESTAMP_UTC:
return ValueDate.fromMillis(
((ValueTimestampUtc) this).getUtcDateTimeMillis());
case TIMESTAMP_TZ:
return ValueDate.fromDateValue(
((ValueTimestampTimeZone) this).getDateValue());
}
break;
}
......@@ -753,6 +784,9 @@ public abstract class Value {
case TIMESTAMP_UTC:
return ValueTime.fromMillis(
((ValueTimestampUtc) this).getUtcDateTimeMillis());
case TIMESTAMP_TZ:
return ValueTime.fromNanos(
((ValueTimestampTimeZone) this).getTimeNanos());
}
break;
}
......@@ -768,6 +802,10 @@ public abstract class Value {
return ValueTimestamp.fromMillisNanos(
((ValueTimestampUtc) this).getUtcDateTimeMillis(),
((ValueTimestampUtc) this).getNanosSinceLastMillis());
case TIMESTAMP_TZ:
return ValueTimestamp.fromDateValueAndNanos(
((ValueTimestampTimeZone) this).getDateValue(),
((ValueTimestampTimeZone) this).getTimeNanos());
}
break;
}
......@@ -793,6 +831,9 @@ public abstract class Value {
return ValueTimestampUtc.fromMillisNanos(
((ValueTimestamp) this).getTimestamp().getTime(),
((ValueTimestamp) this).getTimestamp().getNanos());
case TIMESTAMP_TZ:
// TODO
throw DbException.getUnsupportedException("unimplemented");
}
break;
}
......@@ -835,6 +876,9 @@ public abstract class Value {
(byte) x
});
}
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
break;
}
......@@ -844,6 +888,9 @@ public abstract class Value {
case BLOB:
return ValueJavaObject.getNoCopy(
null, getBytesNoCopy(), getDataHandler());
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
break;
}
......@@ -852,6 +899,9 @@ public abstract class Value {
case BYTES:
return ValueLobDb.createSmallLob(
Value.BLOB, getBytesNoCopy());
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
break;
}
......@@ -859,6 +909,9 @@ public abstract class Value {
switch (getType()) {
case BYTES:
return ValueUuid.get(getBytesNoCopy());
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
}
case GEOMETRY:
......@@ -870,6 +923,9 @@ public abstract class Value {
if (DataType.isGeometry(object)) {
return ValueGeometry.getFromGeometry(object);
}
case TIMESTAMP_TZ:
throw DbException.get(
ErrorCode.DATA_CONVERSION_ERROR_1, getString());
}
}
// conversion by parsing the string value
......@@ -911,6 +967,8 @@ public abstract class Value {
return ValueTimestamp.parse(s.trim());
case TIMESTAMP_UTC:
return ValueTimestampUtc.parse(s.trim());
case TIMESTAMP_TZ:
return ValueTimestampTimeZone.parse(s.trim());
case BYTES:
return ValueBytes.getNoCopy(
StringUtils.convertHexToBytes(s.trim()));
......
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.TimeZone;
import org.h2.api.ErrorCode;
import org.h2.api.TimestampWithTimeZone;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/**
* Implementation of the TIMESTAMP WITH TIMEZONE data type.
*
* @see <a href="https://en.wikipedia.org/wiki/ISO_8601#Time_zone_designators">ISO 8601 Time zone designators</a>
*/
public class ValueTimestampTimeZone extends Value {
/**
* The precision in digits.
*/
public static final int PRECISION = 30;
/**
* The display size of the textual representation of a timestamp.
* Example: 2001-01-01 23:59:59.000 +10:00
*/
static final int DISPLAY_SIZE = 30;
/**
* The default scale for timestamps.
*/
static final int DEFAULT_SCALE = 10;
/**
* A bit field with bits for the year, month, and day (see DateTimeUtils for
* encoding)
*/
private final long dateValue;
/**
* The nanoseconds since midnight.
*/
private final long timeNanos;
/**
* Time zone offset from UTC in minutes, range of -12hours to +12hours
*/
private final short timeZoneOffsetMins;
private ValueTimestampTimeZone(long dateValue, long timeNanos, short timeZoneOffsetMins) {
if (timeNanos < 0 || timeNanos >= 24L * 60 * 60 * 1000 * 1000 * 1000) {
throw new IllegalArgumentException("timeNanos out of range " + timeNanos);
}
if (timeZoneOffsetMins < (-12*60) || timeZoneOffsetMins >= (12*60)) {
throw new IllegalArgumentException("timeZoneOffsetMins out of range " + timeZoneOffsetMins);
}
this.dateValue = dateValue;
this.timeNanos = timeNanos;
this.timeZoneOffsetMins = timeZoneOffsetMins;
}
/**
* Get or create a date value for the given date.
*
* @param dateValue the date value, a bit field with bits for the year,
* month, and day
* @param timeNanos the nanoseconds since midnight
* @return the value
*/
public static ValueTimestampTimeZone fromDateValueAndNanos(long dateValue, long timeNanos, short timeZoneOffsetMins) {
return (ValueTimestampTimeZone) Value.cache(new ValueTimestampTimeZone(dateValue, timeNanos, timeZoneOffsetMins));
}
/**
* Get or create a timestamp value for the given timestamp.
*
* @param timestamp the timestamp
* @return the value
*/
public static ValueTimestampTimeZone get(TimestampWithTimeZone timestamp) {
long ms = timestamp.getTime();
long nanos = timestamp.getNanos() % 1000000;
long dateValue = DateTimeUtils.dateValueFromDate(ms);
nanos += DateTimeUtils.nanosFromDate(ms);
return fromDateValueAndNanos(dateValue, nanos, timestamp.getTimeZoneOffsetMins());
}
/**
* Get or create a timestamp value for the given date/time in millis.
*
* @param ms the milliseconds
* @param nanos the nanoseconds
* @return the value
*/
public static ValueTimestampTimeZone fromMillisNanos(long ms, int nanos, short timeZoneOffsetMins) {
long dateValue = DateTimeUtils.dateValueFromDate(ms);
long timeNanos = nanos + DateTimeUtils.nanosFromDate(ms);
return fromDateValueAndNanos(dateValue, timeNanos, timeZoneOffsetMins);
}
/**
* Get or create a timestamp value for the given date/time in millis.
*
* @param ms the milliseconds
* @return the value
*/
public static ValueTimestampTimeZone fromMillis(long ms, short timeZoneOffsetMins) {
long dateValue = DateTimeUtils.dateValueFromDate(ms);
long nanos = DateTimeUtils.nanosFromDate(ms);
return fromDateValueAndNanos(dateValue, nanos, timeZoneOffsetMins);
}
/**
* Parse a string to a ValueTimestamp. This method supports the format
* +/-year-month-day hour:minute:seconds.fractional and an optional timezone
* part.
*
* @param s the string to parse
* @return the date
*/
public static ValueTimestampTimeZone parse(String s) {
try {
return parseTry(s);
} catch (Exception e) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
e, "TIMESTAMP WITH TIMEZONE", s);
}
}
private static ValueTimestampTimeZone parseTry(String s) {
int dateEnd = s.indexOf(' ');
if (dateEnd < 0) {
// ISO 8601 compatibility
dateEnd = s.indexOf('T');
}
int timeStart;
if (dateEnd < 0) {
dateEnd = s.length();
timeStart = -1;
} else {
timeStart = dateEnd + 1;
}
long dateValue = DateTimeUtils.parseDateValue(s, 0, dateEnd);
long nanos;
short tz_mins = 0;
if (timeStart < 0) {
nanos = 0;
} else {
int timeEnd = s.length();
if (s.endsWith("Z")) {
timeEnd--;
} else {
int timeZoneStart = s.indexOf('+', dateEnd);
if (timeZoneStart < 0) {
timeZoneStart = s.indexOf('-', dateEnd);
}
TimeZone tz = null;
if (timeZoneStart >= 0) {
String tzName = "GMT" + s.substring(timeZoneStart);
tz = TimeZone.getTimeZone(tzName);
if (!tz.getID().startsWith(tzName)) {
throw new IllegalArgumentException(
tzName + " (" + tz.getID() + "?)");
}
timeEnd = timeZoneStart;
} else {
timeZoneStart = s.indexOf(' ', dateEnd + 1);
if (timeZoneStart > 0) {
String tzName = s.substring(timeZoneStart + 1);
tz = TimeZone.getTimeZone(tzName);
if (!tz.getID().startsWith(tzName)) {
throw new IllegalArgumentException(tzName);
}
timeEnd = timeZoneStart;
}
}
if (tz != null) {
long millis = DateTimeUtils.convertDateValueToDate(dateValue).getTime();
tz_mins = (short) (tz.getOffset(millis) / 1000 / 60);
}
}
nanos = DateTimeUtils.parseTimeNanos(s, dateEnd + 1, timeEnd, true);
}
return ValueTimestampTimeZone.fromDateValueAndNanos(dateValue, nanos, tz_mins);
}
/**
* A bit field with bits for the year, month, and day (see DateTimeUtils for
* encoding).
*
* @return the data value
*/
public long getDateValue() {
return dateValue;
}
/**
* The nanoseconds since midnight.
*
* @return the nanoseconds
*/
public long getTimeNanos() {
return timeNanos;
}
/**
* The timezone offset in minutes.
*
* @return the offset
*/
public short getTimeZoneOffsetMins() {
return timeZoneOffsetMins;
}
@Override
public Timestamp getTimestamp() {
Timestamp ts = DateTimeUtils.convertDateValueToTimestamp(dateValue, timeNanos);
return new TimestampWithTimeZone(ts.getTime(), ts.getNanos(), getTimeZoneOffsetMins());
}
@Override
public int getType() {
return Value.TIMESTAMP_TZ;
}
@Override
public String getString() {
StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
ValueDate.appendDate(buff, dateValue);
buff.append(' ');
ValueTime.appendTime(buff, timeNanos, true);
buff.append(' ');
appendTimeZone(buff, timeZoneOffsetMins);
return buff.toString();
}
/**
* Append a time zone to the string builder.
*
* @param buff the target string builder
* @param tz the time zone in minutes
*/
private static void appendTimeZone(StringBuilder buff, short tz) {
if (tz < 0) {
buff.append('-');
tz = (short)-tz;
}
int hours = tz / 60;
tz -= hours * 60;
int mins = tz;
StringUtils.appendZeroPadded(buff, 2, hours);
if (mins != 0) {
buff.append(':');
StringUtils.appendZeroPadded(buff, 2, mins);
}
}
@Override
public String getSQL() {
return "TIMESTAMP WITH TIMEZONE '" + getString() + "'";
}
@Override
public long getPrecision() {
return PRECISION;
}
@Override
public int getScale() {
return DEFAULT_SCALE;
}
@Override
public int getDisplaySize() {
return DISPLAY_SIZE;
}
@Override
public Value convertScale(boolean onlyToSmallerScale, int targetScale) {
if (targetScale >= DEFAULT_SCALE) {
return this;
}
if (targetScale < 0) {
throw DbException.getInvalidValueException("scale", targetScale);
}
long n = timeNanos;
BigDecimal bd = BigDecimal.valueOf(n);
bd = bd.movePointLeft(9);
bd = ValueDecimal.setScale(bd, targetScale);
bd = bd.movePointRight(9);
long n2 = bd.longValue();
if (n2 == n) {
return this;
}
return fromDateValueAndNanos(dateValue, n2, timeZoneOffsetMins);
}
@Override
protected int compareSecure(Value o, CompareMode mode) {
ValueTimestampTimeZone t = (ValueTimestampTimeZone) o;
int c = MathUtils.compareLong(dateValue, t.dateValue);
if (c != 0) {
return c;
}
c = MathUtils.compareLong(timeNanos, t.timeNanos);
if (c != 0) {
return c;
}
return MathUtils.compareInt(timeZoneOffsetMins, t.timeZoneOffsetMins);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (!(other instanceof ValueTimestampTimeZone)) {
return false;
}
ValueTimestampTimeZone x = (ValueTimestampTimeZone) other;
return dateValue == x.dateValue && timeNanos == x.timeNanos && timeZoneOffsetMins == x.timeZoneOffsetMins;
}
@Override
public int hashCode() {
return (int) (dateValue ^ (dateValue >>> 32) ^ timeNanos ^ (timeNanos >>> 32) ^ timeZoneOffsetMins);
}
@Override
public Object getObject() {
return getTimestamp();
}
@Override
public void set(PreparedStatement prep, int parameterIndex)
throws SQLException {
prep.setString(parameterIndex, getString());
}
@Override
public Value add(Value v) {
throw DbException.getUnsupportedException("manipulating TIMESTAMP WITH TIMEZONE values is unsupported");
}
@Override
public Value subtract(Value v) {
throw DbException.getUnsupportedException("manipulating TIMESTAMP WITH TIMEZONE values is unsupported");
}
}
......@@ -24,6 +24,7 @@ import org.h2.test.db.TestCases;
import org.h2.test.db.TestCheckpoint;
import org.h2.test.db.TestCluster;
import org.h2.test.db.TestCompatibility;
import org.h2.test.db.TestCompatibilityOracle;
import org.h2.test.db.TestCsv;
import org.h2.test.db.TestDateStorage;
import org.h2.test.db.TestDeadlock;
......@@ -46,7 +47,6 @@ import org.h2.test.db.TestMultiThread;
import org.h2.test.db.TestMultiThreadedKernel;
import org.h2.test.db.TestOpenClose;
import org.h2.test.db.TestOptimizations;
import org.h2.test.db.TestCompatibilityOracle;
import org.h2.test.db.TestOptimizerHints;
import org.h2.test.db.TestOutOfMemory;
import org.h2.test.db.TestPowerOff;
......@@ -178,8 +178,8 @@ import org.h2.test.unit.TestFileSystem;
import org.h2.test.unit.TestFtp;
import org.h2.test.unit.TestIntArray;
import org.h2.test.unit.TestIntIntHashMap;
import org.h2.test.unit.TestJmx;
import org.h2.test.unit.TestIntPerfectHash;
import org.h2.test.unit.TestJmx;
import org.h2.test.unit.TestLocale;
import org.h2.test.unit.TestMathUtils;
import org.h2.test.unit.TestModifyOnWrite;
......@@ -203,6 +203,8 @@ import org.h2.test.unit.TestSort;
import org.h2.test.unit.TestStreams;
import org.h2.test.unit.TestStringCache;
import org.h2.test.unit.TestStringUtils;
import org.h2.test.unit.TestTimeStampUtc;
import org.h2.test.unit.TestTimeStampWithTimeZone;
import org.h2.test.unit.TestTools;
import org.h2.test.unit.TestTraceSystem;
import org.h2.test.unit.TestUtils;
......@@ -851,6 +853,8 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestSort());
addTest(new TestStreams());
addTest(new TestStringUtils());
addTest(new TestTimeStampUtc());
addTest(new TestTimeStampWithTimeZone());
addTest(new TestTraceSystem());
addTest(new TestUpgrade());
addTest(new TestUsingIndex());
......
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, and the
* EPL 1.0 (http://h2database.com/html/license.html). Initial Developer: H2
* Group
*/
package org.h2.test.unit;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.api.TimestampWithTimeZone;
import org.h2.test.TestBase;
/**
*/
public class TestTimeStampWithTimeZone extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public void test() throws SQLException {
deleteDb("timestamp_tz");
test1();
deleteDb("timestamp_tz");
}
private void test1() throws SQLException {
Connection conn = getConnection("timestamp_tz");
Statement stat = conn.createStatement();
stat.execute("create table test(id identity, t1 timestamp with timezone)");
stat.execute("insert into test(t1) values('1970-01-01 12:00:00.00+00:15')");
ResultSet rs = stat.executeQuery("select t1 from test");
rs.next();
assertTrue(new TimestampWithTimeZone(36000000, 00, (short)15).equals(rs.getTimestamp(1)));
conn.close();
}
}
......@@ -12,7 +12,6 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.Random;
import org.h2.api.JavaObjectSerializer;
import org.h2.engine.Constants;
import org.h2.store.DataHandler;
......@@ -47,6 +46,7 @@ import org.h2.value.ValueStringFixed;
import org.h2.value.ValueStringIgnoreCase;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
import org.h2.value.ValueTimestampUtc;
import org.h2.value.ValueUuid;
......@@ -166,6 +166,8 @@ public class TestValueMemory extends TestBase implements DataHandler {
return ValueTimestamp.fromMillis(random.nextLong());
case Value.TIMESTAMP_UTC:
return ValueTimestampUtc.fromMillis(random.nextLong());
case Value.TIMESTAMP_TZ:
return ValueTimestampTimeZone.fromMillis(random.nextLong(), (short)0);
case Value.BYTES:
return ValueBytes.get(randomBytes(random.nextInt(1000)));
case Value.STRING:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论