Skip to content

Commit

Permalink
#787 Backport #786 to Jaybird 5
Browse files Browse the repository at this point in the history
Account for Java expecting offset name to be prefixed by GMT
  • Loading branch information
mrotteveel committed Mar 9, 2024
1 parent d84badb commit d69dcdf
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/docs/asciidoc/release_notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ This may affect custom protocol implementations extending `AbstractWireOperation
This change also improves performance of `updateRow()`, `insertRow()`, `deleteRow()` and `refreshRow()`.
The best row identifier or `RDB$DB_KEY` were detected _each time_ when calling `updateRow()`, `insertRow()`, `deleteRow()`, or `refreshRow()`.
This has been improved so this detection is done once, and in a way that non-updatable result sets can now be downgraded to `CONCUR_READ_ONLY` instead of throwing an exception when performing the modification.
* Fixed: Use of offset timezone names (e.g. `+05:00`) for `sessionTimeZone` would result in a warning being logged, and an incorrect conversion applied (in UTC instead of the offset) when using the legacy time types -- backported from Jaybird 6 (https://github.com/FirebirdSQL/jaybird/issues/787[#787])
* ...
[#jaybird-5-0-3-changelog]
Expand Down
14 changes: 13 additions & 1 deletion src/main/org/firebirdsql/gds/impl/GDSHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

import java.sql.SQLException;
import java.util.TimeZone;
import java.util.function.Predicate;
import java.util.regex.Pattern;

import static java.util.Objects.requireNonNull;
import static org.firebirdsql.jaybird.props.PropertyConstants.SESSION_TIME_ZONE_SERVER;
Expand All @@ -40,6 +42,9 @@
*/
public final class GDSHelper {

private static final Predicate<String> OFFSET_ZONE_NAME_PREDICATE =
Pattern.compile("[+-]\\d{2}:\\d{2}").asMatchPredicate();

/**
* @deprecated will be removed in Jaybird 6, use {@link org.firebirdsql.jaybird.props.PropertyConstants#DEFAULT_BLOB_BUFFER_SIZE}
*/
Expand Down Expand Up @@ -268,7 +273,7 @@ private TimeZone initSessionTimeZone() {
if (sessionTimeZoneName == null || SESSION_TIME_ZONE_SERVER.equalsIgnoreCase(sessionTimeZoneName)) {
return sessionTimeZone = TimeZone.getDefault();
}
TimeZone timeZone = TimeZone.getTimeZone(sessionTimeZoneName);
TimeZone timeZone = getTimeZone(sessionTimeZoneName);
if ("GMT".equals(timeZone.getID()) && !"GMT".equalsIgnoreCase(sessionTimeZoneName)) {
LoggerFactory.getLogger(getClass()).warnf("TimeZone fallback to GMT from %s; possible cause: value of "
+ "sessionTimeZone unknown in Java. Time and Timestamp values may yield unexpected values. "
Expand All @@ -277,6 +282,13 @@ private TimeZone initSessionTimeZone() {
return sessionTimeZone = timeZone;
}

private static TimeZone getTimeZone(String timeZoneName) {
if (OFFSET_ZONE_NAME_PREDICATE.test(timeZoneName)) {
timeZoneName = "GMT" + timeZoneName;
}
return TimeZone.getTimeZone(timeZoneName);
}

/**
* @see FbAttachment#withLock()
*/
Expand Down
32 changes: 32 additions & 0 deletions src/test/org/firebirdsql/jdbc/SessionTimeZoneTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@

import org.firebirdsql.common.extension.UsesDatabaseExtension;
import org.firebirdsql.gds.ISCConstants;
import org.firebirdsql.jaybird.props.PropertyNames;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import java.sql.*;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Properties;
import java.util.TimeZone;
Expand All @@ -36,6 +39,7 @@
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
Expand Down Expand Up @@ -111,6 +115,34 @@ void errorOnInvalidTimeZoneName() {
fbMessageStartsWith(ISCConstants.isc_invalid_timezone_region, "does_not_exist")));
}


/**
* Rationale: Firebird expects offset name {@code [+-]HH:MM}, while Java expects {@code GMT[+-]HH:MM}.
*/
@Test
void verifyOffsetTimeZoneBehaviour() throws Exception {
final String firebirdZoneName = "+05:17";
Properties props = getDefaultPropertiesForConnection();
props.setProperty(PropertyNames.sessionTimeZone, firebirdZoneName);
try (Connection connection = DriverManager.getConnection(getUrl(), props);
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(
"select\n" +
" rdb$get_context('SYSTEM', 'SESSION_TIMEZONE') as TZ_NAME,\n" +
" localtimestamp as LOCAL_TS\n" +
"from RDB$DATABASE")) {
assertTrue(rs.next(), "expected a row");
assertEquals(firebirdZoneName, rs.getString(1));
Timestamp timestampValue = rs.getTimestamp(2);
assertEquals((double) System.currentTimeMillis(), timestampValue.getTime(), 1000,
"Unexpected value with offset time zone");

assertNotEquals(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES),
rs.getObject(2, LocalDateTime.class).truncatedTo(ChronoUnit.MINUTES),
"sanity check: expected a value not equal to the current default zone");
}
}

@SuppressWarnings("SameParameterValue")
private void checkForTimeZone(String zoneName, String timeValue, String expectedLocal, String expectedZonedString)
throws SQLException {
Expand Down

0 comments on commit d69dcdf

Please sign in to comment.