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},