From 1270607f3bbca49c63eabf021bac410b9914cd21 Mon Sep 17 00:00:00 2001 From: candiduslynx Date: Tue, 15 Aug 2023 10:35:03 +0300 Subject: [PATCH 1/2] impl --- .../java/io/cloudquery/scalar/Timestamp.java | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 lib/src/main/java/io/cloudquery/scalar/Timestamp.java diff --git a/lib/src/main/java/io/cloudquery/scalar/Timestamp.java b/lib/src/main/java/io/cloudquery/scalar/Timestamp.java new file mode 100644 index 0000000..32cbd09 --- /dev/null +++ b/lib/src/main/java/io/cloudquery/scalar/Timestamp.java @@ -0,0 +1,113 @@ +package io.cloudquery.scalar; + +import org.apache.arrow.vector.types.TimeUnit; +import org.apache.arrow.vector.types.pojo.ArrowType; + +import java.time.*; + +public class Timestamp implements Scalar { + public static final ZoneId zoneID = ZoneOffset.UTC.normalized(); + + // TODO: add more units support later + private static final ArrowType dt = new ArrowType.Timestamp(TimeUnit.MILLISECOND, zoneID.toString()); + + protected ZonedDateTime value; + + public Timestamp() { + } + + public Timestamp(Object value) throws ValidationException { + this.set(value); + } + + @Override + public String toString() { + if (this.value != null) { + return this.value.toString(); + } + return NULL_VALUE_STRING; + } + + @Override + public boolean isValid() { + return this.value != null; + } + + @Override + public ArrowType dataType() { + return dt; + } + + @Override + public void set(Object value) throws ValidationException { + if (value == null) { + this.value = null; + return; + } + + if (value instanceof Scalar scalar) { + if (!scalar.isValid()) { + this.value = null; + return; + } + + if (scalar instanceof Timestamp Timestamp) { + this.value = Timestamp.value; + return; + } + + this.set(scalar.get()); + return; + } + + if (value instanceof ZonedDateTime timestamp) { + this.value = timestamp.withZoneSameInstant(zoneID); + return; + } + + if (value instanceof LocalDate date) { + this.value = date.atStartOfDay(zoneID); + return; + } + + if (value instanceof LocalDateTime date) { + this.value = date.atZone(zoneID); + return; + } + + if (value instanceof Integer integer) { + this.value = ZonedDateTime.ofInstant(Instant.ofEpochMilli(integer), ZoneOffset.UTC.normalized()); + return; + } + + if (value instanceof Long longValue) { + this.value = ZonedDateTime.ofInstant(Instant.ofEpochMilli(longValue), ZoneOffset.UTC.normalized()); + return; + } + + if (value instanceof CharSequence sequence) { + this.value = ZonedDateTime.parse(sequence); + return; + } + + throw new ValidationException(ValidationException.NO_CONVERSION_AVAILABLE, this.dataType(), value); + } + + @Override + public Object get() { + return this.value; // null or proper value + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + + if (!(other instanceof Timestamp o)) { + return false; + } + + return this.value == o.value || this.value.equals(o.value); + } +} From 6de4162d1831e930a8f3c5ec71ed29f460ea211e Mon Sep 17 00:00:00 2001 From: candiduslynx Date: Tue, 15 Aug 2023 13:42:27 +0300 Subject: [PATCH 2/2] tests --- .../java/io/cloudquery/scalar/Timestamp.java | 6 +- .../io/cloudquery/scalar/TimestampTest.java | 138 ++++++++++++++++++ 2 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 lib/src/test/java/io/cloudquery/scalar/TimestampTest.java diff --git a/lib/src/main/java/io/cloudquery/scalar/Timestamp.java b/lib/src/main/java/io/cloudquery/scalar/Timestamp.java index 32cbd09..3377bae 100644 --- a/lib/src/main/java/io/cloudquery/scalar/Timestamp.java +++ b/lib/src/main/java/io/cloudquery/scalar/Timestamp.java @@ -6,7 +6,7 @@ import java.time.*; public class Timestamp implements Scalar { - public static final ZoneId zoneID = ZoneOffset.UTC.normalized(); + public static final ZoneId zoneID = ZoneOffset.UTC; // TODO: add more units support later private static final ArrowType dt = new ArrowType.Timestamp(TimeUnit.MILLISECOND, zoneID.toString()); @@ -76,12 +76,12 @@ public void set(Object value) throws ValidationException { } if (value instanceof Integer integer) { - this.value = ZonedDateTime.ofInstant(Instant.ofEpochMilli(integer), ZoneOffset.UTC.normalized()); + this.value = ZonedDateTime.ofInstant(Instant.ofEpochMilli(integer), ZoneOffset.UTC); return; } if (value instanceof Long longValue) { - this.value = ZonedDateTime.ofInstant(Instant.ofEpochMilli(longValue), ZoneOffset.UTC.normalized()); + this.value = ZonedDateTime.ofInstant(Instant.ofEpochMilli(longValue), ZoneOffset.UTC); return; } diff --git a/lib/src/test/java/io/cloudquery/scalar/TimestampTest.java b/lib/src/test/java/io/cloudquery/scalar/TimestampTest.java new file mode 100644 index 0000000..71098b8 --- /dev/null +++ b/lib/src/test/java/io/cloudquery/scalar/TimestampTest.java @@ -0,0 +1,138 @@ +package io.cloudquery.scalar; + +import org.apache.arrow.vector.types.TimeUnit; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +import static org.junit.jupiter.api.Assertions.*; + + +public class TimestampTest { + @Test + public void testNew() { + assertDoesNotThrow(() -> { + new Timestamp(); + }); + } + + @Test + public void testNewWithValidParam() { + assertDoesNotThrow(() -> { + new Timestamp(1); + new Timestamp("2011-12-03T10:15:30+01:00[Europe/Paris]"); + new Timestamp(ZonedDateTime.now()); + + Scalar s = new Timestamp(ZonedDateTime.now()); + new Timestamp(s); + }); + } + + @Test + public void testNewWithInvalidParam() { + assertThrows(ValidationException.class, () -> { + new Timestamp(false); + }); + } + + @Test + public void testToString() { + Timestamp timestamp = new Timestamp(); + assertEquals(Scalar.NULL_VALUE_STRING, timestamp.toString()); + + assertDoesNotThrow(() -> { + timestamp.set(1); + }); + assertEquals("1970-01-01T00:00:00.001Z", timestamp.toString()); + + String ts = ZonedDateTime.now(ZoneOffset.UTC).toString(); + assertDoesNotThrow(() -> { + timestamp.set(ts); + }); + assertEquals(ts, timestamp.toString()); + } + + @Test + public void testDataType() { + Timestamp timestamp = new Timestamp(); + assertEquals(new ArrowType.Timestamp(TimeUnit.MILLISECOND, "Z"), timestamp.dataType()); + } + + @Test + public void testIsValid() { + Timestamp timestamp = new Timestamp(); + assertFalse(timestamp.isValid()); + + assertDoesNotThrow(() -> { + timestamp.set(1L); + }); + assertTrue(timestamp.isValid()); + } + + @Test + public void testSet() { + Timestamp timestamp = new Timestamp(); + assertDoesNotThrow(() -> { + timestamp.set(1); + timestamp.set(1L); + timestamp.set("2011-12-03T10:15:30+01:00[Europe/Paris]"); + timestamp.set(ZonedDateTime.now()); + + Scalar s = new Timestamp(ZonedDateTime.now()); + timestamp.set(s); + }); + } + + @Test + public void testSetWithInvalidParam() { + Timestamp timestamp = new Timestamp(); + assertThrows(ValidationException.class, () -> { + timestamp.set(false); + }); + } + + @Test + public void testGet() { + Timestamp timestamp = new Timestamp(); + assertFalse(timestamp.isValid()); + assertNull(timestamp.get()); + + ZonedDateTime ts = ZonedDateTime.now(ZoneOffset.UTC); + assertDoesNotThrow(() -> { + timestamp.set(ts); + }); + assertTrue(timestamp.isValid()); + assertEquals(ts, timestamp.get()); + + assertDoesNotThrow(() -> { + timestamp.set(0); + }); + assertTrue(timestamp.isValid()); + assertEquals(Instant.EPOCH.atZone(ZoneOffset.UTC), timestamp.get()); + } + + @Test + public void testEquals() { + Timestamp a = new Timestamp(); + Timestamp b = new Timestamp(); + assertEquals(a, b); + assertNotEquals(a, null); + assertNotEquals(a, new Bool()); // we can't cast Bool to Timestamp + assertNotEquals(null, a); + + assertDoesNotThrow(() -> { + a.set(-1L); + }); + assertNotEquals(a, b); + + assertDoesNotThrow(() -> { + for (Object obj : new Object[]{null, 0, 0L, -1, -1L, 1, 1L, "1970-01-01T00:00:00.001Z", Instant.EPOCH.atZone(ZoneOffset.UTC)}) { + a.set(obj); + assertEquals(a, new Timestamp(obj)); + } + }); + } +}