提交 8224ba44 authored 作者: Thomas Mueller's avatar Thomas Mueller

Date, time, and timestamp data type processing has been re-implemented.

上级 69e5bb70
...@@ -659,7 +659,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat ...@@ -659,7 +659,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
if (x == null) { if (x == null) {
setParameter(parameterIndex, ValueNull.INSTANCE); setParameter(parameterIndex, ValueNull.INSTANCE);
} else { } else {
setParameter(parameterIndex, DateTimeUtils.convertTimestampToUniversal(x, calendar)); setParameter(parameterIndex, DateTimeUtils.convertTimestampToUTC(x, calendar));
} }
} catch (Exception e) { } catch (Exception e) {
throw logAndConvert(e); throw logAndConvert(e);
......
...@@ -697,19 +697,16 @@ public abstract class Value { ...@@ -697,19 +697,16 @@ public abstract class Value {
// because a date has the time set to 0, the result will be 0 // because a date has the time set to 0, the result will be 0
return ValueTime.get(0); return ValueTime.get(0);
case TIMESTAMP: case TIMESTAMP:
// need to normalize the year, month and day return ValueTime.get(((ValueTimestamp) this).getNanos());
return ValueTime.get(new Time(getTimestamp().getTime()));
} }
break; break;
} }
case TIMESTAMP: { case TIMESTAMP: {
switch (getType()) { switch (getType()) {
case TIME: case TIME:
// TODO return DateTimeUtils.normalizeTimestamp(0, ((ValueTime) this).getNanos());
return ValueTimestamp.get(new Timestamp(getTime().getTime()));
case DATE: case DATE:
// TODO return ValueTimestamp.get(((ValueDate) this).getDateValue(), 0);
return ValueTimestamp.get(new Timestamp(getDate().getTime()));
} }
break; break;
} }
......
...@@ -9,8 +9,11 @@ package org.h2.value; ...@@ -9,8 +9,11 @@ package org.h2.value;
import java.sql.Date; import java.sql.Date;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/** /**
* Implementation of the DATE data type. * Implementation of the DATE data type.
...@@ -41,8 +44,12 @@ public class ValueDate extends Value { ...@@ -41,8 +44,12 @@ public class ValueDate extends Value {
* @return the date * @return the date
*/ */
public static ValueDate parse(String s) { public static ValueDate parse(String s) {
Value x = DateTimeUtils.parse(s, Value.DATE); try {
return (ValueDate) Value.cache(x); return get(DateTimeUtils.parseDateValue(s, 0, s.length()));
} catch (Exception e) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
e, "DATE", s);
}
} }
public Date getDate() { public Date getDate() {
...@@ -67,7 +74,7 @@ public class ValueDate extends Value { ...@@ -67,7 +74,7 @@ public class ValueDate extends Value {
public String getString() { public String getString() {
StringBuilder buff = new StringBuilder(DISPLAY_SIZE); StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
DateTimeUtils.appendDate(buff, dateValue); appendDate(buff, dateValue);
return buff.toString(); return buff.toString();
} }
...@@ -94,8 +101,7 @@ public class ValueDate extends Value { ...@@ -94,8 +101,7 @@ public class ValueDate extends Value {
* @return the value * @return the value
*/ */
public static ValueDate get(Date date) { public static ValueDate get(Date date) {
long x = DateTimeUtils.dateValueFromDate(date.getTime()); return get(DateTimeUtils.dateValueFromDate(date.getTime()));
return get(x);
} }
/** /**
...@@ -119,4 +125,19 @@ public class ValueDate extends Value { ...@@ -119,4 +125,19 @@ public class ValueDate extends Value {
return other instanceof ValueDate && dateValue == (((ValueDate) other).dateValue); return other instanceof ValueDate && dateValue == (((ValueDate) other).dateValue);
} }
static void appendDate(StringBuilder buff, long dateValue) {
int y = DateTimeUtils.yearFromDateValue(dateValue);
int m = DateTimeUtils.monthFromDateValue(dateValue);
int d = DateTimeUtils.dayFromDateValue(dateValue);
if (y > 0 && y < 10000) {
StringUtils.appendZeroPadded(buff, 4, y);
} else {
buff.append(y);
}
buff.append('-');
StringUtils.appendZeroPadded(buff, 2, m);
buff.append('-');
StringUtils.appendZeroPadded(buff, 2, d);
}
} }
...@@ -9,8 +9,11 @@ package org.h2.value; ...@@ -9,8 +9,11 @@ package org.h2.value;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Time; import java.sql.Time;
import org.h2.constant.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/** /**
* Implementation of the TIME data type. * Implementation of the TIME data type.
...@@ -40,8 +43,14 @@ public class ValueTime extends Value { ...@@ -40,8 +43,14 @@ public class ValueTime extends Value {
* @param s the string to parse * @param s the string to parse
* @return the time * @return the time
*/ */
public static ValueTime parse(String s) { public static ValueTime parse(String s) {
return new ValueTime(DateTimeUtils.parseTime(s)); try {
return get(DateTimeUtils.parseTimeNanos(s, 0, s.length(), false));
} catch (Exception e) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
e, "TIME", s);
}
} }
public Time getTime() { public Time getTime() {
...@@ -66,7 +75,7 @@ public class ValueTime extends Value { ...@@ -66,7 +75,7 @@ public class ValueTime extends Value {
public String getString() { public String getString() {
StringBuilder buff = new StringBuilder(DISPLAY_SIZE); StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
DateTimeUtils.appendTime(buff, nanos, false); appendTime(buff, nanos, false);
return buff.toString(); return buff.toString();
} }
...@@ -93,8 +102,7 @@ public class ValueTime extends Value { ...@@ -93,8 +102,7 @@ public class ValueTime extends Value {
* @return the value * @return the value
*/ */
public static ValueTime get(Time time) { public static ValueTime get(Time time) {
long x = DateTimeUtils.nanosFromDate(time.getTime()); return get(DateTimeUtils.nanosFromDate(time.getTime()));
return get(x);
} }
/** /**
...@@ -136,4 +144,46 @@ public class ValueTime extends Value { ...@@ -136,4 +144,46 @@ public class ValueTime extends Value {
return ValueTime.get((long) (nanos / v.getDouble())); return ValueTime.get((long) (nanos / v.getDouble()));
} }
public int getSignum() {
return Long.signum(nanos);
}
public Value negate() {
return ValueTime.get(-nanos);
}
static void appendTime(StringBuilder buff, long n, boolean alwaysAddMillis) {
if (n < 0) {
buff.append('-');
n = -n;
}
long ms = n / 1000000;
n -= ms * 1000000;
long s = ms / 1000;
ms -= s * 1000;
long m = s / 60;
s -= m * 60;
long h = m / 60;
m -= h * 60;
StringUtils.appendZeroPadded(buff, 2, h);
buff.append(':');
StringUtils.appendZeroPadded(buff, 2, m);
buff.append(':');
StringUtils.appendZeroPadded(buff, 2, s);
if (alwaysAddMillis || ms > 0 || n > 0) {
buff.append('.');
int start = buff.length();
StringUtils.appendZeroPadded(buff, 3, ms);
if (n > 0) {
StringUtils.appendZeroPadded(buff, 6, n);
}
for (int i = buff.length() - 1; i > start; i--) {
if (buff.charAt(i) != '0') {
break;
}
buff.deleteCharAt(i);
}
}
}
} }
...@@ -7,9 +7,13 @@ ...@@ -7,9 +7,13 @@
package org.h2.value; package org.h2.value;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.Date;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Calendar;
import java.util.TimeZone;
import org.h2.constant.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
...@@ -71,14 +75,85 @@ public class ValueTimestamp extends Value { ...@@ -71,14 +75,85 @@ public class ValueTimestamp extends Value {
} }
/** /**
* Parse a string to a ValueTimestamp. * 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 * @param s the string to parse
* @return the date * @return the date
*/ */
public static ValueTimestamp parse(String s) { public static ValueTimestamp parse(String s) {
Value x = DateTimeUtils.parse(s, Value.TIMESTAMP); try {
return (ValueTimestamp) Value.cache(x); return parseTry(s);
} catch (Exception e) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
e, "TIMESTAMP", s);
}
}
private static ValueTimestamp 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;
if (timeStart < 0) {
nanos = 0;
} else {
int timeEnd = s.length();
TimeZone tz = null;
if (s.endsWith("Z")) {
tz = TimeZone.getTimeZone("UTC");
timeEnd--;
while (s.charAt(timeEnd - 1) == ' ') {
timeEnd--;
}
} else {
int timeZoneStart = s.indexOf('+', dateEnd);
if (timeZoneStart < 0) {
timeZoneStart = s.indexOf('-', dateEnd);
}
if (timeZoneStart >= 0) {
String tzName = "GMT" + s.substring(timeZoneStart);
tz = TimeZone.getTimeZone(tzName);
if (!tz.getID().startsWith(tzName)) {
throw new IllegalArgumentException(tzName);
}
timeEnd = timeZoneStart;
while (s.charAt(timeEnd - 1) == ' ') {
timeEnd--;
}
}
}
nanos = DateTimeUtils.parseTimeNanos(s, dateEnd + 1, timeEnd, true);
if (tz != null) {
int year = DateTimeUtils.yearFromDateValue(dateValue);
int month = DateTimeUtils.monthFromDateValue(dateValue);
int day = DateTimeUtils.dayFromDateValue(dateValue);
long ms = nanos / 1000000;
nanos -= ms * 1000000;
long second = ms / 1000;
ms -= second * 1000;
int minute = (int) (second / 60);
second -= minute * 60;
int hour = minute / 60;
minute -= hour * 60;
long millis = DateTimeUtils.getMillis(tz, year, month, day, hour, minute, (int) second, (int) ms);
ms = DateTimeUtils.convertToLocal(new Date(millis), Calendar.getInstance(TimeZone.getTimeZone("UTC")));
dateValue = DateTimeUtils.dateValueFromDate(ms);
nanos += (ms - DateTimeUtils.absoluteDayFromDateValue(dateValue) * DateTimeUtils.MILLIS_PER_DAY) * 1000000;
}
}
return ValueTimestamp.get(dateValue, nanos);
} }
public int getType() { public int getType() {
...@@ -97,9 +172,9 @@ public class ValueTimestamp extends Value { ...@@ -97,9 +172,9 @@ public class ValueTimestamp extends Value {
public String getString() { public String getString() {
// TODO verify display size // TODO verify display size
StringBuilder buff = new StringBuilder(DISPLAY_SIZE); StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
DateTimeUtils.appendDate(buff, dateValue); ValueDate.appendDate(buff, dateValue);
buff.append(' '); buff.append(' ');
DateTimeUtils.appendTime(buff, nanos, true); ValueTime.appendTime(buff, nanos, true);
return buff.toString(); return buff.toString();
} }
...@@ -138,10 +213,10 @@ public class ValueTimestamp extends Value { ...@@ -138,10 +213,10 @@ public class ValueTimestamp extends Value {
} }
public Value convertScale(boolean onlyToSmallerScale, int targetScale) { public Value convertScale(boolean onlyToSmallerScale, int targetScale) {
if (targetScale == DEFAULT_SCALE) { if (targetScale >= DEFAULT_SCALE) {
return this; return this;
} }
if (targetScale < 0 || targetScale > DEFAULT_SCALE) { if (targetScale < 0) {
throw DbException.getInvalidValueException("scale", targetScale); throw DbException.getInvalidValueException("scale", targetScale);
} }
long n = nanos; long n = nanos;
...@@ -175,7 +250,7 @@ public class ValueTimestamp extends Value { ...@@ -175,7 +250,7 @@ public class ValueTimestamp extends Value {
ValueTimestamp t = (ValueTimestamp) v.convertTo(Value.TIMESTAMP); ValueTimestamp t = (ValueTimestamp) v.convertTo(Value.TIMESTAMP);
long d1 = DateTimeUtils.absoluteDayFromDateValue(dateValue); long d1 = DateTimeUtils.absoluteDayFromDateValue(dateValue);
long d2 = DateTimeUtils.absoluteDayFromDateValue(t.dateValue); long d2 = DateTimeUtils.absoluteDayFromDateValue(t.dateValue);
return DateTimeUtils.normalize(d1 + d2, nanos + t.nanos); return DateTimeUtils.normalizeTimestamp(d1 + d2, nanos + t.nanos);
} }
public Value subtract(Value v) { public Value subtract(Value v) {
...@@ -183,7 +258,7 @@ public class ValueTimestamp extends Value { ...@@ -183,7 +258,7 @@ public class ValueTimestamp extends Value {
ValueTimestamp t = (ValueTimestamp) v.convertTo(Value.TIMESTAMP); ValueTimestamp t = (ValueTimestamp) v.convertTo(Value.TIMESTAMP);
long d1 = DateTimeUtils.absoluteDayFromDateValue(dateValue); long d1 = DateTimeUtils.absoluteDayFromDateValue(dateValue);
long d2 = DateTimeUtils.absoluteDayFromDateValue(t.dateValue); long d2 = DateTimeUtils.absoluteDayFromDateValue(t.dateValue);
return DateTimeUtils.normalize(d1 - d2, nanos - t.nanos); return DateTimeUtils.normalizeTimestamp(d1 - d2, nanos - t.nanos);
} }
} }
...@@ -18,7 +18,7 @@ import org.h2.constant.SysProperties; ...@@ -18,7 +18,7 @@ import org.h2.constant.SysProperties;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.unit.TestDate; import org.h2.test.unit.TestDate;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.value.Value; import org.h2.value.ValueTimestamp;
/** /**
* Tests the date transfer and storage. * Tests the date transfer and storage.
...@@ -111,7 +111,7 @@ public class TestDateStorage extends TestBase { ...@@ -111,7 +111,7 @@ public class TestDateStorage extends TestBase {
} }
private static void testCurrentTimeZone() { private static void testCurrentTimeZone() {
for (int year = 1970; year < 2050; year += 3) { for (int year = 1890; year < 2050; year += 3) {
for (int month = 1; month <= 12; month++) { for (int month = 1; month <= 12; month++) {
for (int day = 1; day < 29; day++) { for (int day = 1; day < 29; day++) {
for (int hour = 0; hour < 24; hour++) { for (int hour = 0; hour < 24; hour++) {
...@@ -123,8 +123,7 @@ public class TestDateStorage extends TestBase { ...@@ -123,8 +123,7 @@ public class TestDateStorage extends TestBase {
} }
private static void test(int year, int month, int day, int hour) { private static void test(int year, int month, int day, int hour) {
DateTimeUtils.parse(year + "-" + month + "-" + day + " " + hour + ":00:00", ValueTimestamp.parse(year + "-" + month + "-" + day + " " + hour + ":00:00");
Value.TIMESTAMP);
} }
private void testAllTimeZones() throws SQLException { private void testAllTimeZones() throws SQLException {
......
...@@ -7269,12 +7269,12 @@ SELECT XD+1, XD-1, XD-XD FROM TEST; ...@@ -7269,12 +7269,12 @@ SELECT XD+1, XD-1, XD-XD FROM TEST;
SELECT ID, CAST(XT AS DATE) T2D, CAST(XTS AS DATE) TS2D, SELECT ID, CAST(XT AS DATE) T2D, CAST(XTS AS DATE) TS2D,
CAST(XD AS TIME) D2T, CAST(XTS AS TIME) TS2T, CAST(XD AS TIME) D2T, CAST(XTS AS TIME) TS2T,
CAST(XT AS TIMESTAMP) D2TS, CAST(XD AS TIMESTAMP) D2TS FROM TEST; CAST(XT AS TIMESTAMP) D2TS, CAST(XD AS TIMESTAMP) D2TS FROM TEST;
> ID T2D TS2D D2T TS2T D2TS D2TS > ID T2D TS2D D2T TS2T D2TS D2TS
> ---- ---------- ---------- -------- ------------ --------------------- --------------------- > ---- ---------- ---------- -------- ------------------ --------------------- ---------------------
> 0 1970-01-01 0002-03-04 00:00:00 00:00:00 1970-01-01 00:00:00.0 0001-02-03 00:00:00.0 > 0 1970-01-01 0002-03-04 00:00:00 00:00:00 1970-01-01 00:00:00.0 0001-02-03 00:00:00.0
> 1 1970-01-01 0007-08-09 00:00:00 00:01:02 1970-01-01 01:02:03.0 0004-05-06 00:00:00.0 > 1 1970-01-01 0007-08-09 00:00:00 00:01:02 1970-01-01 01:02:03.0 0004-05-06 00:00:00.0
> 2 1970-01-01 1999-12-31 00:00:00 23:59:59.123 1970-01-01 23:59:59.0 1999-12-31 00:00:00.0 > 2 1970-01-01 1999-12-31 00:00:00 23:59:59.123456789 1970-01-01 23:59:59.0 1999-12-31 00:00:00.0
> null null null null null null null > null null null null null null null
> rows: 4 > rows: 4
SCRIPT SIMPLE NOPASSWORDS NOSETTINGS; SCRIPT SIMPLE NOPASSWORDS NOSETTINGS;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论