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

kou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-java.git


The following commit(s) were added to refs/heads/main by this push:
     new 53a9ccd8f GH-1300: Use Locale.ROOT when building C Data Interface 
format strings (#1301)
53a9ccd8f is described below

commit 53a9ccd8f272bd576ec0524b704c1a1fdab20948
Author: Sotaro Hikita <[email protected]>
AuthorDate: Tue Sep 22 15:40:40 2026 +0900

    GH-1300: Use Locale.ROOT when building C Data Interface format strings 
(#1301)
    
    `Format.asString` built the `FixedSizeList` (`+w:%d`), `FixedSizeBinary`
    (`w:%d`) and `Decimal` (`d:%d,%d[,%d]`) format strings with
    `String.format` and no explicit `Locale`. `java.util.Formatter`
    localises the digits of `%d` with the default locale, so a JVM running
    under `ar-EG`, `bn-BD`, `mr-IN` and similar locales exported `+w:٨`
    instead of `+w:8`, which arrow-rs and other implementations reject.
    
    This passes `Locale.ROOT` to the four calls and adds
    `FormatTest.testAsStringIgnoresDefaultLocale`, which sets the default
    locale to `ar-EG` for the duration of the test and asserts the ASCII
    output.
    
    Closes #1300.
---
 c/src/main/java/org/apache/arrow/c/Format.java     |  8 ++++----
 c/src/test/java/org/apache/arrow/c/FormatTest.java | 17 +++++++++++++++++
 2 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/c/src/main/java/org/apache/arrow/c/Format.java 
b/c/src/main/java/org/apache/arrow/c/Format.java
index 7ce99614d..7f546fca2 100644
--- a/c/src/main/java/org/apache/arrow/c/Format.java
+++ b/c/src/main/java/org/apache/arrow/c/Format.java
@@ -63,10 +63,10 @@ final class Format {
         {
           ArrowType.Decimal type = (ArrowType.Decimal) arrowType;
           if (type.getBitWidth() == 128) {
-            return String.format("d:%d,%d", type.getPrecision(), 
type.getScale());
+            return String.format(Locale.ROOT, "d:%d,%d", type.getPrecision(), 
type.getScale());
           }
           return String.format(
-              "d:%d,%d,%d", type.getPrecision(), type.getScale(), 
type.getBitWidth());
+              Locale.ROOT, "d:%d,%d,%d", type.getPrecision(), type.getScale(), 
type.getBitWidth());
         }
       case Duration:
         {
@@ -88,12 +88,12 @@ final class Format {
       case FixedSizeBinary:
         {
           ArrowType.FixedSizeBinary type = (ArrowType.FixedSizeBinary) 
arrowType;
-          return String.format("w:%d", type.getByteWidth());
+          return String.format(Locale.ROOT, "w:%d", type.getByteWidth());
         }
       case FixedSizeList:
         {
           ArrowType.FixedSizeList type = (ArrowType.FixedSizeList) arrowType;
-          return String.format("+w:%d", type.getListSize());
+          return String.format(Locale.ROOT, "+w:%d", type.getListSize());
         }
       case FloatingPoint:
         {
diff --git a/c/src/test/java/org/apache/arrow/c/FormatTest.java 
b/c/src/test/java/org/apache/arrow/c/FormatTest.java
index c77332433..3691ca095 100644
--- a/c/src/test/java/org/apache/arrow/c/FormatTest.java
+++ b/c/src/test/java/org/apache/arrow/c/FormatTest.java
@@ -20,6 +20,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.util.Locale;
 import org.apache.arrow.vector.types.DateUnit;
 import org.apache.arrow.vector.types.FloatingPointPrecision;
 import org.apache.arrow.vector.types.IntervalUnit;
@@ -156,4 +157,20 @@ public class FormatTest {
     assertThrows(UnsupportedOperationException.class, () -> Format.asType(":", 
0L));
     assertThrows(NumberFormatException.class, () -> Format.asType("w:1,2,3", 
0L));
   }
+
+  @Test
+  public void testAsStringIgnoresDefaultLocale() {
+    // Locales that use digits other than 0-9 (Arabic-Indic, Bengali, 
Devanagari, ...)
+    // must not leak into the format string, which other implementations parse 
as ASCII.
+    Locale saved = Locale.getDefault();
+    try {
+      Locale.setDefault(Locale.forLanguageTag("ar-EG"));
+      assertEquals("d:10,2", Format.asString(new ArrowType.Decimal(10, 2, 
128)));
+      assertEquals("d:10,2,256", Format.asString(new ArrowType.Decimal(10, 2, 
256)));
+      assertEquals("w:16", Format.asString(new ArrowType.FixedSizeBinary(16)));
+      assertEquals("+w:8", Format.asString(new ArrowType.FixedSizeList(8)));
+    } finally {
+      Locale.setDefault(saved);
+    }
+  }
 }

Reply via email to