This is an automated email from the ASF dual-hosted git repository.
lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new eda5c400f [core] Introduce data structure for timestamp with local
zone (#3857)
eda5c400f is described below
commit eda5c400f50b1033af2848572d27a406104145d3
Author: Jingsong Lee <[email protected]>
AuthorDate: Thu Aug 1 10:53:34 2024 +0800
[core] Introduce data structure for timestamp with local zone (#3857)
---
.../{Timestamp.java => LocalZoneTimestamp.java} | 112 +++++++------------
.../java/org/apache/paimon/data/Timestamp.java | 17 ++-
.../apache/paimon/data/LocalZoneTimestampTest.java | 121 +++++++++++++++++++++
3 files changed, 172 insertions(+), 78 deletions(-)
diff --git a/paimon-common/src/main/java/org/apache/paimon/data/Timestamp.java
b/paimon-common/src/main/java/org/apache/paimon/data/LocalZoneTimestamp.java
similarity index 57%
copy from paimon-common/src/main/java/org/apache/paimon/data/Timestamp.java
copy to
paimon-common/src/main/java/org/apache/paimon/data/LocalZoneTimestamp.java
index 7441d25e7..a7ea220f9 100644
--- a/paimon-common/src/main/java/org/apache/paimon/data/Timestamp.java
+++ b/paimon-common/src/main/java/org/apache/paimon/data/LocalZoneTimestamp.java
@@ -20,45 +20,36 @@ package org.apache.paimon.data;
import org.apache.paimon.annotation.Public;
import org.apache.paimon.types.LocalZonedTimestampType;
-import org.apache.paimon.types.TimestampType;
-import org.apache.paimon.utils.Preconditions;
import java.io.Serializable;
import java.time.Instant;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
+
+import static org.apache.paimon.data.Timestamp.MICROS_PER_MILLIS;
+import static org.apache.paimon.data.Timestamp.NANOS_PER_MICROS;
+import static org.apache.paimon.utils.Preconditions.checkArgument;
/**
- * An internal data structure representing data of {@link TimestampType} and
{@link
- * LocalZonedTimestampType}.
+ * An internal data structure representing data of {@link
LocalZonedTimestampType}.
*
* <p>This data structure is immutable and consists of a milliseconds and
nanos-of-millisecond since
* {@code 1970-01-01 00:00:00}. It might be stored in a compact representation
(as a long value) if
* values are small enough.
*
- * @since 0.4.0
+ * @since 0.9.0
*/
@Public
-public final class Timestamp implements Comparable<Timestamp>, Serializable {
+public final class LocalZoneTimestamp implements
Comparable<LocalZoneTimestamp>, Serializable {
private static final long serialVersionUID = 1L;
- // the number of milliseconds in a day
- private static final long MILLIS_PER_DAY = 86400000; // = 24 * 60 * 60 *
1000
-
- public static final long MICROS_PER_MILLIS = 1000L;
-
- public static final long NANOS_PER_MICROS = 1000L;
-
// this field holds the integral second and the milli-of-second
private final long millisecond;
// this field holds the nano-of-millisecond
private final int nanoOfMillisecond;
- private Timestamp(long millisecond, int nanoOfMillisecond) {
- Preconditions.checkArgument(nanoOfMillisecond >= 0 &&
nanoOfMillisecond <= 999_999);
+ private LocalZoneTimestamp(long millisecond, int nanoOfMillisecond) {
+ checkArgument(nanoOfMillisecond >= 0 && nanoOfMillisecond <= 999_999);
this.millisecond = millisecond;
this.nanoOfMillisecond = nanoOfMillisecond;
}
@@ -77,30 +68,16 @@ public final class Timestamp implements
Comparable<Timestamp>, Serializable {
return nanoOfMillisecond;
}
- /** Converts this {@link Timestamp} object to a {@link
java.sql.Timestamp}. */
+ /** Converts this {@link LocalZoneTimestamp} object to a {@link
java.sql.Timestamp}. */
public java.sql.Timestamp toSQLTimestamp() {
- return java.sql.Timestamp.valueOf(toLocalDateTime());
+ return java.sql.Timestamp.from(toInstant());
}
- public Timestamp toMillisTimestamp() {
+ public LocalZoneTimestamp toMillisTimestamp() {
return fromEpochMillis(millisecond);
}
- /** Converts this {@link Timestamp} object to a {@link LocalDateTime}. */
- public LocalDateTime toLocalDateTime() {
- int date = (int) (millisecond / MILLIS_PER_DAY);
- int time = (int) (millisecond % MILLIS_PER_DAY);
- if (time < 0) {
- --date;
- time += MILLIS_PER_DAY;
- }
- long nanoOfDay = time * 1_000_000L + nanoOfMillisecond;
- LocalDate localDate = LocalDate.ofEpochDay(date);
- LocalTime localTime = LocalTime.ofNanoOfDay(nanoOfDay);
- return LocalDateTime.of(localDate, localTime);
- }
-
- /** Converts this {@link Timestamp} object to a {@link Instant}. */
+ /** Converts this {@link LocalZoneTimestamp} object to a {@link Instant}.
*/
public Instant toInstant() {
long epochSecond = millisecond / 1000;
int milliOfSecond = (int) (millisecond % 1000);
@@ -112,14 +89,14 @@ public final class Timestamp implements
Comparable<Timestamp>, Serializable {
return Instant.ofEpochSecond(epochSecond, nanoAdjustment);
}
- /** Converts this {@link Timestamp} object to micros. */
+ /** Converts this {@link LocalZoneTimestamp} object to micros. */
public long toMicros() {
long micros = Math.multiplyExact(millisecond, MICROS_PER_MILLIS);
return micros + nanoOfMillisecond / NANOS_PER_MICROS;
}
@Override
- public int compareTo(Timestamp that) {
+ public int compareTo(LocalZoneTimestamp that) {
int cmp = Long.compare(this.millisecond, that.millisecond);
if (cmp == 0) {
cmp = this.nanoOfMillisecond - that.nanoOfMillisecond;
@@ -129,17 +106,17 @@ public final class Timestamp implements
Comparable<Timestamp>, Serializable {
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof Timestamp)) {
+ if (!(obj instanceof LocalZoneTimestamp)) {
return false;
}
- Timestamp that = (Timestamp) obj;
+ LocalZoneTimestamp that = (LocalZoneTimestamp) obj;
return this.millisecond == that.millisecond
&& this.nanoOfMillisecond == that.nanoOfMillisecond;
}
@Override
public String toString() {
- return toLocalDateTime().toString();
+ return toSQLTimestamp().toLocalDateTime().toString();
}
@Override
@@ -152,78 +129,65 @@ public final class Timestamp implements
Comparable<Timestamp>, Serializable {
// Constructor Utilities
//
------------------------------------------------------------------------------------------
- /** Creates an instance of {@link Timestamp} for now. */
- public static Timestamp now() {
- return fromLocalDateTime(LocalDateTime.now());
+ /** Creates an instance of {@link LocalZoneTimestamp} for now. */
+ public static LocalZoneTimestamp now() {
+ return fromInstant(Instant.now());
}
/**
- * Creates an instance of {@link Timestamp} from milliseconds.
+ * Creates an instance of {@link LocalZoneTimestamp} from milliseconds.
*
* <p>The nanos-of-millisecond field will be set to zero.
*
* @param milliseconds the number of milliseconds since {@code 1970-01-01
00:00:00}; a negative
* number is the number of milliseconds before {@code 1970-01-01
00:00:00}
*/
- public static Timestamp fromEpochMillis(long milliseconds) {
- return new Timestamp(milliseconds, 0);
+ public static LocalZoneTimestamp fromEpochMillis(long milliseconds) {
+ return new LocalZoneTimestamp(milliseconds, 0);
}
/**
- * Creates an instance of {@link Timestamp} from milliseconds and a
nanos-of-millisecond.
+ * Creates an instance of {@link LocalZoneTimestamp} from milliseconds and
a
+ * nanos-of-millisecond.
*
* @param milliseconds the number of milliseconds since {@code 1970-01-01
00:00:00}; a negative
* number is the number of milliseconds before {@code 1970-01-01
00:00:00}
* @param nanosOfMillisecond the nanoseconds within the millisecond, from
0 to 999,999
*/
- public static Timestamp fromEpochMillis(long milliseconds, int
nanosOfMillisecond) {
- return new Timestamp(milliseconds, nanosOfMillisecond);
- }
-
- /**
- * Creates an instance of {@link Timestamp} from an instance of {@link
LocalDateTime}.
- *
- * @param dateTime an instance of {@link LocalDateTime}
- */
- public static Timestamp fromLocalDateTime(LocalDateTime dateTime) {
- long epochDay = dateTime.toLocalDate().toEpochDay();
- long nanoOfDay = dateTime.toLocalTime().toNanoOfDay();
-
- long millisecond = epochDay * MILLIS_PER_DAY + nanoOfDay / 1_000_000;
- int nanoOfMillisecond = (int) (nanoOfDay % 1_000_000);
-
- return new Timestamp(millisecond, nanoOfMillisecond);
+ public static LocalZoneTimestamp fromEpochMillis(long milliseconds, int
nanosOfMillisecond) {
+ return new LocalZoneTimestamp(milliseconds, nanosOfMillisecond);
}
/**
- * Creates an instance of {@link Timestamp} from an instance of {@link
java.sql.Timestamp}.
+ * Creates an instance of {@link LocalZoneTimestamp} from an instance of
{@link
+ * java.sql.Timestamp}.
*
* @param timestamp an instance of {@link java.sql.Timestamp}
*/
- public static Timestamp fromSQLTimestamp(java.sql.Timestamp timestamp) {
- return fromLocalDateTime(timestamp.toLocalDateTime());
+ public static LocalZoneTimestamp fromSQLTimestamp(java.sql.Timestamp
timestamp) {
+ return fromInstant(timestamp.toInstant());
}
/**
- * Creates an instance of {@link Timestamp} from an instance of {@link
Instant}.
+ * Creates an instance of {@link LocalZoneTimestamp} from an instance of
{@link Instant}.
*
* @param instant an instance of {@link Instant}
*/
- public static Timestamp fromInstant(Instant instant) {
+ public static LocalZoneTimestamp fromInstant(Instant instant) {
long epochSecond = instant.getEpochSecond();
int nanoSecond = instant.getNano();
long millisecond = epochSecond * 1_000 + nanoSecond / 1_000_000;
int nanoOfMillisecond = nanoSecond % 1_000_000;
- return new Timestamp(millisecond, nanoOfMillisecond);
+ return new LocalZoneTimestamp(millisecond, nanoOfMillisecond);
}
- /** Creates an instance of {@link Timestamp} from micros. */
- public static Timestamp fromMicros(long micros) {
+ /** Creates an instance of {@link LocalZoneTimestamp} from micros. */
+ public static LocalZoneTimestamp fromMicros(long micros) {
long mills = Math.floorDiv(micros, MICROS_PER_MILLIS);
long nanos = (micros - mills * MICROS_PER_MILLIS) * NANOS_PER_MICROS;
- return Timestamp.fromEpochMillis(mills, (int) nanos);
+ return LocalZoneTimestamp.fromEpochMillis(mills, (int) nanos);
}
/**
diff --git a/paimon-common/src/main/java/org/apache/paimon/data/Timestamp.java
b/paimon-common/src/main/java/org/apache/paimon/data/Timestamp.java
index 7441d25e7..14ec3ce40 100644
--- a/paimon-common/src/main/java/org/apache/paimon/data/Timestamp.java
+++ b/paimon-common/src/main/java/org/apache/paimon/data/Timestamp.java
@@ -30,13 +30,15 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
/**
- * An internal data structure representing data of {@link TimestampType} and
{@link
- * LocalZonedTimestampType}.
+ * An internal data structure representing data of {@link TimestampType}.
*
* <p>This data structure is immutable and consists of a milliseconds and
nanos-of-millisecond since
* {@code 1970-01-01 00:00:00}. It might be stored in a compact representation
(as a long value) if
* values are small enough.
*
+ * <p>Legacy: This class represents {@link LocalZonedTimestampType} too, now
it is recommended to
+ * use {@link LocalZoneTimestamp}.
+ *
* @since 0.4.0
*/
@Public
@@ -45,7 +47,7 @@ public final class Timestamp implements
Comparable<Timestamp>, Serializable {
private static final long serialVersionUID = 1L;
// the number of milliseconds in a day
- private static final long MILLIS_PER_DAY = 86400000; // = 24 * 60 * 60 *
1000
+ public static final long MILLIS_PER_DAY = 86400000; // = 24 * 60 * 60 *
1000
public static final long MICROS_PER_MILLIS = 1000L;
@@ -100,7 +102,12 @@ public final class Timestamp implements
Comparable<Timestamp>, Serializable {
return LocalDateTime.of(localDate, localTime);
}
- /** Converts this {@link Timestamp} object to a {@link Instant}. */
+ /**
+ * Converts this {@link Timestamp} object to a {@link Instant}.
+ *
+ * @deprecated use {@link LocalZoneTimestamp}.
+ */
+ @Deprecated
public Instant toInstant() {
long epochSecond = millisecond / 1000;
int milliOfSecond = (int) (millisecond % 1000);
@@ -208,7 +215,9 @@ public final class Timestamp implements
Comparable<Timestamp>, Serializable {
* Creates an instance of {@link Timestamp} from an instance of {@link
Instant}.
*
* @param instant an instance of {@link Instant}
+ * @deprecated use {@link LocalZoneTimestamp}.
*/
+ @Deprecated
public static Timestamp fromInstant(Instant instant) {
long epochSecond = instant.getEpochSecond();
int nanoSecond = instant.getNano();
diff --git
a/paimon-common/src/test/java/org/apache/paimon/data/LocalZoneTimestampTest.java
b/paimon-common/src/test/java/org/apache/paimon/data/LocalZoneTimestampTest.java
new file mode 100644
index 000000000..53712d89a
--- /dev/null
+++
b/paimon-common/src/test/java/org/apache/paimon/data/LocalZoneTimestampTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.data;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.util.TimeZone;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** Test for {@link LocalZoneTimestamp}. */
+public class LocalZoneTimestampTest {
+
+ @Test
+ public void testNormal() {
+ // From long to TimestampData and vice versa
+
assertThat(LocalZoneTimestamp.fromEpochMillis(1123L).getMillisecond()).isEqualTo(1123L);
+
assertThat(LocalZoneTimestamp.fromEpochMillis(-1123L).getMillisecond()).isEqualTo(-1123L);
+
+ assertThat(LocalZoneTimestamp.fromEpochMillis(1123L,
45678).getMillisecond())
+ .isEqualTo(1123L);
+ assertThat(LocalZoneTimestamp.fromEpochMillis(1123L,
45678).getNanoOfMillisecond())
+ .isEqualTo(45678);
+
+ assertThat(LocalZoneTimestamp.fromEpochMillis(-1123L,
45678).getMillisecond())
+ .isEqualTo(-1123L);
+ assertThat(LocalZoneTimestamp.fromEpochMillis(-1123L,
45678).getNanoOfMillisecond())
+ .isEqualTo(45678);
+
+ // From TimestampData to TimestampData and vice versa
+ java.sql.Timestamp t19 = java.sql.Timestamp.valueOf("1969-01-02
00:00:00.123456789");
+ java.sql.Timestamp t16 = java.sql.Timestamp.valueOf("1969-01-02
00:00:00.123456");
+ java.sql.Timestamp t13 = java.sql.Timestamp.valueOf("1969-01-02
00:00:00.123");
+ java.sql.Timestamp t10 = java.sql.Timestamp.valueOf("1969-01-02
00:00:00");
+
+
assertThat(LocalZoneTimestamp.fromSQLTimestamp(t19).toSQLTimestamp()).isEqualTo(t19);
+
assertThat(LocalZoneTimestamp.fromSQLTimestamp(t16).toSQLTimestamp()).isEqualTo(t16);
+
assertThat(LocalZoneTimestamp.fromSQLTimestamp(t13).toSQLTimestamp()).isEqualTo(t13);
+
assertThat(LocalZoneTimestamp.fromSQLTimestamp(t10).toSQLTimestamp()).isEqualTo(t10);
+
+ java.sql.Timestamp t2 = java.sql.Timestamp.valueOf("1979-01-02
00:00:00.123456");
+
assertThat(LocalZoneTimestamp.fromSQLTimestamp(t2).toSQLTimestamp()).isEqualTo(t2);
+
+ java.sql.Timestamp t3 = new java.sql.Timestamp(1572333940000L);
+
assertThat(LocalZoneTimestamp.fromSQLTimestamp(t3).toSQLTimestamp()).isEqualTo(t3);
+
+ // From Instant to TimestampData and vice versa
+ Instant instant1 = Instant.ofEpochMilli(123L);
+ Instant instant2 = Instant.ofEpochSecond(0L, 123456789L);
+ Instant instant3 = Instant.ofEpochSecond(-2L, 123456789L);
+
+
assertThat(LocalZoneTimestamp.fromInstant(instant1).toInstant()).isEqualTo(instant1);
+
assertThat(LocalZoneTimestamp.fromInstant(instant2).toInstant()).isEqualTo(instant2);
+
assertThat(LocalZoneTimestamp.fromInstant(instant3).toInstant()).isEqualTo(instant3);
+ }
+
+ @Test
+ public void testDaylightSavingTime() {
+ TimeZone tz = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
+
+ java.sql.Timestamp dstBegin2018 =
java.sql.Timestamp.valueOf("2018-03-11 03:00:00");
+
assertThat(LocalZoneTimestamp.fromSQLTimestamp(dstBegin2018).toSQLTimestamp())
+ .isEqualTo(dstBegin2018);
+
+ java.sql.Timestamp dstBegin2019 =
java.sql.Timestamp.valueOf("2019-03-10 02:00:00");
+
assertThat(LocalZoneTimestamp.fromSQLTimestamp(dstBegin2019).toSQLTimestamp())
+ .isEqualTo(dstBegin2019);
+
+ TimeZone.setDefault(tz);
+ }
+
+ @Test
+ public void testToString() {
+
+ java.sql.Timestamp t = java.sql.Timestamp.valueOf("1969-01-02
00:00:00.123456789");
+ assertThat(LocalZoneTimestamp.fromSQLTimestamp(t).toString())
+ .isEqualTo("1969-01-02T00:00:00.123456789");
+
+ assertThat(LocalZoneTimestamp.fromEpochMillis(123L).toString())
+ .isEqualTo(
+ Instant.ofEpochMilli(123)
+ .atZone(ZoneId.systemDefault())
+ .toLocalDateTime()
+ .toString());
+
+ Instant instant = Instant.ofEpochSecond(0L, 123456789L);
+ assertThat(LocalZoneTimestamp.fromInstant(instant).toString())
+
.isEqualTo(instant.atZone(ZoneId.systemDefault()).toLocalDateTime().toString());
+ }
+
+ @Test
+ public void testToMicros() {
+ java.sql.Timestamp t = java.sql.Timestamp.valueOf("2005-01-02
00:00:00.123456789");
+ assertThat(LocalZoneTimestamp.fromSQLTimestamp(t).toString())
+ .isEqualTo("2005-01-02T00:00:00.123456789");
+ assertThat(
+ LocalZoneTimestamp.fromMicros(
+
LocalZoneTimestamp.fromSQLTimestamp(t).toMicros())
+ .toString())
+ .isEqualTo("2005-01-02T00:00:00.123456");
+ }
+}