Skip to content

Commit 2afc1d3

Browse files
authored
GH-1311: Use Locale.ROOT when formatting JDBC interval strings (#1312)
## What's Changed `formatIntervalYear` and `formatIntervalDay` build the Oracle-style interval strings the JDBC interval accessor returns from `getString()`/`getObject()` with `String.format` and no explicit locale, so `java.util.Formatter` renders the `%d` digits using the JVM default locale. On a JVM whose default locale uses non-ASCII digits (`ar-EG`, `bn-BD`, `mr-IN`, ...) an interval comes back as `+٠٢١-٠٢` instead of `+021-02`, which corrupts the value handed to JDBC clients and breaks anything parsing it as ASCII. Pass `Locale.ROOT` to both calls, the same fix GH-1300 applied to the C Data Interface format strings. Closes #1311.
1 parent 7cd07b1 commit 2afc1d3

2 files changed

Lines changed: 26 additions & 2 deletions

File tree

‎flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/IntervalStringUtils.java‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.time.Duration;
2020
import java.time.Period;
21+
import java.util.Locale;
2122
import org.apache.arrow.vector.util.DateUtility;
2223

2324
/**
@@ -43,7 +44,7 @@ public static String formatIntervalYear(final Period p) {
4344
final int years = (int) (months / DateUtility.yearsToMonths);
4445
months = months % DateUtility.yearsToMonths;
4546

46-
return String.format("%c%03d-%02d", neg ? '-' : '+', years, months);
47+
return String.format(Locale.ROOT, "%c%03d-%02d", neg ? '-' : '+', years, months);
4748
}
4849

4950
/**
@@ -73,6 +74,13 @@ public static String formatIntervalDay(final Duration d) {
7374
millis = millis % DateUtility.secondsToMillis;
7475

7576
return String.format(
76-
"%c%03d %02d:%02d:%02d.%03d", neg ? '-' : '+', days, hours, minutes, seconds, millis);
77+
Locale.ROOT,
78+
"%c%03d %02d:%02d:%02d.%03d",
79+
neg ? '-' : '+',
80+
days,
81+
hours,
82+
minutes,
83+
seconds,
84+
millis);
7785
}
7886
}

‎flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcIntervalVectorAccessorTest.java‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.time.Duration;
2626
import java.time.Period;
2727
import java.time.format.DateTimeParseException;
28+
import java.util.Locale;
2829
import java.util.function.Supplier;
2930
import java.util.stream.Stream;
3031
import org.apache.arrow.driver.jdbc.accessor.ArrowFlightJdbcAccessorFactory;
@@ -36,6 +37,7 @@
3637
import org.apache.arrow.vector.PeriodDuration;
3738
import org.apache.arrow.vector.ValueVector;
3839
import org.junit.jupiter.api.AfterEach;
40+
import org.junit.jupiter.api.Test;
3941
import org.junit.jupiter.api.extension.RegisterExtension;
4042
import org.junit.jupiter.params.ParameterizedTest;
4143
import org.junit.jupiter.params.provider.Arguments;
@@ -254,6 +256,20 @@ public void testIntervalDayWithJodaPeriodObject(Supplier<ValueVector> vectorSupp
254256
assertEquals("-1567 00:00:00.000", formatIntervalDay(Duration.ofDays(-1567)));
255257
}
256258

259+
@Test
260+
public void testFormatIntervalIgnoresDefaultLocale() {
261+
// Locales such as Arabic-Indic render %d with non-ASCII digits; the interval strings handed
262+
// back through getString() must stay ASCII regardless of the JVM default locale.
263+
Locale saved = Locale.getDefault();
264+
try {
265+
Locale.setDefault(Locale.forLanguageTag("ar-EG"));
266+
assertEquals("+021-02", formatIntervalYear(Period.of(21, 2, 0)));
267+
assertEquals("+001 18:25:16.766", formatIntervalDay(Duration.parse("PT42H25M16.766S")));
268+
} finally {
269+
Locale.setDefault(saved);
270+
}
271+
}
272+
257273
@ParameterizedTest
258274
@MethodSource("data")
259275
public void testShouldGetStringReturnCorrectString(Supplier<ValueVector> vectorSupplier)

0 commit comments

Comments
 (0)