This is an automated email from the ASF dual-hosted git repository.

terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 0e7012fd0eb Fix Firebird TIMESTAMP_TZ offset decoding and encoding 
(#40338)
0e7012fd0eb is described below

commit 0e7012fd0eb07d893d7b23733d2f9eff7fea4db8
Author: Liang Zhang <[email protected]>
AuthorDate: Thu Oct 22 19:26:07 2026 +0800

    Fix Firebird TIMESTAMP_TZ offset decoding and encoding (#40338)
---
 .../FirebirdTimestampTZBinaryProtocolValue.java    |  5 +-
 .../protocol/util/FirebirdDateTimeUtils.java       | 20 ++++---
 .../FirebirdExecuteStatementPacketTest.java        | 13 +++++
 ...FirebirdTimestampTZBinaryProtocolValueTest.java | 65 ++++++++++++++--------
 .../protocol/util/FirebirdDateTimeUtilsTest.java   | 27 ++++++---
 .../generic/FirebirdFetchResponsePacketTest.java   | 21 +++++++
 6 files changed, 111 insertions(+), 40 deletions(-)

diff --git 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/FirebirdTimestampTZBinaryProtocolValue.java
 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/FirebirdTimestampTZBinaryProtocolValue.java
index 35ea854a366..07694d3e201 100644
--- 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/FirebirdTimestampTZBinaryProtocolValue.java
+++ 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/FirebirdTimestampTZBinaryProtocolValue.java
@@ -19,6 +19,7 @@ package 
org.apache.shardingsphere.database.protocol.firebird.packet.command.quer
 
 import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.statement.execute.protocol.util.FirebirdDateTimeUtils;
 import 
org.apache.shardingsphere.database.protocol.firebird.payload.FirebirdPacketPayload;
+import org.firebirdsql.gds.ng.tz.TimeZoneMapping;
 
 import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
@@ -31,7 +32,7 @@ public final class FirebirdTimestampTZBinaryProtocolValue 
implements FirebirdBin
     
     @Override
     public Object read(final FirebirdPacketPayload payload) {
-        return FirebirdDateTimeUtils.getDateTimeWithOffset(payload.readInt4(), 
payload.readInt4(), payload.readInt4());
+        return FirebirdDateTimeUtils.getDateTimeWithOffset(payload.readInt4(), 
payload.readInt4(), payload.readInt4() & 0xFFFF);
     }
     
     @Override
@@ -39,7 +40,7 @@ public final class FirebirdTimestampTZBinaryProtocolValue 
implements FirebirdBin
         LocalDateTime localDateTime = ((OffsetDateTime) 
value).atZoneSameInstant(ZoneOffset.UTC).toLocalDateTime();
         payload.writeInt4(FirebirdDateTimeUtils.getEncodedDate(localDateTime));
         payload.writeInt4(new 
FirebirdDateTimeUtils(localDateTime).getEncodedTime());
-        payload.writeInt4(((OffsetDateTime) 
value).getOffset().getTotalSeconds() / 60);
+        
payload.writeInt4(TimeZoneMapping.getInstance().toTimeZoneId(((OffsetDateTime) 
value).getOffset()));
     }
     
     @Override
diff --git 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/util/FirebirdDateTimeUtils.java
 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/util/FirebirdDateTimeUtils.java
index cb5ad604742..2517cd2d90b 100644
--- 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/util/FirebirdDateTimeUtils.java
+++ 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/util/FirebirdDateTimeUtils.java
@@ -19,10 +19,13 @@ package 
org.apache.shardingsphere.database.protocol.firebird.packet.command.quer
 
 import lombok.Getter;
 import lombok.Setter;
+import org.firebirdsql.gds.ng.tz.TimeZoneMapping;
 
 import java.sql.Timestamp;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
 
 /**
  * Firebird date and time utility class.
@@ -192,15 +195,16 @@ public final class FirebirdDateTimeUtils {
     }
     
     /**
-     * Convert encoded date and time values with offset to {@link Timestamp}.
+     * Convert encoded UTC date and time values with a Firebird time zone ID 
to {@link OffsetDateTime}.
      *
-     * @param encodedDate encoded date as integer
-     * @param encodedTime encoded time as integer
-     * @param offset offset value in minutes or seconds (pending 
implementation details)
-     * @return timestamp representation of the encoded date and time with 
offset
+     * @param encodedDate encoded UTC date as integer
+     * @param encodedTime encoded UTC time as integer
+     * @param encodedTimeZone Firebird time zone ID
+     * @return date and time with the offset for the encoded time zone
      */
-    public static Timestamp getDateTimeWithOffset(final int encodedDate, final 
int encodedTime, final int offset) {
-        // TODO add time zone support
-        return new 
FirebirdDateTimeUtils().setDate(encodedDate).setTime(encodedTime).asTimestamp();
+    public static OffsetDateTime getDateTimeWithOffset(final int encodedDate, 
final int encodedTime, final int encodedTimeZone) {
+        FirebirdDateTimeUtils dateTime = new 
FirebirdDateTimeUtils().setDate(encodedDate).setTime(encodedTime);
+        return LocalDateTime.of(dateTime.year, dateTime.month, dateTime.day, 
dateTime.hour, dateTime.minute, dateTime.second, dateTime.fractions * 
NANOSECONDS_PER_FRACTION).toInstant(ZoneOffset.UTC)
+                
.atZone(TimeZoneMapping.getInstance().timeZoneById(encodedTimeZone)).toOffsetDateTime();
     }
 }
diff --git 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/FirebirdExecuteStatementPacketTest.java
 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/FirebirdExecuteStatementPacketTest.java
index 8caaedfb7c1..247dae55549 100644
--- 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/FirebirdExecuteStatementPacketTest.java
+++ 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/FirebirdExecuteStatementPacketTest.java
@@ -33,6 +33,7 @@ import org.junit.jupiter.params.provider.MethodSource;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.time.OffsetDateTime;
 import java.util.Collections;
 import java.util.stream.Stream;
 
@@ -78,6 +79,18 @@ class FirebirdExecuteStatementPacketTest {
         assertThat(packet.getParameterValues(), 
is(Collections.singletonList(expectedParameterValue)));
     }
     
+    @Test
+    void assertExecuteStatementPacketWithTimestampWithTimeZoneParameter() {
+        
when(payload.readInt4()).thenReturn(FirebirdCommandPacketType.EXECUTE.getValue(),
 1, 2, 0, 1, 60310, 801301234, 1559);
+        when(payload.readBuffer()).thenReturn(byteBuf);
+        when(byteBuf.isReadable()).thenReturn(true);
+        when(byteBuf.readUnsignedByte()).thenReturn((short) 5, (short) 0, 
(short) BlrConstants.blr_timestamp_tz, (short) BlrConstants.blr_end);
+        when(byteBuf.skipBytes(anyInt())).thenReturn(byteBuf);
+        FirebirdExecuteStatementPacket actual = new 
FirebirdExecuteStatementPacket(payload, 
FirebirdProtocolVersion.PROTOCOL_VERSION13);
+        assertThat(actual.getParameterTypes(), 
is(Collections.singletonList(FirebirdBinaryColumnType.TIMESTAMP_TZ)));
+        assertThat(actual.getParameterValues(), 
is(Collections.singletonList(OffsetDateTime.parse("2024-01-02T00:15:30.123400+02:00"))));
+    }
+    
     @Test
     void assertExecuteStatementPacketWithNullParameterValue() {
         when(payload.readInt1()).thenReturn(1);
diff --git 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/FirebirdTimestampTZBinaryProtocolValueTest.java
 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/FirebirdTimestampTZBinaryProtocolValueTest.java
index 94a6f421b63..5579306ab19 100644
--- 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/FirebirdTimestampTZBinaryProtocolValueTest.java
+++ 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/FirebirdTimestampTZBinaryProtocolValueTest.java
@@ -17,21 +17,23 @@
 
 package 
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.statement.execute.protocol;
 
-import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.statement.execute.protocol.util.FirebirdDateTimeUtils;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
 import 
org.apache.shardingsphere.database.protocol.firebird.payload.FirebirdPacketPayload;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
-import java.time.LocalDateTime;
+import java.nio.charset.StandardCharsets;
 import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.inOrder;
 
 @ExtendWith(MockitoExtension.class)
 class FirebirdTimestampTZBinaryProtocolValueTest {
@@ -39,25 +41,44 @@ class FirebirdTimestampTZBinaryProtocolValueTest {
     @Mock
     private FirebirdPacketPayload payload;
     
-    @Test
-    void assertRead() {
-        LocalDateTime dateTime = LocalDateTime.of(2024, 1, 1, 12, 0);
-        int encodedDate = FirebirdDateTimeUtils.getEncodedDate(dateTime);
-        int encodedTime = new FirebirdDateTimeUtils(dateTime).getEncodedTime();
-        when(payload.readInt4()).thenReturn(encodedDate, encodedTime, 60);
-        assertThat(new FirebirdTimestampTZBinaryProtocolValue().read(payload), 
is(FirebirdDateTimeUtils.getDateTimeWithOffset(encodedDate, encodedTime, 60)));
+    @ParameterizedTest(name = "{2}: {3}")
+    @CsvSource({
+            "60310, 801301234, 1559, 2024-01-02T00:15:30.123400+02:00",
+            "60617, 234000000, 65361, 2024-11-03T01:30:00-05:00",
+            "60617, 234000000, -175, 2024-11-03T01:30:00-05:00"
+    })
+    void assertRead(final int encodedDate, final int encodedTime, final int 
encodedTimeZone, final String expected) {
+        ByteBuf buffer = Unpooled.buffer();
+        try {
+            
buffer.writeInt(encodedDate).writeInt(encodedTime).writeInt(encodedTimeZone).writeInt(0x12345678);
+            FirebirdPacketPayload actualPayload = new 
FirebirdPacketPayload(buffer, StandardCharsets.UTF_8);
+            Object actual = new 
FirebirdTimestampTZBinaryProtocolValue().read(actualPayload);
+            assertThat(actual, is(OffsetDateTime.parse(expected)));
+            assertThat(buffer.readerIndex(), is(12));
+            assertThat(actualPayload.readInt4(), is(0x12345678));
+        } finally {
+            buffer.release();
+        }
     }
     
-    @Test
-    void assertWrite() {
-        OffsetDateTime value = OffsetDateTime.of(LocalDateTime.of(2024, 1, 1, 
12, 0), ZoneOffset.ofHours(2));
-        LocalDateTime utc = 
value.atZoneSameInstant(ZoneOffset.UTC).toLocalDateTime();
-        int encodedDate = FirebirdDateTimeUtils.getEncodedDate(utc);
-        int encodedTime = new FirebirdDateTimeUtils(utc).getEncodedTime();
-        new FirebirdTimestampTZBinaryProtocolValue().write(payload, value);
-        verify(payload).writeInt4(encodedDate);
-        verify(payload).writeInt4(encodedTime);
-        verify(payload).writeInt4(120);
+    @ParameterizedTest(name = "{0}")
+    @CsvSource({
+            "2024-01-01T12:00:00Z, 60310, 432000000, 1439",
+            "2024-01-01T12:00:00+02:00, 60310, 360000000, 1559",
+            "2024-01-01T12:00:00-05:00, 60310, 612000000, 1139",
+            "2024-01-01T12:00:00+05:30, 60310, 234000000, 1769",
+            "2024-01-01T01:00:00+08:00, 60309, 612000000, 1919",
+            "2024-01-01T23:30:00-05:00, 60311, 162000000, 1139",
+            "2024-01-02T00:15:30.123400+02:00, 60310, 801301234, 1559",
+            "2024-03-10T02:30:00Z, 60379, 90000000, 1439",
+            "2024-11-03T01:30:00-05:00, 60617, 234000000, 1139"
+    })
+    void assertWrite(final String value, final int encodedDate, final int 
encodedTime, final int encodedTimeZone) {
+        new FirebirdTimestampTZBinaryProtocolValue().write(payload, 
OffsetDateTime.parse(value));
+        InOrder ordered = inOrder(payload);
+        ordered.verify(payload).writeInt4(encodedDate);
+        ordered.verify(payload).writeInt4(encodedTime);
+        ordered.verify(payload).writeInt4(encodedTimeZone);
     }
     
     @Test
diff --git 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/util/FirebirdDateTimeUtilsTest.java
 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/util/FirebirdDateTimeUtilsTest.java
index 007ac18aad4..c8a17e751e1 100644
--- 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/util/FirebirdDateTimeUtilsTest.java
+++ 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/util/FirebirdDateTimeUtilsTest.java
@@ -18,10 +18,13 @@
 package 
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.statement.execute.protocol.util;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 
 import java.sql.Timestamp;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.OffsetDateTime;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
@@ -89,14 +92,22 @@ class FirebirdDateTimeUtilsTest {
         assertThat(actualDateTime.toLocalDateTime(), is(LocalDateTime.of(2026, 
10, 2, 12, 34, 56, 789_000_000)));
     }
     
-    @Test
-    void assertGetDateTimeWithOffset() {
-        LocalDateTime sourceDateTime = LocalDateTime.of(2025, 1, 1, 9, 30, 0);
-        int encodedDate = FirebirdDateTimeUtils.getEncodedDate(sourceDateTime);
-        int encodedTime = new 
FirebirdDateTimeUtils(sourceDateTime).getEncodedTime();
-        Timestamp actualDateTime = 
FirebirdDateTimeUtils.getDateTimeWithOffset(encodedDate, encodedTime, 60);
-        LocalDateTime expectedDateTime = LocalDateTime.of(2025, 1, 1, 
sourceDateTime.getHour(), sourceDateTime.getMinute(), 
sourceDateTime.getSecond(), 0);
-        assertThat(actualDateTime.toLocalDateTime(), is(expectedDateTime));
+    @ParameterizedTest(name = "{3}")
+    @CsvSource({
+            "60310, 432000000, 1439, 2024-01-01T12:00:00Z",
+            "60310, 360000000, 1559, 2024-01-01T12:00:00+02:00",
+            "60310, 612000000, 1139, 2024-01-01T12:00:00-05:00",
+            "60310, 234000000, 1769, 2024-01-01T12:00:00+05:30",
+            "60309, 612000000, 1919, 2024-01-01T01:00:00+08:00",
+            "60311, 162000000, 1139, 2024-01-01T23:30:00-05:00",
+            "60310, 801301234, 1559, 2024-01-02T00:15:30.123400+02:00",
+            "60379, 90000000, 1439, 2024-03-10T02:30:00Z",
+            "60617, 234000000, 1139, 2024-11-03T01:30:00-05:00",
+            "60617, 234000000, 65361, 2024-11-03T01:30:00-05:00"
+    })
+    void assertGetDateTimeWithOffset(final int encodedDate, final int 
encodedTime, final int encodedTimeZone, final String expected) {
+        OffsetDateTime actual = 
FirebirdDateTimeUtils.getDateTimeWithOffset(encodedDate, encodedTime, 
encodedTimeZone);
+        assertThat(actual, is(OffsetDateTime.parse(expected)));
     }
     
     @Test
diff --git 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/generic/FirebirdFetchResponsePacketTest.java
 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/generic/FirebirdFetchResponsePacketTest.java
index 389f54cf385..6604a7b4f1f 100644
--- 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/generic/FirebirdFetchResponsePacketTest.java
+++ 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/generic/FirebirdFetchResponsePacketTest.java
@@ -34,6 +34,7 @@ import org.mockito.MockedStatic;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 import java.nio.charset.StandardCharsets;
+import java.time.OffsetDateTime;
 import java.util.Collections;
 
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -109,6 +110,26 @@ class FirebirdFetchResponsePacketTest {
         assertThat(byteBuf.readByte(), is((byte) 0));
     }
     
+    @Test
+    void assertWriteWithTimestampWithTimeZoneCellData() {
+        ByteBuf actual = Unpooled.buffer();
+        try {
+            FirebirdPacketPayload packetPayload = new 
FirebirdPacketPayload(actual, StandardCharsets.UTF_8);
+            BinaryRow row = new BinaryRow(Collections.singleton(new 
BinaryCell(FirebirdBinaryColumnType.TIMESTAMP_TZ, 
OffsetDateTime.parse("2024-01-02T00:15:30.123400+02:00"))));
+            
FirebirdFetchResponsePacket.getFetchRowPacket(row).write(packetPayload);
+            assertThat(actual.readableBytes(), is(28));
+            assertThat(actual.readInt(), is(66));
+            assertThat(actual.readInt(), is(0));
+            assertThat(actual.readInt(), is(1));
+            assertThat(actual.readInt(), is(0));
+            assertThat(actual.readInt(), is(60310));
+            assertThat(actual.readInt(), is(801301234));
+            assertThat(actual.readInt(), is(1559));
+        } finally {
+            actual.release();
+        }
+    }
+    
     @Test
     void assertWriteWithNullCellData() {
         when(payload.getByteBuf()).thenReturn(byteBuf);

Reply via email to