From 5e8c9dd33769d081b174e39b3e9037e16c100761 Mon Sep 17 00:00:00 2001 From: Evgen Bielozorov Date: Wed, 29 Jul 2026 16:24:56 +0200 Subject: [PATCH] fix: use locale-independent digits in temporal toString MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Month, Minute, Second and Timespan render their value via DecimalFormat with the JVM default locale, so under locales whose default digits are not 0-9 (Arabic, Persian, Bengali, ...) toString emitted non-ASCII digits, e.g. Month(22) -> "٢٠٠١-١١" instead of "2001-11". These are the canonical textual forms of kdb+ temporal atoms and must always use ASCII digits. Format i2()/i9() with DecimalFormatSymbols for Locale.ROOT so the output is identical regardless of the JVM default locale. Add a test that pins the digits under an Arabic-Indic locale. --- javakdb/src/main/java/com/kx/c.java | 6 ++++-- javakdb/src/test/java/com/kx/CTest.java | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/javakdb/src/main/java/com/kx/c.java b/javakdb/src/main/java/com/kx/c.java index 93f078a..2bfe5c6 100644 --- a/javakdb/src/main/java/com/kx/c.java +++ b/javakdb/src/main/java/com/kx/c.java @@ -39,8 +39,10 @@ import java.time.Instant; import java.time.ZoneOffset; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.Arrays; import java.util.Calendar; +import java.util.Locale; import java.util.TimeZone; import java.util.UUID; import javax.net.ssl.SSLSocket; @@ -2033,7 +2035,7 @@ public static Flip td(Object tbl) throws UnsupportedEncodingException{ * @return String representation of int with zero padding */ static String i2(int i){ - return new DecimalFormat("00").format(i); + return new DecimalFormat("00",DecimalFormatSymbols.getInstance(Locale.ROOT)).format(i); } /** * Creates a string from int with left padding of 0s, if less than 9 digits @@ -2041,6 +2043,6 @@ static String i2(int i){ * @return String representation of int with zero padding */ static String i9(int i){ - return new DecimalFormat("000000000").format(i); + return new DecimalFormat("000000000",DecimalFormatSymbols.getInstance(Locale.ROOT)).format(i); } } diff --git a/javakdb/src/test/java/com/kx/CTest.java b/javakdb/src/test/java/com/kx/CTest.java index 330ce2b..4d505e7 100644 --- a/javakdb/src/test/java/com/kx/CTest.java +++ b/javakdb/src/test/java/com/kx/CTest.java @@ -1105,6 +1105,23 @@ public void testTimespanToString() Assert.assertEquals("", mon.toString()); } + @Test + public void testTemporalToStringLocaleIndependent() + { + // The canonical kdb+ textual form of temporal types must always use ASCII digits, + // independent of the JVM default locale (e.g. Arabic-Indic digit locales). + java.util.Locale previous = java.util.Locale.getDefault(); + try { + java.util.Locale.setDefault(java.util.Locale.forLanguageTag("ar-EG")); + Assert.assertEquals("2001-11", new c.Month(22).toString()); + Assert.assertEquals("00:22", new c.Minute(22).toString()); + Assert.assertEquals("00:00:22", new c.Second(22).toString()); + Assert.assertEquals("00:00:00.000000022", new c.Timespan(22).toString()); + } finally { + java.util.Locale.setDefault(previous); + } + } + @Test public void testTimespanEquals() {