提交 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
if (x == null) {
setParameter(parameterIndex, ValueNull.INSTANCE);
} else {
setParameter(parameterIndex, DateTimeUtils.convertTimestampToUniversal(x, calendar));
setParameter(parameterIndex, DateTimeUtils.convertTimestampToUTC(x, calendar));
}
} catch (Exception e) {
throw logAndConvert(e);
......
......@@ -697,19 +697,16 @@ public abstract class Value {
// because a date has the time set to 0, the result will be 0
return ValueTime.get(0);
case TIMESTAMP:
// need to normalize the year, month and day
return ValueTime.get(new Time(getTimestamp().getTime()));
return ValueTime.get(((ValueTimestamp) this).getNanos());
}
break;
}
case TIMESTAMP: {
switch (getType()) {
case TIME:
// TODO
return ValueTimestamp.get(new Timestamp(getTime().getTime()));
return DateTimeUtils.normalizeTimestamp(0, ((ValueTime) this).getNanos());
case DATE:
// TODO
return ValueTimestamp.get(new Timestamp(getDate().getTime()));
return ValueTimestamp.get(((ValueDate) this).getDateValue(), 0);
}
break;
}
......
......@@ -9,8 +9,11 @@ package org.h2.value;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/**
* Implementation of the DATE data type.
......@@ -41,8 +44,12 @@ public class ValueDate extends Value {
* @return the date
*/
public static ValueDate parse(String s) {
Value x = DateTimeUtils.parse(s, Value.DATE);
return (ValueDate) Value.cache(x);
try {
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() {
......@@ -67,7 +74,7 @@ public class ValueDate extends Value {
public String getString() {
StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
DateTimeUtils.appendDate(buff, dateValue);
appendDate(buff, dateValue);
return buff.toString();
}
......@@ -94,8 +101,7 @@ public class ValueDate extends Value {
* @return the value
*/
public static ValueDate get(Date date) {
long x = DateTimeUtils.dateValueFromDate(date.getTime());
return get(x);
return get(DateTimeUtils.dateValueFromDate(date.getTime()));
}
/**
......@@ -119,4 +125,19 @@ public class ValueDate extends Value {
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;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Time;
import org.h2.constant.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/**
* Implementation of the TIME data type.
......@@ -40,8 +43,14 @@ public class ValueTime extends Value {
* @param s the string to parse
* @return the time
*/
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() {
......@@ -66,7 +75,7 @@ public class ValueTime extends Value {
public String getString() {
StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
DateTimeUtils.appendTime(buff, nanos, false);
appendTime(buff, nanos, false);
return buff.toString();
}
......@@ -93,8 +102,7 @@ public class ValueTime extends Value {
* @return the value
*/
public static ValueTime get(Time time) {
long x = DateTimeUtils.nanosFromDate(time.getTime());
return get(x);
return get(DateTimeUtils.nanosFromDate(time.getTime()));
}
/**
......@@ -136,4 +144,46 @@ public class ValueTime extends Value {
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 @@
package org.h2.value;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
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.util.DateTimeUtils;
import org.h2.util.MathUtils;
......@@ -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
* @return the date
*/
public static ValueTimestamp parse(String s) {
Value x = DateTimeUtils.parse(s, Value.TIMESTAMP);
return (ValueTimestamp) Value.cache(x);
try {
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() {
......@@ -97,9 +172,9 @@ public class ValueTimestamp extends Value {
public String getString() {
// TODO verify display size
StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
DateTimeUtils.appendDate(buff, dateValue);
ValueDate.appendDate(buff, dateValue);
buff.append(' ');
DateTimeUtils.appendTime(buff, nanos, true);
ValueTime.appendTime(buff, nanos, true);
return buff.toString();
}
......@@ -138,10 +213,10 @@ public class ValueTimestamp extends Value {
}
public Value convertScale(boolean onlyToSmallerScale, int targetScale) {
if (targetScale == DEFAULT_SCALE) {
if (targetScale >= DEFAULT_SCALE) {
return this;
}
if (targetScale < 0 || targetScale > DEFAULT_SCALE) {
if (targetScale < 0) {
throw DbException.getInvalidValueException("scale", targetScale);
}
long n = nanos;
......@@ -175,7 +250,7 @@ public class ValueTimestamp extends Value {
ValueTimestamp t = (ValueTimestamp) v.convertTo(Value.TIMESTAMP);
long d1 = DateTimeUtils.absoluteDayFromDateValue(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) {
......@@ -183,7 +258,7 @@ public class ValueTimestamp extends Value {
ValueTimestamp t = (ValueTimestamp) v.convertTo(Value.TIMESTAMP);
long d1 = DateTimeUtils.absoluteDayFromDateValue(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;
import org.h2.test.TestBase;
import org.h2.test.unit.TestDate;
import org.h2.util.DateTimeUtils;
import org.h2.value.Value;
import org.h2.value.ValueTimestamp;
/**
* Tests the date transfer and storage.
......@@ -111,7 +111,7 @@ public class TestDateStorage extends TestBase {
}
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 day = 1; day < 29; day++) {
for (int hour = 0; hour < 24; hour++) {
......@@ -123,8 +123,7 @@ public class TestDateStorage extends TestBase {
}
private static void test(int year, int month, int day, int hour) {
DateTimeUtils.parse(year + "-" + month + "-" + day + " " + hour + ":00:00",
Value.TIMESTAMP);
ValueTimestamp.parse(year + "-" + month + "-" + day + " " + hour + ":00:00");
}
private void testAllTimeZones() throws SQLException {
......
......@@ -7270,10 +7270,10 @@ SELECT ID, CAST(XT AS DATE) T2D, CAST(XTS AS DATE) TS2D,
CAST(XD AS TIME) D2T, CAST(XTS AS TIME) TS2T,
CAST(XT AS TIMESTAMP) D2TS, CAST(XD AS TIMESTAMP) D2TS FROM TEST;
> 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
> 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
> rows: 4
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论