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);