提交 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);
......
...@@ -18,7 +18,6 @@ import java.util.Locale; ...@@ -18,7 +18,6 @@ import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueDate; import org.h2.value.ValueDate;
import org.h2.value.ValueTime; import org.h2.value.ValueTime;
...@@ -29,26 +28,19 @@ import org.h2.value.ValueTimestamp; ...@@ -29,26 +28,19 @@ import org.h2.value.ValueTimestamp;
*/ */
public class DateTimeUtils { public class DateTimeUtils {
private static final int DEFAULT_YEAR = 1970; public static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000L;
private static final int DEFAULT_MONTH = 1;
private static final int DEFAULT_DAY = 1; private static final long NANOS_PER_DAY = MILLIS_PER_DAY * 1000000;
private static final int DEFAULT_HOUR = 0;
private static final int SHIFT_YEAR = 9; private static final int SHIFT_YEAR = 9;
private static final int SHIFT_MONTH = 5; private static final int SHIFT_MONTH = 5;
private static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000L; private static final int[] NORMAL_DAYS_PER_MONTH = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
private static final long NANOS_PER_DAY = MILLIS_PER_DAY * 1000000;
private static final int[] NORMAL_DAYS_PER_MONTH = {
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
/** /**
* Offsets of month within a year, starting with March, April,... * Offsets of month within a year, starting with March, April,...
*/ */
private static final int[] DAYS_OFFSET = private static final int[] DAYS_OFFSET = { 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366 };
{ 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366 };
private static int zoneOffset; private static int zoneOffset;
private static Calendar cachedCalendar; private static Calendar cachedCalendar;
...@@ -77,6 +69,28 @@ public class DateTimeUtils { ...@@ -77,6 +69,28 @@ public class DateTimeUtils {
return cachedCalendar; return cachedCalendar;
} }
/**
* Convert the date to the specified time zone.
*
* @param x the date
* @param calendar the calendar
* @return the date using the correct time zone
*/
public static Date convertDateToCalendar(Date x, Calendar calendar) {
return x == null ? null : new Date(convertToLocal(x, calendar));
}
/**
* Convert the time to the specified time zone.
*
* @param x the time
* @param calendar the calendar
* @return the time using the correct time zone
*/
public static Time convertTimeToCalendar(Time x, Calendar calendar) {
return x == null ? null : new Time(convertToLocal(x, calendar));
}
/** /**
* Convert the timestamp to the specified time zone. * Convert the timestamp to the specified time zone.
* *
...@@ -86,7 +100,7 @@ public class DateTimeUtils { ...@@ -86,7 +100,7 @@ public class DateTimeUtils {
*/ */
public static Timestamp convertTimestampToCalendar(Timestamp x, Calendar calendar) { public static Timestamp convertTimestampToCalendar(Timestamp x, Calendar calendar) {
if (x != null) { if (x != null) {
Timestamp y = new Timestamp(getLocalTime(x, calendar)); Timestamp y = new Timestamp(convertToLocal(x, calendar));
// fix the nano seconds // fix the nano seconds
y.setNanos(x.getNanos()); y.setNanos(x.getNanos());
x = y; x = y;
...@@ -102,7 +116,7 @@ public class DateTimeUtils { ...@@ -102,7 +116,7 @@ public class DateTimeUtils {
* @return the date in UTC * @return the date in UTC
*/ */
public static Value convertDateToUTC(Date x, Calendar source) { public static Value convertDateToUTC(Date x, Calendar source) {
return ValueDate.get(new Date(getUniversalTime(source, x))); return ValueDate.get(new Date(convertToUTC(x, source)));
} }
/** /**
...@@ -113,7 +127,7 @@ public class DateTimeUtils { ...@@ -113,7 +127,7 @@ public class DateTimeUtils {
* @return the time in UTC * @return the time in UTC
*/ */
public static Value convertTimeToUTC(Time x, Calendar source) { public static Value convertTimeToUTC(Time x, Calendar source) {
return ValueTime.get(new Time(getUniversalTime(source, x))); return ValueTime.get(new Time(convertToUTC(x, source)));
} }
/** /**
...@@ -123,8 +137,8 @@ public class DateTimeUtils { ...@@ -123,8 +137,8 @@ public class DateTimeUtils {
* @param source the calendar * @param source the calendar
* @return the timestamp in UTC * @return the timestamp in UTC
*/ */
public static Value convertTimestampToUniversal(Timestamp x, Calendar source) { public static Value convertTimestampToUTC(Timestamp x, Calendar source) {
Timestamp y = new Timestamp(getUniversalTime(source, x)); Timestamp y = new Timestamp(convertToUTC(x, source));
// fix the nano seconds // fix the nano seconds
y.setNanos(x.getNanos()); y.setNanos(x.getNanos());
return ValueTimestamp.get(y); return ValueTimestamp.get(y);
...@@ -133,11 +147,11 @@ public class DateTimeUtils { ...@@ -133,11 +147,11 @@ public class DateTimeUtils {
/** /**
* Convert the date value to UTC using the given calendar. * Convert the date value to UTC using the given calendar.
* *
* @param source the source calendar
* @param x the date * @param x the date
* @param source the source calendar
* @return the UTC number of milliseconds. * @return the UTC number of milliseconds.
*/ */
private static long getUniversalTime(Calendar source, java.util.Date x) { private static long convertToUTC(java.util.Date x, Calendar source) {
if (source == null) { if (source == null) {
throw DbException.getInvalidValueException("calendar", null); throw DbException.getInvalidValueException("calendar", null);
} }
...@@ -150,7 +164,7 @@ public class DateTimeUtils { ...@@ -150,7 +164,7 @@ public class DateTimeUtils {
} }
} }
private static long getLocalTime(java.util.Date x, Calendar target) { public static long convertToLocal(java.util.Date x, Calendar target) {
if (target == null) { if (target == null) {
throw DbException.getInvalidValueException("calendar", null); throw DbException.getInvalidValueException("calendar", null);
} }
...@@ -174,177 +188,67 @@ public class DateTimeUtils { ...@@ -174,177 +188,67 @@ public class DateTimeUtils {
to.set(Calendar.MILLISECOND, from.get(Calendar.MILLISECOND)); to.set(Calendar.MILLISECOND, from.get(Calendar.MILLISECOND));
} }
/** public static long parseDateValue(String s, int start, int end) {
* Convert the date to the specified time zone. if (s.charAt(start) == '+') {
* // +year
* @param x the date start++;
* @param calendar the calendar
* @return the date using the correct time zone
*/
public static Date convertDateToCalendar(Date x, Calendar calendar) {
return x == null ? null : new Date(getLocalTime(x, calendar));
}
/**
* Convert the time to the specified time zone.
*
* @param x the time
* @param calendar the calendar
* @return the time using the correct time zone
*/
public static Time convertTimeToCalendar(Time x, Calendar calendar) {
return x == null ? null : new Time(getLocalTime(x, calendar));
}
/**
* Parse a date, time or timestamp value. This method supports the format
* +/-year-month-day hour:minute:seconds.fractional and an optional timezone
* part.
*
* @param original the original string
* @param type the value type (Value.TIME, TIMESTAMP, or DATE)
* @return the date object
*/
public static Value parse(String original, int type) {
String s = original;
if (s == null) {
return null;
} }
try { // start at position 1 to support "-year"
int timeStart; int s1 = s.indexOf('-', start + 1);
TimeZone tz = null; int s2 = s.indexOf('-', s1 + 1);
if (type == Value.TIME) { if (s1 <= 0 || s2 <= s1) {
timeStart = 0; throw new IllegalArgumentException(s);
} else { }
timeStart = s.indexOf(' ') + 1; int year = Integer.parseInt(s.substring(start, s1));
if (timeStart <= 0) { int month = Integer.parseInt(s.substring(s1 + 1, s2));
// ISO 8601 compatibility int day = Integer.parseInt(s.substring(s2 + 1, end));
timeStart = s.indexOf('T') + 1; if (!isValidDate(year, month, day)) {
} throw new IllegalArgumentException(year + "-" + month + "-" + day);
}
int year = DEFAULT_YEAR, month = DEFAULT_MONTH, day = DEFAULT_DAY;
if (type != Value.TIME) {
if (s.startsWith("+")) {
// +year
s = s.substring(1);
}
// start at position 1 to support -year
int s1 = s.indexOf('-', 1);
int s2 = s.indexOf('-', s1 + 1);
if (s1 <= 0 || s2 <= s1) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
DataType.getDataType(type).name, s);
}
year = Integer.parseInt(s.substring(0, s1));
month = Integer.parseInt(s.substring(s1 + 1, s2));
int end = timeStart == 0 ? s.length() : timeStart - 1;
day = Integer.parseInt(s.substring(s2 + 1, end));
}
int hour = DEFAULT_HOUR, minute = 0, second = 0;
long millis = 0, nanos = 0;
int s1 = s.indexOf(':', timeStart);
if (type == Value.TIME || (type == Value.TIMESTAMP && s1 >= 0)) {
int s2 = s.indexOf(':', s1 + 1);
int s3 = s.indexOf('.', s2 + 1);
if (s1 <= 0 || s2 <= s1) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
DataType.getDataType(type).name, original);
}
if (s.endsWith("Z")) {
s = s.substring(0, s.length() - 1);
tz = TimeZone.getTimeZone("UTC");
} else {
int timeZoneStart = s.indexOf('+', s2 + 1);
if (timeZoneStart < 0) {
timeZoneStart = s.indexOf('-', s2 + 1);
}
if (timeZoneStart >= 0) {
String tzName = "GMT" + s.substring(timeZoneStart);
tz = TimeZone.getTimeZone(tzName);
if (!tz.getID().startsWith(tzName)) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
DataType.getDataType(type).name,
original + " (" + tz.getID() + " <>" + tzName + ")");
}
s = s.substring(0, timeZoneStart).trim();
}
}
hour = Integer.parseInt(s.substring(timeStart, s1));
minute = Integer.parseInt(s.substring(s1 + 1, s2));
if (s3 < 0) {
second = Integer.parseInt(s.substring(s2 + 1));
} else {
second = Integer.parseInt(s.substring(s2 + 1, s3));
String n = (s + "000000000").substring(s3 + 1, s3 + 10);
nanos = Integer.parseInt(n);
millis = nanos / 1000000;
nanos -= millis * 1000000;
}
}
if (!isValidDate(year, month, day)) {
throw new IllegalArgumentException(year + "-" + month + "-" + day);
}
if (!isValidTime(hour, minute, second)) {
throw new IllegalArgumentException(hour + ":" + minute + ":" + second);
}
long dateValue;
if (tz == null) {
dateValue = dateValue(year, month, day);
} else {
long ms = getMillis(tz, year, month, day, hour, minute, second, (int) millis);
ms = DateTimeUtils.getLocalTime(new Date(ms),
Calendar.getInstance(TimeZone.getTimeZone("UTC")));
dateValue = dateValueFromDate(ms);
// TODO verify this always works
hour = minute = second = 0;
millis = ms - absoluteDayFromDateValue(dateValue) * MILLIS_PER_DAY;
}
if (type == Value.DATE) {
return ValueDate.get(dateValue);
} else if (type == Value.TIMESTAMP) {
nanos += (((((hour * 60L) + minute) * 60) + second) * 1000 + millis) * 1000000;
return ValueTimestamp.get(dateValue, nanos);
} else {
throw DbException.throwInternalError("type:" + type);
}
} catch (IllegalArgumentException e) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
e, DataType.getDataType(type).name, original);
} }
return dateValue(year, month, day);
} }
public static long parseTime(String s) { public static long parseTimeNanos(String s, int start, int end, boolean timeOfDay) {
int hour = 0, minute = 0, second = 0, nanos = 0; int hour = 0, minute = 0, second = 0;
int s1 = s.indexOf(':'); long nanos = 0;
int s1 = s.indexOf(':', start);
int s2 = s.indexOf(':', s1 + 1); int s2 = s.indexOf(':', s1 + 1);
int s3 = s.indexOf('.', s2 + 1); int s3 = s.indexOf('.', s2 + 1);
if (s1 <= 0 || s2 <= s1) { if (s1 <= 0 || s2 <= s1) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2, throw new IllegalArgumentException(s);
"TIME", s);
} }
try { boolean negative;
hour = Integer.parseInt(s.substring(0, s1)); hour = Integer.parseInt(s.substring(start, s1));
minute = Integer.parseInt(s.substring(s1 + 1, s2)); if (hour < 0) {
if (s3 < 0) { if (timeOfDay) {
second = Integer.parseInt(s.substring(s2 + 1)); throw new IllegalArgumentException(s);
} else {
second = Integer.parseInt(s.substring(s2 + 1, s3));
String n = (s + "000000000").substring(s3 + 1, s3 + 10);
nanos = Integer.parseInt(n);
} }
} catch (NumberFormatException e) { negative = true;
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2, hour = -hour;
"TIME", s); } else {
negative = false;
} }
if (minute < 0 || minute >= 60 || second < 0 || second >= 60) { minute = Integer.parseInt(s.substring(s1 + 1, s2));
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2, if (s3 < 0) {
"TIME", s); second = Integer.parseInt(s.substring(s2 + 1, end));
} else {
second = Integer.parseInt(s.substring(s2 + 1, s3));
String n = (s.substring(s3 + 1, end) + "000000000").substring(0, 9);
nanos = Integer.parseInt(n);
} }
return ((((hour * 60L) + minute) * 60) + second) * 1000000000 + nanos; if (hour >= 2000000 || minute < 0 || minute >= 60 || second < 0 || second >= 60) {
throw new IllegalArgumentException(s);
}
if (timeOfDay && hour >= 24) {
throw new IllegalArgumentException(s);
}
nanos += ((((hour * 60L) + minute) * 60) + second) * 1000000000;
return negative ? -nanos : nanos;
} }
/** /**
* Calculate the milliseconds for the given date and time in the specified timezone. * Calculate the milliseconds for the given date and time in the specified
* timezone.
* *
* @param tz the timezone * @param tz the timezone
* @param year the absolute year (positive or negative) * @param year the absolute year (positive or negative)
...@@ -357,7 +261,6 @@ public class DateTimeUtils { ...@@ -357,7 +261,6 @@ public class DateTimeUtils {
* @return the number of milliseconds * @return the number of milliseconds
*/ */
public static long getMillis(TimeZone tz, int year, int month, int day, int hour, int minute, int second, int millis) { public static long getMillis(TimeZone tz, int year, int month, int day, int hour, int minute, int second, int millis) {
int todoInternal;
try { try {
return getTimeTry(false, tz, year, month, day, hour, minute, second, millis); return getTimeTry(false, tz, year, month, day, hour, minute, second, millis);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
...@@ -506,7 +409,6 @@ public class DateTimeUtils { ...@@ -506,7 +409,6 @@ public class DateTimeUtils {
* the December 28th always belongs to the last week. * the December 28th always belongs to the last week.
* *
* @author Robert Rathsack * @author Robert Rathsack
*
* @param date the date object which week of year should be calculated * @param date the date object which week of year should be calculated
* @return the week of the year * @return the week of the year
*/ */
...@@ -631,20 +533,6 @@ public class DateTimeUtils { ...@@ -631,20 +533,6 @@ public class DateTimeUtils {
return day <= ((year & 3) != 0 ? 28 : 29); return day <= ((year & 3) != 0 ? 28 : 29);
} }
/**
* Verify if the specified time is valid.
*
* @param hour the hour
* @param minute the minute
* @param second the second
* @return true if it is valid
*/
public static boolean isValidTime(int hour, int minute, int second) {
return hour >= 0 && hour < 24 &&
minute >= 0 && minute < 60 &&
second >= 0 && second < 60;
}
public static Date convertDateValueToDate(long dateValue) { public static Date convertDateValueToDate(long dateValue) {
long millis = getMillis(TimeZone.getDefault(), long millis = getMillis(TimeZone.getDefault(),
yearFromDateValue(dateValue), yearFromDateValue(dateValue),
...@@ -685,11 +573,11 @@ public class DateTimeUtils { ...@@ -685,11 +573,11 @@ public class DateTimeUtils {
return new Time(ms); return new Time(ms);
} }
private static int yearFromDateValue(long x) { public static int yearFromDateValue(long x) {
return (int) (x >>> SHIFT_YEAR); return (int) (x >>> SHIFT_YEAR);
} }
private static int monthFromDateValue(long x) { public static int monthFromDateValue(long x) {
return (int) (x >>> SHIFT_MONTH) & 15; return (int) (x >>> SHIFT_MONTH) & 15;
} }
...@@ -727,7 +615,7 @@ public class DateTimeUtils { ...@@ -727,7 +615,7 @@ public class DateTimeUtils {
} }
} }
public static ValueTimestamp normalize(long absoluteDay, long nanos) { public static ValueTimestamp normalizeTimestamp(long absoluteDay, long nanos) {
if (nanos > NANOS_PER_DAY || nanos < 0) { if (nanos > NANOS_PER_DAY || nanos < 0) {
long d; long d;
if (nanos > NANOS_PER_DAY) { if (nanos > NANOS_PER_DAY) {
...@@ -749,7 +637,7 @@ public class DateTimeUtils { ...@@ -749,7 +637,7 @@ public class DateTimeUtils {
y--; y--;
m += 12; m += 12;
} }
long a = ((y * 2922L) >>> 3) + DAYS_OFFSET[m - 3] + d - 719484; long a = ((y * 2922L) >> 3) + DAYS_OFFSET[m - 3] + d - 719484;
if (y <= 1582 && ((y < 1582) || (m * 100 + d < 1005))) { if (y <= 1582 && ((y < 1582) || (m * 100 + d < 1005))) {
// Julian calendar (cutover at 1582-10-04 / 1582-10-15) // Julian calendar (cutover at 1582-10-04 / 1582-10-15)
a += 13; a += 13;
...@@ -794,55 +682,4 @@ public class DateTimeUtils { ...@@ -794,55 +682,4 @@ public class DateTimeUtils {
return dateValue(y, m + 3, (int) d); return dateValue(y, m + 3, (int) d);
} }
public 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);
}
public static void appendTime(StringBuilder buff, long n, boolean alwaysAddMillis) {
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;
if (h < 0) {
buff.append(h);
} else {
StringUtils.appendZeroPadded(buff, 2, h);
}
buff.append(':');
StringUtils.appendZeroPadded(buff, 2, m);
buff.append(':');
StringUtils.appendZeroPadded(buff, 2, s);
if (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);
}
} else if (alwaysAddMillis) {
buff.append(".0");
}
}
} }
...@@ -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;
......
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
*/ */
package org.h2.test.unit; package org.h2.test.unit;
import java.sql.Date;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
...@@ -17,8 +19,11 @@ import org.h2.store.Data; ...@@ -17,8 +19,11 @@ import org.h2.store.Data;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.Profiler;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueDate; import org.h2.value.ValueDate;
import org.h2.value.ValueDouble;
import org.h2.value.ValueInt;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
import org.h2.value.ValueString; import org.h2.value.ValueString;
import org.h2.value.ValueTime; import org.h2.value.ValueTime;
...@@ -43,21 +48,241 @@ public class TestDate extends TestBase { ...@@ -43,21 +48,241 @@ public class TestDate extends TestBase {
} }
public void test() throws SQLException { public void test() throws SQLException {
int test;
//Profiler prof = new Profiler();
//prof.startCollecting();
testValueDate();
testValueTime();
testValueTimestamp();
testValidDate(); testValidDate();
testValidTime(); testAbsoluteDay();
testCalculateLocalMillis(); testCalculateLocalMillis();
testTimeOperationsAcrossTimeZones(); testTimeOperationsAcrossTimeZones();
testDateTimeUtils(); testDateTimeUtils();
//System.out.println(prof.getTop(5));
}
private void testValueDate() {
assertEquals("2000-01-01", ValueDate.get(Date.valueOf("2000-01-01")).getString());
assertEquals("0-00-00", ValueDate.get(0).getString());
assertEquals("9999-12-31", ValueDate.parse("9999-12-31").getString());
assertEquals("-9999-12-31", ValueDate.parse("-9999-12-31").getString());
assertEquals(
Integer.MAX_VALUE + "-12-31",
ValueDate.parse(Integer.MAX_VALUE + "-12-31").getString());
assertEquals(
Integer.MIN_VALUE + "-12-31",
ValueDate.parse(Integer.MIN_VALUE + "-12-31").getString());
ValueDate d1 = ValueDate.parse("2001-01-01");
assertEquals("2001-01-01", d1.getDate().toString());
assertEquals("DATE '2001-01-01'", d1.getSQL());
assertEquals("DATE '2001-01-01'", d1.toString());
assertEquals(Value.DATE, d1.getType());
long dv = d1.getDateValue();
assertEquals((int) ((dv >>> 32) ^ dv), d1.hashCode());
assertEquals(d1.getString().length(), d1.getDisplaySize());
assertEquals(ValueDate.PRECISION, d1.getPrecision());
assertEquals("java.sql.Date", d1.getObject().getClass().getName());
ValueDate d1b = ValueDate.parse("2001-01-01");
assertTrue(d1 == d1b);
Value.clearCache();
d1b = ValueDate.parse("2001-01-01");
assertFalse(d1 == d1b);
assertTrue(d1.equals(d1));
assertTrue(d1.equals(d1b));
assertTrue(d1b.equals(d1));
assertEquals(0, d1.compareTo(d1b, null));
assertEquals(0, d1b.compareTo(d1, null));
ValueDate d2 = ValueDate.parse("2002-02-02");
assertFalse(d1.equals(d2));
assertFalse(d2.equals(d1));
assertEquals(-1, d1.compareTo(d2, null));
assertEquals(1, d2.compareTo(d1, null));
// can't convert using java.util.Date
assertEquals(
Integer.MAX_VALUE + "-12-31 00:00:00.0",
ValueDate.parse(Integer.MAX_VALUE + "-12-31").
convertTo(Value.TIMESTAMP).getString());
assertEquals(
Integer.MIN_VALUE + "-12-31 00:00:00.0",
ValueDate.parse(Integer.MIN_VALUE + "-12-31").
convertTo(Value.TIMESTAMP).getString());
assertEquals(
"00:00:00",
ValueDate.parse(Integer.MAX_VALUE + "-12-31").
convertTo(Value.TIME).getString());
assertEquals(
"00:00:00",
ValueDate.parse(Integer.MIN_VALUE + "-12-31").
convertTo(Value.TIME).getString());
}
private void testValueTime() {
assertEquals("10:20:30", ValueTime.get(Time.valueOf("10:20:30")).getString());
assertEquals("00:00:00", ValueTime.get(0).getString());
assertEquals("23:59:59", ValueTime.parse("23:59:59").getString());
assertEquals("99:59:59", ValueTime.parse("99:59:59").getString());
assertEquals("-99:02:03.001002003", ValueTime.parse("-99:02:03.001002003").getString());
assertEquals("-99:02:03.001002", ValueTime.parse("-99:02:03.001002000").getString());
assertEquals("-99:02:03", ValueTime.parse("-99:02:03.0000000000001").getString());
assertEquals("1999999:59:59.999999999",
ValueTime.parse("1999999:59:59.999999999").getString());
assertEquals("-1999999:59:59.999999999",
ValueTime.parse("-1999999:59:59.999999999").getString());
ValueTime t1 = ValueTime.parse("11:11:11");
assertEquals("11:11:11", t1.getTime().toString());
assertEquals("1970-01-01", t1.getDate().toString());
assertEquals("TIME '11:11:11'", t1.getSQL());
assertEquals("TIME '11:11:11'", t1.toString());
assertEquals(1, t1.getSignum());
assertEquals(-1, t1.negate().getSignum());
assertEquals(0, t1.multiply(ValueInt.get(0)).getSignum());
assertEquals(0, t1.subtract(t1).getSignum());
assertEquals("05:35:35.5", t1.multiply(ValueDouble.get(0.5)).getString());
assertEquals("22:22:22", t1.divide(ValueDouble.get(0.5)).getString());
assertEquals("-11:11:11", t1.negate().getString());
assertEquals("11:11:11", t1.negate().negate().getString());
assertEquals(Value.TIME, t1.getType());
long nanos = t1.getNanos();
assertEquals((int) ((nanos >>> 32) ^ nanos), t1.hashCode());
assertEquals(t1.getString().length(), t1.getDisplaySize());
assertEquals(ValueTime.PRECISION, t1.getPrecision());
assertEquals("java.sql.Time", t1.getObject().getClass().getName());
ValueTime t1b = ValueTime.parse("11:11:11");
assertTrue(t1 == t1b);
Value.clearCache();
t1b = ValueTime.parse("11:11:11");
assertFalse(t1 == t1b);
assertTrue(t1.equals(t1));
assertTrue(t1.equals(t1b));
assertTrue(t1b.equals(t1));
assertEquals(0, t1.compareTo(t1b, null));
assertEquals(0, t1b.compareTo(t1, null));
ValueTime t2 = ValueTime.parse("22:22:22");
assertFalse(t1.equals(t2));
assertFalse(t2.equals(t1));
assertEquals("33:33:33", t1.add(t2).getString());
assertEquals("33:33:33", t1.multiply(ValueInt.get(4)).subtract(t1).getString());
assertEquals(-1, t1.compareTo(t2, null));
assertEquals(1, t2.compareTo(t1, null));
// can't convert using java.util.Date
assertEquals(
"1969-12-31 23:00:00.0",
ValueTime.parse("-1:00:00").
convertTo(Value.TIMESTAMP).getString());
assertEquals(
"1970-01-01",
ValueTime.parse("-1:00:00").
convertTo(Value.DATE).getString());
}
private void testValueTimestamp() {
assertEquals("2001-02-03 04:05:06.0", ValueTimestamp.get(
Timestamp.valueOf("2001-02-03 04:05:06")).getString());
assertEquals("2001-02-03 04:05:06.001002003", ValueTimestamp.get(
Timestamp.valueOf("2001-02-03 04:05:06.001002003")).getString());
assertEquals("0-00-00 00:00:00.0", ValueTimestamp.get(0, 0).getString());
assertEquals("9999-12-31 23:59:59.0",
ValueTimestamp.parse("9999-12-31 23:59:59").getString());
assertEquals(
Integer.MAX_VALUE + "-12-31 01:02:03.04050607",
ValueTimestamp.parse(Integer.MAX_VALUE + "-12-31 01:02:03.0405060708").getString());
assertEquals(
Integer.MIN_VALUE + "-12-31 01:02:03.04050607",
ValueTimestamp.parse(Integer.MIN_VALUE + "-12-31 01:02:03.0405060708").getString());
ValueTimestamp t1 = ValueTimestamp.parse("2001-01-01 01:01:01.111");
assertEquals("2001-01-01 01:01:01.111", t1.getTimestamp().toString());
assertEquals("2001-01-01", t1.getDate().toString());
assertEquals("01:01:01", t1.getTime().toString());
assertEquals("TIMESTAMP '2001-01-01 01:01:01.111'", t1.getSQL());
assertEquals("TIMESTAMP '2001-01-01 01:01:01.111'", t1.toString());
assertEquals(Value.TIMESTAMP, t1.getType());
long dateValue = t1.getDateValue();
long nanos = t1.getNanos();
assertEquals((int) ((dateValue >>> 32) ^ dateValue ^
(nanos >>> 32) ^ nanos),
t1.hashCode());
assertEquals(t1.getString().length(), t1.getDisplaySize());
assertEquals(ValueTimestamp.PRECISION, t1.getPrecision());
assertEquals(10, t1.getScale());
assertEquals("java.sql.Timestamp", t1.getObject().getClass().getName());
ValueTimestamp t1b = ValueTimestamp.parse("2001-01-01 01:01:01.111");
assertTrue(t1 == t1b);
Value.clearCache();
t1b = ValueTimestamp.parse("2001-01-01 01:01:01.111");
assertFalse(t1 == t1b);
assertTrue(t1.equals(t1));
assertTrue(t1.equals(t1b));
assertTrue(t1b.equals(t1));
assertEquals(0, t1.compareTo(t1b, null));
assertEquals(0, t1b.compareTo(t1, null));
ValueTimestamp t2 = ValueTimestamp.parse("2002-02-02 02:02:02.222");
assertFalse(t1.equals(t2));
assertFalse(t2.equals(t1));
assertEquals(-1, t1.compareTo(t2, null));
assertEquals(1, t2.compareTo(t1, null));
t1 = ValueTimestamp.parse("2001-01-01 01:01:01.123456789");
assertEquals("2001-01-01 01:01:01.123456789", t1.getString());
assertEquals("2001-01-01 01:01:01.123456789", t1.convertScale(true, 10).getString());
assertEquals("2001-01-01 01:01:01.123456789", t1.convertScale(true, 9).getString());
assertEquals("2001-01-01 01:01:01.12345679", t1.convertScale(true, 8).getString());
assertEquals("2001-01-01 01:01:01.1234568", t1.convertScale(true, 7).getString());
assertEquals("2001-01-01 01:01:01.123457", t1.convertScale(true, 6).getString());
assertEquals("2001-01-01 01:01:01.12346", t1.convertScale(true, 5).getString());
assertEquals("2001-01-01 01:01:01.1235", t1.convertScale(true, 4).getString());
assertEquals("2001-01-01 01:01:01.123", t1.convertScale(true, 3).getString());
assertEquals("2001-01-01 01:01:01.12", t1.convertScale(true, 2).getString());
assertEquals("2001-01-01 01:01:01.1", t1.convertScale(true, 1).getString());
assertEquals("2001-01-01 01:01:01.0", t1.convertScale(true, 0).getString());
t1 = ValueTimestamp.parse("-2001-01-01 01:01:01.123456789");
assertEquals("-2001-01-01 01:01:01.123457", t1.convertScale(true, 6).getString());
// classes do not match
assertFalse(ValueTimestamp.parse("2001-01-01").equals(ValueDate.parse("2001-01-01")));
assertEquals("2001-01-01 01:01:01.0",
ValueTimestamp.parse("2001-01-01").add(
ValueTime.parse("01:01:01")).getString());
assertEquals("2001-01-02 01:01:01.0",
ValueTimestamp.parse("2001-01-01").add(
ValueTime.parse("25:01:01")).getString());
assertEquals("1010-10-10 00:00:00.0",
ValueTimestamp.parse("1010-10-10 10:10:10").subtract(
ValueTime.parse("10:10:10")).getString());
assertEquals("1010-10-10 10:00:00.0",
ValueTimestamp.parse("1010-10-11 10:10:10").subtract(
ValueTime.parse("24:10:10")).getString());
assertEquals("-2001-01-01 01:01:01.0",
ValueTimestamp.parse("-2001-01-01").add(
ValueTime.parse("01:01:01")).getString());
assertEquals("-1010-10-10 00:00:00.0",
ValueTimestamp.parse("-1010-10-10 10:10:10").subtract(
ValueTime.parse("10:10:10")).getString());
} }
private void testValidTime() { private void testAbsoluteDay() {
for (int h = -1; h < 28; h++) { long next = Long.MIN_VALUE;
for (int m = -1; m < 65; m++) { for (int y = -2000; y < 3000; y++) {
for (int s = -1; s < 65; s++) { for (int m = -3; m <= 14; m++) {
boolean valid = DateTimeUtils.isValidTime(h, m, s); for (int d = -2; d <= 35; d++) {
boolean expected = h >= 0 && h < 24 && m >= 0 && m < 60 && if (!DateTimeUtils.isValidDate(y, m, d)) {
s >= 0 && s < 60; continue;
assertEquals(expected, valid); }
long date = DateTimeUtils.dateValue(y, m, d);
long abs = DateTimeUtils.absoluteDayFromDateValue(date);
if (abs != next && next != Long.MIN_VALUE) {
assertEquals(abs, next);
}
next = abs + 1;
long d2 = DateTimeUtils.dateValueFromAbsoluteDay(abs);
assertEquals(date, d2);
assertEquals(y, DateTimeUtils.yearFromDateValue(date));
assertEquals(m, DateTimeUtils.monthFromDateValue(date));
assertEquals(d, DateTimeUtils.dayFromDateValue(date));
} }
} }
} }
...@@ -67,13 +292,15 @@ public class TestDate extends TestBase { ...@@ -67,13 +292,15 @@ public class TestDate extends TestBase {
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
c.setLenient(false); c.setLenient(false);
for (int y = -2000; y < 3000; y++) { for (int y = -2000; y < 3000; y++) {
for (int m = -10; m <= 20; m++) { for (int m = -3; m <= 14; m++) {
for (int d = -10; d <= 40; d++) { for (int d = -2; d <= 35; d++) {
boolean valid = DateTimeUtils.isValidDate(y, m, d); boolean valid = DateTimeUtils.isValidDate(y, m, d);
if (m < 1 || m > 12) { if (m < 1 || m > 12) {
assertFalse(valid); assertFalse(valid);
} else if (d < 1 || d > 31) { } else if (d < 1 || d > 31) {
assertFalse(valid); assertFalse(valid);
} else if (y != 1582 && d >= 1 && d <= 27) {
assertTrue(valid);
} else { } else {
if (y <= 0) { if (y <= 0) {
c.set(Calendar.ERA, GregorianCalendar.BC); c.set(Calendar.ERA, GregorianCalendar.BC);
...@@ -104,17 +331,18 @@ public class TestDate extends TestBase { ...@@ -104,17 +331,18 @@ public class TestDate extends TestBase {
try { try {
for (TimeZone tz : TestDate.getDistinctTimeZones()) { for (TimeZone tz : TestDate.getDistinctTimeZones()) {
TimeZone.setDefault(tz); TimeZone.setDefault(tz);
for (int y = 1900; y < 2039; y++) { for (int y = 1900; y < 2039; y += 10) {
if (y == 1993) { if (y == 1993) {
// timezone change in Kwajalein // timezone change in Kwajalein
} else if (y == 1995) { } else if (y == 1995) {
// timezone change in Enderbury and Kiritimati // timezone change in Enderbury and Kiritimati
} }
for (int m = 1; m <= 12; m++) { for (int m = 1; m <= 12; m++) {
if (m != 3 && m != 4 && m != 10 && m != 11) {
// only test daylight saving time transitions
continue;
}
for (int day = 1; day < 29; day++) { for (int day = 1; day < 29; day++) {
if (y == 1582 && m == 10 && day >= 5 && day <= 14) {
continue;
}
testDate(y, m, day); testDate(y, m, day);
} }
} }
...@@ -214,8 +442,8 @@ public class TestDate extends TestBase { ...@@ -214,8 +442,8 @@ public class TestDate extends TestBase {
} }
private void testDateTimeUtils() { private void testDateTimeUtils() {
ValueTimestamp ts1 = (ValueTimestamp) DateTimeUtils.parse("-999-08-07 13:14:15.16", Value.TIMESTAMP); ValueTimestamp ts1 = ValueTimestamp.parse("-999-08-07 13:14:15.16");
ValueTimestamp ts2 = (ValueTimestamp) DateTimeUtils.parse("19999-08-07 13:14:15.16", Value.TIMESTAMP); ValueTimestamp ts2 = ValueTimestamp.parse("19999-08-07 13:14:15.16");
ValueTime t1 = (ValueTime) ts1.convertTo(Value.TIME); ValueTime t1 = (ValueTime) ts1.convertTo(Value.TIME);
ValueTime t2 = (ValueTime) ts2.convertTo(Value.TIME); ValueTime t2 = (ValueTime) ts2.convertTo(Value.TIME);
ValueDate d1 = (ValueDate) ts1.convertTo(Value.DATE); ValueDate d1 = (ValueDate) ts1.convertTo(Value.DATE);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论