diff --git a/src/main/java/com/cedarsoftware/util/convert/BigDecimalConversions.java b/src/main/java/com/cedarsoftware/util/convert/BigDecimalConversions.java index 6e4c1f37..e4e11f36 100644 --- a/src/main/java/com/cedarsoftware/util/convert/BigDecimalConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/BigDecimalConversions.java @@ -26,6 +26,8 @@ * limitations under the License. */ final class BigDecimalConversions { + private BigDecimalConversions() { } + static Instant toInstant(Object from, Converter converter) { BigDecimal time = (BigDecimal) from; long seconds = time.longValue() / 1000; diff --git a/src/main/java/com/cedarsoftware/util/convert/BigIntegerConversions.java b/src/main/java/com/cedarsoftware/util/convert/BigIntegerConversions.java index 4743d49a..c5e53947 100644 --- a/src/main/java/com/cedarsoftware/util/convert/BigIntegerConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/BigIntegerConversions.java @@ -2,6 +2,9 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.sql.Timestamp; +import java.time.Duration; +import java.time.Instant; import java.util.UUID; /** @@ -22,6 +25,11 @@ * limitations under the License. */ final class BigIntegerConversions { + static final BigInteger MILLION = BigInteger.valueOf(1_000_000); + static final BigInteger BILLION = BigInteger.valueOf(1_000_000_000); + + private BigIntegerConversions() { } + static BigDecimal toBigDecimal(Object from, Converter converter) { return new BigDecimal((BigInteger)from); } @@ -52,4 +60,41 @@ static UUID toUUID(Object from, Converter converter) { // Create UUID from string return UUID.fromString(uuidString); } + + /** + * Epoch nanos to Timestamp + */ + static Timestamp toTimestamp(Object from, Converter converter) { + BigInteger nanoseconds = (BigInteger) from; + Duration duration = toDuration(nanoseconds, converter); + Instant epoch = Instant.EPOCH; + + // Add the duration to the epoch + Instant timestampInstant = epoch.plus(duration); + + // Convert Instant to Timestamp + return Timestamp.from(timestampInstant); + } + + /** + * Epoch nanos to Instant + */ + static Instant toInstant(Object from, Converter converter) { + BigInteger nanoseconds = (BigInteger) from; + BigInteger[] secondsAndNanos = nanoseconds.divideAndRemainder(BILLION); + long seconds = secondsAndNanos[0].longValue(); // Total seconds + int nanos = secondsAndNanos[1].intValue(); // Nanoseconds part + return Instant.ofEpochSecond(seconds, nanos); + } + + /** + * Epoch nanos to Duration + */ + static Duration toDuration(Object from, Converter converter) { + BigInteger nanoseconds = (BigInteger) from; + BigInteger[] secondsAndNanos = nanoseconds.divideAndRemainder(BILLION); + long seconds = secondsAndNanos[0].longValue(); + int nanos = secondsAndNanos[1].intValue(); + return Duration.ofSeconds(seconds, nanos); + } } diff --git a/src/main/java/com/cedarsoftware/util/convert/Converter.java b/src/main/java/com/cedarsoftware/util/convert/Converter.java index 2a26234b..9da07f91 100644 --- a/src/main/java/com/cedarsoftware/util/convert/Converter.java +++ b/src/main/java/com/cedarsoftware/util/convert/Converter.java @@ -299,7 +299,8 @@ private static void buildFactoryConversions() { CONVERSION_DB.put(pair(AtomicLong.class, BigInteger.class), NumberConversions::integerTypeToBigInteger); CONVERSION_DB.put(pair(Date.class, BigInteger.class), DateConversions::toBigInteger); CONVERSION_DB.put(pair(java.sql.Date.class, BigInteger.class), DateConversions::toBigInteger); - CONVERSION_DB.put(pair(Timestamp.class, BigInteger.class), DateConversions::toBigInteger); + CONVERSION_DB.put(pair(Timestamp.class, BigInteger.class), TimestampConversions::toBigInteger); + CONVERSION_DB.put(pair(Duration.class, BigInteger.class), DurationConversions::toBigInteger); CONVERSION_DB.put(pair(Instant.class, BigInteger.class), InstantConversions::toBigInteger); CONVERSION_DB.put(pair(LocalDate.class, BigInteger.class), LocalDateConversions::toBigInteger); CONVERSION_DB.put(pair(LocalDateTime.class, BigInteger.class), LocalDateTimeConversions::toBigInteger); @@ -467,13 +468,14 @@ private static void buildFactoryConversions() { 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), DoubleConversions::toTimestamp); - CONVERSION_DB.put(pair(BigInteger.class, Timestamp.class), NumberConversions::toTimestamp); + CONVERSION_DB.put(pair(BigInteger.class, Timestamp.class), BigIntegerConversions::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); CONVERSION_DB.put(pair(java.sql.Date.class, Timestamp.class), DateConversions::toTimestamp); CONVERSION_DB.put(pair(Date.class, Timestamp.class), DateConversions::toTimestamp); + CONVERSION_DB.put(pair(Duration.class, Timestamp.class), DurationConversions::toTimestamp); CONVERSION_DB.put(pair(Instant.class,Timestamp.class), InstantConversions::toTimestamp); CONVERSION_DB.put(pair(LocalDate.class, Timestamp.class), LocalDateConversions::toTimestamp); CONVERSION_DB.put(pair(LocalDateTime.class, Timestamp.class), LocalDateTimeConversions::toTimestamp); @@ -718,6 +720,8 @@ private static void buildFactoryConversions() { // Duration conversions supported CONVERSION_DB.put(pair(Void.class, Duration.class), VoidConversions::toNull); CONVERSION_DB.put(pair(Duration.class, Duration.class), Converter::identity); + CONVERSION_DB.put(pair(BigInteger.class, Duration.class), BigIntegerConversions::toDuration); + CONVERSION_DB.put(pair(Timestamp.class, Duration.class), TimestampConversions::toDuration); CONVERSION_DB.put(pair(String.class, Duration.class), StringConversions::toDuration); CONVERSION_DB.put(pair(Map.class, Duration.class), MapConversions::toDuration); @@ -729,7 +733,7 @@ private static void buildFactoryConversions() { 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), DoubleConversions::toInstant); - CONVERSION_DB.put(pair(BigInteger.class, Instant.class), NumberConversions::toInstant); + CONVERSION_DB.put(pair(BigInteger.class, Instant.class), BigIntegerConversions::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); diff --git a/src/main/java/com/cedarsoftware/util/convert/DurationConversions.java b/src/main/java/com/cedarsoftware/util/convert/DurationConversions.java index fecb7025..8fa7fc29 100644 --- a/src/main/java/com/cedarsoftware/util/convert/DurationConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/DurationConversions.java @@ -1,6 +1,9 @@ package com.cedarsoftware.util.convert; +import java.math.BigInteger; +import java.sql.Timestamp; import java.time.Duration; +import java.time.Instant; import java.util.Map; import com.cedarsoftware.util.CompactLinkedMap; @@ -34,4 +37,20 @@ static Map toMap(Object from, Converter converter) { target.put("nanos", nanos); return target; } + + static BigInteger toBigInteger(Object from, Converter converter) { + Duration duration = (Duration) from; + BigInteger seconds = BigInteger.valueOf(duration.getSeconds()); + BigInteger nanos = BigInteger.valueOf(duration.getNano()); + + // Convert seconds to nanoseconds and add the nanosecond part + return seconds.multiply(BigIntegerConversions.BILLION).add(nanos); + } + + static Timestamp toTimestamp(Object from, Converter converter) { + Duration duration = (Duration) from; + Instant epoch = Instant.EPOCH; + Instant timeAfterDuration = epoch.plus(duration); + return Timestamp.from(timeAfterDuration); + } } diff --git a/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java b/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java index 10651159..5acaca8c 100644 --- a/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java @@ -88,7 +88,13 @@ static Calendar toCalendar(Object from, Converter converter) { } static BigInteger toBigInteger(Object from, Converter converter) { - return BigInteger.valueOf(toLong(from, converter)); + Instant instant = (Instant) from; + // Get seconds and nanoseconds from the Instant + long seconds = instant.getEpochSecond(); + int nanoseconds = instant.getNano(); + + // Convert the entire time to nanoseconds + return BigInteger.valueOf(seconds).multiply(BigIntegerConversions.BILLION).add(BigInteger.valueOf(nanoseconds)); } static BigDecimal toBigDecimal(Object from, Converter converter) { diff --git a/src/main/java/com/cedarsoftware/util/convert/TimestampConversions.java b/src/main/java/com/cedarsoftware/util/convert/TimestampConversions.java index 271ad151..387f375c 100644 --- a/src/main/java/com/cedarsoftware/util/convert/TimestampConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/TimestampConversions.java @@ -1,7 +1,10 @@ package com.cedarsoftware.util.convert; import java.math.BigDecimal; +import java.math.BigInteger; import java.sql.Timestamp; +import java.time.Duration; +import java.time.Instant; /** * @author John DeRegnaucourt (jdereg@gmail.com) @@ -21,6 +24,8 @@ * limitations under the License. */ final class TimestampConversions { + private static final BigInteger MILLION = BigInteger.valueOf(1_000_000); + private TimestampConversions() {} static double toDouble(Object from, Converter converter) { @@ -42,4 +47,16 @@ static BigDecimal toBigDecimal(Object from, Converter converter) { // Convert time to fractional milliseconds return BigDecimal.valueOf(epochMillis).add(BigDecimal.valueOf(nanoPart, 6)); // Dividing by 1_000_000 with scale 6 } + + static BigInteger toBigInteger(Object from, Converter converter) { + Duration duration = toDuration(from, converter); + return DurationConversions.toBigInteger(duration, converter); + } + + static Duration toDuration(Object from, Converter converter) { + Timestamp timestamp = (Timestamp) from; + Instant epoch = Instant.EPOCH; + Instant timestampInstant = timestamp.toInstant(); + return Duration.between(epoch, timestampInstant); + } } diff --git a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java index bdd63a95..5482aa18 100644 --- a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java +++ b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java @@ -1060,47 +1060,29 @@ public ZoneId getZoneId() { {(char) 1, 1d}, {(char) 0, 0d}, }); - TEST_DB.put(pair(Instant.class, Double.class), new Object[][]{ - {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(Instant.class, Double.class), new Object[][]{ // JDK 1.8 cannot handle the format +01:00 in Instant.parse(). JDK11+ handle it fine. + {ZonedDateTime.parse("2024-02-12T11:38:00+01:00").toInstant(), 1707734280000d, true}, + {ZonedDateTime.parse("2024-02-12T11:38:00.123+01:00").toInstant(), 1707734280123d, true}, + {ZonedDateTime.parse("2024-02-12T11:38:00.1234+01:00").toInstant(), 1707734280123.4d}, // fractional milliseconds (nano support) - no reverse because of IEEE-754 limitations + {ZonedDateTime.parse("2024-02-12T11:38:00.1234+01:00").toInstant(), 1.7077342801234E12}, // 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 }); 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, true}, // Epoch millis in Tokyo timezone (at start of day - no time) + {ZonedDateTime.parse("2024-02-12T11:38:00+01:00").withZoneSameInstant(TOKYO_Z).toLocalDate(), 1.7076636E12, true}, // Epoch millis in Tokyo timezone (at start of day - no time) + {ZonedDateTime.parse("2024-02-12T11:38:00.123456789+01:00").withZoneSameInstant(TOKYO_Z).toLocalDate(), 1.7076636E12}, // Only to start of day resolution }); 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, 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, 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(); - }, 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 (no reverse - IEEE-754 limitations) + {ZonedDateTime.parse("2024-02-12T11:38:00+01:00").withZoneSameInstant(TOKYO_Z).toLocalDateTime(), 1.70773428E12, true}, // Epoch millis in Tokyo timezone + {ZonedDateTime.parse("2024-02-12T11:38:00.123+01:00").withZoneSameInstant(TOKYO_Z).toLocalDateTime(), 1.707734280123E12, true}, // Epoch millis in Tokyo timezone + {ZonedDateTime.parse("2024-02-12T11:38:00.1239+01:00").withZoneSameInstant(TOKYO_Z).toLocalDateTime(), 1707734280123.9d}, // Epoch millis in Tokyo timezone (no reverse - IEEE-754 limitations) + {ZonedDateTime.parse("2024-02-12T11:38:00.123937482+01:00").withZoneSameInstant(TOKYO_Z).toLocalDateTime(), 1707734280123.937482d}, // Epoch millis in Tokyo timezone (no reverse - IEEE-754 limitations) }); 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.123456789+01:00"), 1707734280123.456789d}, {ZonedDateTime.parse("2024-02-12T11:38:00.123937482+01:00"), 1707734280123.937482d}, }); TEST_DB.put(pair(Date.class, Double.class), new Object[][]{ @@ -1108,6 +1090,8 @@ public ZoneId getZoneId() { {new Date(Integer.MIN_VALUE), (double) Integer.MIN_VALUE, true}, {new Date(0), 0d, true}, {new Date(now), (double) now, true}, + {Date.from(ZonedDateTime.parse("2024-02-18T06:31:55.987654321+00:00").toInstant()), 1708237915987.0d, true }, // Date only has millisecond resolution + {Date.from(ZonedDateTime.parse("2024-02-18T06:31:55.123456789+00:00").toInstant()), 1708237915123d, true }, // Date only has millisecond resolution {new Date(Integer.MAX_VALUE), (double) Integer.MAX_VALUE, true}, {new Date(Long.MAX_VALUE), (double) Long.MAX_VALUE, true}, }); @@ -1116,31 +1100,37 @@ public ZoneId getZoneId() { {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(ZonedDateTime.parse("2024-02-18T06:31:55.987654321+00:00").toInstant().toEpochMilli()), 1708237915987.0d, true }, // java.sql.Date only has millisecond resolution + {new java.sql.Date(ZonedDateTime.parse("2024-02-18T06:31:55.123456789+00:00").toInstant().toEpochMilli()), 1708237915123d, true }, // java.sql.Date only has millisecond resolution {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}, {new Timestamp(Integer.MIN_VALUE), (double) Integer.MIN_VALUE}, {new Timestamp(0), 0d, true}, {new Timestamp(now), (double) now}, + { Timestamp.from(ZonedDateTime.parse("2024-02-18T06:31:55.987654321+00:00").toInstant()), 1708237915987.654321d }, // no reverse due to IEEE-754 limitations + { Timestamp.from(ZonedDateTime.parse("2024-02-18T06:31:55.123456789+00:00").toInstant()), 1708237915123.456789d }, // no reverse due to IEEE-754 limitations {new Timestamp(Integer.MAX_VALUE), (double) Integer.MAX_VALUE}, {new Timestamp(Long.MAX_VALUE), (double) Long.MAX_VALUE}, }); + TEST_DB.put(pair(Calendar.class, Double.class), new Object[][]{ + {(Supplier) () -> { + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.setTimeZone(TOKYO_TZ); + cal.set(2024, Calendar.FEBRUARY, 12, 11, 38, 0); + return cal; + }, 1707705480000d}, + {(Supplier) () -> { + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.setTimeZone(TOKYO_TZ); + cal.setTimeInMillis(now); // Calendar maintains time to millisecond resolution + return cal; + }, (double)now} + }); TEST_DB.put(pair(AtomicBoolean.class, Double.class), new Object[][]{ {new AtomicBoolean(true), 1d}, {new AtomicBoolean(false), 0d}, @@ -1177,22 +1167,6 @@ public ZoneId getZoneId() { {new BigDecimal("-9007199254740991"), -9007199254740991d}, {new BigDecimal("9007199254740991"), 9007199254740991d}, }); - TEST_DB.put(pair(Calendar.class, Double.class), new Object[][]{ - {(Supplier) () -> { - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.setTimeZone(TOKYO_TZ); - cal.set(2024, Calendar.FEBRUARY, 12, 11, 38, 0); - return cal; - }, 1707705480000d}, - {(Supplier) () -> { - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.setTimeZone(TOKYO_TZ); - cal.setTimeInMillis(now); // Calendar maintains time to millisecond resolution - return cal; - }, (double)now} - }); TEST_DB.put(pair(Number.class, Double.class), new Object[][]{ {2.5f, 2.5d} }); @@ -1576,26 +1550,49 @@ public ZoneId getZoneId() { { new java.sql.Date(Long.MAX_VALUE), BigInteger.valueOf(Long.MAX_VALUE), true }, }); TEST_DB.put(pair(Timestamp.class, BigInteger.class), new Object[][]{ - { new Timestamp(0), BigInteger.valueOf(0), true }, - { new Timestamp(now), BigInteger.valueOf(now), true }, -// { (Supplier) () -> { -// Timestamp ts = new Timestamp(now); -// ts.setNanos(1); -// return ts; -// }, (Supplier) () -> { -// Timestamp ts = new Timestamp(now); -// long milliseconds = ts.getTime(); -// int nanoseconds = ts.getNanos(); -// BigInteger nanos = BigInteger.valueOf(milliseconds).multiply(BigInteger.valueOf(1000000)) -// .add(BigInteger.valueOf(nanoseconds)); -// return nanos; -// } -// }, - { new Timestamp(Long.MIN_VALUE), BigInteger.valueOf(Long.MIN_VALUE), true }, - { new Timestamp(Long.MAX_VALUE), BigInteger.valueOf(Long.MAX_VALUE), true }, + { Timestamp.from(ZonedDateTime.parse("0000-01-01T00:00:00.000000000+00:00").toInstant()), new BigInteger("-62167219200000000000"), true }, + { Timestamp.from(ZonedDateTime.parse("0001-02-18T19:58:01.000000000+00:00").toInstant()), new BigInteger("-62131377719000000000"), true }, + { Timestamp.from(ZonedDateTime.parse("1969-12-31T23:59:59.000000000+00:00").toInstant()), BigInteger.valueOf(-1000000000), true }, + { Timestamp.from(ZonedDateTime.parse("1969-12-31T23:59:59.000000001+00:00").toInstant()), BigInteger.valueOf(-999999999), true }, + { Timestamp.from(ZonedDateTime.parse("1969-12-31T23:59:59.100000000+00:00").toInstant()), BigInteger.valueOf(-900000000), true }, + { Timestamp.from(ZonedDateTime.parse("1969-12-31T23:59:59.900000000+00:00").toInstant()), BigInteger.valueOf(-100000000), true }, + { Timestamp.from(ZonedDateTime.parse("1969-12-31T23:59:59.999999999+00:00").toInstant()), BigInteger.valueOf(-1), true }, + { Timestamp.from(ZonedDateTime.parse("1970-01-01T00:00:00.000000000+00:00").toInstant()), BigInteger.valueOf(0), true }, + { Timestamp.from(ZonedDateTime.parse("1970-01-01T00:00:00.000000001+00:00").toInstant()), BigInteger.valueOf(1), true }, + { Timestamp.from(ZonedDateTime.parse("1970-01-01T00:00:00.100000000+00:00").toInstant()), BigInteger.valueOf(100000000), true }, + { Timestamp.from(ZonedDateTime.parse("1970-01-01T00:00:00.900000000+00:00").toInstant()), BigInteger.valueOf(900000000), true }, + { Timestamp.from(ZonedDateTime.parse("1970-01-01T00:00:00.999999999+00:00").toInstant()), BigInteger.valueOf(999999999), true }, + { Timestamp.from(ZonedDateTime.parse("1970-01-01T00:00:01.000000000+00:00").toInstant()), BigInteger.valueOf(1000000000), true }, + { Timestamp.from(ZonedDateTime.parse("9999-02-18T19:58:01.000000000+00:00").toInstant()), new BigInteger("253374983881000000000"), true }, + }); + TEST_DB.put(pair(Duration.class, BigInteger.class), new Object[][] { + { Duration.ofNanos(-1000000), BigInteger.valueOf(-1000000), true}, + { Duration.ofNanos(-1000), BigInteger.valueOf(-1000), true}, + { Duration.ofNanos(-1), BigInteger.valueOf(-1), true}, + { Duration.ofNanos(0), BigInteger.valueOf(0), true}, + { Duration.ofNanos(1), BigInteger.valueOf(1), true}, + { Duration.ofNanos(1000), BigInteger.valueOf(1000), true}, + { Duration.ofNanos(1000000), BigInteger.valueOf(1000000), true}, + { Duration.ofNanos(Integer.MAX_VALUE), BigInteger.valueOf(Integer.MAX_VALUE), true}, + { Duration.ofNanos(Integer.MIN_VALUE), BigInteger.valueOf(Integer.MIN_VALUE), true}, + { Duration.ofNanos(Long.MAX_VALUE), BigInteger.valueOf(Long.MAX_VALUE), true}, + { Duration.ofNanos(Long.MIN_VALUE), BigInteger.valueOf(Long.MIN_VALUE), true}, }); TEST_DB.put(pair(Instant.class, BigInteger.class), new Object[][]{ - {Instant.ofEpochMilli(now), BigInteger.valueOf(now)}, + { ZonedDateTime.parse("0000-01-01T00:00:00.000000000+00:00").toInstant(), new BigInteger("-62167219200000000000"), true }, + { ZonedDateTime.parse("0001-02-18T19:58:01.000000000+00:00").toInstant(), new BigInteger("-62131377719000000000"), true }, + { ZonedDateTime.parse("1969-12-31T23:59:59.000000000+00:00").toInstant(), BigInteger.valueOf(-1000000000), true }, + { ZonedDateTime.parse("1969-12-31T23:59:59.000000001+00:00").toInstant(), BigInteger.valueOf(-999999999), true }, + { ZonedDateTime.parse("1969-12-31T23:59:59.100000000+00:00").toInstant(), BigInteger.valueOf(-900000000), true }, + { ZonedDateTime.parse("1969-12-31T23:59:59.900000000+00:00").toInstant(), BigInteger.valueOf(-100000000), true }, + { ZonedDateTime.parse("1969-12-31T23:59:59.999999999+00:00").toInstant(), BigInteger.valueOf(-1), true }, + { ZonedDateTime.parse("1970-01-01T00:00:00.000000000+00:00").toInstant(), BigInteger.valueOf(0), true }, + { ZonedDateTime.parse("1970-01-01T00:00:00.000000001+00:00").toInstant(), BigInteger.valueOf(1), true }, + { ZonedDateTime.parse("1970-01-01T00:00:00.100000000+00:00").toInstant(), BigInteger.valueOf(100000000), true }, + { ZonedDateTime.parse("1970-01-01T00:00:00.900000000+00:00").toInstant(), BigInteger.valueOf(900000000), true }, + { ZonedDateTime.parse("1970-01-01T00:00:00.999999999+00:00").toInstant(), BigInteger.valueOf(999999999), true }, + { ZonedDateTime.parse("1970-01-01T00:00:01.000000000+00:00").toInstant(), BigInteger.valueOf(1000000000), true }, + { ZonedDateTime.parse("9999-02-18T19:58:01.000000000+00:00").toInstant(), new BigInteger("253374983881000000000"), true }, }); TEST_DB.put(pair(LocalDate.class, BigInteger.class), new Object[][]{ {(Supplier) () -> { @@ -1655,6 +1652,14 @@ public ZoneId getZoneId() { {Year.of(2024), BigInteger.valueOf(2024)}, }); + ///////////////////////////////////////////////////////////// + // BigDecimal + ///////////////////////////////////////////////////////////// + TEST_DB.put(pair(Timestamp.class, BigDecimal.class), new Object[][] { + { Timestamp.from(ZonedDateTime.parse("2024-02-18T06:31:55.987654321+00:00").toInstant()), new BigDecimal("1708237915987.654321"), true }, + { Timestamp.from(ZonedDateTime.parse("2024-02-18T06:31:55.123456789+00:00").toInstant()), new BigDecimal("1708237915123.456789"), true }, + }); + ///////////////////////////////////////////////////////////// // Instant ///////////////////////////////////////////////////////////// @@ -1665,7 +1670,9 @@ public ZoneId getZoneId() { {"2024-12-31T23:59:59.999999999Z", Instant.parse("2024-12-31T23:59:59.999999999Z")}, }); + ///////////////////////////////////////////////////////////// // MonthDay + ///////////////////////////////////////////////////////////// TEST_DB.put(pair(Void.class, MonthDay.class), new Object[][]{ {null, null}, }); @@ -1817,6 +1824,29 @@ public ZoneId getZoneId() { {mapOf("zone", mapOf("_v", TOKYO_Z)), TOKYO_Z}, }); + ///////////////////////////////////////////////////////////// + // Timestamp + ///////////////////////////////////////////////////////////// + TEST_DB.put(pair(BigDecimal.class, Timestamp.class), new Object[][] { + { new BigDecimal("1708237915987.654321"), Timestamp.from(ZonedDateTime.parse("2024-02-18T06:31:55.987654321+00:00").toInstant()), true }, + { new BigDecimal("1708237915123.456789"), Timestamp.from(ZonedDateTime.parse("2024-02-18T06:31:55.123456789+00:00").toInstant()), true }, + }); + TEST_DB.put(pair(Duration.class, Timestamp.class), new Object[][] { + { Duration.ofNanos(-1000000001), Timestamp.from(ZonedDateTime.parse("1969-12-31T23:59:58.999999999+00:00").toInstant()), true}, + { Duration.ofNanos(-1000000000), Timestamp.from(ZonedDateTime.parse("1969-12-31T23:59:59.000000000+00:00").toInstant()), true}, + { Duration.ofNanos(-999999999), Timestamp.from(ZonedDateTime.parse("1969-12-31T23:59:59.000000001+00:00").toInstant()), true}, + { Duration.ofNanos(-1), Timestamp.from(ZonedDateTime.parse("1969-12-31T23:59:59.999999999+00:00").toInstant()), true}, + { Duration.ofNanos(0), Timestamp.from(ZonedDateTime.parse("1970-01-01T00:00:00.000000000+00:00").toInstant()), true}, + { Duration.ofNanos(1), Timestamp.from(ZonedDateTime.parse("1970-01-01T00:00:00.000000001+00:00").toInstant()), true}, + { Duration.ofNanos(999999999), Timestamp.from(ZonedDateTime.parse("1970-01-01T00:00:00.999999999+00:00").toInstant()), true}, + { Duration.ofNanos(1000000000), Timestamp.from(ZonedDateTime.parse("1970-01-01T00:00:01.000000000+00:00").toInstant()), true}, + { Duration.ofNanos(1000000001), Timestamp.from(ZonedDateTime.parse("1970-01-01T00:00:01.000000001+00:00").toInstant()), true}, + { Duration.ofNanos(686629800000000001L), Timestamp.from(ZonedDateTime.parse("1991-10-05T02:30:00.000000001Z").toInstant()), true }, + { Duration.ofNanos(1199145600000000001L), Timestamp.from(ZonedDateTime.parse("2008-01-01T00:00:00.000000001Z").toInstant()), true }, + { Duration.ofNanos(1708255140987654321L), Timestamp.from(ZonedDateTime.parse("2024-02-18T11:19:00.987654321Z").toInstant()), true }, + { Duration.ofNanos(2682374400000000001L), Timestamp.from(ZonedDateTime.parse("2055-01-01T00:00:00.000000001Z").toInstant()), true }, + }); + ///////////////////////////////////////////////////////////// // ZoneOffset ///////////////////////////////////////////////////////////// diff --git a/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java b/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java index e564da77..e92f4bc5 100644 --- a/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java +++ b/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java @@ -856,7 +856,23 @@ private static Stream epochMillis_withLocalDateTimeInformation() { Arguments.of(946702799959L, NEW_YORK, LDT_MILLENNIUM_NY), Arguments.of(946702799959L, CHICAGO, LDT_MILLENNIUM_CHICAGO), Arguments.of(946702799959L, LOS_ANGELES, LDT_MILLENNIUM_LA) + ); + } + private static Stream epochNanos_withLocalDateTimeInformation() { + return Stream.of( + Arguments.of(1687622249729000000L, TOKYO, LDT_2023_TOKYO), + Arguments.of(1687622249729000000L, PARIS, LDT_2023_PARIS), + Arguments.of(1687622249729000000L, GMT, LDT_2023_GMT), + Arguments.of(1687622249729000000L, NEW_YORK, LDT_2023_NY), + Arguments.of(1687622249729000000L, CHICAGO, LDT_2023_CHICAGO), + Arguments.of(1687622249729000000L, LOS_ANGELES, LDT_2023_LA), + Arguments.of(946702799959000000L, TOKYO, LDT_MILLENNIUM_TOKYO), + Arguments.of(946702799959000000L, PARIS, LDT_MILLENNIUM_PARIS), + Arguments.of(946702799959000000L, GMT, LDT_MILLENNIUM_GMT), + Arguments.of(946702799959000000L, NEW_YORK, LDT_MILLENNIUM_NY), + Arguments.of(946702799959000000L, CHICAGO, LDT_MILLENNIUM_CHICAGO), + Arguments.of(946702799959000000L, LOS_ANGELES, LDT_MILLENNIUM_LA) ); } @@ -1308,17 +1324,7 @@ void testInstantToCalendar(long epochMilli, ZoneId zoneId, LocalDateTime expecte assertThat(actual.getTime().getTime()).isEqualTo(epochMilli); assertThat(actual.getTimeZone()).isEqualTo(TimeZone.getTimeZone(zoneId)); } - - @ParameterizedTest - @MethodSource("epochMillis_withLocalDateTimeInformation") - void testInstantToBigInteger(long epochMilli, ZoneId zoneId, LocalDateTime expected) - { - Instant instant = Instant.ofEpochMilli(epochMilli); - Converter converter = new Converter(createCustomZones(zoneId)); - BigInteger actual = converter.convert(instant, BigInteger.class); - assertThat(actual.longValue()).isEqualTo(epochMilli); - } - + @ParameterizedTest @MethodSource("epochMillis_withLocalDateTimeInformation") void testInstantToBigDecimal(long epochMilli, ZoneId zoneId, LocalDateTime expected) @@ -2280,7 +2286,7 @@ void testDateFromOthers() assert sqlDate.getTime() == now; // BigInteger to Timestamp - bigInt = new BigInteger("" + now); + bigInt = new BigInteger("" + now * 1000000L); tstamp = this.converter.convert(bigInt, Timestamp.class); assert tstamp.getTime() == now;