From 0483bd4fc8e75a1d77846bcd6d03eb2b3909d3db Mon Sep 17 00:00:00 2001 From: Mahendra Chavan Date: Tue, 24 Dec 2024 10:45:32 +0530 Subject: [PATCH] Fix OffsetDateTime conversion for pre-Gregorian dates (#2568) * Fix OffsetDateTime conversion for pre-Gregorian dates * Formatting changes * Removed unused import --- .../java/microsoft/sql/DateTimeOffset.java | 33 +++++++++++++++---- .../jdbc/datatypes/DataTypesTest.java | 28 ++++++++++++++++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/main/java/microsoft/sql/DateTimeOffset.java b/src/main/java/microsoft/sql/DateTimeOffset.java index bf9e95c7b..dd1de85b2 100644 --- a/src/main/java/microsoft/sql/DateTimeOffset.java +++ b/src/main/java/microsoft/sql/DateTimeOffset.java @@ -5,6 +5,8 @@ package microsoft.sql; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.Calendar; import java.util.Locale; import java.util.TimeZone; @@ -190,7 +192,6 @@ public String toString() { .substring(2), // -> "123456" formattedOffset); } - return result; } @@ -257,12 +258,32 @@ public java.sql.Timestamp getTimestamp() { * @return OffsetDateTime equivalent to this DateTimeOffset object. */ public java.time.OffsetDateTime getOffsetDateTime() { - java.time.ZoneOffset zoneOffset = java.time.ZoneOffset.ofTotalSeconds(60 * minutesOffset); - java.time.LocalDateTime localDateTime = java.time.LocalDateTime.ofEpochSecond(utcMillis / 1000, nanos, - zoneOffset); - return java.time.OffsetDateTime.of(localDateTime, zoneOffset); + // Format the offset as +hh:mm or -hh:mm. Zero offset is formatted as +00:00. + String formattedOffset = (minutesOffset < 0) ? + String.format(Locale.US, "-%1$02d:%2$02d", -minutesOffset / 60, -minutesOffset % 60) : + String.format(Locale.US, "+%1$02d:%2$02d", minutesOffset / 60, minutesOffset % 60); + + // Create a Calendar instance with the time zone set to GMT plus the formatted offset + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT" + formattedOffset), Locale.US); + // Initialize the calendar with the UTC milliseconds value + calendar.setTimeInMillis(utcMillis); + + // Extract the date and time components from the calendar + int year = calendar.get(Calendar.YEAR); + int month = calendar.get(Calendar.MONTH) + 1; // Calendar.MONTH is zero-based + int day = calendar.get(Calendar.DAY_OF_MONTH); + int hour = calendar.get(Calendar.HOUR_OF_DAY); + int minute = calendar.get(Calendar.MINUTE); + int second = calendar.get(Calendar.SECOND); + + // Create the ZoneOffset from the minutesOffset + ZoneOffset offset = ZoneOffset.ofTotalSeconds(minutesOffset * 60); + + // Create and return the OffsetDateTime + return OffsetDateTime.of(year, month, day, hour, minute, second, nanos, offset); } - + + /** * Returns this DateTimeOffset object's offset value. * diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/DataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/DataTypesTest.java index 445d23dd0..d1bb30a2c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/DataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/DataTypesTest.java @@ -1945,6 +1945,34 @@ public void testDateTimeOffsetValueOfOffsetDateTime() throws Exception { assertEquals(expected, DateTimeOffset.valueOf(roundUp).getOffsetDateTime()); assertEquals(expected, DateTimeOffset.valueOf(roundDown).getOffsetDateTime()); } + + @Test + public void testPreGregorianDateTime() throws Exception { + try (Connection conn = getConnection(); + Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);) { + + conn.setAutoCommit(false); + TestUtils.dropTableIfExists(escapedTableName, stmt); + + stmt.executeUpdate("CREATE TABLE " + escapedTableName + " (dob datetimeoffset(7) null)"); + stmt.executeUpdate("INSERT INTO " + escapedTableName + " VALUES ('1500-12-16 00:00:00.0000000+08:00')"); + stmt.executeUpdate("INSERT INTO " + escapedTableName + " VALUES ('1400-09-27 09:30:00.0000000+08:00')"); + stmt.executeUpdate("INSERT INTO " + escapedTableName + " VALUES ('2024-12-16 23:40:00.0000000+08:00')"); + + try (ResultSet rs = stmt.executeQuery("select dob from " + escapedTableName + " order by dob")) { + while (rs.next()) { + String strDateTimeOffset = rs.getString(1).substring(0, 10); + DateTimeOffset objDateTimeOffset = (DateTimeOffset) rs.getObject(1); + OffsetDateTime objOffsetDateTime = objDateTimeOffset.getOffsetDateTime(); + + String strOffsetDateTime = objOffsetDateTime.toString().substring(0, 10); + assertEquals(strDateTimeOffset, strOffsetDateTime, "Mismatch found in DateTimeOffset : " + + objDateTimeOffset + " and OffsetDateTime : " + objOffsetDateTime); + } + } + TestUtils.dropTableIfExists(escapedTableName, stmt); + } + } static LocalDateTime getUnstorableValue() throws Exception { ZoneId systemTimezone = ZoneId.systemDefault();