Unverified 提交 73ac0e4d authored 作者: Noel Grandin's avatar Noel Grandin 提交者: GitHub

Merge pull request #825 from katzyn/misc

Merge code for parsing and formatting timestamp values
......@@ -7,7 +7,6 @@ package org.h2.api;
import java.io.Serializable;
import org.h2.util.DateTimeUtils;
import org.h2.util.StringUtils;
/**
* How we expose "TIMESTAMP WITH TIMEZONE" in our ResultSets.
......@@ -110,62 +109,7 @@ public class TimestampWithTimeZone implements Serializable, Cloneable {
@Override
public String toString() {
StringBuilder buff = new StringBuilder();
int y = DateTimeUtils.yearFromDateValue(dateValue);
int month = 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, month);
buff.append('-');
StringUtils.appendZeroPadded(buff, 2, d);
buff.append(' ');
long nanos = timeNanos;
long ms = nanos / 1000000;
nanos -= ms * 1000000;
long s = ms / 1000;
ms -= s * 1000;
long min = s / 60;
s -= min * 60;
long h = min / 60;
min -= h * 60;
StringUtils.appendZeroPadded(buff, 2, h);
buff.append(':');
StringUtils.appendZeroPadded(buff, 2, min);
buff.append(':');
StringUtils.appendZeroPadded(buff, 2, s);
buff.append('.');
int start = buff.length();
StringUtils.appendZeroPadded(buff, 3, ms);
if (nanos > 0) {
StringUtils.appendZeroPadded(buff, 6, nanos);
}
for (int i = buff.length() - 1; i > start; i--) {
if (buff.charAt(i) != '0') {
break;
}
buff.deleteCharAt(i);
}
short tz = timeZoneOffsetMins;
if (tz < 0) {
buff.append('-');
tz = (short) -tz;
} else {
buff.append('+');
}
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);
}
return buff.toString();
return DateTimeUtils.timestampTimeZoneToString(dateValue, timeNanos, timeZoneOffsetMins);
}
@Override
......
......@@ -12,7 +12,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.StringUtils;
/**
* Implementation of the DATE data type.
......@@ -99,7 +98,7 @@ public class ValueDate extends Value {
@Override
public String getString() {
StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
appendDate(buff, dateValue);
DateTimeUtils.appendDate(buff, dateValue);
return buff.toString();
}
......@@ -148,25 +147,4 @@ public class ValueDate extends Value {
prep.setDate(parameterIndex, getDate());
}
/**
* Append a date to the string builder.
*
* @param buff the target string builder
* @param dateValue the date value
*/
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);
}
}
......@@ -12,7 +12,6 @@ import org.h2.api.ErrorCode;
import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.StringUtils;
/**
* Implementation of the TIME data type.
......@@ -52,7 +51,7 @@ public class ValueTime extends Value {
if (!SysProperties.UNLIMITED_TIME_RANGE) {
if (nanos < 0L || nanos >= 86400000000000L) {
StringBuilder builder = new StringBuilder();
appendTime(builder, nanos, false);
DateTimeUtils.appendTime(builder, nanos, false);
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
"TIME", builder.toString());
}
......@@ -116,7 +115,7 @@ public class ValueTime extends Value {
@Override
public String getString() {
StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
appendTime(buff, nanos, false);
DateTimeUtils.appendTime(buff, nanos, false);
return buff.toString();
}
......@@ -196,52 +195,4 @@ public class ValueTime extends Value {
return ValueTime.fromNanos(-nanos);
}
/**
* Append a time to the string builder.
*
* @param buff the target string builder
* @param nanos the time in nanoseconds
* @param alwaysAddMillis whether to always add at least ".0"
*/
static void appendTime(StringBuilder buff, long nanos,
boolean alwaysAddMillis) {
if (nanos < 0) {
buff.append('-');
nanos = -nanos;
}
/*
* nanos now either in range from 0 to Long.MAX_VALUE or equals to
* Long.MIN_VALUE. We need to divide nanos by 1000000 with unsigned division to
* get correct result. The simplest way to do this with such constraints is to
* divide -nanos by -1000000.
*/
long ms = -nanos / -1000000;
nanos -= 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 || nanos > 0) {
buff.append('.');
int start = buff.length();
StringUtils.appendZeroPadded(buff, 3, ms);
if (nanos > 0) {
StringUtils.appendZeroPadded(buff, 6, nanos);
}
for (int i = buff.length() - 1; i > start; i--) {
if (buff.charAt(i) != '0') {
break;
}
buff.deleteCharAt(i);
}
}
}
}
......@@ -6,11 +6,9 @@
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.TimeZone;
import org.h2.api.ErrorCode;
import org.h2.engine.Mode;
import org.h2.message.DbException;
......@@ -129,104 +127,13 @@ public class ValueTimestamp extends Value {
*/
public static ValueTimestamp parse(String s, Mode mode) {
try {
return parseTry(s, mode);
return (ValueTimestamp) DateTimeUtils.parseTimestamp(s, mode, false);
} catch (Exception e) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
e, "TIMESTAMP", s);
}
}
/**
* See:
* https://stackoverflow.com/questions/3976616/how-to-find-nth-occurrence-of-character-in-a-string#answer-3976656
*/
private static int findNthIndexOf(String str, char chr, int n) {
int pos = str.indexOf(chr);
while (--n > 0 && pos != -1)
pos = str.indexOf(chr, pos + 1);
return pos;
}
private static ValueTimestamp parseTry(String s, Mode mode) {
int dateEnd = s.indexOf(' ');
if (dateEnd < 0) {
// ISO 8601 compatibility
dateEnd = s.indexOf('T');
if (dateEnd < 0 && mode != null && mode.allowDB2TimestampFormat) {
// DB2 also allows dash between date and time
dateEnd = findNthIndexOf(s, '-', 3);
}
}
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 = DateTimeUtils.UTC;
timeEnd--;
} else {
int timeZoneStart = s.indexOf('+', dateEnd + 1);
if (timeZoneStart < 0) {
timeZoneStart = s.indexOf('-', dateEnd + 1);
}
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;
}
}
}
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),
DateTimeUtils.createGregorianCalendar(DateTimeUtils.UTC));
long md = DateTimeUtils.MILLIS_PER_DAY;
long absoluteDay = (ms >= 0 ? ms : ms - md + 1) / md;
dateValue = DateTimeUtils.dateValueFromAbsoluteDay(absoluteDay);
ms -= absoluteDay * md;
nanos += ms * 1000000;
}
}
return ValueTimestamp.fromDateValueAndNanos(dateValue, nanos);
}
/**
* A bit field with bits for the year, month, and day (see DateTimeUtils for
* encoding).
......@@ -259,9 +166,9 @@ public class ValueTimestamp extends Value {
@Override
public String getString() {
StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
ValueDate.appendDate(buff, dateValue);
DateTimeUtils.appendDate(buff, dateValue);
buff.append(' ');
ValueTime.appendTime(buff, timeNanos, true);
DateTimeUtils.appendTime(buff, timeNanos, true);
return buff.toString();
}
......
......@@ -15,7 +15,6 @@ import org.h2.api.ErrorCode;
import org.h2.api.TimestampWithTimeZone;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.StringUtils;
/**
* Implementation of the TIMESTAMP WITH TIME ZONE data type.
......@@ -34,7 +33,7 @@ public class ValueTimestampTimeZone extends Value {
* 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;
public static final int DISPLAY_SIZE = 30;
/**
* The default scale for timestamps.
......@@ -114,72 +113,13 @@ public class ValueTimestampTimeZone extends Value {
*/
public static ValueTimestampTimeZone parse(String s) {
try {
return parseTry(s);
return (ValueTimestampTimeZone) DateTimeUtils.parseTimestamp(s, null, true);
} catch (Exception e) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2, e,
"TIMESTAMP WITH TIME ZONE", 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 tzMinutes = 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
.convertDateValueToMillis(DateTimeUtils.UTC, dateValue);
tzMinutes = (short) (tz.getOffset(millis) / 1000 / 60);
}
}
nanos = DateTimeUtils.parseTimeNanos(s, dateEnd + 1, timeEnd, true);
}
return ValueTimestampTimeZone.fromDateValueAndNanos(dateValue, nanos,
tzMinutes);
}
/**
* A bit field with bits for the year, month, and day (see DateTimeUtils for
* encoding).
......@@ -233,35 +173,7 @@ public class ValueTimestampTimeZone extends Value {
@Override
public String getString() {
StringBuilder buff = new StringBuilder(DISPLAY_SIZE);
ValueDate.appendDate(buff, dateValue);
buff.append(' ');
ValueTime.appendTime(buff, timeNanos, true);
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;
} else {
buff.append('+');
}
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);
}
return DateTimeUtils.timestampTimeZoneToString(dateValue, timeNanos, timeZoneOffsetMins);
}
@Override
......
......@@ -3,7 +3,6 @@
-- Initial Developer: H2 Group
--
CREATE TABLE tab_with_timezone(x TIMESTAMP WITH TIME ZONE);
> ok
......@@ -14,3 +13,16 @@ SELECT "Query".* FROM (select * from tab_with_timezone where x > '2016-01-01') A
> X
> ------------------------
> 2017-01-01 00:00:00.0+00
DELETE FROM tab_with_timezone;
> update count: 1
INSERT INTO tab_with_timezone VALUES ('2018-03-25 01:59:00 Europe/Berlin'), ('2018-03-25 03:00:00 Europe/Berlin');
> update count: 2
SELECT * FROM tab_with_timezone ORDER BY X;
> X
> ------------------------
> 2018-03-25 01:59:00.0+01
> 2018-03-25 03:00:00.0+02
> rows (ordered): 2
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论