diff --git a/core/src/main/java/org/apache/iceberg/SingleValueParser.java b/core/src/main/java/org/apache/iceberg/SingleValueParser.java index 3de6a0bcc663..c7f07ea1a2d4 100644 --- a/core/src/main/java/org/apache/iceberg/SingleValueParser.java +++ b/core/src/main/java/org/apache/iceberg/SingleValueParser.java @@ -140,6 +140,20 @@ public static Object fromJson(Type type, JsonNode defaultValue) { } else { return DateTimeUtil.isoTimestampToMicros(defaultValue.textValue()); } + case TIMESTAMP_NANO: + Preconditions.checkArgument( + defaultValue.isTextual(), "Cannot parse default as a %s value: %s", type, defaultValue); + if (((Types.TimestampNanoType) type).shouldAdjustToUTC()) { + String timestampTzNano = defaultValue.textValue(); + Preconditions.checkArgument( + DateTimeUtil.isUTCTimestamptz(timestampTzNano), + "Cannot parse default as a %s value: %s, offset must be +00:00", + type, + defaultValue); + return DateTimeUtil.isoTimestamptzToNanos(timestampTzNano); + } else { + return DateTimeUtil.isoTimestampToNanos(defaultValue.textValue()); + } case FIXED: Preconditions.checkArgument( defaultValue.isTextual(), "Cannot parse default as a %s value: %s", type, defaultValue); @@ -289,6 +303,15 @@ public static void toJson(Type type, Object defaultValue, JsonGenerator generato generator.writeString(DateTimeUtil.microsToIsoTimestamp((Long) defaultValue)); } break; + case TIMESTAMP_NANO: + Preconditions.checkArgument( + defaultValue instanceof Long, "Invalid default %s value: %s", type, defaultValue); + if (((Types.TimestampNanoType) type).shouldAdjustToUTC()) { + generator.writeString(DateTimeUtil.nanosToIsoTimestamptz((Long) defaultValue)); + } else { + generator.writeString(DateTimeUtil.nanosToIsoTimestamp((Long) defaultValue)); + } + break; case STRING: Preconditions.checkArgument( defaultValue instanceof CharSequence, diff --git a/core/src/test/java/org/apache/iceberg/TestSingleValueParser.java b/core/src/test/java/org/apache/iceberg/TestSingleValueParser.java index cc1578b0e081..92a5f3caff06 100644 --- a/core/src/test/java/org/apache/iceberg/TestSingleValueParser.java +++ b/core/src/test/java/org/apache/iceberg/TestSingleValueParser.java @@ -46,6 +46,8 @@ public void testValidDefaults() throws IOException { {Types.TimeType.get(), "\"10:15:30\""}, {Types.TimestampType.withoutZone(), "\"2007-12-03T10:15:30\""}, {Types.TimestampType.withZone(), "\"2007-12-03T10:15:30+00:00\""}, + {Types.TimestampNanoType.withoutZone(), "\"2007-12-03T10:15:30.123456789\""}, + {Types.TimestampNanoType.withZone(), "\"2007-12-03T10:15:30.123456789+00:00\""}, {Types.StringType.get(), "\"foo\""}, {Types.UUIDType.get(), "\"eb26bdb1-a1d8-4aa6-990e-da940875492c\""}, {Types.FixedType.ofLength(2), "\"111f\""}, @@ -157,6 +159,15 @@ public void testInvalidTimestamptz() { .hasMessageStartingWith("Cannot parse default as a timestamptz value"); } + @Test + public void testInvalidTimestamptzNano() { + Type expectedType = Types.TimestampNanoType.withZone(); + String defaultJson = "\"2007-12-03T10:15:30+01:00\""; + assertThatThrownBy(() -> defaultValueParseAndUnParseRoundTrip(expectedType, defaultJson)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("Cannot parse default as a timestamptz_ns value"); + } + // serialize to json and deserialize back should return the same result private static String defaultValueParseAndUnParseRoundTrip(Type type, String defaultValue) { Object javaDefaultValue = SingleValueParser.fromJson(type, defaultValue);