diff --git a/api/src/main/java/org/apache/iceberg/expressions/Literals.java b/api/src/main/java/org/apache/iceberg/expressions/Literals.java index ee47035b1e72..88fcab633812 100644 --- a/api/src/main/java/org/apache/iceberg/expressions/Literals.java +++ b/api/src/main/java/org/apache/iceberg/expressions/Literals.java @@ -300,8 +300,7 @@ public Literal to(Type type) { case TIMESTAMP: return (Literal) new TimestampLiteral(value()); case TIMESTAMP_NANO: - // assume micros and convert to nanos to match the behavior in the timestamp case above - return new TimestampLiteral(value()).to(type); + return (Literal) new TimestampNanoLiteral(value()); case DATE: if ((long) Integer.MAX_VALUE < value()) { return aboveMax(); diff --git a/api/src/test/java/org/apache/iceberg/expressions/TestTimestampLiteralConversions.java b/api/src/test/java/org/apache/iceberg/expressions/TestTimestampLiteralConversions.java index 379ad4db5e97..79e75b59a55c 100644 --- a/api/src/test/java/org/apache/iceberg/expressions/TestTimestampLiteralConversions.java +++ b/api/src/test/java/org/apache/iceberg/expressions/TestTimestampLiteralConversions.java @@ -21,6 +21,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.time.LocalDate; import java.time.format.DateTimeParseException; import org.apache.iceberg.types.Types; import org.apache.iceberg.util.DateTimeUtil; @@ -107,6 +108,33 @@ public void testTimestampMicrosToDateConversion() { assertThat(dateOrdinal).isEqualTo(-1); } + @Test + public void testTimestampNanoWithLongLiteral() { + // verify round-trip between timestamp_ns and long + Literal timestampNano = + Literal.of("2017-11-16T14:31:08.000000001").to(Types.TimestampNanoType.withoutZone()); + assertThat(timestampNano.value()).isEqualTo(1510842668000000001L); + + Literal longLiteral = + Literal.of(1510842668000000001L).to(Types.TimestampNanoType.withoutZone()); + assertThat(longLiteral).isEqualTo(timestampNano); + + // cast long literal to temporal types + assertThat(longLiteral.to(Types.DateType.get()).value()) + .isEqualTo((int) LocalDate.of(2017, 11, 16).toEpochDay()); + + assertThat(longLiteral.to(Types.TimestampType.withoutZone()).value()) + .isEqualTo(1510842668000000L); + + assertThat(longLiteral.to(Types.TimestampType.withZone()).value()).isEqualTo(1510842668000000L); + + assertThat(longLiteral.to(Types.TimestampNanoType.withoutZone()).value()) + .isEqualTo(1510842668000000001L); + + assertThat(longLiteral.to(Types.TimestampNanoType.withZone()).value()) + .isEqualTo(1510842668000000001L); + } + @Test public void testTimestampNanoToTimestampConversion() { Literal timestamp = @@ -158,6 +186,33 @@ public void testTimestampNanosToDateConversion() { assertThat(dateOrdinal).isEqualTo(-1); } + @Test + public void testTimestampNanoWithZoneWithLongLiteral() { + // verify round-trip between timestamptz_ns and long + Literal timestampNanoWithZone = + Literal.of("2017-11-16T14:31:08.000000001+01:00").to(Types.TimestampNanoType.withZone()); + assertThat(timestampNanoWithZone.value()).isEqualTo(1510839068000000001L); + + Literal longLiteral = + Literal.of(1510839068000000001L).to(Types.TimestampNanoType.withZone()); + assertThat(longLiteral).isEqualTo(timestampNanoWithZone); + + // cast long literal to temporal types + assertThat(longLiteral.to(Types.DateType.get()).value()) + .isEqualTo((int) LocalDate.of(2017, 11, 16).toEpochDay()); + + assertThat(longLiteral.to(Types.TimestampType.withoutZone()).value()) + .isEqualTo(1510839068000000L); + + assertThat(longLiteral.to(Types.TimestampType.withZone()).value()).isEqualTo(1510839068000000L); + + assertThat(longLiteral.to(Types.TimestampNanoType.withoutZone()).value()) + .isEqualTo(1510839068000000001L); + + assertThat(longLiteral.to(Types.TimestampNanoType.withoutZone()).value()) + .isEqualTo(1510839068000000001L); + } + @Test public void testTimestampNanosWithZoneConversion() { Literal isoTimestampNanosWithZoneOffset = diff --git a/api/src/test/java/org/apache/iceberg/types/TestConversions.java b/api/src/test/java/org/apache/iceberg/types/TestConversions.java index e207cfd8d59a..00dc2f5df260 100644 --- a/api/src/test/java/org/apache/iceberg/types/TestConversions.java +++ b/api/src/test/java/org/apache/iceberg/types/TestConversions.java @@ -104,16 +104,14 @@ public void testByteBufferConversions() { .isEqualTo(new byte[] {-128, 26, 6, 0, 0, 0, 0, 0}); assertThat(Literal.of(400000L).to(TimestampType.withZone()).toByteBuffer().array()) .isEqualTo(new byte[] {-128, 26, 6, 0, 0, 0, 0, 0}); - // values passed to assertConversion and Literal.of differ because Literal.of(...) assumes - // the value is in micros, which gets converted when to(TimestampNanoType) is called assertConversion( - 400000000L, TimestampNanoType.withoutZone(), new byte[] {0, -124, -41, 23, 0, 0, 0, 0}); + 400000L, TimestampNanoType.withoutZone(), new byte[] {-128, 26, 6, 0, 0, 0, 0, 0}); assertConversion( - 400000000L, TimestampNanoType.withZone(), new byte[] {0, -124, -41, 23, 0, 0, 0, 0}); + 400000L, TimestampNanoType.withZone(), new byte[] {-128, 26, 6, 0, 0, 0, 0, 0}); assertThat(Literal.of(400000L).to(TimestampNanoType.withoutZone()).toByteBuffer().array()) - .isEqualTo(new byte[] {0, -124, -41, 23, 0, 0, 0, 0}); + .isEqualTo(new byte[] {-128, 26, 6, 0, 0, 0, 0, 0}); assertThat(Literal.of(400000L).to(TimestampNanoType.withZone()).toByteBuffer().array()) - .isEqualTo(new byte[] {0, -124, -41, 23, 0, 0, 0, 0}); + .isEqualTo(new byte[] {-128, 26, 6, 0, 0, 0, 0, 0}); // strings are stored as UTF-8 bytes (without length) // 'A' -> 65, 'B' -> 66, 'C' -> 67