diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java index 0d5ef434e..cc0e7713b 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java @@ -11,19 +11,24 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Timestamp; import java.time.OffsetDateTime; import java.time.OffsetTime; - import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; import com.microsoft.sqlserver.jdbc.RandomUtil; import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; +import com.microsoft.sqlserver.jdbc.SQLServerResultSet; import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; +import microsoft.sql.DateTimeOffset; + @RunWith(JUnitPlatform.class) public class SetObjectTest extends AbstractTest { @@ -130,4 +135,110 @@ public void testSetObjectWithOffsetTime_sendTimeAsDatetimeDisabled() throws SQLE } } } + + /** + * Tests DateTimeOffset Conversions when 'setSendTimeAsDatetime' connection property is false. + * + * @throws SQLException + */ + @Test + public void testDateTimeOffsetConversions() throws SQLException { + testDTOConversionsInternal(true); + testDTOConversionsInternal(false); + } + + /** + * Test sending and retrieving of data as OffsetTime, OffsetDateTime, DateTimeOffset or as String interchangeably. + * + * @param sendTimeAsDateTime + * Toggles SendTimeAsDatetime connection property on connection + * @throws SQLException + */ + private void testDTOConversionsInternal(boolean sendTimeAsDateTime) throws SQLException { + try (Connection con = getConnection()) { + ((SQLServerConnection) con).setSendTimeAsDatetime(sendTimeAsDateTime); + + final String date = sendTimeAsDateTime ? "1970-01-01" : "1900-01-01"; + final String time = "11:22:33.123456700"; + final String offset = "+01:00"; // Europe/Paris TimeZone + final int minutesOffset = 60; // Europe/Paris TimeZone + + final String timestampString = date + " " + time; + + // Fix nanoseconds from SQL DTO + final String expectedDTOString = timestampString.substring(0, timestampString.length() - 2) + " " + offset; + final String sqlDto = date + "T" + time + offset; // SQL Server's interpretation of DateTimeOffset + final OffsetTime offsetTime = OffsetTime.parse(time + offset); + final OffsetDateTime offsetDateTime = OffsetDateTime.parse(sqlDto); + final DateTimeOffset dateTimeOffset = DateTimeOffset.valueOf(Timestamp.from(offsetDateTime.toInstant()), + minutesOffset); + + try (Statement stmt = con.createStatement()) { + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (id INT PRIMARY KEY, dto DATETIMEOFFSET)"); + try { + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con + .prepareStatement("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (id, dto) VALUES (?, ?)")) { + + // Set Object - OffsetTime + pstmt.setInt(1, 1); + pstmt.setObject(2, offsetTime); + pstmt.executeUpdate(); + + // Set Object - OffsetDateTime + pstmt.setInt(1, 2); + pstmt.setObject(2, offsetDateTime); + pstmt.executeUpdate(); + + // Set Object - DateTimeOffset + pstmt.setInt(1, 3); + pstmt.setObject(2, dateTimeOffset); + pstmt.executeUpdate(); + + // Set DateTimeOffset + pstmt.setInt(1, 4); + pstmt.setDateTimeOffset(2, dateTimeOffset); + pstmt.executeUpdate(); + + // Set String + pstmt.setInt(1, 5); + pstmt.setString(2, expectedDTOString); + pstmt.executeUpdate(); + } + + String query = "SELECT dto FROM " + AbstractSQLGenerator.escapeIdentifier(tableName); + verifyDateTimeOffsetValues(stmt, query, dateTimeOffset, offsetDateTime, expectedDTOString, sqlDto); + + for (int i = 1; i <= 5; i++) { + query = "SELECT dto FROM " + AbstractSQLGenerator.escapeIdentifier(tableName) + " WHERE id = " + + i + " AND dto = '" + sqlDto + "'"; + verifyDateTimeOffsetValues(stmt, query, dateTimeOffset, offsetDateTime, expectedDTOString, + sqlDto); + } + } finally { + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + } + } + } + } + + private void verifyDateTimeOffsetValues(Statement stmt, String query, DateTimeOffset dateTimeOffset, + OffsetDateTime offsetDateTime, String expectedDTOString, String sqlDto) throws SQLException { + try (SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery(query)) { + while (rs.next()) { + assertEquals(dateTimeOffset, rs.getObject(1)); + assertEquals(expectedDTOString, rs.getObject(1).toString()); + + assertEquals(dateTimeOffset, rs.getDateTimeOffset(1)); + assertEquals(expectedDTOString, rs.getDateTimeOffset(1).toString()); + + assertEquals(dateTimeOffset, rs.getObject(1, DateTimeOffset.class)); + assertEquals(expectedDTOString, rs.getObject(1, DateTimeOffset.class).toString()); + + assertEquals(offsetDateTime, rs.getObject(1, OffsetDateTime.class)); + assertEquals(sqlDto, rs.getObject(1, OffsetDateTime.class).toString()); + } + } + } }