diff --git a/src/main/java/com/cedarsoftware/util/convert/BigDecimalConversions.java b/src/main/java/com/cedarsoftware/util/convert/BigDecimalConversions.java
new file mode 100644
index 00000000..6e4c1f37
--- /dev/null
+++ b/src/main/java/com/cedarsoftware/util/convert/BigDecimalConversions.java
@@ -0,0 +1,60 @@
+package com.cedarsoftware.util.convert;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZonedDateTime;
+import java.util.UUID;
+
+/**
+ * @author John DeRegnaucourt (jdereg@gmail.com)
+ *
+ * Copyright (c) Cedar Software LLC
+ *
+ * Licensed 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
+ *
+ * License
+ *
+ * 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.
+ */
+final class BigDecimalConversions {
+ static Instant toInstant(Object from, Converter converter) {
+ BigDecimal time = (BigDecimal) from;
+ long seconds = time.longValue() / 1000;
+ int nanos = time.remainder(BigDecimal.valueOf(1000)).multiply(BigDecimal.valueOf(1_000_000)).intValue();
+ return Instant.ofEpochSecond(seconds, nanos);
+ }
+
+ static LocalDateTime toLocalDateTime(Object from, Converter converter) {
+ return toZonedDateTime(from, converter).toLocalDateTime();
+ }
+
+ static ZonedDateTime toZonedDateTime(Object from, Converter converter) {
+ return toInstant(from, converter).atZone(converter.getOptions().getZoneId());
+ }
+
+ static Timestamp toTimestamp(Object from, Converter converter) {
+ return Timestamp.from(toInstant(from, converter));
+ }
+
+ static BigInteger toBigInteger(Object from, Converter converter) {
+ return ((BigDecimal)from).toBigInteger();
+ }
+
+ static String toString(Object from, Converter converter) {
+ return ((BigDecimal) from).stripTrailingZeros().toPlainString();
+ }
+
+ static UUID toUUID(Object from, Converter converter) {
+ BigInteger bigInt = ((BigDecimal) from).toBigInteger();
+ return BigIntegerConversions.toUUID(bigInt, converter);
+ }
+}
diff --git a/src/main/java/com/cedarsoftware/util/convert/BigIntegerConversions.java b/src/main/java/com/cedarsoftware/util/convert/BigIntegerConversions.java
new file mode 100644
index 00000000..4743d49a
--- /dev/null
+++ b/src/main/java/com/cedarsoftware/util/convert/BigIntegerConversions.java
@@ -0,0 +1,55 @@
+package com.cedarsoftware.util.convert;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.UUID;
+
+/**
+ * @author John DeRegnaucourt (jdereg@gmail.com)
+ *
+ * Copyright (c) Cedar Software LLC
+ *
+ * Licensed 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
+ *
+ * License
+ *
+ * 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.
+ */
+final class BigIntegerConversions {
+ static BigDecimal toBigDecimal(Object from, Converter converter) {
+ return new BigDecimal((BigInteger)from);
+ }
+
+ static UUID toUUID(Object from, Converter converter) {
+ BigInteger bigInteger = (BigInteger) from;
+ if (bigInteger.signum() < 0) {
+ throw new IllegalArgumentException("Cannot convert a negative number [" + bigInteger + "] to a UUID");
+ }
+ StringBuilder hex = new StringBuilder(bigInteger.toString(16));
+
+ // Pad the string to 32 characters with leading zeros (if necessary)
+ while (hex.length() < 32) {
+ hex.insert(0, "0");
+ }
+
+ // Split into two 64-bit parts
+ String highBitsHex = hex.substring(0, 16);
+ String lowBitsHex = hex.substring(16, 32);
+
+ // Combine and format into standard UUID format
+ String uuidString = highBitsHex.substring(0, 8) + "-" +
+ highBitsHex.substring(8, 12) + "-" +
+ highBitsHex.substring(12, 16) + "-" +
+ lowBitsHex.substring(0, 4) + "-" +
+ lowBitsHex.substring(4, 16);
+
+ // Create UUID from string
+ return UUID.fromString(uuidString);
+ }
+}
diff --git a/src/main/java/com/cedarsoftware/util/convert/Converter.java b/src/main/java/com/cedarsoftware/util/convert/Converter.java
index 909eae90..2a26234b 100644
--- a/src/main/java/com/cedarsoftware/util/convert/Converter.java
+++ b/src/main/java/com/cedarsoftware/util/convert/Converter.java
@@ -230,7 +230,7 @@ private static void buildFactoryConversions() {
CONVERSION_DB.put(pair(ZonedDateTime.class, Double.class), ZonedDateTimeConversions::toDouble);
CONVERSION_DB.put(pair(Date.class, Double.class), DateConversions::toDouble);
CONVERSION_DB.put(pair(java.sql.Date.class, Double.class), DateConversions::toDouble);
- CONVERSION_DB.put(pair(Timestamp.class, Double.class), DateConversions::toDouble);
+ CONVERSION_DB.put(pair(Timestamp.class, Double.class), TimestampConversions::toDouble);
CONVERSION_DB.put(pair(AtomicBoolean.class, Double.class), AtomicBooleanConversions::toDouble);
CONVERSION_DB.put(pair(AtomicInteger.class, Double.class), NumberConversions::toDouble);
CONVERSION_DB.put(pair(AtomicLong.class, Double.class), NumberConversions::toDouble);
@@ -293,7 +293,7 @@ private static void buildFactoryConversions() {
CONVERSION_DB.put(pair(Boolean.class, BigInteger.class), BooleanConversions::toBigInteger);
CONVERSION_DB.put(pair(Character.class, BigInteger.class), CharacterConversions::toBigInteger);
CONVERSION_DB.put(pair(BigInteger.class, BigInteger.class), Converter::identity);
- CONVERSION_DB.put(pair(BigDecimal.class, BigInteger.class), NumberConversions::bigDecimalToBigInteger);
+ CONVERSION_DB.put(pair(BigDecimal.class, BigInteger.class), BigDecimalConversions::toBigInteger);
CONVERSION_DB.put(pair(AtomicBoolean.class, BigInteger.class), AtomicBooleanConversions::toBigInteger);
CONVERSION_DB.put(pair(AtomicInteger.class, BigInteger.class), NumberConversions::integerTypeToBigInteger);
CONVERSION_DB.put(pair(AtomicLong.class, BigInteger.class), NumberConversions::integerTypeToBigInteger);
@@ -323,13 +323,13 @@ private static void buildFactoryConversions() {
CONVERSION_DB.put(pair(Boolean.class, BigDecimal.class), BooleanConversions::toBigDecimal);
CONVERSION_DB.put(pair(Character.class, BigDecimal.class), CharacterConversions::toBigDecimal);
CONVERSION_DB.put(pair(BigDecimal.class, BigDecimal.class), Converter::identity);
- CONVERSION_DB.put(pair(BigInteger.class, BigDecimal.class), NumberConversions::bigIntegerToBigDecimal);
+ CONVERSION_DB.put(pair(BigInteger.class, BigDecimal.class), BigIntegerConversions::toBigDecimal);
CONVERSION_DB.put(pair(AtomicBoolean.class, BigDecimal.class), AtomicBooleanConversions::toBigDecimal);
CONVERSION_DB.put(pair(AtomicInteger.class, BigDecimal.class), NumberConversions::integerTypeToBigDecimal);
CONVERSION_DB.put(pair(AtomicLong.class, BigDecimal.class), NumberConversions::integerTypeToBigDecimal);
CONVERSION_DB.put(pair(Date.class, BigDecimal.class), DateConversions::toBigDecimal);
CONVERSION_DB.put(pair(java.sql.Date.class, BigDecimal.class), DateConversions::toBigDecimal);
- CONVERSION_DB.put(pair(Timestamp.class, BigDecimal.class), DateConversions::toBigDecimal);
+ CONVERSION_DB.put(pair(Timestamp.class, BigDecimal.class), TimestampConversions::toBigDecimal);
CONVERSION_DB.put(pair(Instant.class, BigDecimal.class), InstantConversions::toBigDecimal);
CONVERSION_DB.put(pair(LocalDate.class, BigDecimal.class), LocalDateConversions::toBigDecimal);
CONVERSION_DB.put(pair(LocalDateTime.class, BigDecimal.class), LocalDateTimeConversions::toBigDecimal);
@@ -466,9 +466,9 @@ private static void buildFactoryConversions() {
CONVERSION_DB.put(pair(Short.class, Timestamp.class), UNSUPPORTED);
CONVERSION_DB.put(pair(Integer.class, Timestamp.class), UNSUPPORTED);
CONVERSION_DB.put(pair(Long.class, Timestamp.class), NumberConversions::toTimestamp);
- CONVERSION_DB.put(pair(Double.class, Timestamp.class), NumberConversions::toTimestamp);
+ CONVERSION_DB.put(pair(Double.class, Timestamp.class), DoubleConversions::toTimestamp);
CONVERSION_DB.put(pair(BigInteger.class, Timestamp.class), NumberConversions::toTimestamp);
- CONVERSION_DB.put(pair(BigDecimal.class, Timestamp.class), NumberConversions::toTimestamp);
+ CONVERSION_DB.put(pair(BigDecimal.class, Timestamp.class), BigDecimalConversions::toTimestamp);
CONVERSION_DB.put(pair(AtomicInteger.class, Timestamp.class), UNSUPPORTED);
CONVERSION_DB.put(pair(AtomicLong.class, Timestamp.class), NumberConversions::toTimestamp);
CONVERSION_DB.put(pair(Timestamp.class, Timestamp.class), DateConversions::toTimestamp);
@@ -538,9 +538,9 @@ private static void buildFactoryConversions() {
CONVERSION_DB.put(pair(Short.class, LocalDateTime.class), UNSUPPORTED);
CONVERSION_DB.put(pair(Integer.class, LocalDateTime.class), UNSUPPORTED);
CONVERSION_DB.put(pair(Long.class, LocalDateTime.class), NumberConversions::toLocalDateTime);
- CONVERSION_DB.put(pair(Double.class, LocalDateTime.class), NumberConversions::toLocalDateTime);
+ CONVERSION_DB.put(pair(Double.class, LocalDateTime.class), DoubleConversions::toLocalDateTime);
CONVERSION_DB.put(pair(BigInteger.class, LocalDateTime.class), NumberConversions::toLocalDateTime);
- CONVERSION_DB.put(pair(BigDecimal.class, LocalDateTime.class), NumberConversions::toLocalDateTime);
+ CONVERSION_DB.put(pair(BigDecimal.class, LocalDateTime.class), BigDecimalConversions::toLocalDateTime);
CONVERSION_DB.put(pair(AtomicInteger.class, LocalDateTime.class), UNSUPPORTED);
CONVERSION_DB.put(pair(AtomicLong.class, LocalDateTime.class), NumberConversions::toLocalDateTime);
CONVERSION_DB.put(pair(java.sql.Date.class, LocalDateTime.class), DateConversions::toLocalDateTime);
@@ -587,9 +587,9 @@ private static void buildFactoryConversions() {
CONVERSION_DB.put(pair(Short.class, ZonedDateTime.class), UNSUPPORTED);
CONVERSION_DB.put(pair(Integer.class, ZonedDateTime.class), UNSUPPORTED);
CONVERSION_DB.put(pair(Long.class, ZonedDateTime.class), NumberConversions::toZonedDateTime);
- CONVERSION_DB.put(pair(Double.class, ZonedDateTime.class), NumberConversions::toZonedDateTime);
+ CONVERSION_DB.put(pair(Double.class, ZonedDateTime.class), DoubleConversions::toZonedDateTime);
CONVERSION_DB.put(pair(BigInteger.class, ZonedDateTime.class), NumberConversions::toZonedDateTime);
- CONVERSION_DB.put(pair(BigDecimal.class, ZonedDateTime.class), NumberConversions::toZonedDateTime);
+ CONVERSION_DB.put(pair(BigDecimal.class, ZonedDateTime.class), BigDecimalConversions::toZonedDateTime);
CONVERSION_DB.put(pair(AtomicInteger.class, ZonedDateTime.class), UNSUPPORTED);
CONVERSION_DB.put(pair(AtomicLong.class, ZonedDateTime.class), NumberConversions::toZonedDateTime);
CONVERSION_DB.put(pair(java.sql.Date.class, ZonedDateTime.class), DateConversions::toZonedDateTime);
@@ -622,8 +622,8 @@ private static void buildFactoryConversions() {
CONVERSION_DB.put(pair(Void.class, UUID.class), VoidConversions::toNull);
CONVERSION_DB.put(pair(UUID.class, UUID.class), Converter::identity);
CONVERSION_DB.put(pair(String.class, UUID.class), StringConversions::toUUID);
- CONVERSION_DB.put(pair(BigInteger.class, UUID.class), NumberConversions::bigIntegerToUUID);
- CONVERSION_DB.put(pair(BigDecimal.class, UUID.class), NumberConversions::bigDecimalToUUID);
+ CONVERSION_DB.put(pair(BigInteger.class, UUID.class), BigIntegerConversions::toUUID);
+ CONVERSION_DB.put(pair(BigDecimal.class, UUID.class), BigDecimalConversions::toUUID);
CONVERSION_DB.put(pair(Map.class, UUID.class), MapConversions::toUUID);
// Class conversions supported
@@ -649,7 +649,7 @@ private static void buildFactoryConversions() {
CONVERSION_DB.put(pair(Boolean.class, String.class), StringConversions::toString);
CONVERSION_DB.put(pair(Character.class, String.class), CharacterConversions::toString);
CONVERSION_DB.put(pair(BigInteger.class, String.class), StringConversions::toString);
- CONVERSION_DB.put(pair(BigDecimal.class, String.class), NumberConversions::bigDecimalToString);
+ CONVERSION_DB.put(pair(BigDecimal.class, String.class), BigDecimalConversions::toString);
CONVERSION_DB.put(pair(AtomicBoolean.class, String.class), StringConversions::toString);
CONVERSION_DB.put(pair(AtomicInteger.class, String.class), StringConversions::toString);
CONVERSION_DB.put(pair(AtomicLong.class, String.class), StringConversions::toString);
@@ -728,9 +728,9 @@ private static void buildFactoryConversions() {
CONVERSION_DB.put(pair(Short.class, Instant.class), UNSUPPORTED);
CONVERSION_DB.put(pair(Integer.class, Instant.class), UNSUPPORTED);
CONVERSION_DB.put(pair(Long.class, Instant.class), NumberConversions::toInstant);
- CONVERSION_DB.put(pair(Double.class, Instant.class), NumberConversions::toInstant);
+ CONVERSION_DB.put(pair(Double.class, Instant.class), DoubleConversions::toInstant);
CONVERSION_DB.put(pair(BigInteger.class, Instant.class), NumberConversions::toInstant);
- CONVERSION_DB.put(pair(BigDecimal.class, Instant.class), NumberConversions::toInstant);
+ CONVERSION_DB.put(pair(BigDecimal.class, Instant.class), BigDecimalConversions::toInstant);
CONVERSION_DB.put(pair(AtomicInteger.class, Instant.class), UNSUPPORTED);
CONVERSION_DB.put(pair(AtomicLong.class, Instant.class), NumberConversions::toInstant);
CONVERSION_DB.put(pair(java.sql.Date.class, Instant.class), DateConversions::toInstant);
diff --git a/src/main/java/com/cedarsoftware/util/convert/DoubleConversions.java b/src/main/java/com/cedarsoftware/util/convert/DoubleConversions.java
new file mode 100644
index 00000000..395f1a4e
--- /dev/null
+++ b/src/main/java/com/cedarsoftware/util/convert/DoubleConversions.java
@@ -0,0 +1,51 @@
+package com.cedarsoftware.util.convert;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZonedDateTime;
+
+/**
+ * @author John DeRegnaucourt (jdereg@gmail.com)
+ *
+ * Copyright (c) Cedar Software LLC
+ *
+ * Licensed 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
+ *
+ * License
+ *
+ * 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.
+ */
+final class DoubleConversions {
+ private DoubleConversions() { }
+
+ static Instant toInstant(Object from, Converter converter) {
+ double d = (Double) from;
+ long seconds = (long) d / 1000;
+ int nanoAdjustment = (int) ((d - seconds * 1000) * 1_000_000);
+ return Instant.ofEpochSecond(seconds, nanoAdjustment);
+ }
+
+ static LocalDateTime toLocalDateTime(Object from, Converter converter) {
+ return toZonedDateTime(from, converter).toLocalDateTime();
+ }
+
+ static ZonedDateTime toZonedDateTime(Object from, Converter converter) {
+ return toInstant(from, converter).atZone(converter.getOptions().getZoneId());
+ }
+
+ static Timestamp toTimestamp(Object from, Converter converter) {
+ double milliseconds = (Double) from;
+ long millisPart = (long) milliseconds;
+ int nanosPart = (int) ((milliseconds - millisPart) * 1_000_000);
+ Timestamp timestamp = new Timestamp(millisPart);
+ timestamp.setNanos(timestamp.getNanos() + nanosPart);
+ return timestamp;
+ }
+}
diff --git a/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java b/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java
index acf843fb..10651159 100644
--- a/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java
+++ b/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java
@@ -52,11 +52,7 @@ static ZonedDateTime toZonedDateTime(Object from, Converter converter) {
static long toLong(Object from, Converter converter) {
return ((Instant) from).toEpochMilli();
}
-
- static float toFloat(Object from, Converter converter) {
- return toLong(from, converter);
- }
-
+
/**
* @return double number of milliseconds. When integerized, the number returned is always the number of epoch
* milliseconds. If the Instant specified resolution further than milliseconds, the double returned captures
@@ -66,9 +62,9 @@ static float toFloat(Object from, Converter converter) {
*/
static double toDouble(Object from, Converter converter) {
Instant instant = (Instant) from;
- long millis = instant.toEpochMilli();
- int nanos = instant.getNano();
- return millis + (nanos % 1_000_000) / 1_000_000.0d;
+ long seconds = instant.getEpochSecond();
+ int nanoAdjustment = instant.getNano();
+ return (double) seconds * 1000 + (double) nanoAdjustment / 1_000_000;
}
static AtomicLong toAtomicLong(Object from, Converter converter) {
@@ -96,7 +92,10 @@ static BigInteger toBigInteger(Object from, Converter converter) {
}
static BigDecimal toBigDecimal(Object from, Converter converter) {
- return BigDecimal.valueOf(toLong(from, converter));
+ Instant instant = (Instant) from;
+ long seconds = instant.getEpochSecond();
+ int nanos = instant.getNano();
+ return BigDecimal.valueOf(seconds * 1000).add(BigDecimal.valueOf(nanos, 6));
}
static LocalDateTime toLocalDateTime(Object from, Converter converter) {
diff --git a/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java b/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java
index 55549cff..0d96f2ac 100644
--- a/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java
+++ b/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java
@@ -12,7 +12,6 @@
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
-import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -118,19 +117,7 @@ static AtomicLong toAtomicLong(Object from, Converter converter) {
static AtomicInteger toAtomicInteger(Object from, Converter converter) {
return new AtomicInteger(toInt(from, converter));
}
-
- static BigDecimal bigIntegerToBigDecimal(Object from, Converter converter) {
- return new BigDecimal((BigInteger)from);
- }
-
- static BigInteger bigDecimalToBigInteger(Object from, Converter converter) {
- return ((BigDecimal)from).toBigInteger();
- }
-
- static String bigDecimalToString(Object from, Converter converter) {
- return ((BigDecimal) from).stripTrailingZeros().toPlainString();
- }
-
+
static BigDecimal toBigDecimal(Object from, Converter converter) {
return new BigDecimal(StringUtilities.trimToEmpty(from.toString()));
}
@@ -168,39 +155,7 @@ static boolean isBigDecimalNotZero(Object from, Converter converter) {
static BigInteger toBigInteger(Object from, Converter converter) {
return new BigInteger(StringUtilities.trimToEmpty(from.toString()));
}
-
- static UUID bigIntegerToUUID(Object from, Converter converter) {
- BigInteger bigInteger = (BigInteger) from;
- if (bigInteger.signum() < 0) {
- throw new IllegalArgumentException("Cannot convert a negative number [" + bigInteger + "] to a UUID");
- }
- StringBuilder hex = new StringBuilder(bigInteger.toString(16));
-
- // Pad the string to 32 characters with leading zeros (if necessary)
- while (hex.length() < 32) {
- hex.insert(0, "0");
- }
-
- // Split into two 64-bit parts
- String highBitsHex = hex.substring(0, 16);
- String lowBitsHex = hex.substring(16, 32);
-
- // Combine and format into standard UUID format
- String uuidString = highBitsHex.substring(0, 8) + "-" +
- highBitsHex.substring(8, 12) + "-" +
- highBitsHex.substring(12, 16) + "-" +
- lowBitsHex.substring(0, 4) + "-" +
- lowBitsHex.substring(4, 16);
-
- // Create UUID from string
- return UUID.fromString(uuidString);
- }
-
- static UUID bigDecimalToUUID(Object from, Converter converter) {
- BigInteger bigInt = ((BigDecimal) from).toBigInteger();
- return bigIntegerToUUID(bigInt, converter);
- }
-
+
/**
* @param from - object that is a number to be converted to char
* @param converter - instance of converter mappings to use.
diff --git a/src/main/java/com/cedarsoftware/util/convert/TimestampConversions.java b/src/main/java/com/cedarsoftware/util/convert/TimestampConversions.java
new file mode 100644
index 00000000..271ad151
--- /dev/null
+++ b/src/main/java/com/cedarsoftware/util/convert/TimestampConversions.java
@@ -0,0 +1,45 @@
+package com.cedarsoftware.util.convert;
+
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+
+/**
+ * @author John DeRegnaucourt (jdereg@gmail.com)
+ *
+ * Copyright (c) Cedar Software LLC
+ *
+ * Licensed 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
+ *
+ * License
+ *
+ * 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.
+ */
+final class TimestampConversions {
+ private TimestampConversions() {}
+
+ static double toDouble(Object from, Converter converter) {
+ Timestamp timestamp = (Timestamp) from;
+ long timeInMilliseconds = timestamp.getTime();
+ int nanoseconds = timestamp.getNanos();
+ // Subtract the milliseconds part of the nanoseconds to avoid double counting
+ double additionalNanos = nanoseconds % 1_000_000 / 1_000_000.0;
+ return timeInMilliseconds + additionalNanos;
+ }
+
+ static BigDecimal toBigDecimal(Object from, Converter converter) {
+ Timestamp timestamp = (Timestamp) from;
+ long epochMillis = timestamp.getTime();
+
+ // Get nanoseconds part (fraction of the current millisecond)
+ int nanoPart = timestamp.getNanos() % 1_000_000;
+
+ // Convert time to fractional milliseconds
+ return BigDecimal.valueOf(epochMillis).add(BigDecimal.valueOf(nanoPart, 6)); // Dividing by 1_000_000 with scale 6
+ }
+}
diff --git a/src/main/java/com/cedarsoftware/util/convert/ZonedDateTimeConversions.java b/src/main/java/com/cedarsoftware/util/convert/ZonedDateTimeConversions.java
index c306e634..0eaee542 100644
--- a/src/main/java/com/cedarsoftware/util/convert/ZonedDateTimeConversions.java
+++ b/src/main/java/com/cedarsoftware/util/convert/ZonedDateTimeConversions.java
@@ -40,7 +40,8 @@ static long toLong(Object from, Converter converter) {
}
static double toDouble(Object from, Converter converter) {
- return ((ZonedDateTime) from).toInstant().toEpochMilli(); // speed over shorter code.
+ ZonedDateTime zdt = (ZonedDateTime) from;
+ return InstantConversions.toDouble(zdt.toInstant(), converter);
}
static Instant toInstant(Object from, Converter converter) {
diff --git a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java
index 899bea19..bdd63a95 100644
--- a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java
+++ b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java
@@ -805,34 +805,20 @@ public ZoneId getZoneId() {
TEST_DB.put(pair(Instant.class, Long.class), new Object[][]{
{ZonedDateTime.parse("2024-02-12T11:38:00.123456789+01:00").toInstant(), 1707734280123L}, // maintains millis (best long can do)
{ZonedDateTime.parse("2024-02-12T11:38:00.123999+01:00").toInstant(), 1707734280123L}, // maintains millis (best long can do)
- {ZonedDateTime.parse("2024-02-12T11:38:00+01:00").toInstant(), 1707734280000L},
+ {ZonedDateTime.parse("2024-02-12T11:38:00+01:00").toInstant(), 1707734280000L, true},
});
TEST_DB.put(pair(LocalDate.class, Long.class), new Object[][]{
- {(Supplier) () -> {
- ZonedDateTime zdt = ZonedDateTime.parse("2024-02-12T11:38:00+01:00");
- zdt = zdt.withZoneSameInstant(TOKYO_Z);
- return zdt.toLocalDate();
- }, 1707663600000L}, // Epoch millis in Tokyo timezone (at start of day - no time)
+ {ZonedDateTime.parse("2024-02-12T11:38:00+01:00").withZoneSameInstant(TOKYO_Z).toLocalDate(), 1707663600000L, true}, // Epoch millis in Tokyo timezone (at start of day - no time)
});
TEST_DB.put(pair(LocalDateTime.class, Long.class), new Object[][]{
- {(Supplier) () -> {
- ZonedDateTime zdt = ZonedDateTime.parse("2024-02-12T11:38:00+01:00");
- zdt = zdt.withZoneSameInstant(TOKYO_Z);
- return zdt.toLocalDateTime();
- }, 1707734280000L}, // Epoch millis in Tokyo timezone
- {(Supplier) () -> {
- ZonedDateTime zdt = ZonedDateTime.parse("2024-02-12T11:38:00.123+01:00"); // maintains millis (best long can do)
- zdt = zdt.withZoneSameInstant(TOKYO_Z);
- return zdt.toLocalDateTime();
- }, 1707734280123L}, // Epoch millis in Tokyo timezone
- {(Supplier) () -> {
- ZonedDateTime zdt = ZonedDateTime.parse("2024-02-12T11:38:00.12399+01:00"); // maintains millis (best long can do)
- zdt = zdt.withZoneSameInstant(TOKYO_Z);
- return zdt.toLocalDateTime();
- }, 1707734280123L}, // Epoch millis in Tokyo timezone
+ {ZonedDateTime.parse("2024-02-12T11:38:00+01:00").withZoneSameInstant(TOKYO_Z).toLocalDateTime(), 1707734280000L, true}, // Epoch millis in Tokyo timezone
+ {ZonedDateTime.parse("2024-02-12T11:38:00.123+01:00").withZoneSameInstant(TOKYO_Z).toLocalDateTime(), 1707734280123L, true}, // maintains millis (best long can do)
+ {ZonedDateTime.parse("2024-02-12T11:38:00.12399+01:00").withZoneSameInstant(TOKYO_Z).toLocalDateTime(), 1707734280123L}, // maintains millis (best long can do)
});
TEST_DB.put(pair(ZonedDateTime.class, Long.class), new Object[][]{
- {ZonedDateTime.parse("2024-02-12T11:38:00+01:00"), 1707734280000L},
+ {ZonedDateTime.parse("2024-02-12T11:38:00+01:00"), 1707734280000L}, // no reverse, because zone name added by .toString()s
+ {ZonedDateTime.parse("2024-02-12T11:38:00.123+01:00"), 1707734280123L},
+ {ZonedDateTime.parse("2024-02-12T11:38:00.1234+01:00"), 1707734280123L}, // long only supports to millisecond
});
TEST_DB.put(pair(Calendar.class, Long.class), new Object[][]{
{(Supplier) () -> {
@@ -1075,49 +1061,53 @@ public ZoneId getZoneId() {
{(char) 0, 0d},
});
TEST_DB.put(pair(Instant.class, Double.class), new Object[][]{
- {ZonedDateTime.parse("2024-02-12T11:38:00+01:00").toInstant(), 1707734280000d},
- {ZonedDateTime.parse("2024-02-12T11:38:00.123+01:00").toInstant(), 1707734280123d},
- {ZonedDateTime.parse("2024-02-12T11:38:00.1234+01:00").toInstant(), 1707734280123.4d}, // fractional milliseconds (nano support)
- {ZonedDateTime.parse("2024-02-12T11:38:00.1239+01:00").toInstant(), 1707734280123.9d},
- {ZonedDateTime.parse("2024-02-12T11:38:00.123937482+01:00").toInstant(), 1707734280123.937482d}, // nano = one-millionth of a milli
+ {Instant.parse("2024-02-12T11:38:00+01:00"), 1707734280000d, true},
+ {Instant.parse("2024-02-12T11:38:00.123+01:00"), 1707734280123d, true},
+ {Instant.parse("2024-02-12T11:38:00.1234+01:00"), 1707734280123.4d}, // fractional milliseconds (nano support) - no reverse because of IEEE-754 limitations
+ {Instant.parse("2024-02-12T11:38:00.1234+01:00"), 1.7077342801234E12}, // fractional milliseconds (nano support)
+ {Instant.parse("2024-02-12T11:38:00.1239+01:00"), 1707734280123.9d},
+ {Instant.parse("2024-02-12T11:38:00.123937482+01:00"), 1707734280123.937482d}, // nano = one-millionth of a milli
});
TEST_DB.put(pair(LocalDate.class, Double.class), new Object[][]{
{(Supplier) () -> {
ZonedDateTime zdt = ZonedDateTime.parse("2024-02-12T11:38:00+01:00");
zdt = zdt.withZoneSameInstant(TOKYO_Z);
return zdt.toLocalDate();
- }, 1.7076636E12}, // Epoch millis in Tokyo timezone (at start of day - no time)
+ }, 1.7076636E12, true}, // Epoch millis in Tokyo timezone (at start of day - no time)
});
TEST_DB.put(pair(LocalDateTime.class, Double.class), new Object[][]{
{(Supplier) () -> {
ZonedDateTime zdt = ZonedDateTime.parse("2024-02-12T11:38:00+01:00");
zdt = zdt.withZoneSameInstant(TOKYO_Z);
return zdt.toLocalDateTime();
- }, 1.70773428E12}, // Epoch millis in Tokyo timezone
+ }, 1.70773428E12, true}, // Epoch millis in Tokyo timezone
{(Supplier) () -> {
ZonedDateTime zdt = ZonedDateTime.parse("2024-02-12T11:38:00.123+01:00");
zdt = zdt.withZoneSameInstant(TOKYO_Z);
return zdt.toLocalDateTime();
- }, 1.707734280123E12}, // Epoch millis in Tokyo timezone
+ }, 1.707734280123E12, true}, // Epoch millis in Tokyo timezone
{(Supplier) () -> {
ZonedDateTime zdt = ZonedDateTime.parse("2024-02-12T11:38:00.1239+01:00");
zdt = zdt.withZoneSameInstant(TOKYO_Z);
return zdt.toLocalDateTime();
- }, 1.7077342801239E12}, // Epoch millis in Tokyo timezone
+ }, 1707734280123.9d}, // Epoch millis in Tokyo timezone (no reverse - IEEE-754 limitations)
{(Supplier) () -> {
ZonedDateTime zdt = ZonedDateTime.parse("2024-02-12T11:38:00.123937482+01:00");
zdt = zdt.withZoneSameInstant(TOKYO_Z);
return zdt.toLocalDateTime();
- }, 1707734280123.937482d}, // Epoch millis in Tokyo timezone
+ }, 1707734280123.937482d}, // Epoch millis in Tokyo timezone (no reverse - IEEE-754 limitations)
});
- TEST_DB.put(pair(ZonedDateTime.class, Double.class), new Object[][]{
+ TEST_DB.put(pair(ZonedDateTime.class, Double.class), new Object[][]{ // no reverse due to .toString adding zone name
{ZonedDateTime.parse("2024-02-12T11:38:00+01:00"), 1707734280000d},
+ {ZonedDateTime.parse("2024-02-12T11:38:00.123+01:00"), 1707734280123d},
+ {ZonedDateTime.parse("2024-02-12T11:38:00.1234+01:00"), 1707734280123.4d},
+ {ZonedDateTime.parse("2024-02-12T11:38:00.123937482+01:00"), 1707734280123.937482d},
});
- // Left off here (need to fix ZoneDateTime and Timestamp)
TEST_DB.put(pair(Date.class, Double.class), new Object[][]{
{new Date(Long.MIN_VALUE), (double) Long.MIN_VALUE, true},
{new Date(Integer.MIN_VALUE), (double) Integer.MIN_VALUE, true},
{new Date(0), 0d, true},
+ {new Date(now), (double) now, true},
{new Date(Integer.MAX_VALUE), (double) Integer.MAX_VALUE, true},
{new Date(Long.MAX_VALUE), (double) Long.MAX_VALUE, true},
});
@@ -1125,15 +1115,31 @@ public ZoneId getZoneId() {
{new java.sql.Date(Long.MIN_VALUE), (double) Long.MIN_VALUE, true},
{new java.sql.Date(Integer.MIN_VALUE), (double) Integer.MIN_VALUE, true},
{new java.sql.Date(0), 0d, true},
+ {new java.sql.Date(now), (double) now, true},
{new java.sql.Date(Integer.MAX_VALUE), (double) Integer.MAX_VALUE, true},
{new java.sql.Date(Long.MAX_VALUE), (double) Long.MAX_VALUE, true},
});
+
+
+
+ TEST_DB.put(pair(Timestamp.class, BigDecimal.class), new Object[][] {
+ { Timestamp.from(Instant.parse("2024-02-18T06:31:55.987654321+00:00")), new BigDecimal("1708237915987.654321"), true },
+ { Timestamp.from(Instant.parse("2024-02-18T06:31:55.123456789+00:00")), new BigDecimal("1708237915123.456789"), true },
+ });
+ TEST_DB.put(pair(BigDecimal.class, Timestamp.class), new Object[][] {
+ { new BigDecimal("1708237915987.654321"), Timestamp.from(Instant.parse("2024-02-18T06:31:55.987654321+00:00")), true },
+ { new BigDecimal("1708237915123.456789"), Timestamp.from(Instant.parse("2024-02-18T06:31:55.123456789+00:00")), true },
+ });
+
+
+
TEST_DB.put(pair(Timestamp.class, Double.class), new Object[][]{
- {new Timestamp(Long.MIN_VALUE), (double) Long.MIN_VALUE, true},
- {new Timestamp(Integer.MIN_VALUE), (double) Integer.MIN_VALUE, true},
+ {new Timestamp(Long.MIN_VALUE), (double) Long.MIN_VALUE},
+ {new Timestamp(Integer.MIN_VALUE), (double) Integer.MIN_VALUE},
{new Timestamp(0), 0d, true},
- {new Timestamp(Integer.MAX_VALUE), (double) Integer.MAX_VALUE, true},
- {new Timestamp(Long.MAX_VALUE), (double) Long.MAX_VALUE, true},
+ {new Timestamp(now), (double) now},
+ {new Timestamp(Integer.MAX_VALUE), (double) Integer.MAX_VALUE},
+ {new Timestamp(Long.MAX_VALUE), (double) Long.MAX_VALUE},
});
TEST_DB.put(pair(AtomicBoolean.class, Double.class), new Object[][]{
{new AtomicBoolean(true), 1d},