提交 10650972 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Merge parsing of timestamps with and without time zone into shared method

上级 134712c8
...@@ -15,6 +15,7 @@ import java.util.GregorianCalendar; ...@@ -15,6 +15,7 @@ import java.util.GregorianCalendar;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.Mode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueDate; import org.h2.value.ValueDate;
...@@ -434,6 +435,117 @@ public class DateTimeUtils { ...@@ -434,6 +435,117 @@ public class DateTimeUtils {
return negative ? -nanos : nanos; return negative ? -nanos : nanos;
} }
/**
* 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;
}
/**
* Parses timestamp value from the specified string.
*
* @param s
* string to parse
* @param mode
* database mode, or {@code null}
* @param withTimeZone
* if {@code true} return {@link ValueTimestampTimeZone} instead of
* {@link ValueTimestamp}
* @return parsed timestamp
*/
public static Value parseTimestamp(String s, Mode mode, boolean withTimeZone) {
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 = parseDateValue(s, 0, dateEnd);
long nanos;
short tzMinutes = 0;
if (timeStart < 0) {
nanos = 0;
} else {
int timeEnd = s.length();
TimeZone tz = null;
if (s.endsWith("Z")) {
tz = 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 = parseTimeNanos(s, dateEnd + 1, timeEnd, true);
if (tz != null) {
if (withTimeZone) {
if (tz != UTC) {
long millis = convertDateValueToMillis(UTC, dateValue);
tzMinutes = (short) (tz.getOffset(millis) / 1000 / 60);
}
} else {
int year = yearFromDateValue(dateValue);
int month = monthFromDateValue(dateValue);
int day = 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 = getMillis(tz, year, month, day, hour, minute, (int) second, (int) ms);
ms = convertToLocal(new Date(millis), createGregorianCalendar(UTC));
long md = MILLIS_PER_DAY;
long absoluteDay = (ms >= 0 ? ms : ms - md + 1) / md;
dateValue = dateValueFromAbsoluteDay(absoluteDay);
ms -= absoluteDay * md;
nanos += ms * 1000000;
}
}
}
if (withTimeZone) {
return ValueTimestampTimeZone.fromDateValueAndNanos(dateValue, nanos, tzMinutes);
}
return ValueTimestamp.fromDateValueAndNanos(dateValue, nanos);
}
/** /**
* Calculate the milliseconds since 1970-01-01 (UTC) for the given date and * Calculate the milliseconds since 1970-01-01 (UTC) for the given date and
* time (in the specified timezone). * time (in the specified timezone).
......
...@@ -6,11 +6,9 @@ ...@@ -6,11 +6,9 @@
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.TimeZone;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.Mode; import org.h2.engine.Mode;
import org.h2.message.DbException; import org.h2.message.DbException;
...@@ -129,104 +127,13 @@ public class ValueTimestamp extends Value { ...@@ -129,104 +127,13 @@ public class ValueTimestamp extends Value {
*/ */
public static ValueTimestamp parse(String s, Mode mode) { public static ValueTimestamp parse(String s, Mode mode) {
try { try {
return parseTry(s, mode); return (ValueTimestamp) DateTimeUtils.parseTimestamp(s, mode, false);
} catch (Exception e) { } catch (Exception e) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2, throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
e, "TIMESTAMP", s); 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 * A bit field with bits for the year, month, and day (see DateTimeUtils for
* encoding). * encoding).
......
...@@ -113,72 +113,13 @@ public class ValueTimestampTimeZone extends Value { ...@@ -113,72 +113,13 @@ public class ValueTimestampTimeZone extends Value {
*/ */
public static ValueTimestampTimeZone parse(String s) { public static ValueTimestampTimeZone parse(String s) {
try { try {
return parseTry(s); return (ValueTimestampTimeZone) DateTimeUtils.parseTimestamp(s, null, true);
} catch (Exception e) { } catch (Exception e) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2, e, throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2, e,
"TIMESTAMP WITH TIME ZONE", s); "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 * A bit field with bits for the year, month, and day (see DateTimeUtils for
* encoding). * encoding).
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论