From 411cb0263ece4902ab089c1b14046711795e689f Mon Sep 17 00:00:00 2001 From: yangjie01 Date: Sat, 5 Sep 2026 03:16:21 +0800 Subject: [PATCH] [common] Support all TIMESTAMP precisions in the numeric string cast fromMillisToTimestamp handled only precisions 0, 3, 6 and 9 and threw "Unsupported precision" for the rest, so a numeric string could not be cast to TIMESTAMP(1/2/4/5/7/8) even though TimestampType allows 0..9. Those four cases are one formula sampled at four points: the value counts units of 10^-precision seconds, so a unit is 10^(3 - precision) milliseconds. Keep the formula and drop the cases. --- .../paimon/utils/BinaryStringUtils.java | 36 ++++++++----------- .../paimon/utils/BinaryStringUtilsTest.java | 16 +++++++-- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/paimon-common/src/main/java/org/apache/paimon/utils/BinaryStringUtils.java b/paimon-common/src/main/java/org/apache/paimon/utils/BinaryStringUtils.java index 74d1c726bfdf..68d6c6689602 100644 --- a/paimon-common/src/main/java/org/apache/paimon/utils/BinaryStringUtils.java +++ b/paimon-common/src/main/java/org/apache/paimon/utils/BinaryStringUtils.java @@ -321,31 +321,25 @@ public static Timestamp toTimestamp(BinaryString input, int precision, TimeZone return DateTimeUtils.parseTimestampData(input.toString(), precision, timeZone); } + private static final long[] POW10 = {1L, 10L, 100L, 1_000L, 10_000L, 100_000L, 1_000_000L}; + // Helper method to convert epoch to Timestamp with the provided precision. private static Timestamp fromMillisToTimestamp(long epoch, int precision) { - // Calculate milliseconds and nanoseconds from epoch based on precision + if (precision < 0 || precision > 9) { + throw new RuntimeException("Unsupported precision: " + precision); + } + + // epoch counts units of 10^-precision seconds, so one unit is 10^(3 - precision) + // milliseconds: seconds at precision 0, millis at 3, micros at 6, nanos at 9. long millis; int nanosOfMillis; - - switch (precision) { - case 0: // seconds - millis = epoch * 1000; - nanosOfMillis = 0; - break; - case 3: // milliseconds - millis = epoch; - nanosOfMillis = 0; - break; - case 6: // microseconds - millis = epoch / 1000; - nanosOfMillis = (int) ((epoch % 1000) * 1000); - break; - case 9: // nanoseconds - millis = epoch / 1_000_000; - nanosOfMillis = (int) (epoch % 1_000_000); - break; - default: - throw new RuntimeException("Unsupported precision: " + precision); + if (precision > 3) { + long divisor = POW10[precision - 3]; + millis = epoch / divisor; + nanosOfMillis = (int) ((epoch % divisor) * 1_000_000L / divisor); + } else { + millis = epoch * POW10[3 - precision]; + nanosOfMillis = 0; } // If nanoseconds is negative, remove a millisecond diff --git a/paimon-common/src/test/java/org/apache/paimon/utils/BinaryStringUtilsTest.java b/paimon-common/src/test/java/org/apache/paimon/utils/BinaryStringUtilsTest.java index b3b360259c4d..7c434f61c6cb 100644 --- a/paimon-common/src/test/java/org/apache/paimon/utils/BinaryStringUtilsTest.java +++ b/paimon-common/src/test/java/org/apache/paimon/utils/BinaryStringUtilsTest.java @@ -52,7 +52,19 @@ class BinaryStringUtilsTest { // One second and one nanosecond before epoch in nanoseconds // The negative nanosecond gets flipped and the milliseconds decremented "-1000000001, 9, -1001, 999999", - "-86400123456, 6, -86400124, 544000" + "-86400123456, 6, -86400124, 544000", + // Intermediate precisions scale the unit by ten per step from the anchors. + "36000000, 4, 3600000, 0", // one hour in 0.1 ms units + "360000000, 5, 3600000, 0", // one hour in 0.01 ms units + "36000000000, 7, 3600000, 0", // one hour in 100 ns units + "360000000000, 8, 3600000, 0", // one hour in 10 ns units + "36000, 1, 3600000, 0", // one hour in 0.1 s units + "360000, 2, 3600000, 0", // one hour in 0.01 s units + // A remainder below one millisecond becomes nanos-of-millisecond + "17000000001, 5, 170000000, 10000", // one 0.01 ms unit past the millisecond + "170000000012, 8, 1700000, 120", // twelve 10 ns units past the millisecond + // Negative epoch: the nanos-of-millisecond offset stays positive + "-1700000001, 7, -170001, 999900" }) void testToTimestamp(String input, int precision, long expectedMillis, int expectedNanos) { BinaryString binaryInput = BinaryString.fromString(input); @@ -63,7 +75,7 @@ void testToTimestamp(String input, int precision, long expectedMillis, int expec } @ParameterizedTest - @ValueSource(ints = {1, 2, 4, 5, 7, 8, 10, -1}) + @ValueSource(ints = {10, -1}) void testInvalidPrecisions(int precision) { BinaryString input = BinaryString.fromString("1609459200");