add feature of support LocalDateTime and test code
This commit is contained in:
BIN
open_source/httpclient/httpclient-4.5.13.jar
Normal file
BIN
open_source/httpclient/httpclient-4.5.13.jar
Normal file
Binary file not shown.
BIN
open_source/httpcore/httpcore-4.4.13.jar
Normal file
BIN
open_source/httpcore/httpcore-4.4.13.jar
Normal file
Binary file not shown.
BIN
open_source/jackson/jackson-annotations-2.11.2.jar
Normal file
BIN
open_source/jackson/jackson-annotations-2.11.2.jar
Normal file
Binary file not shown.
BIN
open_source/jackson/jackson-core-2.11.2.jar
Normal file
BIN
open_source/jackson/jackson-core-2.11.2.jar
Normal file
Binary file not shown.
BIN
open_source/jackson/jackson-databind-2.11.2.jar
Normal file
BIN
open_source/jackson/jackson-databind-2.11.2.jar
Normal file
Binary file not shown.
BIN
open_source/java-sdk-core/java-sdk-core-3.0.12.jar
Normal file
BIN
open_source/java-sdk-core/java-sdk-core-3.0.12.jar
Normal file
Binary file not shown.
BIN
open_source/joda-time/joda-time-2.10.6.jar
Normal file
BIN
open_source/joda-time/joda-time-2.10.6.jar
Normal file
Binary file not shown.
@ -40,6 +40,7 @@ import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.math.RoundingMode;
|
||||
@ -182,6 +183,8 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
|
||||
sqlTypeToOid.put(Types.DATE, Oid.DATE);
|
||||
sqlTypeToOid.put(Types.TIME, Oid.UNSPECIFIED);
|
||||
sqlTypeToOid.put(Types.TIMESTAMP, Oid.UNSPECIFIED);
|
||||
sqlTypeToOid.put(Types.TIME_WITH_TIMEZONE, Oid.UNSPECIFIED);
|
||||
sqlTypeToOid.put(Types.TIMESTAMP_WITH_TIMEZONE, Oid.UNSPECIFIED);
|
||||
sqlTypeToOid.put(Types.BOOLEAN, Oid.BOOL);
|
||||
sqlTypeToOid.put(Types.BIT, Oid.BOOL);
|
||||
sqlTypeToOid.put(Types.BINARY, Oid.BYTEA);
|
||||
@ -555,6 +558,9 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
|
||||
java.sql.Date tmpd;
|
||||
if (in instanceof java.util.Date) {
|
||||
tmpd = new java.sql.Date(((java.util.Date) in).getTime());
|
||||
} else if (in instanceof java.time.LocalDate) {
|
||||
setDate(parameterIndex, (java.time.LocalDate) in);
|
||||
break;
|
||||
} else {
|
||||
tmpd = connection.getTimestampUtils().toDate(getDefaultCalendar(), in.toString());
|
||||
}
|
||||
@ -568,6 +574,9 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
|
||||
java.sql.Time tmpt;
|
||||
if (in instanceof java.util.Date) {
|
||||
tmpt = new java.sql.Time(((java.util.Date) in).getTime());
|
||||
} else if (in instanceof java.time.LocalTime) {
|
||||
setTime(parameterIndex, (java.time.LocalTime) in);
|
||||
break;
|
||||
} else {
|
||||
tmpt = connection.getTimestampUtils().toTime(getDefaultCalendar(), in.toString());
|
||||
}
|
||||
@ -583,12 +592,27 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
|
||||
java.sql.Timestamp tmpts;
|
||||
if (in instanceof java.util.Date) {
|
||||
tmpts = new java.sql.Timestamp(((java.util.Date) in).getTime());
|
||||
} else if (in instanceof java.time.LocalDateTime) {
|
||||
setTimestamp(parameterIndex, (java.time.LocalDateTime) in);
|
||||
break;
|
||||
} else {
|
||||
tmpts = connection.getTimestampUtils().toTimestamp(getDefaultCalendar(), in.toString());
|
||||
}
|
||||
setTimestamp(parameterIndex, tmpts);
|
||||
}
|
||||
break;
|
||||
case Types.TIMESTAMP_WITH_TIMEZONE:
|
||||
if (in instanceof java.time.OffsetDateTime) {
|
||||
setTimestamp(parameterIndex, (java.time.OffsetDateTime) in);
|
||||
} else if (in instanceof PGTimestamp) {
|
||||
setObject(parameterIndex, in);
|
||||
} else {
|
||||
throw new PSQLException(
|
||||
GT.tr("Cannot cast an instance of {0} to type {1}",
|
||||
in.getClass().getName(), "Types.TIMESTAMP_WITH_TIMEZONE"),
|
||||
PSQLState.INVALID_PARAMETER_TYPE);
|
||||
}
|
||||
break;
|
||||
case Types.BOOLEAN:
|
||||
case Types.BIT:
|
||||
setBoolean(parameterIndex, BooleanTypeUtil.castToBoolean(in));
|
||||
@ -923,6 +947,14 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
|
||||
setObjectOfByte(parameterIndex, x);
|
||||
} else if (x instanceof java.sql.Date || x instanceof Time || x instanceof Timestamp) {
|
||||
setObjectOfDate(parameterIndex, x);
|
||||
} else if (x instanceof java.time.LocalDate) {
|
||||
setDate(parameterIndex, (java.time.LocalDate) x);
|
||||
} else if (x instanceof java.time.LocalTime) {
|
||||
setTime(parameterIndex, (java.time.LocalTime) x);
|
||||
} else if (x instanceof java.time.LocalDateTime) {
|
||||
setTimestamp(parameterIndex, (java.time.LocalDateTime) x);
|
||||
} else if (x instanceof java.time.OffsetDateTime) {
|
||||
setTimestamp(parameterIndex, (java.time.OffsetDateTime) x);
|
||||
} else if (x instanceof Boolean) {
|
||||
setBoolean(parameterIndex, (Boolean) x);
|
||||
} else if (x instanceof Blob) {
|
||||
@ -1318,6 +1350,28 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
|
||||
bindString(i, connection.getTimestampUtils().toString(cal, t), oid);
|
||||
}
|
||||
|
||||
private void setDate(int i, java.time.LocalDate localDate) throws SQLException {
|
||||
int oid = Oid.DATE;
|
||||
bindString(i, connection.getTimestampUtils().toString(localDate), oid);
|
||||
}
|
||||
|
||||
private void setTime(int i, java.time.LocalTime localTime) throws SQLException {
|
||||
int oid = Oid.TIME;
|
||||
bindString(i, connection.getTimestampUtils().toString(localTime), oid);
|
||||
}
|
||||
|
||||
private void setTimestamp(int i, java.time.LocalDateTime localDateTime)
|
||||
throws SQLException {
|
||||
int oid = Oid.TIMESTAMP;
|
||||
bindString(i, connection.getTimestampUtils().toString(localDateTime), oid);
|
||||
}
|
||||
|
||||
private void setTimestamp(int i, java.time.OffsetDateTime offsetDateTime)
|
||||
throws SQLException {
|
||||
int oid = Oid.TIMESTAMPTZ;
|
||||
bindString(i, connection.getTimestampUtils().toString(offsetDateTime), oid);
|
||||
}
|
||||
|
||||
public ParameterMetaData createParameterMetaData(BaseConnection conn, int[] oids)
|
||||
throws SQLException {
|
||||
return new PgParameterMetaData(conn, oids);
|
||||
|
||||
@ -562,6 +562,29 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
|
||||
return connection.getTimestampUtils().toTime(cal, string);
|
||||
}
|
||||
|
||||
private java.time.LocalTime getLocalTime(int i) throws SQLException {
|
||||
byte[] value = getRawValue(i);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isBinary(i)) {
|
||||
int col = i - 1;
|
||||
int oid = fields[col].getOID();
|
||||
if (oid == Oid.TIME) {
|
||||
return connection.getTimestampUtils().toLocalTimeBin(value);
|
||||
} else {
|
||||
throw new PSQLException(
|
||||
GT.tr("Cannot convert the column of type {0} to requested type {1}.",
|
||||
Oid.toString(oid), "time"),
|
||||
PSQLState.DATA_TYPE_MISMATCH);
|
||||
}
|
||||
}
|
||||
|
||||
String string = getString(i);
|
||||
return connection.getTimestampUtils().toLocalTime(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException {
|
||||
checkResultSet(i);
|
||||
@ -607,6 +630,69 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
|
||||
return connection.getTimestampUtils().toTimestamp(cal, string);
|
||||
}
|
||||
|
||||
private java.time.OffsetDateTime getOffsetDateTime(int i) throws SQLException {
|
||||
byte[] value = getRawValue(i);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int col = i - 1;
|
||||
int oid = fields[col].getOID();
|
||||
|
||||
if (isBinary(i)) {
|
||||
if (oid == Oid.TIMESTAMPTZ || oid == Oid.TIMESTAMP) {
|
||||
return connection.getTimestampUtils().toOffsetDateTimeBin(value);
|
||||
} else if (oid == Oid.TIMETZ) {
|
||||
// JDBC spec says timetz must be supported
|
||||
Time time = getTime(i);
|
||||
if (time == null) {
|
||||
return null;
|
||||
}
|
||||
return connection.getTimestampUtils().toOffsetDateTime(time);
|
||||
} else {
|
||||
throw new PSQLException(
|
||||
GT.tr("Cannot convert the column of type {0} to requested type {1}.",
|
||||
Oid.toString(oid), "timestamptz"),
|
||||
PSQLState.DATA_TYPE_MISMATCH);
|
||||
}
|
||||
}
|
||||
|
||||
// If this is actually a timestamptz, the server-provided timezone will override
|
||||
// the one we pass in, which is the desired behaviour. Otherwise, we'll
|
||||
// interpret the timezone-less value in the provided timezone.
|
||||
String string = getString(i);
|
||||
if (oid == Oid.TIMETZ) {
|
||||
// JDBC spec says timetz must be supported
|
||||
// If server sends us a TIMETZ, we ensure java counterpart has date of 1970-01-01
|
||||
Calendar cal = getDefaultCalendar();
|
||||
Time time = connection.getTimestampUtils().toTime(cal, string);
|
||||
return connection.getTimestampUtils().toOffsetDateTime(time);
|
||||
}
|
||||
return connection.getTimestampUtils().toOffsetDateTime(string);
|
||||
}
|
||||
|
||||
private java.time.LocalDateTime getLocalDateTime(int i) throws SQLException {
|
||||
byte[] value = getRawValue(i);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int col = i - 1;
|
||||
int oid = fields[col].getOID();
|
||||
if (oid != Oid.TIMESTAMP) {
|
||||
throw new PSQLException(
|
||||
GT.tr("Cannot convert the column of type {0} to requested type {1}.",
|
||||
Oid.toString(oid), "timestamp"),
|
||||
PSQLState.DATA_TYPE_MISMATCH);
|
||||
}
|
||||
if (isBinary(i)) {
|
||||
return connection.getTimestampUtils().toLocalDateTimeBin(value);
|
||||
}
|
||||
|
||||
String string = getString(i);
|
||||
return connection.getTimestampUtils().toLocalDateTime(string);
|
||||
}
|
||||
|
||||
public java.sql.Date getDate(String c, java.util.Calendar cal) throws SQLException {
|
||||
return getDate(findColumn(c), cal);
|
||||
}
|
||||
@ -2722,6 +2808,27 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the result set is not closed, it's positioned on a valid row and that the given
|
||||
* column number is valid. Also updates the {@link #wasNullFlag} to correct value.
|
||||
*
|
||||
* @param column The column number to check. Range starts from 1.
|
||||
* @return byte[] value or null
|
||||
* @throws SQLException If state or column is invalid.
|
||||
*/
|
||||
protected byte[] getRawValue(int column) throws SQLException {
|
||||
checkClosed();
|
||||
if (this_row == null) {
|
||||
throw new PSQLException(
|
||||
GT.tr("ResultSet not positioned properly, perhaps you need to call next."),
|
||||
PSQLState.INVALID_CURSOR_STATE);
|
||||
}
|
||||
checkColumnIndex(column);
|
||||
byte[] bytes = this_row[column - 1];
|
||||
wasNullFlag = bytes == null;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the result set is not closed, it's positioned on a valid row and that the given
|
||||
* column number is valid. Also updates the {@link #wasNullFlag} to correct value.
|
||||
@ -3246,14 +3353,16 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
|
||||
PSQLState.INVALID_PARAMETER_VALUE);
|
||||
}
|
||||
} else if (type == Timestamp.class) {
|
||||
if (sqlType == Types.TIMESTAMP) {
|
||||
if (sqlType == Types.TIMESTAMP
|
||||
|| sqlType == Types.TIME_WITH_TIMEZONE) {
|
||||
return type.cast(getTimestamp(columnIndex));
|
||||
} else {
|
||||
throw new PSQLException(GT.tr("conversion to {0} from {1} not supported", type, sqlType),
|
||||
PSQLState.INVALID_PARAMETER_VALUE);
|
||||
}
|
||||
} else if (type == Calendar.class) {
|
||||
if (sqlType == Types.TIMESTAMP) {
|
||||
if (sqlType == Types.TIMESTAMP
|
||||
|| sqlType == Types.TIME_WITH_TIMEZONE) {
|
||||
Timestamp timestampValue = getTimestamp(columnIndex);
|
||||
if (wasNull()) {
|
||||
return null;
|
||||
@ -3316,6 +3425,53 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
|
||||
} catch (UnknownHostException e) {
|
||||
throw new SQLException("could not create inet address from string '" + addressString + "'");
|
||||
}
|
||||
// JSR-310 support
|
||||
} else if (type == java.time.LocalDate.class) {
|
||||
if (sqlType == Types.DATE) {
|
||||
Date dateValue = getDate(columnIndex);
|
||||
if (dateValue == null) {
|
||||
return null;
|
||||
}
|
||||
long time = dateValue.getTime();
|
||||
if (time == PGStatement.DATE_POSITIVE_INFINITY) {
|
||||
return type.cast(java.time.LocalDate.MAX);
|
||||
}
|
||||
if (time == PGStatement.DATE_NEGATIVE_INFINITY) {
|
||||
return type.cast(java.time.LocalDate.MIN);
|
||||
}
|
||||
return type.cast(dateValue.toLocalDate());
|
||||
} else if (sqlType == Types.TIMESTAMP) {
|
||||
java.time.LocalDateTime localDateTimeValue = getLocalDateTime(columnIndex);
|
||||
if (localDateTimeValue == null) {
|
||||
return null;
|
||||
}
|
||||
return type.cast(localDateTimeValue.toLocalDate());
|
||||
} else {
|
||||
throw new PSQLException(GT.tr("conversion to {0} from {1} not supported", type, getPGType(columnIndex)),
|
||||
PSQLState.INVALID_PARAMETER_VALUE);
|
||||
}
|
||||
} else if (type == java.time.LocalTime.class) {
|
||||
if (sqlType == Types.TIME) {
|
||||
return type.cast(getLocalTime(columnIndex));
|
||||
} else {
|
||||
throw new PSQLException(GT.tr("conversion to {0} from {1} not supported", type, getPGType(columnIndex)),
|
||||
PSQLState.INVALID_PARAMETER_VALUE);
|
||||
}
|
||||
} else if (type == java.time.LocalDateTime.class) {
|
||||
if (sqlType == Types.TIMESTAMP) {
|
||||
return type.cast(getLocalDateTime(columnIndex));
|
||||
} else {
|
||||
throw new PSQLException(GT.tr("conversion to {0} from {1} not supported", type, getPGType(columnIndex)),
|
||||
PSQLState.INVALID_PARAMETER_VALUE);
|
||||
}
|
||||
} else if (type == java.time.OffsetDateTime.class) {
|
||||
if (sqlType == Types.TIMESTAMP_WITH_TIMEZONE || sqlType == Types.TIMESTAMP) {
|
||||
java.time.OffsetDateTime offsetDateTime = getOffsetDateTime(columnIndex);
|
||||
return type.cast(offsetDateTime);
|
||||
} else {
|
||||
throw new PSQLException(GT.tr("conversion to {0} from {1} not supported", type, getPGType(columnIndex)),
|
||||
PSQLState.INVALID_PARAMETER_VALUE);
|
||||
}
|
||||
} else if (PGobject.class.isAssignableFrom(type)) {
|
||||
Object object;
|
||||
if (isBinary(columnIndex)) {
|
||||
|
||||
@ -40,6 +40,16 @@ public class TimestampUtils {
|
||||
private static final char[][] NUMBERS;
|
||||
private static final HashMap<String, TimeZone> GMT_ZONES = new HashMap<String, TimeZone>();
|
||||
private static final int MAX_NANOS_BEFORE_WRAP_ON_ROUND = 999999500;
|
||||
private static final java.time.Duration ONE_MICROSECOND = java.time.Duration.ofNanos(1000);
|
||||
// LocalTime.MAX is 23:59:59.999_999_999, and it wraps to 24:00:00 when nanos exceed 999_999_499
|
||||
// since PostgreSQL has microsecond resolution only
|
||||
private static final java.time.LocalTime MAX_TIME = java.time.LocalTime.MAX.minus(java.time.Duration.ofNanos(500));
|
||||
private static final java.time.OffsetDateTime MAX_OFFSET_DATETIME = java.time.OffsetDateTime.MAX.minus(java.time.Duration.ofMillis(500));
|
||||
private static final java.time.LocalDateTime MAX_LOCAL_DATETIME = java.time.LocalDateTime.MAX.minus(java.time.Duration.ofMillis(500));
|
||||
// low value for dates is 4713 BC
|
||||
private static final java.time.LocalDate MIN_LOCAL_DATE = java.time.LocalDate.of(4713, 1, 1).with(java.time.temporal.ChronoField.ERA, java.time.chrono.IsoEra.BCE.getValue());
|
||||
private static final java.time.LocalDateTime MIN_LOCAL_DATETIME = MIN_LOCAL_DATE.atStartOfDay();
|
||||
private static final java.time.OffsetDateTime MIN_OFFSET_DATETIME = MIN_LOCAL_DATETIME.atOffset(java.time.ZoneOffset.UTC);
|
||||
private static final Field DEFAULT_TIME_ZONE_FIELD;
|
||||
private static Log LOGGER = Logger.getLogger(TimestampUtils.class.getName());
|
||||
|
||||
@ -112,7 +122,7 @@ public class TimestampUtils {
|
||||
private final boolean usesDouble;
|
||||
private final Provider<TimeZone> timeZoneProvider;
|
||||
|
||||
TimestampUtils(boolean usesDouble, Provider<TimeZone> timeZoneProvider) {
|
||||
public TimestampUtils(boolean usesDouble, Provider<TimeZone> timeZoneProvider) {
|
||||
this.usesDouble = usesDouble;
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
@ -391,6 +401,143 @@ public class TimestampUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string and return a LocalTime representing its value.
|
||||
*
|
||||
* @param s The ISO formated time string to parse.
|
||||
* @return null if s is null or a LocalTime of the parsed string s.
|
||||
* @throws SQLException if there is a problem parsing s.
|
||||
*/
|
||||
public java.time.LocalTime toLocalTime(String s) throws SQLException {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (s.equals("24:00:00")) {
|
||||
return java.time.LocalTime.MAX;
|
||||
}
|
||||
|
||||
try {
|
||||
return java.time.LocalTime.parse(s);
|
||||
} catch (java.time.format.DateTimeParseException nfe) {
|
||||
throw new PSQLException(
|
||||
GT.tr("Bad value for type timestamp/date/time: {1}", s),
|
||||
PSQLState.BAD_DATETIME_FORMAT, nfe);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string and return a LocalDateTime representing its value.
|
||||
*
|
||||
* @param s The ISO formated date string to parse.
|
||||
* @return null if s is null or a LocalDateTime of the parsed string s.
|
||||
* @throws SQLException if there is a problem parsing s.
|
||||
*/
|
||||
public java.time.LocalDateTime toLocalDateTime(String s) throws SQLException {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int slen = s.length();
|
||||
|
||||
// convert postgres's infinity values to internal infinity magic value
|
||||
if (slen == 8 && s.equals("infinity")) {
|
||||
return java.time.LocalDateTime.MAX;
|
||||
}
|
||||
|
||||
if (slen == 9 && s.equals("-infinity")) {
|
||||
return java.time.LocalDateTime.MIN;
|
||||
}
|
||||
|
||||
ParsedTimestamp ts = parseBackendTimestamp(s);
|
||||
|
||||
// intentionally ignore time zone
|
||||
// 2004-10-19 10:23:54+03:00 is 2004-10-19 10:23:54 locally
|
||||
java.time.LocalDateTime result = java.time.LocalDateTime.of(ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, ts.nanos);
|
||||
if (ts.era == GregorianCalendar.BC) {
|
||||
return result.with(java.time.temporal.ChronoField.ERA, java.time.chrono.IsoEra.BCE.getValue());
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string and return a LocalDateTime representing its value.
|
||||
*
|
||||
* @param s The ISO formated date string to parse.
|
||||
* @return null if s is null or a LocalDateTime of the parsed string s.
|
||||
* @throws SQLException if there is a problem parsing s.
|
||||
*/
|
||||
public java.time.OffsetDateTime toOffsetDateTime(
|
||||
String s) throws SQLException {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int slen = s.length();
|
||||
|
||||
// convert postgres's infinity values to internal infinity magic value
|
||||
if (slen == 8 && s.equals("infinity")) {
|
||||
return java.time.OffsetDateTime.MAX;
|
||||
}
|
||||
|
||||
if (slen == 9 && s.equals("-infinity")) {
|
||||
return java.time.OffsetDateTime.MIN;
|
||||
}
|
||||
|
||||
ParsedTimestamp ts = parseBackendTimestamp(s);
|
||||
|
||||
Calendar tz = ts.tz;
|
||||
int offsetSeconds;
|
||||
if (tz == null) {
|
||||
offsetSeconds = 0;
|
||||
} else {
|
||||
offsetSeconds = tz.get(Calendar.ZONE_OFFSET) / 1000;
|
||||
}
|
||||
java.time.ZoneOffset zoneOffset = java.time.ZoneOffset.ofTotalSeconds(offsetSeconds);
|
||||
// Postgres is always UTC
|
||||
java.time.OffsetDateTime result = java.time.OffsetDateTime.of(ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, ts.nanos, zoneOffset)
|
||||
.withOffsetSameInstant(java.time.ZoneOffset.UTC);
|
||||
if (ts.era == GregorianCalendar.BC) {
|
||||
return result.with(java.time.temporal.ChronoField.ERA, java.time.chrono.IsoEra.BCE.getValue());
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset date time object matching the given bytes with Oid#TIMETZ.
|
||||
*
|
||||
* @param t the time value
|
||||
* @return the matching offset date time
|
||||
*/
|
||||
public java.time.OffsetDateTime toOffsetDateTime(Time t) {
|
||||
// hardcode utc because the backend does not provide us the timezone
|
||||
// hardcode UNIX epoch, JDBC requires OffsetDateTime but doesn't describe what date should be used
|
||||
return t.toLocalTime().atDate(java.time.LocalDate.of(1970, 1, 1)).atOffset(java.time.ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset date time object matching the given bytes with Oid#TIMESTAMPTZ.
|
||||
*
|
||||
* @param bytes The binary encoded local date time value.
|
||||
* @return The parsed local date time object.
|
||||
* @throws PSQLException If binary format could not be parsed.
|
||||
*/
|
||||
public java.time.OffsetDateTime toOffsetDateTimeBin(byte[] bytes) throws PSQLException {
|
||||
ParsedBinaryTimestamp parsedTimestamp = this.toProlepticParsedTimestampBin(bytes);
|
||||
if (parsedTimestamp.infinity == Infinity.POSITIVE) {
|
||||
return java.time.OffsetDateTime.MAX;
|
||||
} else if (parsedTimestamp.infinity == Infinity.NEGATIVE) {
|
||||
return java.time.OffsetDateTime.MIN;
|
||||
}
|
||||
|
||||
// hardcode utc because the backend does not provide us the timezone
|
||||
// Postgres is always UTC
|
||||
java.time.Instant instant = java.time.Instant.ofEpochSecond(parsedTimestamp.millis / 1000L, parsedTimestamp.nanos);
|
||||
return java.time.OffsetDateTime.ofInstant(instant, java.time.ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
public synchronized Time toTime(Calendar cal, String s) throws SQLException {
|
||||
// 1) Parse backend string
|
||||
@ -675,6 +822,111 @@ public class TimestampUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized String toString(java.time.LocalDate localDate) {
|
||||
if (java.time.LocalDate.MAX.equals(localDate)) {
|
||||
return "infinity";
|
||||
} else if (localDate.isBefore(MIN_LOCAL_DATE)) {
|
||||
return "-infinity";
|
||||
}
|
||||
|
||||
sbuf.setLength(0);
|
||||
|
||||
appendDate(sbuf, localDate);
|
||||
appendEra(sbuf, localDate);
|
||||
|
||||
return sbuf.toString();
|
||||
}
|
||||
|
||||
public synchronized String toString(java.time.LocalTime localTime) {
|
||||
|
||||
sbuf.setLength(0);
|
||||
|
||||
if (localTime.isAfter(MAX_TIME)) {
|
||||
return "24:00:00";
|
||||
}
|
||||
|
||||
int nano = localTime.getNano();
|
||||
if (nanosExceed499(nano)) {
|
||||
// Technically speaking this is not a proper rounding, however
|
||||
// it relies on the fact that appendTime just truncates 000..999 nanosecond part
|
||||
localTime = localTime.plus(ONE_MICROSECOND);
|
||||
}
|
||||
appendTime(sbuf, localTime);
|
||||
|
||||
return sbuf.toString();
|
||||
}
|
||||
|
||||
public synchronized String toString(java.time.OffsetDateTime offsetDateTime) {
|
||||
if (offsetDateTime.isAfter(MAX_OFFSET_DATETIME)) {
|
||||
return "infinity";
|
||||
} else if (offsetDateTime.isBefore(MIN_OFFSET_DATETIME)) {
|
||||
return "-infinity";
|
||||
}
|
||||
|
||||
sbuf.setLength(0);
|
||||
|
||||
int nano = offsetDateTime.getNano();
|
||||
if (nanosExceed499(nano)) {
|
||||
// Technically speaking this is not a proper rounding, however
|
||||
// it relies on the fact that appendTime just truncates 000..999 nanosecond part
|
||||
offsetDateTime = offsetDateTime.plus(ONE_MICROSECOND);
|
||||
}
|
||||
java.time.LocalDateTime localDateTime = offsetDateTime.toLocalDateTime();
|
||||
java.time.LocalDate localDate = localDateTime.toLocalDate();
|
||||
appendDate(sbuf, localDate);
|
||||
sbuf.append(' ');
|
||||
appendTime(sbuf, localDateTime.toLocalTime());
|
||||
appendTimeZone(sbuf, offsetDateTime.getOffset());
|
||||
appendEra(sbuf, localDate);
|
||||
|
||||
return sbuf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats {@link java.time.LocalDateTime} to be sent to the backend, thus it adds time zone.
|
||||
* Do not use this method in {@link java.sql.ResultSet#getString(int)}
|
||||
* @param localDateTime The local date to format as a String
|
||||
* @return The formatted local date
|
||||
*/
|
||||
public synchronized String toString(java.time.LocalDateTime localDateTime) {
|
||||
if (localDateTime.isAfter(MAX_LOCAL_DATETIME)) {
|
||||
return "infinity";
|
||||
} else if (localDateTime.isBefore(MIN_LOCAL_DATETIME)) {
|
||||
return "-infinity";
|
||||
}
|
||||
|
||||
// LocalDateTime is always passed with time zone so backend can decide between timestamp and timestamptz
|
||||
java.time.ZonedDateTime zonedDateTime = localDateTime.atZone(getDefaultTz().toZoneId());
|
||||
return toString(zonedDateTime.toOffsetDateTime());
|
||||
}
|
||||
|
||||
private static void appendDate(StringBuilder sb, java.time.LocalDate localDate) {
|
||||
int year = localDate.get(java.time.temporal.ChronoField.YEAR_OF_ERA);
|
||||
int month = localDate.getMonthValue();
|
||||
int day = localDate.getDayOfMonth();
|
||||
appendDate(sb, year, month, day);
|
||||
}
|
||||
|
||||
private static void appendTime(StringBuilder sb, java.time.LocalTime localTime) {
|
||||
int hours = localTime.getHour();
|
||||
int minutes = localTime.getMinute();
|
||||
int seconds = localTime.getSecond();
|
||||
int nanos = localTime.getNano();
|
||||
appendTime(sb, hours, minutes, seconds, nanos);
|
||||
}
|
||||
|
||||
private void appendTimeZone(StringBuilder sb, java.time.ZoneOffset offset) {
|
||||
int offsetSeconds = offset.getTotalSeconds();
|
||||
|
||||
appendTimeZone(sb, offsetSeconds);
|
||||
}
|
||||
|
||||
private static void appendEra(StringBuilder sb, java.time.LocalDate localDate) {
|
||||
if (localDate.get(java.time.temporal.ChronoField.ERA) == java.time.chrono.IsoEra.BCE.getValue()) {
|
||||
sb.append(" BC");
|
||||
}
|
||||
}
|
||||
|
||||
private static int skipWhitespace(char[] s, int start) {
|
||||
int slen = s.length;
|
||||
for (int i = start; i < slen; i++) {
|
||||
@ -816,6 +1068,32 @@ public class TimestampUtils {
|
||||
return convertToTime(millis, tz); // Ensure date part is 1970-01-01
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL Time object matching the given bytes with {@link Oid#TIME}.
|
||||
*
|
||||
* @param bytes The binary encoded time value.
|
||||
* @return The parsed time object.
|
||||
* @throws PSQLException If binary format could not be parsed.
|
||||
*/
|
||||
public java.time.LocalTime toLocalTimeBin(byte[] bytes) throws PSQLException {
|
||||
if (bytes.length != 8) {
|
||||
throw new PSQLException(GT.tr("Unsupported binary encoding of {0}.", "time"),
|
||||
PSQLState.BAD_DATETIME_FORMAT);
|
||||
}
|
||||
|
||||
long micros;
|
||||
|
||||
if (usesDouble) {
|
||||
double seconds = ByteConverter.float8(bytes, 0);
|
||||
|
||||
micros = (long) (seconds * 1000000d);
|
||||
} else {
|
||||
micros = ByteConverter.int8(bytes, 0);
|
||||
}
|
||||
|
||||
return java.time.LocalTime.ofNanoOfDay(micros * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL Timestamp object matching the given bytes with {@link Oid#TIMESTAMP} or
|
||||
* {@link Oid#TIMESTAMPTZ}.
|
||||
@ -1174,4 +1452,102 @@ public class TimestampUtils {
|
||||
}
|
||||
return TimeZone.getTimeZone(timeZone);
|
||||
}
|
||||
|
||||
private ParsedBinaryTimestamp toParsedTimestampBinPlain(byte[] bytes)
|
||||
throws PSQLException {
|
||||
|
||||
if (bytes.length != 8) {
|
||||
throw new PSQLException(GT.tr("Unsupported binary encoding of {0}.", "timestamp"),
|
||||
PSQLState.BAD_DATETIME_FORMAT);
|
||||
}
|
||||
|
||||
long secs;
|
||||
int nanos;
|
||||
|
||||
if (usesDouble) {
|
||||
double time = ByteConverter.float8(bytes, 0);
|
||||
if (time == Double.POSITIVE_INFINITY) {
|
||||
ParsedBinaryTimestamp ts = new ParsedBinaryTimestamp();
|
||||
ts.infinity = Infinity.POSITIVE;
|
||||
return ts;
|
||||
} else if (time == Double.NEGATIVE_INFINITY) {
|
||||
ParsedBinaryTimestamp ts = new ParsedBinaryTimestamp();
|
||||
ts.infinity = Infinity.NEGATIVE;
|
||||
return ts;
|
||||
}
|
||||
|
||||
secs = (long) time;
|
||||
nanos = (int) ((time - secs) * 1000000);
|
||||
} else {
|
||||
long time = ByteConverter.int8(bytes, 0);
|
||||
|
||||
// compatibility with text based receiving, not strictly necessary
|
||||
// and can actually be confusing because there are timestamps
|
||||
// that are larger than infinite
|
||||
if (time == Long.MAX_VALUE) {
|
||||
ParsedBinaryTimestamp ts = new ParsedBinaryTimestamp();
|
||||
ts.infinity = Infinity.POSITIVE;
|
||||
return ts;
|
||||
} else if (time == Long.MIN_VALUE) {
|
||||
ParsedBinaryTimestamp ts = new ParsedBinaryTimestamp();
|
||||
ts.infinity = Infinity.NEGATIVE;
|
||||
return ts;
|
||||
}
|
||||
|
||||
secs = time / 1000000;
|
||||
nanos = (int) (time - secs * 1000000);
|
||||
}
|
||||
if (nanos < 0) {
|
||||
secs--;
|
||||
nanos += 1000000;
|
||||
}
|
||||
nanos *= 1000;
|
||||
|
||||
long millis = secs * 1000L;
|
||||
|
||||
ParsedBinaryTimestamp ts = new ParsedBinaryTimestamp();
|
||||
ts.millis = millis;
|
||||
ts.nanos = nanos;
|
||||
return ts;
|
||||
}
|
||||
|
||||
private ParsedBinaryTimestamp toProlepticParsedTimestampBin(byte[] bytes)
|
||||
throws PSQLException {
|
||||
|
||||
ParsedBinaryTimestamp ts = toParsedTimestampBinPlain(bytes);
|
||||
if (ts.infinity != null) {
|
||||
return ts;
|
||||
}
|
||||
|
||||
long secs = ts.millis / 1000L;
|
||||
|
||||
// postgres epoc to java epoc
|
||||
secs += 946684800L;
|
||||
long millis = secs * 1000L;
|
||||
|
||||
ts.millis = millis;
|
||||
return ts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local date time object matching the given bytes with {@link Oid#TIMESTAMP} or
|
||||
* {@link Oid#TIMESTAMPTZ}.
|
||||
* @param bytes The binary encoded local date time value.
|
||||
*
|
||||
* @return The parsed local date time object.
|
||||
* @throws PSQLException If binary format could not be parsed.
|
||||
*/
|
||||
public java.time.LocalDateTime toLocalDateTimeBin(byte[] bytes) throws PSQLException {
|
||||
|
||||
ParsedBinaryTimestamp parsedTimestamp = this.toProlepticParsedTimestampBin(bytes);
|
||||
if (parsedTimestamp.infinity == Infinity.POSITIVE) {
|
||||
return java.time.LocalDateTime.MAX;
|
||||
} else if (parsedTimestamp.infinity == Infinity.NEGATIVE) {
|
||||
return java.time.LocalDateTime.MIN;
|
||||
}
|
||||
|
||||
// hardcode utc because the backend does not provide us the timezone
|
||||
// Postgres is always UTC
|
||||
return java.time.LocalDateTime.ofEpochSecond(parsedTimestamp.millis / 1000L, parsedTimestamp.nanos, java.time.ZoneOffset.UTC);
|
||||
}
|
||||
}
|
||||
|
||||
125
pgjdbc/src/test/java/org/postgresql/localtimedate/BaseTest4.java
Normal file
125
pgjdbc/src/test/java/org/postgresql/localtimedate/BaseTest4.java
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2004, PostgreSQL Global Development Group
|
||||
* See the LICENSE file in the project root for more information.
|
||||
*/
|
||||
|
||||
package org.postgresql.localtimedate;
|
||||
|
||||
import org.postgresql.PGConnection;
|
||||
import org.postgresql.PGProperty;
|
||||
import org.postgresql.core.Version;
|
||||
import org.postgresql.jdbc.PreferQueryMode;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
|
||||
public class BaseTest4 {
|
||||
|
||||
public enum BinaryMode {
|
||||
REGULAR, FORCE
|
||||
}
|
||||
|
||||
public enum ReWriteBatchedInserts {
|
||||
YES, NO
|
||||
}
|
||||
|
||||
public enum AutoCommit {
|
||||
YES, NO
|
||||
}
|
||||
|
||||
public enum StringType {
|
||||
UNSPECIFIED, VARCHAR;
|
||||
}
|
||||
|
||||
protected Connection con;
|
||||
private BinaryMode binaryMode;
|
||||
private ReWriteBatchedInserts reWriteBatchedInserts;
|
||||
protected PreferQueryMode preferQueryMode;
|
||||
private StringType stringType;
|
||||
|
||||
protected void updateProperties(Properties props) {
|
||||
if (binaryMode == BinaryMode.FORCE) {
|
||||
forceBinary(props);
|
||||
}
|
||||
if (reWriteBatchedInserts == ReWriteBatchedInserts.YES) {
|
||||
PGProperty.REWRITE_BATCHED_INSERTS.set(props, true);
|
||||
}
|
||||
if (stringType != null) {
|
||||
PGProperty.STRING_TYPE.set(props, stringType.name().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
protected void forceBinary(Properties props) {
|
||||
PGProperty.PREPARE_THRESHOLD.set(props, -1);
|
||||
}
|
||||
|
||||
public final void setBinaryMode(BinaryMode binaryMode) {
|
||||
this.binaryMode = binaryMode;
|
||||
}
|
||||
|
||||
public StringType getStringType() {
|
||||
return stringType;
|
||||
}
|
||||
|
||||
public void setStringType(StringType stringType) {
|
||||
this.stringType = stringType;
|
||||
}
|
||||
|
||||
public void setReWriteBatchedInserts(
|
||||
ReWriteBatchedInserts reWriteBatchedInserts) {
|
||||
this.reWriteBatchedInserts = reWriteBatchedInserts;
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws SQLException {
|
||||
TestUtil.closeDB(con);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Properties props = new Properties();
|
||||
updateProperties(props);
|
||||
con = TestUtil.openDB(props);
|
||||
PGConnection pg = con.unwrap(PGConnection.class);
|
||||
preferQueryMode = pg == null ? PreferQueryMode.EXTENDED : pg.getPreferQueryMode();
|
||||
}
|
||||
|
||||
public void assumeByteaSupported() {
|
||||
Assume.assumeTrue("bytea is not supported in simple protocol execution mode",
|
||||
preferQueryMode.compareTo(PreferQueryMode.EXTENDED) >= 0);
|
||||
}
|
||||
|
||||
public void assumeCallableStatementsSupported() {
|
||||
Assume.assumeTrue("callable statements are not fully supported in simple protocol execution mode",
|
||||
preferQueryMode.compareTo(PreferQueryMode.EXTENDED) >= 0);
|
||||
}
|
||||
|
||||
public void assumeBinaryModeRegular() {
|
||||
Assume.assumeTrue(binaryMode == BinaryMode.REGULAR);
|
||||
}
|
||||
|
||||
public void assumeBinaryModeForce() {
|
||||
Assume.assumeTrue(binaryMode == BinaryMode.FORCE);
|
||||
Assume.assumeTrue(preferQueryMode != PreferQueryMode.SIMPLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for {@code Assume.assumeTrue(TestUtil.haveMinimumServerVersion(conn, version)}.
|
||||
*/
|
||||
public void assumeMinimumServerVersion(String message, Version version) throws SQLException {
|
||||
Assume.assumeTrue(message, TestUtil.haveMinimumServerVersion(con, version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for {@code Assume.assumeTrue(TestUtil.haveMinimumServerVersion(conn, version)}.
|
||||
*/
|
||||
public void assumeMinimumServerVersion(Version version) throws SQLException {
|
||||
Assume.assumeTrue(TestUtil.haveMinimumServerVersion(con, version));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2017, PostgreSQL Global Development Group
|
||||
* See the LICENSE file in the project root for more information.
|
||||
*/
|
||||
|
||||
package org.postgresql.localtimedate;
|
||||
import org.postgresql.core.ServerVersion;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class GetObject310InfinityTests extends BaseTest4 {
|
||||
private final String expression;
|
||||
private final String pgType;
|
||||
private final Class<?> klass;
|
||||
private final Object expectedValue;
|
||||
|
||||
public GetObject310InfinityTests(BinaryMode binaryMode, String expression,
|
||||
String pgType, Class<?> klass, Object expectedValue) {
|
||||
setBinaryMode(binaryMode);
|
||||
this.expression = expression;
|
||||
this.pgType = pgType;
|
||||
this.klass = klass;
|
||||
this.expectedValue = expectedValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
Assume.assumeTrue("PostgreSQL 8.3 does not support 'infinity' for 'date'",
|
||||
!"date".equals(pgType) || TestUtil.haveMinimumServerVersion(con, ServerVersion.v8_4));
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "binary = {0}, expr = {1}, pgType = {2}, klass = {3}")
|
||||
public static Iterable<Object[]> data() throws IllegalAccessException {
|
||||
Collection<Object[]> ids = new ArrayList<Object[]>();
|
||||
for (BinaryMode binaryMode : BinaryMode.values()) {
|
||||
for (String expression : Arrays.asList("-infinity", "infinity")) {
|
||||
for (String pgType : Arrays.asList("date", "timestamp",
|
||||
"timestamp with time zone")) {
|
||||
for (Class<?> klass : Arrays.asList(LocalDate.class, LocalDateTime.class,
|
||||
OffsetDateTime.class)) {
|
||||
if (klass.equals(LocalDate.class) && !pgType.equals("date")) {
|
||||
continue;
|
||||
}
|
||||
if (klass.equals(LocalDateTime.class) && !pgType.startsWith("timestamp")) {
|
||||
continue;
|
||||
}
|
||||
if (klass.equals(OffsetDateTime.class) && !pgType.startsWith("timestamp")) {
|
||||
continue;
|
||||
}
|
||||
if (klass.equals(LocalDateTime.class) && pgType.equals("timestamp with time zone")) {
|
||||
// org.postgresql.util.PSQLException: Cannot convert the column of type TIMESTAMPTZ to requested type timestamp.
|
||||
continue;
|
||||
}
|
||||
Field field = null;
|
||||
try {
|
||||
field = klass.getField(expression.startsWith("-") ? "MIN" : "MAX");
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new IllegalStateException("No min/max field in " + klass, e);
|
||||
}
|
||||
Object expected = field.get(null);
|
||||
ids.add(new Object[]{binaryMode, expression, pgType, klass, expected});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws SQLException {
|
||||
PreparedStatement stmt = con.prepareStatement("select '" + expression + "'::" + pgType);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
rs.next();
|
||||
Object res = rs.getObject(1, klass);
|
||||
Assert.assertEquals(expectedValue, res);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,372 @@
|
||||
/*
|
||||
* Copyright (c) 2004, PostgreSQL Global Development Group
|
||||
* See the LICENSE file in the project root for more information.
|
||||
*/
|
||||
|
||||
package org.postgresql.localtimedate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import org.postgresql.core.ServerVersion;
|
||||
import org.postgresql.util.PSQLException;
|
||||
import org.postgresql.util.PSQLState;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.chrono.IsoEra;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class GetObject310Test extends BaseTest4 {
|
||||
|
||||
private static final TimeZone saveTZ = TimeZone.getDefault();
|
||||
|
||||
private static final ZoneOffset UTC = ZoneOffset.UTC; // +0000 always
|
||||
private static final ZoneOffset GMT03 = ZoneOffset.of("+03:00"); // +0300 always
|
||||
private static final ZoneOffset GMT05 = ZoneOffset.of("-05:00"); // -0500 always
|
||||
private static final ZoneOffset GMT13 = ZoneOffset.of("+13:00"); // +1300 always
|
||||
|
||||
public GetObject310Test(BinaryMode binaryMode) {
|
||||
setBinaryMode(binaryMode);
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "binary = {0}")
|
||||
public static Iterable<Object[]> data() {
|
||||
Collection<Object[]> ids = new ArrayList<Object[]>();
|
||||
for (BinaryMode binaryMode : BinaryMode.values()) {
|
||||
ids.add(new Object[]{binaryMode});
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
TestUtil.createTable(con, "table1", "timestamp_without_time_zone_column timestamp without time zone,"
|
||||
+ "timestamp_with_time_zone_column timestamp with time zone,"
|
||||
+ "date_column date,"
|
||||
+ "time_without_time_zone_column time without time zone,"
|
||||
+ "time_with_time_zone_column time with time zone"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws SQLException {
|
||||
TimeZone.setDefault(saveTZ);
|
||||
TestUtil.dropTable(con, "table1");
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior getObject for date columns.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLocalDate() throws SQLException {
|
||||
Statement stmt = con.createStatement();
|
||||
stmt.executeUpdate(TestUtil.insertSQL("table1","date_column","DATE '1999-01-08'"));
|
||||
|
||||
ResultSet rs = stmt.executeQuery(TestUtil.selectSQL("table1", "date_column"));
|
||||
try {
|
||||
assertTrue(rs.next());
|
||||
LocalDate localDate = LocalDate.of(1999, 1, 8);
|
||||
assertEquals(localDate, rs.getObject("date_column", LocalDate.class));
|
||||
assertEquals(localDate, rs.getObject(1, LocalDate.class));
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior getObject for time columns.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLocalTime() throws SQLException {
|
||||
Statement stmt = con.createStatement();
|
||||
stmt.executeUpdate(TestUtil.insertSQL("table1","time_without_time_zone_column","TIME '04:05:06.123456'"));
|
||||
|
||||
ResultSet rs = stmt.executeQuery(TestUtil.selectSQL("table1", "time_without_time_zone_column"));
|
||||
try {
|
||||
assertTrue(rs.next());
|
||||
LocalTime localTime = LocalTime.of(4, 5, 6, 123456000);
|
||||
assertEquals(localTime, rs.getObject("time_without_time_zone_column", LocalTime.class));
|
||||
assertEquals(localTime, rs.getObject(1, LocalTime.class));
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior getObject for time columns with null.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLocalTimeNull() throws SQLException {
|
||||
Statement stmt = con.createStatement();
|
||||
stmt.executeUpdate(TestUtil.insertSQL("table1","time_without_time_zone_column","NULL"));
|
||||
|
||||
ResultSet rs = stmt.executeQuery(TestUtil.selectSQL("table1", "time_without_time_zone_column"));
|
||||
try {
|
||||
assertTrue(rs.next());
|
||||
assertNull(rs.getObject("time_without_time_zone_column", LocalTime.class));
|
||||
assertNull(rs.getObject(1, LocalTime.class));
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior getObject for time columns with null.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLocalTimeInvalidType() throws SQLException {
|
||||
Statement stmt = con.createStatement();
|
||||
stmt.executeUpdate(TestUtil.insertSQL("table1","time_with_time_zone_column", "TIME '04:05:06.123456-08:00'"));
|
||||
|
||||
ResultSet rs = stmt.executeQuery(TestUtil.selectSQL("table1", "time_with_time_zone_column"));
|
||||
try {
|
||||
assertTrue(rs.next());
|
||||
try {
|
||||
assertNull(rs.getObject("time_with_time_zone_column", LocalTime.class));
|
||||
} catch (PSQLException e) {
|
||||
assertTrue(e.getSQLState().equals(PSQLState.DATA_TYPE_MISMATCH.getState())
|
||||
|| e.getSQLState().equals(PSQLState.BAD_DATETIME_FORMAT.getState()));
|
||||
}
|
||||
try {
|
||||
assertNull(rs.getObject(1, LocalTime.class));
|
||||
} catch (PSQLException e) {
|
||||
assertTrue(e.getSQLState().equals(PSQLState.DATA_TYPE_MISMATCH.getState())
|
||||
|| e.getSQLState().equals(PSQLState.BAD_DATETIME_FORMAT.getState()));
|
||||
}
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior getObject for timestamp columns.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLocalDateTime() throws SQLException {
|
||||
assumeTrue(TestUtil.haveIntegerDateTimes(con));
|
||||
|
||||
List<String> zoneIdsToTest = new ArrayList<String>();
|
||||
zoneIdsToTest.add("Africa/Casablanca"); // It is something like GMT+0..GMT+1
|
||||
zoneIdsToTest.add("America/Adak"); // It is something like GMT-10..GMT-9
|
||||
zoneIdsToTest.add("Atlantic/Azores"); // It is something like GMT-1..GMT+0
|
||||
zoneIdsToTest.add("Europe/Moscow"); // It is something like GMT+3..GMT+4 for 2000s
|
||||
zoneIdsToTest.add("Pacific/Apia"); // It is something like GMT+13..GMT+14
|
||||
zoneIdsToTest.add("Pacific/Niue"); // It is something like GMT-11..GMT-11
|
||||
for (int i = -12; i <= 13; i++) {
|
||||
zoneIdsToTest.add(String.format("GMT%+02d", i));
|
||||
}
|
||||
|
||||
List<String> datesToTest = Arrays.asList("2015-09-03T12:00:00", "2015-06-30T23:59:58",
|
||||
"1997-06-30T23:59:59", "1997-07-01T00:00:00", "2012-06-30T23:59:59", "2012-07-01T00:00:00",
|
||||
"2015-06-30T23:59:59", "2015-07-01T00:00:00", "2005-12-31T23:59:59", "2006-01-01T00:00:00",
|
||||
"2008-12-31T23:59:59", "2009-01-01T00:00:00", /* "2015-06-30T23:59:60", */ "2015-07-31T00:00:00",
|
||||
"2015-07-31T00:00:01", "2015-07-31T00:00:00.000001",
|
||||
|
||||
// On 2000-03-26 02:00:00 Moscow went to DST, thus local time became 03:00:00
|
||||
"2000-03-26T01:59:59", "2000-03-26T02:00:00", "2000-03-26T02:00:01", "2000-03-26T02:59:59",
|
||||
"2000-03-26T03:00:00", "2000-03-26T03:00:01", "2000-03-26T03:59:59", "2000-03-26T04:00:00",
|
||||
"2000-03-26T04:00:01", "2000-03-26T04:00:00.000001",
|
||||
|
||||
// This is a pre-1970 date, so check if it is rounded properly
|
||||
"1950-07-20T02:00:00",
|
||||
|
||||
// Ensure the calendar is proleptic
|
||||
"1582-09-30T00:00:00", "1582-10-16T00:00:00",
|
||||
|
||||
// On 2000-10-29 03:00:00 Moscow went to regular time, thus local time became 02:00:00
|
||||
"2000-10-29T01:59:59", "2000-10-29T02:00:00", "2000-10-29T02:00:01", "2000-10-29T02:59:59",
|
||||
"2000-10-29T03:00:00", "2000-10-29T03:00:01", "2000-10-29T03:59:59", "2000-10-29T04:00:00",
|
||||
"2000-10-29T04:00:01", "2000-10-29T04:00:00.000001");
|
||||
|
||||
for (String zoneId : zoneIdsToTest) {
|
||||
ZoneId zone = ZoneId.of(zoneId);
|
||||
for (String date : datesToTest) {
|
||||
localTimestamps(zone, date);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void localTimestamps(ZoneId zoneId, String timestamp) throws SQLException {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(zoneId));
|
||||
Statement stmt = con.createStatement();
|
||||
try {
|
||||
stmt.executeUpdate(TestUtil.insertSQL("table1","timestamp_without_time_zone_column","TIMESTAMP '" + timestamp + "'"));
|
||||
|
||||
ResultSet rs = stmt.executeQuery(TestUtil.selectSQL("table1", "timestamp_without_time_zone_column"));
|
||||
try {
|
||||
assertTrue(rs.next());
|
||||
LocalDateTime localDateTime = LocalDateTime.parse(timestamp);
|
||||
assertEquals(localDateTime, rs.getObject("timestamp_without_time_zone_column", LocalDateTime.class));
|
||||
assertEquals(localDateTime, rs.getObject(1, LocalDateTime.class));
|
||||
|
||||
//Also test that we get the correct values when retrieving the data as LocalDate objects
|
||||
assertEquals(localDateTime.toLocalDate(), rs.getObject("timestamp_without_time_zone_column", LocalDate.class));
|
||||
assertEquals(localDateTime.toLocalDate(), rs.getObject(1, LocalDate.class));
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
stmt.executeUpdate("DELETE FROM table1");
|
||||
} finally {
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior getObject for timestamp with time zone columns.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTimestampWithTimeZone() throws SQLException {
|
||||
runGetOffsetDateTime(UTC);
|
||||
runGetOffsetDateTime(GMT03);
|
||||
runGetOffsetDateTime(GMT05);
|
||||
runGetOffsetDateTime(GMT13);
|
||||
}
|
||||
|
||||
private void runGetOffsetDateTime(ZoneOffset offset) throws SQLException {
|
||||
Statement stmt = con.createStatement();
|
||||
try {
|
||||
stmt.executeUpdate(TestUtil.insertSQL("table1","timestamp_with_time_zone_column","TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54.123456" + offset.toString() + "'"));
|
||||
|
||||
ResultSet rs = stmt.executeQuery(TestUtil.selectSQL("table1", "timestamp_with_time_zone_column"));
|
||||
try {
|
||||
assertTrue(rs.next());
|
||||
LocalDateTime localDateTime = LocalDateTime.of(2004, 10, 19, 10, 23, 54, 123456000);
|
||||
|
||||
OffsetDateTime offsetDateTime = localDateTime.atOffset(offset).withOffsetSameInstant(ZoneOffset.UTC);
|
||||
assertEquals(offsetDateTime, rs.getObject("timestamp_with_time_zone_column", OffsetDateTime.class));
|
||||
assertEquals(offsetDateTime, rs.getObject(1, OffsetDateTime.class));
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
stmt.executeUpdate("DELETE FROM table1");
|
||||
} finally {
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBcTimestamp() throws SQLException {
|
||||
|
||||
Statement stmt = con.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT '1582-09-30 12:34:56 BC'::timestamp");
|
||||
try {
|
||||
assertTrue(rs.next());
|
||||
LocalDateTime expected = LocalDateTime.of(1582, 9, 30, 12, 34, 56)
|
||||
.with(ChronoField.ERA, IsoEra.BCE.getValue());
|
||||
LocalDateTime actual = rs.getObject(1, LocalDateTime.class);
|
||||
assertEquals(expected, actual);
|
||||
assertFalse(rs.next());
|
||||
} finally {
|
||||
rs.close();
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBcTimestamptz() throws SQLException {
|
||||
|
||||
Statement stmt = con.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT '1582-09-30 12:34:56Z BC'::timestamp");
|
||||
try {
|
||||
assertTrue(rs.next());
|
||||
OffsetDateTime expected = OffsetDateTime.of(1582, 9, 30, 12, 34, 56, 0, UTC)
|
||||
.with(ChronoField.ERA, IsoEra.BCE.getValue());
|
||||
OffsetDateTime actual = rs.getObject(1, OffsetDateTime.class);
|
||||
assertEquals(expected, actual);
|
||||
assertFalse(rs.next());
|
||||
} finally {
|
||||
rs.close();
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProlepticCalendarTimestamp() throws SQLException {
|
||||
// date time ranges and CTEs are both new with 8.4
|
||||
assumeMinimumServerVersion(ServerVersion.v8_4);
|
||||
LocalDateTime start = LocalDate.of(1582, 9, 30).atStartOfDay();
|
||||
LocalDateTime end = LocalDate.of(1582, 10, 16).atStartOfDay();
|
||||
long numberOfDays = Duration.between(start, end).toDays() + 1L;
|
||||
List<LocalDateTime> range = Stream.iterate(start, new LocalDateTimePlusOneDay())
|
||||
.limit(numberOfDays)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
runProlepticTests(LocalDateTime.class, "'1582-09-30 00:00'::timestamp, '1582-10-16 00:00'::timestamp", range);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProlepticCalendarTimestamptz() throws SQLException {
|
||||
// date time ranges and CTEs are both new with 8.4
|
||||
assumeMinimumServerVersion(ServerVersion.v8_4);
|
||||
OffsetDateTime start = LocalDate.of(1582, 9, 30).atStartOfDay().atOffset(UTC);
|
||||
OffsetDateTime end = LocalDate.of(1582, 10, 16).atStartOfDay().atOffset(UTC);
|
||||
long numberOfDays = Duration.between(start, end).toDays() + 1L;
|
||||
List<OffsetDateTime> range = Stream.iterate(start, new OffsetDateTimePlusOneDay())
|
||||
.limit(numberOfDays)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
runProlepticTests(OffsetDateTime.class, "'1582-09-30 00:00:00 Z'::timestamptz, '1582-10-16 00:00:00 Z'::timestamptz", range);
|
||||
}
|
||||
|
||||
private <T extends Temporal> void runProlepticTests(Class<T> clazz, String selectRange, List<T> range) throws SQLException {
|
||||
List<T> temporals = new ArrayList<>(range.size());
|
||||
|
||||
PreparedStatement stmt = con.prepareStatement("SELECT * FROM generate_series(" + selectRange + ", '1 day');");
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
try {
|
||||
while (rs.next()) {
|
||||
T temporal = rs.getObject(1, clazz);
|
||||
temporals.add(temporal);
|
||||
}
|
||||
assertEquals(range, temporals);
|
||||
} finally {
|
||||
rs.close();
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LocalDateTimePlusOneDay implements UnaryOperator<LocalDateTime> {
|
||||
|
||||
@Override
|
||||
public LocalDateTime apply(LocalDateTime x) {
|
||||
return x.plusDays(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static class OffsetDateTimePlusOneDay implements UnaryOperator<OffsetDateTime> {
|
||||
|
||||
@Override
|
||||
public OffsetDateTime apply(OffsetDateTime x) {
|
||||
return x.plusDays(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2004, PostgreSQL Global Development Group
|
||||
* See the LICENSE file in the project root for more information.
|
||||
*/
|
||||
|
||||
package org.postgresql.localtimedate;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({
|
||||
GetObject310InfinityTests.class,
|
||||
GetObject310Test.class,
|
||||
PreparedStatementTest.class,
|
||||
SetObject310Test.class,
|
||||
SetObject310InfinityTests.class,
|
||||
TimestampUtilsTest.class
|
||||
})
|
||||
public class Jdbc42TestSuite {
|
||||
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2004, PostgreSQL Global Development Group
|
||||
* See the LICENSE file in the project root for more information.
|
||||
*/
|
||||
|
||||
package org.postgresql.localtimedate;
|
||||
import org.postgresql.PGProperty;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Properties;
|
||||
|
||||
public class PreparedStatementTest extends BaseTest4 {
|
||||
protected void updateProperties(Properties props) {
|
||||
PGProperty.PREFER_QUERY_MODE.set(props, "simple");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
TestUtil.createTable(con, "timestamptztable", "tstz timestamptz");
|
||||
TestUtil.createTable(con, "timetztable", "ttz timetz");
|
||||
TestUtil.createTable(con, "timetable", "id serial, tt time");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws SQLException {
|
||||
TestUtil.dropTable(con, "timestamptztable");
|
||||
TestUtil.dropTable(con, "timetztable");
|
||||
TestUtil.dropTable(con, "timetable");
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
// @Test
|
||||
public void testSetNumber() throws SQLException {
|
||||
PreparedStatement pstmt = con.prepareStatement("SELECT ? * 2");
|
||||
|
||||
pstmt.setBigDecimal(1, new BigDecimal("1.6"));
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
rs.next();
|
||||
BigDecimal d = rs.getBigDecimal(1);
|
||||
pstmt.close();
|
||||
|
||||
Assert.assertEquals(new BigDecimal("3.2"), d);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampTzSetNull() throws SQLException {
|
||||
PreparedStatement pstmt = con.prepareStatement("INSERT INTO timestamptztable (tstz) VALUES (?)");
|
||||
|
||||
// valid: fully qualified type to setNull()
|
||||
pstmt.setNull(1, Types.TIMESTAMP_WITH_TIMEZONE);
|
||||
pstmt.executeUpdate();
|
||||
|
||||
// valid: fully qualified type to setObject()
|
||||
pstmt.setObject(1, null, Types.TIMESTAMP_WITH_TIMEZONE);
|
||||
pstmt.executeUpdate();
|
||||
|
||||
pstmt.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeTzSetNull() throws SQLException {
|
||||
PreparedStatement pstmt = con.prepareStatement("INSERT INTO timetztable (ttz) VALUES (?)");
|
||||
|
||||
// valid: fully qualified type to setNull()
|
||||
pstmt.setNull(1, Types.TIME_WITH_TIMEZONE);
|
||||
pstmt.executeUpdate();
|
||||
|
||||
// valid: fully qualified type to setObject()
|
||||
pstmt.setObject(1, null, Types.TIME_WITH_TIMEZONE);
|
||||
pstmt.executeUpdate();
|
||||
|
||||
pstmt.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalTimeMax() throws SQLException {
|
||||
PreparedStatement pstmt = con.prepareStatement("INSERT INTO timetable (tt) VALUES (?)");
|
||||
|
||||
pstmt.setObject(1, LocalTime.MAX);
|
||||
pstmt.executeUpdate();
|
||||
|
||||
pstmt.setObject(1, LocalTime.MIN);
|
||||
pstmt.executeUpdate();
|
||||
|
||||
ResultSet rs = con.createStatement().executeQuery("select tt from timetable order by id asc");
|
||||
Assert.assertTrue(rs.next());
|
||||
LocalTime localTime = (LocalTime)rs.getObject(1,LocalTime.class);
|
||||
Assert.assertEquals( LocalTime.MAX, localTime);
|
||||
|
||||
Assert.assertTrue(rs.next());
|
||||
localTime = (LocalTime)rs.getObject(1, LocalTime.class);
|
||||
Assert.assertEquals( LocalTime.MIN, localTime);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2017, PostgreSQL Global Development Group
|
||||
* See the LICENSE file in the project root for more information.
|
||||
*/
|
||||
|
||||
package org.postgresql.localtimedate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.postgresql.core.ServerVersion;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class SetObject310InfinityTests extends BaseTest4 {
|
||||
|
||||
public SetObject310InfinityTests(BinaryMode binaryMode) {
|
||||
setBinaryMode(binaryMode);
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "binary = {0}")
|
||||
public static Iterable<Object[]> data() {
|
||||
Collection<Object[]> ids = new ArrayList<>(2);
|
||||
for (BinaryMode binaryMode : BinaryMode.values()) {
|
||||
ids.add(new Object[]{binaryMode});
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
Assume.assumeTrue("PostgreSQL 8.3 does not support 'infinity' for 'date'",
|
||||
TestUtil.haveMinimumServerVersion(con, ServerVersion.v8_4));
|
||||
super.setUp();
|
||||
TestUtil.createTable(con, "table1", "timestamp_without_time_zone_column timestamp without time zone,"
|
||||
+ "timestamp_with_time_zone_column timestamp with time zone,"
|
||||
+ "date_column date"
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws SQLException {
|
||||
TestUtil.dropTable(con, "table1");
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestamptz() throws SQLException {
|
||||
runTestforType(OffsetDateTime.MAX, OffsetDateTime.MIN, "timestamp_without_time_zone_column", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestamp() throws SQLException {
|
||||
runTestforType(LocalDateTime.MAX, LocalDateTime.MIN, "timestamp_without_time_zone_column", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDate() throws SQLException {
|
||||
runTestforType(LocalDate.MAX, LocalDate.MIN, "date_column", null);
|
||||
}
|
||||
|
||||
private void runTestforType(Object max, Object min, String columnName, Integer type) throws SQLException {
|
||||
insert(max, columnName, type);
|
||||
String readback = readString(columnName);
|
||||
assertEquals("infinity", readback);
|
||||
delete();
|
||||
|
||||
insert(min, columnName, type);
|
||||
readback = readString(columnName);
|
||||
assertEquals("-infinity", readback);
|
||||
delete();
|
||||
}
|
||||
|
||||
private void insert(Object data, String columnName, Integer type) throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement(TestUtil.insertSQL("table1", columnName, "?"));
|
||||
try {
|
||||
if (type != null) {
|
||||
ps.setObject(1, data, type);
|
||||
} else {
|
||||
ps.setObject(1, data);
|
||||
}
|
||||
assertEquals(1, ps.executeUpdate());
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
|
||||
private String readString(String columnName) throws SQLException {
|
||||
Statement st = con.createStatement();
|
||||
try {
|
||||
ResultSet rs = st.executeQuery(TestUtil.selectSQL("table1", columnName));
|
||||
try {
|
||||
assertNotNull(rs);
|
||||
assertTrue(rs.next());
|
||||
return rs.getString(1);
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
} finally {
|
||||
st.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void delete() throws SQLException {
|
||||
Statement st = con.createStatement();
|
||||
try {
|
||||
st.execute("DELETE FROM table1");
|
||||
} finally {
|
||||
st.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,437 @@
|
||||
/*
|
||||
* Copyright (c) 2004, PostgreSQL Global Development Group
|
||||
* See the LICENSE file in the project root for more information.
|
||||
*/
|
||||
|
||||
package org.postgresql.localtimedate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Time;
|
||||
import java.sql.Types;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.chrono.IsoChronology;
|
||||
import java.time.chrono.IsoEra;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.ResolverStyle;
|
||||
import java.time.format.SignStyle;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class SetObject310Test extends BaseTest4 {
|
||||
private static final TimeZone saveTZ = TimeZone.getDefault();
|
||||
|
||||
public static final DateTimeFormatter LOCAL_TIME_FORMATTER =
|
||||
new DateTimeFormatterBuilder()
|
||||
.parseCaseInsensitive()
|
||||
.appendValue(ChronoField.YEAR_OF_ERA, 4, 10, SignStyle.EXCEEDS_PAD)
|
||||
.appendLiteral('-')
|
||||
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
|
||||
.appendLiteral('-')
|
||||
.appendValue(ChronoField.DAY_OF_MONTH, 2)
|
||||
.appendLiteral(' ')
|
||||
.append(DateTimeFormatter.ISO_LOCAL_TIME)
|
||||
.optionalStart()
|
||||
.appendOffset("+HH:mm", "+00")
|
||||
.optionalEnd()
|
||||
.optionalStart()
|
||||
.appendLiteral(' ')
|
||||
.appendPattern("GG")
|
||||
.toFormatter(Locale.ROOT)
|
||||
.withResolverStyle(ResolverStyle.LENIENT)
|
||||
.withChronology(IsoChronology.INSTANCE);
|
||||
|
||||
public SetObject310Test(BinaryMode binaryMode) {
|
||||
setBinaryMode(binaryMode);
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "binary = {0}")
|
||||
public static Iterable<Object[]> data() {
|
||||
Collection<Object[]> ids = new ArrayList<Object[]>();
|
||||
for (BinaryMode binaryMode : BinaryMode.values()) {
|
||||
ids.add(new Object[]{binaryMode});
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
TestUtil.createTable(con, "table1", "timestamp_without_time_zone_column timestamp without time zone,"
|
||||
+ "timestamp_with_time_zone_column timestamp with time zone,"
|
||||
+ "date_column date,"
|
||||
+ "time_without_time_zone_column time without time zone,"
|
||||
+ "time_with_time_zone_column time with time zone"
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws SQLException {
|
||||
TimeZone.setDefault(saveTZ);
|
||||
TestUtil.dropTable(con, "table1");
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
private void insert(Object data, String columnName, Integer type) throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement(TestUtil.insertSQL("table1", columnName, "?"));
|
||||
try {
|
||||
if (type != null) {
|
||||
ps.setObject(1, data, type);
|
||||
} else {
|
||||
ps.setObject(1, data);
|
||||
}
|
||||
assertEquals(1, ps.executeUpdate());
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
|
||||
private String readString(String columnName) throws SQLException {
|
||||
Statement st = con.createStatement();
|
||||
try {
|
||||
ResultSet rs = st.executeQuery(TestUtil.selectSQL("table1", columnName));
|
||||
try {
|
||||
assertNotNull(rs);
|
||||
assertTrue(rs.next());
|
||||
return rs.getString(1);
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
} finally {
|
||||
st.close();
|
||||
}
|
||||
}
|
||||
|
||||
private String insertThenReadStringWithoutType(LocalDateTime data, String columnName) throws SQLException {
|
||||
insert(data, columnName, null);
|
||||
return readString(columnName);
|
||||
}
|
||||
|
||||
private String insertThenReadStringWithType(LocalDateTime data, String columnName) throws SQLException {
|
||||
insert(data, columnName, Types.TIMESTAMP);
|
||||
return readString(columnName);
|
||||
}
|
||||
|
||||
private void insertWithoutType(Object data, String columnName) throws SQLException {
|
||||
insert(data, columnName, null);
|
||||
}
|
||||
|
||||
private <T> T insertThenReadWithoutType(Object data, String columnName, Class<T> expectedType) throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement(TestUtil.insertSQL("table1", columnName, "?"));
|
||||
try {
|
||||
ps.setObject(1, data);
|
||||
assertEquals(1, ps.executeUpdate());
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
|
||||
Statement st = con.createStatement();
|
||||
try {
|
||||
ResultSet rs = st.executeQuery(TestUtil.selectSQL("table1", columnName));
|
||||
try {
|
||||
assertNotNull(rs);
|
||||
|
||||
assertTrue(rs.next());
|
||||
return expectedType.cast(rs.getObject(1));
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
} finally {
|
||||
st.close();
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T insertThenReadWithType(Object data, int sqlType, String columnName, Class<T> expectedType) throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement(TestUtil.insertSQL("table1", columnName, "?"));
|
||||
try {
|
||||
ps.setObject(1, data, sqlType);
|
||||
assertEquals(1, ps.executeUpdate());
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
|
||||
Statement st = con.createStatement();
|
||||
try {
|
||||
ResultSet rs = st.executeQuery(TestUtil.selectSQL("table1", columnName));
|
||||
try {
|
||||
assertNotNull(rs);
|
||||
|
||||
assertTrue(rs.next());
|
||||
return expectedType.cast(rs.getObject(1));
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
} finally {
|
||||
st.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteRows() throws SQLException {
|
||||
Statement st = con.createStatement();
|
||||
try {
|
||||
st.executeUpdate("DELETE FROM table1");
|
||||
} finally {
|
||||
st.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior of setObject for timestamp columns.
|
||||
*/
|
||||
@Test
|
||||
public void testSetLocalDateTime() throws SQLException {
|
||||
List<String> zoneIdsToTest = getZoneIdsToTest();
|
||||
List<String> datesToTest = getDatesToTest();
|
||||
|
||||
for (String zoneId : zoneIdsToTest) {
|
||||
ZoneId zone = ZoneId.of(zoneId);
|
||||
for (String date : datesToTest) {
|
||||
LocalDateTime localDateTime = LocalDateTime.parse(date);
|
||||
String expected = localDateTime.atZone(zone)
|
||||
.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||
.replace('T', ' ');
|
||||
localTimestamps(zone, localDateTime, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior of setObject for timestamp columns.
|
||||
*/
|
||||
@Test
|
||||
public void testSetOffsetDateTime() throws SQLException {
|
||||
List<String> zoneIdsToTest = getZoneIdsToTest();
|
||||
List<TimeZone> storeZones = new ArrayList<TimeZone>();
|
||||
for (String zoneId : zoneIdsToTest) {
|
||||
storeZones.add(TimeZone.getTimeZone(zoneId));
|
||||
}
|
||||
List<String> datesToTest = getDatesToTest();
|
||||
|
||||
for (TimeZone timeZone : storeZones) {
|
||||
ZoneId zoneId = timeZone.toZoneId();
|
||||
for (String date : datesToTest) {
|
||||
LocalDateTime localDateTime = LocalDateTime.parse(date);
|
||||
String expected = date.replace('T', ' ');
|
||||
offsetTimestamps(zoneId, localDateTime, expected, storeZones);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getDatesToTest() {
|
||||
return Arrays.asList("2015-09-03T12:00:00", "2015-06-30T23:59:58",
|
||||
"1997-06-30T23:59:59", "1997-07-01T00:00:00", "2012-06-30T23:59:59", "2012-07-01T00:00:00",
|
||||
"2015-06-30T23:59:59", "2015-07-01T00:00:00", "2005-12-31T23:59:59", "2006-01-01T00:00:00",
|
||||
"2008-12-31T23:59:59", "2009-01-01T00:00:00", /* "2015-06-30T23:59:60", */ "2015-07-31T00:00:00",
|
||||
"2015-07-31T00:00:01", "2015-07-31T00:00:00.000001",
|
||||
|
||||
// On 2000-03-26 02:00:00 Moscow went to DST, thus local time became 03:00:00
|
||||
"2000-03-26T01:59:59", "2000-03-26T02:00:00", "2000-03-26T02:00:01", "2000-03-26T02:59:59",
|
||||
"2000-03-26T03:00:00", "2000-03-26T03:00:01", "2000-03-26T03:59:59", "2000-03-26T04:00:00",
|
||||
"2000-03-26T04:00:01", "2000-03-26T04:00:00.000001",
|
||||
|
||||
// This is a pre-1970 date, so check if it is rounded properly
|
||||
"1950-07-20T02:00:00",
|
||||
|
||||
// Ensure the calendar is proleptic
|
||||
"1582-09-30T00:00:00", "1582-10-16T00:00:00",
|
||||
|
||||
// On 2000-10-29 03:00:00 Moscow went to regular time, thus local time became 02:00:00
|
||||
"2000-10-29T01:59:59", "2000-10-29T02:00:00", "2000-10-29T02:00:01", "2000-10-29T02:59:59",
|
||||
"2000-10-29T03:00:00", "2000-10-29T03:00:01", "2000-10-29T03:59:59", "2000-10-29T04:00:00",
|
||||
"2000-10-29T04:00:01", "2000-10-29T04:00:00.000001");
|
||||
}
|
||||
|
||||
private List<String> getZoneIdsToTest() {
|
||||
List<String> zoneIdsToTest = new ArrayList<String>();
|
||||
zoneIdsToTest.add("Africa/Casablanca"); // It is something like GMT+0..GMT+1
|
||||
zoneIdsToTest.add("America/Adak"); // It is something like GMT-10..GMT-9
|
||||
zoneIdsToTest.add("Atlantic/Azores"); // It is something like GMT-1..GMT+0
|
||||
zoneIdsToTest.add("Europe/Moscow"); // It is something like GMT+3..GMT+4 for 2000s
|
||||
zoneIdsToTest.add("Pacific/Apia"); // It is something like GMT+13..GMT+14
|
||||
zoneIdsToTest.add("Pacific/Niue"); // It is something like GMT-11..GMT-11
|
||||
for (int i = -12; i <= 13; i++) {
|
||||
zoneIdsToTest.add(String.format("GMT%+02d", i));
|
||||
}
|
||||
return zoneIdsToTest;
|
||||
}
|
||||
|
||||
private void localTimestamps(ZoneId zoneId, LocalDateTime localDateTime, String expected) throws SQLException {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(zoneId));
|
||||
String readBack = insertThenReadStringWithoutType(localDateTime, "timestamp_without_time_zone_column");
|
||||
assertEquals(
|
||||
"LocalDateTime=" + localDateTime + ", with TimeZone.default=" + zoneId + ", setObject(int, Object)",
|
||||
expected, readBack);
|
||||
deleteRows();
|
||||
|
||||
readBack = insertThenReadStringWithType(localDateTime, "timestamp_without_time_zone_column");
|
||||
assertEquals(
|
||||
"LocalDateTime=" + localDateTime + ", with TimeZone.default=" + zoneId + ", setObject(int, Object, TIMESTAMP)",
|
||||
expected, readBack);
|
||||
deleteRows();
|
||||
}
|
||||
|
||||
private void offsetTimestamps(ZoneId dataZone, LocalDateTime localDateTime, String expected, List<TimeZone> storeZones) throws SQLException {
|
||||
OffsetDateTime data = localDateTime.atZone(dataZone).toOffsetDateTime();
|
||||
try (PreparedStatement ps = con.prepareStatement(
|
||||
"select ?::timestamp with time zone, ?::timestamp with time zone")) {
|
||||
for (TimeZone storeZone : storeZones) {
|
||||
TimeZone.setDefault(storeZone);
|
||||
ps.setObject(1, data);
|
||||
ps.setObject(2, data, Types.TIMESTAMP_WITH_TIMEZONE);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
rs.next();
|
||||
String noType = rs.getString(1);
|
||||
String noTypeAppend = ifAddAppendString(noType);
|
||||
OffsetDateTime noTypeRes = OffsetDateTime.parse(noType.replace(' ', 'T') + noTypeAppend);
|
||||
assertEquals(
|
||||
"OffsetDateTime=" + data + " (with ZoneId=" + dataZone + "), with TimeZone.default="
|
||||
+ storeZone + ", setObject(int, Object)", data.toInstant(),
|
||||
noTypeRes.toInstant());
|
||||
String withType = rs.getString(2);
|
||||
String withTypeAppend = ifAddAppendString(withType);
|
||||
OffsetDateTime withTypeRes = OffsetDateTime.parse(withType.replace(' ', 'T') + withTypeAppend);
|
||||
assertEquals(
|
||||
"OffsetDateTime=" + data + " (with ZoneId=" + dataZone + "), with TimeZone.default="
|
||||
+ storeZone + ", setObject(int, Object, TIMESTAMP_WITH_TIMEZONE)",
|
||||
data.toInstant(), withTypeRes.toInstant());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String ifAddAppendString(String offsetDateTimeString) {
|
||||
String append = "";
|
||||
if (offsetDateTimeString.length() - offsetDateTimeString.indexOf("+") <= 5) {
|
||||
append = ":00";
|
||||
}
|
||||
return append;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalDateTimeRounding() throws SQLException {
|
||||
LocalDateTime dateTime = LocalDateTime.parse("2018-12-31T23:59:59.999999500");
|
||||
localTimestamps(ZoneOffset.UTC, dateTime, "2019-01-01 00:00:00");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeStampRounding() throws SQLException {
|
||||
// TODO: fix for binary
|
||||
assumeBinaryModeRegular();
|
||||
LocalTime time = LocalTime.parse("23:59:59.999999500");
|
||||
Time actual = insertThenReadWithoutType(time, "time_without_time_zone_column", Time.class);
|
||||
assertEquals(Time.valueOf("24:00:00"), actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeStampRoundingWithType() throws SQLException {
|
||||
// TODO: fix for binary
|
||||
assumeBinaryModeRegular();
|
||||
LocalTime time = LocalTime.parse("23:59:59.999999500");
|
||||
Time actual =
|
||||
insertThenReadWithType(time, Types.TIME, "time_without_time_zone_column", Time.class);
|
||||
assertEquals(Time.valueOf("24:00:00"), actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior of setObject for timestamp columns.
|
||||
*/
|
||||
@Test
|
||||
public void testSetLocalDateTimeBc() throws SQLException {
|
||||
assumeTrue(TestUtil.haveIntegerDateTimes(con));
|
||||
|
||||
// use BC for funsies
|
||||
List<LocalDateTime> bcDates = new ArrayList<LocalDateTime>();
|
||||
bcDates.add(LocalDateTime.parse("1997-06-30T23:59:59.999999").with(ChronoField.ERA, IsoEra.BCE.getValue()));
|
||||
bcDates.add(LocalDateTime.parse("0997-06-30T23:59:59.999999").with(ChronoField.ERA, IsoEra.BCE.getValue()));
|
||||
|
||||
for (LocalDateTime bcDate : bcDates) {
|
||||
String expected = LOCAL_TIME_FORMATTER.format(bcDate);
|
||||
localTimestamps(ZoneOffset.UTC, bcDate, expected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior setObject for date columns.
|
||||
*/
|
||||
@Test
|
||||
public void testSetLocalDateWithType() throws SQLException {
|
||||
LocalDate data = LocalDate.parse("1971-12-15");
|
||||
java.sql.Date actual = insertThenReadWithType(data, Types.DATE, "date_column", java.sql.Date.class);
|
||||
java.sql.Date expected = java.sql.Date.valueOf("1971-12-15");
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior setObject for date columns.
|
||||
*/
|
||||
@Test
|
||||
public void testSetLocalDateWithoutType() throws SQLException {
|
||||
LocalDate data = LocalDate.parse("1971-12-15");
|
||||
java.sql.Date actual = insertThenReadWithoutType(data, "date_column", java.sql.Date.class);
|
||||
java.sql.Date expected = java.sql.Date.valueOf("1971-12-15");
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior setObject for time columns.
|
||||
*/
|
||||
@Test
|
||||
public void testSetLocalTimeAndReadBack() throws SQLException {
|
||||
// TODO: fix for binary mode.
|
||||
// Avoid micros truncation in org.postgresql.jdbc.PgResultSet#internalGetObject
|
||||
assumeBinaryModeRegular();
|
||||
LocalTime data = LocalTime.parse("16:21:51.123456");
|
||||
|
||||
insertWithoutType(data, "time_without_time_zone_column");
|
||||
|
||||
String readBack = readString("time_without_time_zone_column");
|
||||
assertEquals("16:21:51.123456", readBack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior setObject for time columns.
|
||||
*/
|
||||
@Test
|
||||
public void testSetLocalTimeWithType() throws SQLException {
|
||||
LocalTime data = LocalTime.parse("16:21:51");
|
||||
Time actual = insertThenReadWithType(data, Types.TIME, "time_without_time_zone_column", Time.class);
|
||||
Time expected = Time.valueOf("16:21:51");
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior setObject for time columns.
|
||||
*/
|
||||
@Test
|
||||
public void testSetLocalTimeWithoutType() throws SQLException {
|
||||
LocalTime data = LocalTime.parse("16:21:51");
|
||||
Time actual = insertThenReadWithoutType(data, "time_without_time_zone_column", Time.class);
|
||||
Time expected = Time.valueOf("16:21:51");
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
}
|
||||
1065
pgjdbc/src/test/java/org/postgresql/localtimedate/TestUtil.java
Normal file
1065
pgjdbc/src/test/java/org/postgresql/localtimedate/TestUtil.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2019, PostgreSQL Global Development Group
|
||||
* See the LICENSE file in the project root for more information.
|
||||
*/
|
||||
|
||||
package org.postgresql.localtimedate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.postgresql.core.Provider;
|
||||
import org.postgresql.jdbc.TimestampUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalTime;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class TimestampUtilsTest {
|
||||
@Test
|
||||
public void testToStringOfLocalTime() {
|
||||
TimestampUtils timestampUtils = createTimestampUtils();
|
||||
|
||||
assertEquals("00:00:00", timestampUtils.toString(LocalTime.parse("00:00:00")));
|
||||
assertEquals("00:00:00.1", timestampUtils.toString(LocalTime.parse("00:00:00.1")));
|
||||
assertEquals("00:00:00.12", timestampUtils.toString(LocalTime.parse("00:00:00.12")));
|
||||
assertEquals("00:00:00.123", timestampUtils.toString(LocalTime.parse("00:00:00.123")));
|
||||
assertEquals("00:00:00.1234", timestampUtils.toString(LocalTime.parse("00:00:00.1234")));
|
||||
assertEquals("00:00:00.12345", timestampUtils.toString(LocalTime.parse("00:00:00.12345")));
|
||||
assertEquals("00:00:00.123456", timestampUtils.toString(LocalTime.parse("00:00:00.123456")));
|
||||
|
||||
assertEquals("00:00:00.999999", timestampUtils.toString(LocalTime.parse("00:00:00.999999")));
|
||||
assertEquals("00:00:00.999999", timestampUtils.toString(LocalTime.parse("00:00:00.999999499"))); // 499 NanoSeconds
|
||||
assertEquals("00:00:01", timestampUtils.toString(LocalTime.parse("00:00:00.999999500"))); // 500 NanoSeconds
|
||||
|
||||
assertEquals("23:59:59", timestampUtils.toString(LocalTime.parse("23:59:59")));
|
||||
assertEquals("23:59:59.999999", timestampUtils.toString(LocalTime.parse("23:59:59.999999"))); // 0 NanoSeconds
|
||||
assertEquals("23:59:59.999999", timestampUtils.toString(LocalTime.parse("23:59:59.999999499"))); // 499 NanoSeconds
|
||||
assertEquals("24:00:00", timestampUtils.toString(LocalTime.parse("23:59:59.999999500")));// 500 NanoSeconds
|
||||
assertEquals("24:00:00", timestampUtils.toString(LocalTime.parse("23:59:59.999999999")));// 999 NanoSeconds
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToLocalTime() throws SQLException {
|
||||
TimestampUtils timestampUtils = createTimestampUtils();
|
||||
|
||||
assertEquals(LocalTime.parse("00:00:00"), timestampUtils.toLocalTime("00:00:00"));
|
||||
|
||||
assertEquals(LocalTime.parse("00:00:00.1"), timestampUtils.toLocalTime("00:00:00.1"));
|
||||
assertEquals(LocalTime.parse("00:00:00.12"), timestampUtils.toLocalTime("00:00:00.12"));
|
||||
assertEquals(LocalTime.parse("00:00:00.123"), timestampUtils.toLocalTime("00:00:00.123"));
|
||||
assertEquals(LocalTime.parse("00:00:00.1234"), timestampUtils.toLocalTime("00:00:00.1234"));
|
||||
assertEquals(LocalTime.parse("00:00:00.12345"), timestampUtils.toLocalTime("00:00:00.12345"));
|
||||
assertEquals(LocalTime.parse("00:00:00.123456"), timestampUtils.toLocalTime("00:00:00.123456"));
|
||||
assertEquals(LocalTime.parse("00:00:00.999999"), timestampUtils.toLocalTime("00:00:00.999999"));
|
||||
|
||||
assertEquals(LocalTime.parse("23:59:59"), timestampUtils.toLocalTime("23:59:59"));
|
||||
assertEquals(LocalTime.parse("23:59:59.999999"), timestampUtils.toLocalTime("23:59:59.999999")); // 0 NanoSeconds
|
||||
assertEquals(LocalTime.parse("23:59:59.9999999"), timestampUtils.toLocalTime("23:59:59.9999999")); // 900 NanoSeconds
|
||||
assertEquals(LocalTime.parse("23:59:59.99999999"), timestampUtils.toLocalTime("23:59:59.99999999")); // 990 NanoSeconds
|
||||
assertEquals(LocalTime.parse("23:59:59.999999998"), timestampUtils.toLocalTime("23:59:59.999999998")); // 998 NanoSeconds
|
||||
assertEquals(LocalTime.parse("23:59:59.999999999"), timestampUtils.toLocalTime("24:00:00"));
|
||||
}
|
||||
|
||||
private TimestampUtils createTimestampUtils() {
|
||||
return new TimestampUtils(true, (Provider<TimeZone>) TimeZone::getDefault);
|
||||
}
|
||||
}
|
||||
9
prepare_demo.sh
Normal file
9
prepare_demo.sh
Normal file
@ -0,0 +1,9 @@
|
||||
cd shade
|
||||
mvn clean install -Dmaven.test.skip=true
|
||||
rm -rf temp
|
||||
mkdir temp
|
||||
cp target/demo-0.0.1-SNAPSHOT.jar ./temp/
|
||||
cd temp
|
||||
jar -xf demo-0.0.1-SNAPSHOT.jar
|
||||
find ./com -name "*" | sort | xargs zip demo-0.0.1-SNAPSHOT_new.jar
|
||||
mvn install:install-file -Dfile=./demo-0.0.1-SNAPSHOT_new.jar -DgroupId=com.huawei -DartifactId=demo-0.0.1-SNAPSHOT -Dversion=0.0.1 -Dpackaging=jar
|
||||
18
prepare_maven.sh
Normal file
18
prepare_maven.sh
Normal file
@ -0,0 +1,18 @@
|
||||
echo begin run
|
||||
mkdir libs
|
||||
for src in `find open_source -name '*.jar'`
|
||||
do
|
||||
cp $src ./libs/
|
||||
done
|
||||
mvn install:install-file -Dfile=./libs/commons-logging-1.2.jar -DgroupId=commons-logging -DartifactId=commons-logging -Dversion=1.2 -Dpackaging=jar
|
||||
mvn install:install-file -Dfile=./libs/commons-codec-1.11.jar -DgroupId=commons-codec -DartifactId=commons-codec -Dversion=1.11 -Dpackaging=jar
|
||||
mvn install:install-file -Dfile=./libs/httpclient-4.5.13.jar -DgroupId=org.apache.httpcomponents -DartifactId=httpclient -Dversion=4.5.13 -Dpackaging=jar
|
||||
mvn install:install-file -Dfile=./libs/httpcore-4.4.13.jar -DgroupId=org.apache.httpcomponents -DartifactId=httpcore -Dversion=4.4.13 -Dpackaging=jar
|
||||
mvn install:install-file -Dfile=./libs/fastjson-1.2.70.jar -DgroupId=com.alibaba -DartifactId=fastjson -Dversion=1.2.70 -Dpackaging=jar
|
||||
mvn install:install-file -Dfile=./libs/joda-time-2.10.6.jar -DgroupId=joda-time -DartifactId=joda-time -Dversion=2.10.6 -Dpackaging=jar
|
||||
mvn install:install-file -Dfile=./libs/jackson-databind-2.11.2.jar -DgroupId=com.fasterxml.jackson.core -DartifactId=jackson-databind -Dversion=2.11.2 -Dpackaging=jar
|
||||
mvn install:install-file -Dfile=./libs/jackson-core-2.11.2.jar -DgroupId=com.fasterxml.jackson.core -DartifactId=jackson-core -Dversion=2.11.2 -Dpackaging=jar
|
||||
mvn install:install-file -Dfile=./libs/jackson-annotations-2.11.2.jar -DgroupId=com.fasterxml.jackson.core -DartifactId=jackson-annotations -Dversion=2.11.2 -Dpackaging=jar
|
||||
mvn install:install-file -Dfile=./libs/slf4j-api-1.7.30.jar -DgroupId=org.slf4j -DartifactId=slf4j-api -Dversion=1.7.30 -Dpackaging=jar
|
||||
mvn install:install-file -Dfile=./libs/java-sdk-core-3.0.12.jar -DgroupId=com.huawei.apigateway -DartifactId=hw-java-sdk-core -Dversion=3.0.12 -Dpackaging=jar
|
||||
|
||||
Reference in New Issue
Block a user