From b6bc1c01524ddfb59c865ea1343b20a56c16e261 Mon Sep 17 00:00:00 2001 From: Ken Partlow Date: Thu, 25 Jan 2024 13:27:15 -0500 Subject: [PATCH] Updated LocalDateConversions --- .../util/convert/CalendarConversions.java | 5 + .../cedarsoftware/util/convert/Converter.java | 36 +- .../util/convert/DateConversions.java | 5 + .../util/convert/InstantConversions.java | 4 + .../util/convert/LocalDateConversions.java | 13 +- .../convert/LocalDateTimeConversions.java | 18 +- .../util/convert/NumberConversions.java | 19 +- .../util/convert/ConverterTest.java | 348 ++++++++++++++---- 8 files changed, 372 insertions(+), 76 deletions(-) diff --git a/src/main/java/com/cedarsoftware/util/convert/CalendarConversions.java b/src/main/java/com/cedarsoftware/util/convert/CalendarConversions.java index bef02a737..5cc42f647 100644 --- a/src/main/java/com/cedarsoftware/util/convert/CalendarConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/CalendarConversions.java @@ -6,6 +6,7 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; @@ -71,6 +72,10 @@ static LocalDate toLocalDate(Object fromInstance, Converter converter, Converter return toZonedDateTime(fromInstance, options).toLocalDate(); } + static LocalTime toLocalTime(Object fromInstance, Converter converter, ConverterOptions options) { + return toZonedDateTime(fromInstance, options).toLocalTime(); + } + static BigDecimal toBigDecimal(Object fromInstance, Converter converter, ConverterOptions options) { return BigDecimal.valueOf(toLong(fromInstance)); } diff --git a/src/main/java/com/cedarsoftware/util/convert/Converter.java b/src/main/java/com/cedarsoftware/util/convert/Converter.java index cd6aef132..533bc69bd 100644 --- a/src/main/java/com/cedarsoftware/util/convert/Converter.java +++ b/src/main/java/com/cedarsoftware/util/convert/Converter.java @@ -36,6 +36,7 @@ import com.cedarsoftware.util.ClassUtilities; import com.cedarsoftware.util.CollectionUtilities; import com.cedarsoftware.util.DateUtilities; +import com.cedarsoftware.util.StringUtilities; /** * Instance conversion utility. Convert from primitive to other primitives, plus support for Number, Date, @@ -229,7 +230,7 @@ private static void buildFactoryConversions() { DEFAULT_FACTORY.put(pair(Double.class, Double.class), Converter::identity); DEFAULT_FACTORY.put(pair(Boolean.class, Double.class), BooleanConversions::toDouble); DEFAULT_FACTORY.put(pair(Character.class, Double.class), CharacterConversions::toDouble); - DEFAULT_FACTORY.put(pair(Instant.class, Double.class), InstantConversions::toLong); + DEFAULT_FACTORY.put(pair(Instant.class, Double.class), InstantConversions::toDouble); DEFAULT_FACTORY.put(pair(LocalDate.class, Double.class), LocalDateConversions::toLong); DEFAULT_FACTORY.put(pair(LocalDateTime.class, Double.class), LocalDateTimeConversions::toLong); DEFAULT_FACTORY.put(pair(ZonedDateTime.class, Double.class), ZonedDateTimeConversions::toLong); @@ -607,6 +608,36 @@ private static void buildFactoryConversions() { return date.toInstant().atZone(options.getZoneId()).toLocalDateTime(); }); + DEFAULT_FACTORY.put(pair(Void.class, LocalTime.class), VoidConversions::toNull); + DEFAULT_FACTORY.put(pair(Long.class, LocalTime.class), NumberConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(Double.class, LocalTime.class), NumberConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(BigInteger.class, LocalTime.class), NumberConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(BigDecimal.class, LocalTime.class), NumberConversions::toLocalDateTime); + DEFAULT_FACTORY.put(pair(AtomicLong.class, LocalTime.class), NumberConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(java.sql.Date.class, LocalTime.class), DateConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(Timestamp.class, LocalTime.class), DateConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(Date.class, LocalTime.class), DateConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(Instant.class, LocalTime.class), InstantConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(LocalDateTime.class, LocalTime.class), LocalDateTimeConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(LocalDate.class, LocalTime.class), LocalDateConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(LocalTime.class, LocalTime.class), Converter::identity); + DEFAULT_FACTORY.put(pair(ZonedDateTime.class, LocalTime.class), ZonedDateTimeConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(Calendar.class, LocalTime.class), CalendarConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(Number.class, LocalTime.class), NumberConversions::toLocalTime); + DEFAULT_FACTORY.put(pair(Map.class, LocalTime.class), (fromInstance, converter, options) -> { + Map map = (Map) fromInstance; + return converter.fromValueMap(map, LocalTime.class, null, options); + }); + DEFAULT_FACTORY.put(pair(String.class, LocalTime.class), (fromInstance, converter, options) -> { + String str = StringUtilities.trimToEmpty((String)fromInstance); + Date date = DateUtilities.parseDate(str); + if (date == null) { + return null; + } + return converter.convert(date, LocalTime.class, options); + }); + + // ZonedDateTime conversions supported DEFAULT_FACTORY.put(pair(Void.class, ZonedDateTime.class), VoidConversions::toNull); DEFAULT_FACTORY.put(pair(Long.class, ZonedDateTime.class), NumberConversions::toZonedDateTime); @@ -617,6 +648,7 @@ private static void buildFactoryConversions() { DEFAULT_FACTORY.put(pair(java.sql.Date.class, ZonedDateTime.class), DateConversions::toZonedDateTime); DEFAULT_FACTORY.put(pair(Timestamp.class, ZonedDateTime.class), DateConversions::toZonedDateTime); DEFAULT_FACTORY.put(pair(Date.class, ZonedDateTime.class), DateConversions::toZonedDateTime); + DEFAULT_FACTORY.put(pair(Instant.class, ZonedDateTime.class), InstantConversions::toZonedDateTime); DEFAULT_FACTORY.put(pair(LocalDate.class, ZonedDateTime.class), LocalDateConversions::toZonedDateTime); DEFAULT_FACTORY.put(pair(LocalDateTime.class, ZonedDateTime.class), LocalDateTimeConversions::toZonedDateTime); DEFAULT_FACTORY.put(pair(ZonedDateTime.class, ZonedDateTime.class), Converter::identity); @@ -632,7 +664,7 @@ private static void buildFactoryConversions() { if (date == null) { return null; } - return date.toInstant().atZone(options.getZoneId()); + return converter.convert(date, ZonedDateTime.class, options); }); // UUID conversions supported diff --git a/src/main/java/com/cedarsoftware/util/convert/DateConversions.java b/src/main/java/com/cedarsoftware/util/convert/DateConversions.java index d648dae48..0c7335645 100644 --- a/src/main/java/com/cedarsoftware/util/convert/DateConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/DateConversions.java @@ -6,6 +6,7 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; @@ -88,6 +89,10 @@ static LocalDate toLocalDate(Object fromInstance, Converter converter, Converter return toZonedDateTime(fromInstance, options).toLocalDate(); } + static LocalTime toLocalTime(Object fromInstance, Converter converter, ConverterOptions options) { + return toZonedDateTime(fromInstance, options).toLocalTime(); + } + static BigInteger toBigInteger(Object fromInstance, Converter converter, ConverterOptions options) { return BigInteger.valueOf(toLong(fromInstance)); } diff --git a/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java b/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java index 581d84ac6..06ef11843 100644 --- a/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/InstantConversions.java @@ -62,6 +62,10 @@ static BigDecimal toBigDecimal(Object from, Converter converter, ConverterOption return BigDecimal.valueOf(toLong(from)); } + static ZonedDateTime toZonedDateTime(Object from, Converter converter, ConverterOptions options) { + return toZonedDateTime(from, options); + } + static LocalDateTime toLocalDateTime(Object from, Converter converter, ConverterOptions options) { return toZonedDateTime(from, options).toLocalDateTime(); } diff --git a/src/main/java/com/cedarsoftware/util/convert/LocalDateConversions.java b/src/main/java/com/cedarsoftware/util/convert/LocalDateConversions.java index 5e054c67a..c59310255 100644 --- a/src/main/java/com/cedarsoftware/util/convert/LocalDateConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/LocalDateConversions.java @@ -6,15 +6,17 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; +import java.util.GregorianCalendar; import java.util.concurrent.atomic.AtomicLong; public class LocalDateConversions { private static ZonedDateTime toZonedDateTime(Object fromInstance, ConverterOptions options) { - return ((LocalDate)fromInstance).atStartOfDay(options.getZoneId()); + return ((LocalDate)fromInstance).atStartOfDay(options.getSourceZoneIdForLocalDates()).withZoneSameInstant(options.getZoneId()); } static Instant toInstant(Object fromInstance, ConverterOptions options) { @@ -29,6 +31,10 @@ static LocalDateTime toLocalDateTime(Object fromInstance, Converter converter, C return toZonedDateTime(fromInstance, options).toLocalDateTime(); } + static LocalTime toLocalTime(Object fromInstance, Converter converter, ConverterOptions options) { + return toZonedDateTime(fromInstance, options).toLocalTime(); + } + static ZonedDateTime toZonedDateTime(Object fromInstance, Converter converter, ConverterOptions options) { return toZonedDateTime(fromInstance, options).withZoneSameInstant(options.getZoneId()); } @@ -60,7 +66,10 @@ static Timestamp toTimestamp(Object fromInstance, Converter converter, Converter } static Calendar toCalendar(Object fromInstance, Converter converter, ConverterOptions options) { - return CalendarConversions.create(toLong(fromInstance, options), options); + ZonedDateTime time = toZonedDateTime(fromInstance, options); + GregorianCalendar calendar = new GregorianCalendar(options.getTimeZone()); + calendar.setTimeInMillis(time.toInstant().toEpochMilli()); + return calendar; } static java.sql.Date toSqlDate(Object fromInstance, Converter converter, ConverterOptions options) { diff --git a/src/main/java/com/cedarsoftware/util/convert/LocalDateTimeConversions.java b/src/main/java/com/cedarsoftware/util/convert/LocalDateTimeConversions.java index 86940ae5c..d98a2e0c1 100644 --- a/src/main/java/com/cedarsoftware/util/convert/LocalDateTimeConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/LocalDateTimeConversions.java @@ -4,7 +4,9 @@ import java.math.BigInteger; import java.sql.Timestamp; import java.time.Instant; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; @@ -13,7 +15,7 @@ public class LocalDateTimeConversions { private static ZonedDateTime toZonedDateTime(Object fromInstance, ConverterOptions options) { - return ((LocalDateTime)fromInstance).atZone(options.getSourceZoneIdForLocalDates()); + return ((LocalDateTime)fromInstance).atZone(options.getSourceZoneIdForLocalDates()).withZoneSameInstant(options.getZoneId()); } private static Instant toInstant(Object fromInstance, ConverterOptions options) { @@ -25,7 +27,19 @@ private static long toLong(Object fromInstance, ConverterOptions options) { } static ZonedDateTime toZonedDateTime(Object fromInstance, Converter converter, ConverterOptions options) { - return toZonedDateTime(fromInstance, options).withZoneSameInstant(options.getZoneId()); + return toZonedDateTime(fromInstance, options); + } + + static LocalDateTime toLocalDateTime(Object fromInstance, Converter converter, ConverterOptions options) { + return toZonedDateTime(fromInstance, options).toLocalDateTime(); + } + + static LocalDate toLocalDate(Object fromInstance, Converter converter, ConverterOptions options) { + return toZonedDateTime(fromInstance, options).toLocalDate(); + } + + static LocalTime toLocalTime(Object fromInstance, Converter converter, ConverterOptions options) { + return toZonedDateTime(fromInstance, options).toLocalTime(); } static Instant toInstant(Object fromInstance, Converter converter, ConverterOptions options) { diff --git a/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java b/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java index 22abc99e0..2e1632435 100644 --- a/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java @@ -6,6 +6,7 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; @@ -187,8 +188,10 @@ static Date toDate(Object from, Converter converter, ConverterOptions options) { return new Date(toLong(from)); } + static Instant toInstant(Object from) { return Instant.ofEpochMilli(toLong(from)); } + static Instant toInstant(Object from, Converter converter, ConverterOptions options) { - return Instant.ofEpochMilli(toLong(from)); + return toInstant(from); } static java.sql.Date toSqlDate(Object from, Converter converter, ConverterOptions options) { @@ -204,14 +207,22 @@ static Calendar toCalendar(Object from, Converter converter, ConverterOptions op } static LocalDate toLocalDate(Object from, Converter converter, ConverterOptions options) { - return Instant.ofEpochMilli(toLong(from)).atZone(options.getZoneId()).toLocalDate(); + return toZonedDateTime(from, options).toLocalDate(); } static LocalDateTime toLocalDateTime(Object from, Converter converter, ConverterOptions options) { - return Instant.ofEpochMilli(toLong(from)).atZone(options.getZoneId()).toLocalDateTime(); + return toZonedDateTime(from, options).toLocalDateTime(); + } + + static LocalTime toLocalTime(Object from, Converter converter, ConverterOptions options) { + return toZonedDateTime(from, options).toLocalTime(); + } + + static ZonedDateTime toZonedDateTime(Object from, ConverterOptions options) { + return toInstant(from).atZone(options.getZoneId()); } static ZonedDateTime toZonedDateTime(Object from, Converter converter, ConverterOptions options) { - return Instant.ofEpochMilli(toLong(from)).atZone(options.getZoneId()); + return toZonedDateTime(from, options); } } diff --git a/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java b/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java index 0144f4ccb..4b4be2a1c 100644 --- a/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java +++ b/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java @@ -6,6 +6,7 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Calendar; @@ -22,7 +23,6 @@ import java.util.stream.Stream; import com.cedarsoftware.util.DeepEquals; -import com.cedarsoftware.util.TestUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -32,8 +32,6 @@ import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.NullSource; -import static com.cedarsoftware.util.Converter.localDateTimeToMillis; -import static com.cedarsoftware.util.Converter.localDateToMillis; import static com.cedarsoftware.util.Converter.zonedDateTimeToMillis; import static com.cedarsoftware.util.convert.ConverterTest.fubar.bar; import static com.cedarsoftware.util.convert.ConverterTest.fubar.foo; @@ -66,8 +64,26 @@ class ConverterTest { + private static final LocalDateTime LDT_2023_TOKYO = LocalDateTime.of(2023, 6, 25, 0, 57, 29, 729000000); + private static final LocalDateTime LDT_2023_PARIS = LocalDateTime.of(2023, 6, 24, 17, 57, 29, 729000000); + private static final LocalDateTime LDT_2023_GMT = LocalDateTime.of(2023, 6, 24, 15, 57, 29, 729000000); + private static final LocalDateTime LDT_2023_NY = LocalDateTime.of(2023, 6, 24, 11, 57, 29, 729000000); + private static final LocalDateTime LDT_2023_CHICAGO = LocalDateTime.of(2023, 6, 24, 10, 57, 29, 729000000); + private static final LocalDateTime LDT_2023_LA = LocalDateTime.of(2023, 6, 24, 8, 57, 29, 729000000); + private static final LocalDateTime LDT_MILLENNIUM_TOKYO = LocalDateTime.of(2000, 1, 1, 13, 59, 59, 959000000); + private static final LocalDateTime LDT_MILLENNIUM_PARIS = LocalDateTime.of(2000, 1, 1, 5, 59, 59, 959000000); + private static final LocalDateTime LDT_MILLENNIUM_GMT = LocalDateTime.of(2000, 1, 1, 4, 59, 59, 959000000); + private static final LocalDateTime LDT_MILLENNIUM_NY = LocalDateTime.of(1999, 12, 31, 23, 59, 59, 959000000); + private static final LocalDateTime LDT_MILLENNIUM_CHICAGO = LocalDateTime.of(1999, 12, 31, 22, 59, 59, 959000000); + private static final LocalDateTime LDT_MILLENNIUM_LA = LocalDateTime.of(1999, 12, 31, 20, 59, 59, 959000000); private Converter converter; + + private static final LocalDate LD_MILLINNIUM_NY = LocalDate.of(1999, 12, 31); + private static final LocalDate LD_MILLINNIUM_TOKYO = LocalDate.of(2000, 1, 1); + + private static final LocalDate LD_MILLENNIUM_CHICAGO = LocalDate.of(1999, 12, 31); + enum fubar { foo, bar, baz, quz @@ -700,18 +716,18 @@ void testToBoolean_falseCases(Object input) { private static Stream epochMillis_withLocalDateTimeInformation() { return Stream.of( - Arguments.of(1687622249729L, TOKYO, LocalDateTime.of(2023, 6, 25, 0, 57, 29, 729000000)), - Arguments.of(1687622249729L, PARIS, LocalDateTime.of(2023, 6, 24, 17, 57, 29, 729000000)), - Arguments.of(1687622249729L, GMT, LocalDateTime.of(2023, 6, 24, 15, 57, 29, 729000000)), - Arguments.of(1687622249729L, NEW_YORK, LocalDateTime.of(2023, 6, 24, 11, 57, 29, 729000000)), - Arguments.of(1687622249729L, CHICAGO, LocalDateTime.of(2023, 6, 24, 10, 57, 29, 729000000)), - Arguments.of(1687622249729L, LOS_ANGELES, LocalDateTime.of(2023, 6, 24, 8, 57, 29, 729000000)), - Arguments.of(946702799959L, TOKYO, LocalDateTime.of(2000, 1, 1, 13, 59, 59, 959000000)), - Arguments.of(946702799959L, PARIS, LocalDateTime.of(2000, 1, 1, 5, 59, 59, 959000000)), - Arguments.of(946702799959L, GMT, LocalDateTime.of(2000, 1, 1, 4, 59, 59, 959000000)), - Arguments.of(946702799959L, NEW_YORK, LocalDateTime.of(1999, 12, 31, 23, 59, 59, 959000000)), - Arguments.of(946702799959L, CHICAGO, LocalDateTime.of(1999, 12, 31, 22, 59, 59, 959000000)), - Arguments.of(946702799959L, LOS_ANGELES, LocalDateTime.of(1999, 12, 31, 20, 59, 59, 959000000)) + Arguments.of(1687622249729L, TOKYO, LDT_2023_TOKYO), + Arguments.of(1687622249729L, PARIS, LDT_2023_PARIS), + Arguments.of(1687622249729L, GMT, LDT_2023_GMT), + Arguments.of(1687622249729L, NEW_YORK, LDT_2023_NY), + Arguments.of(1687622249729L, CHICAGO, LDT_2023_CHICAGO), + Arguments.of(1687622249729L, LOS_ANGELES, LDT_2023_LA), + Arguments.of(946702799959L, TOKYO, LDT_MILLENNIUM_TOKYO), + Arguments.of(946702799959L, PARIS, LDT_MILLENNIUM_PARIS), + Arguments.of(946702799959L, GMT, LDT_MILLENNIUM_GMT), + Arguments.of(946702799959L, NEW_YORK, LDT_MILLENNIUM_NY), + Arguments.of(946702799959L, CHICAGO, LDT_MILLENNIUM_CHICAGO), + Arguments.of(946702799959L, LOS_ANGELES, LDT_MILLENNIUM_LA) ); } @@ -727,6 +743,7 @@ void testCalendarToLocalDateTime(long epochMilli, ZoneId zoneId, LocalDateTime e assertThat(localDateTime).isEqualTo(expected); } + @ParameterizedTest @MethodSource("epochMillis_withLocalDateTimeInformation") void testCalendarToLocalDateTime_whenCalendarTimeZoneMatches(long epochMilli, ZoneId zoneId, LocalDateTime expected) { @@ -734,64 +751,78 @@ void testCalendarToLocalDateTime_whenCalendarTimeZoneMatches(long epochMilli, Zo calendar.setTimeInMillis(epochMilli); LocalDateTime localDateTime = this.converter.convert(calendar, LocalDateTime.class, createConvertOptions(zoneId, zoneId)); + assertThat(localDateTime).isEqualTo(expected); } - @Test - void testCalendarToLocalDateTime_whenCalendarTimeZoneDoesNotMatch() { + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testCalendarToLocalDateTime_whenCalendarTimeZoneDoesNotMatch(long epochMilli, ZoneId zoneId, LocalDateTime expected) { Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(NEW_YORK)); - calendar.setTimeInMillis(1687622249729L); + calendar.setTimeInMillis(epochMilli); - LocalDateTime localDateTime = this.converter.convert(calendar, LocalDateTime.class, createConvertOptions(NEW_YORK, TOKYO)); + LocalDateTime localDateTime = this.converter.convert(calendar, LocalDateTime.class, createConvertOptions(NEW_YORK, zoneId)); - System.out.println(localDateTime); + assertThat(localDateTime).isEqualTo(expected); + } - assertThat(localDateTime) - .hasYear(2023) - .hasMonthValue(6) - .hasDayOfMonth(25) - .hasHour(0) - .hasMinute(57) - .hasSecond(29) - .hasNano(729000000); + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testCalendar_roundTrip(long epochMilli, ZoneId zoneId, LocalDateTime expected) { + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(zoneId)); + calendar.setTimeInMillis(epochMilli); + + assertThat(calendar.get(Calendar.YEAR)).isEqualTo(expected.getYear()); + assertThat(calendar.get(Calendar.MONTH)).isEqualTo(expected.getMonthValue()-1); + assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(expected.getDayOfMonth()); + assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(expected.getHour()); + assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(expected.getMinute()); + assertThat(calendar.get(Calendar.SECOND)).isEqualTo(expected.getSecond()); + assertThat(calendar.getTimeInMillis()).isEqualTo(epochMilli); + + + LocalDateTime localDateTime = this.converter.convert(calendar, LocalDateTime.class, createConvertOptions(zoneId, TOKYO)); + + Calendar actual = this.converter.convert(localDateTime, Calendar.class, createConvertOptions(TOKYO, zoneId)); + + assertThat(actual.get(Calendar.YEAR)).isEqualTo(expected.getYear()); + assertThat(actual.get(Calendar.MONTH)).isEqualTo(expected.getMonthValue()-1); + assertThat(actual.get(Calendar.DAY_OF_MONTH)).isEqualTo(expected.getDayOfMonth()); + assertThat(actual.get(Calendar.HOUR_OF_DAY)).isEqualTo(expected.getHour()); + assertThat(actual.get(Calendar.MINUTE)).isEqualTo(expected.getMinute()); + assertThat(actual.get(Calendar.SECOND)).isEqualTo(expected.getSecond()); + assertThat(actual.getTimeInMillis()).isEqualTo(epochMilli); } - @Test - void testCalendar_roundTrip() { - // Create LocalDateTime as CHICAGO TIME. - GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone(CHICAGO)); - calendar.setTimeInMillis(1687622249729L); + private static Stream roundTrip_localDates() { + return Stream.of( + Arguments.of(946652400000L, TOKYO, LD_MILLINNIUM_TOKYO), + Arguments.of(946652400000L, NEW_YORK, LD_MILLINNIUM_NY), + Arguments.of(946652400000L, CHICAGO, LD_MILLENNIUM_CHICAGO) + ); + } + @ParameterizedTest + @MethodSource("roundTrip_localDates") + void testCalendar_roundTrip_withLocalDate(long epochMilli, ZoneId zoneId, LocalDate expected) { + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(zoneId)); + calendar.setTimeInMillis(epochMilli); - assertThat(calendar.get(Calendar.MONTH)).isEqualTo(5); - assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(24); - assertThat(calendar.get(Calendar.YEAR)).isEqualTo(2023); - assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(10); - assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(57); - assertThat(calendar.getTimeInMillis()).isEqualTo(1687622249729L); + assertThat(calendar.get(Calendar.YEAR)).isEqualTo(expected.getYear()); + assertThat(calendar.get(Calendar.MONTH)).isEqualTo(expected.getMonthValue()-1); + assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(expected.getDayOfMonth()); + assertThat(calendar.getTimeInMillis()).isEqualTo(epochMilli); - // Convert calendar calendar to TOKYO LocalDateTime - LocalDateTime localDateTime = this.converter.convert(calendar, LocalDateTime.class, createConvertOptions(CHICAGO, TOKYO)); - assertThat(localDateTime) - .hasYear(2023) - .hasMonthValue(6) - .hasDayOfMonth(25) - .hasHour(0) - .hasMinute(57) - .hasSecond(29) - .hasNano(729000000); + LocalDate localDate = this.converter.convert(calendar, LocalDate.class, createConvertOptions(zoneId, TOKYO)); - // Convert Tokyo local date time to CHICAGO Calendar - // We don't know the source ZoneId we are trying to convert. - Calendar actual = this.converter.convert(localDateTime, Calendar.class, createConvertOptions(TOKYO, CHICAGO)); + Calendar actual = this.converter.convert(localDate, Calendar.class, createConvertOptions(TOKYO, zoneId)); - assertThat(actual.get(Calendar.MONTH)).isEqualTo(5); - assertThat(actual.get(Calendar.DAY_OF_MONTH)).isEqualTo(24); - assertThat(actual.get(Calendar.YEAR)).isEqualTo(2023); - assertThat(actual.get(Calendar.HOUR_OF_DAY)).isEqualTo(10); - assertThat(actual.get(Calendar.MINUTE)).isEqualTo(57); - assertThat(actual.getTimeInMillis()).isEqualTo(1687622249729L); + assertThat(actual.get(Calendar.YEAR)).isEqualTo(expected.getYear()); + assertThat(actual.get(Calendar.MONTH)).isEqualTo(expected.getMonthValue()-1); + assertThat(actual.get(Calendar.DAY_OF_MONTH)).isEqualTo(expected.getDayOfMonth()); + + assertThat(actual.getTimeInMillis()).isEqualTo(epochMilli); } @ParameterizedTest @@ -875,6 +906,24 @@ void testDateToLocalDateTime(long epochMilli, ZoneId zoneId, LocalDateTime expec assertThat(localDateTime).isEqualTo(expected); } + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testDateToZonedDateTime(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Date date = new Date(epochMilli); + ZonedDateTime zonedDateTime = this.converter.convert(date, ZonedDateTime.class, createConvertOptions(null, zoneId)); + assertThat(zonedDateTime.toLocalDateTime()).isEqualTo(expected); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testDateToInstant(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Date date = new Date(epochMilli); + Instant actual = this.converter.convert(date, Instant.class, createConvertOptions(null, zoneId)); + assertThat(actual.toEpochMilli()).isEqualTo(epochMilli); + } + @ParameterizedTest @MethodSource("epochMillis_withLocalDateTimeInformation") @@ -885,6 +934,117 @@ void testSqlDateToLocalDateTime(long epochMilli, ZoneId zoneId, LocalDateTime ex assertThat(localDateTime).isEqualTo(expected); } + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testInstantToLong(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Instant instant = Instant.ofEpochMilli(epochMilli); + long actual = this.converter.convert(instant, long.class, createConvertOptions(null, zoneId)); + assertThat(actual).isEqualTo(epochMilli); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testInstantToAtomicLong(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Instant instant = Instant.ofEpochMilli(epochMilli); + AtomicLong actual = this.converter.convert(instant, AtomicLong.class, createConvertOptions(null, zoneId)); + assertThat(actual.get()).isEqualTo(epochMilli); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testInstantToFloat(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Instant instant = Instant.ofEpochMilli(epochMilli); + float actual = this.converter.convert(instant, float.class, createConvertOptions(null, zoneId)); + assertThat(actual).isEqualTo((float)epochMilli); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testInstantToDouble(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Instant instant = Instant.ofEpochMilli(epochMilli); + double actual = this.converter.convert(instant, double.class, createConvertOptions(null, zoneId)); + assertThat(actual).isEqualTo((double)epochMilli); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testInstantToTimestamp(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Instant instant = Instant.ofEpochMilli(epochMilli); + Timestamp actual = this.converter.convert(instant, Timestamp.class, createConvertOptions(null, zoneId)); + assertThat(actual.getTime()).isEqualTo(epochMilli); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testInstantToDate(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Instant instant = Instant.ofEpochMilli(epochMilli); + Date actual = this.converter.convert(instant, Date.class, createConvertOptions(null, zoneId)); + assertThat(actual.getTime()).isEqualTo(epochMilli); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testInstantToSqlDate(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Instant instant = Instant.ofEpochMilli(epochMilli); + java.sql.Date actual = this.converter.convert(instant, java.sql.Date.class, createConvertOptions(null, zoneId)); + assertThat(actual.getTime()).isEqualTo(epochMilli); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testInstantToCalendar(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Instant instant = Instant.ofEpochMilli(epochMilli); + Calendar actual = this.converter.convert(instant, Calendar.class, createConvertOptions(null, zoneId)); + 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); + BigInteger actual = this.converter.convert(instant, BigInteger.class, createConvertOptions(null, zoneId)); + assertThat(actual.longValue()).isEqualTo(epochMilli); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testInstantToBigDecimal(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Instant instant = Instant.ofEpochMilli(epochMilli); + BigDecimal actual = this.converter.convert(instant, BigDecimal.class, createConvertOptions(null, zoneId)); + assertThat(actual.longValue()).isEqualTo(epochMilli); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testInstantToLocalDate(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Instant instant = Instant.ofEpochMilli(epochMilli); + LocalDate actual = this.converter.convert(instant, LocalDate.class, createConvertOptions(null, zoneId)); + assertThat(actual).isEqualTo(expected.toLocalDate()); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testInstantToLocalTime(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Instant instant = Instant.ofEpochMilli(epochMilli); + LocalTime actual = this.converter.convert(instant, LocalTime.class, createConvertOptions(null, zoneId)); + assertThat(actual).isEqualTo(expected.toLocalTime()); + } + + + @ParameterizedTest @MethodSource("epochMillis_withLocalDateTimeInformation") void testTimestampToLocalDateTime(long epochMilli, ZoneId zoneId, LocalDateTime expected) @@ -914,6 +1074,16 @@ private static Stream epochMillis_withLocalDateInformation() { } + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateInformation") + void testCalendarToDouble(long epochMilli, ZoneId zoneId, LocalDate expected) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(epochMilli); + + double d = this.converter.convert(calendar, double.class, createConvertOptions(null, zoneId)); + assertThat(d).isEqualTo((double)epochMilli); + } + @ParameterizedTest @MethodSource("epochMillis_withLocalDateInformation") void testCalendarToLocalDate(long epochMilli, ZoneId zoneId, LocalDate expected) { @@ -924,6 +1094,45 @@ void testCalendarToLocalDate(long epochMilli, ZoneId zoneId, LocalDate expected) assertThat(localDate).isEqualTo(expected); } + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testCalendarToLocalTime(long epochMilli, ZoneId zoneId, LocalDateTime expected) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(epochMilli); + + LocalTime actual = this.converter.convert(calendar, LocalTime.class, createConvertOptions(null, zoneId)); + assertThat(actual).isEqualTo(expected.toLocalTime()); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testCalendarToZonedDateTime(long epochMilli, ZoneId zoneId, LocalDateTime expected) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(epochMilli); + + ZonedDateTime actual = this.converter.convert(calendar, ZonedDateTime.class, createConvertOptions(null, zoneId)); + assertThat(actual.toLocalDateTime()).isEqualTo(expected); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testCalendarToInstant(long epochMilli, ZoneId zoneId, LocalDateTime expected) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(epochMilli); + + Instant actual = this.converter.convert(calendar, Instant.class, createConvertOptions(null, zoneId)); + assertThat(actual.toEpochMilli()).isEqualTo(epochMilli); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testDateToLocalTime(long epochMilli, ZoneId zoneId, LocalDateTime expected) { + Date date = new Date(epochMilli); + + LocalTime actual = this.converter.convert(date, LocalTime.class, createConvertOptions(null, zoneId)); + assertThat(actual).isEqualTo(expected.toLocalTime()); + } + @ParameterizedTest @MethodSource("epochMillis_withLocalDateInformation") void testCalendarToLocalDate_whenCalendarTimeZoneMatches(long epochMilli, ZoneId zoneId, LocalDate expected) { @@ -948,7 +1157,7 @@ void testCalendarToLocalDate_whenCalendarTimeZoneDoesNotMatchTarget_convertsTime } @Test - void testCalendar_testData() { + void testCalendar_testRoundTripWithLocalDate() { // Create LocalDateTime as CHICAGO TIME. GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone(CHICAGO)); @@ -1058,15 +1267,22 @@ void testTimestampToLocalDate(long epochMilli, ZoneId zoneId, LocalDate expected } - private static final LocalDateTime LDT_TOKYO_1 = LocalDateTime.of(2023, 6, 25, 0, 57, 29, 729000000); - private static final LocalDateTime LDT_PARIS_1 = LocalDateTime.of(2023, 6, 24, 17, 57, 29, 729000000); - private static final LocalDateTime LDT_NY_1 = LocalDateTime.of(2023, 6, 24, 11, 57, 29, 729000000); - private static final LocalDateTime LDT_LA_1 = LocalDateTime.of(2023, 6, 24, 8, 57, 29, 729000000); + @ParameterizedTest + @MethodSource("localDateTimeConversion_params") + void testLocalDateToLong(long epochMilli, ZoneId sourceZoneId, LocalDateTime initial, ZoneId targetZoneId, LocalDateTime expected) + { + long milli = this.converter.convert(initial, long.class, createConvertOptions(sourceZoneId, targetZoneId)); + assertThat(milli).isEqualTo(epochMilli); + + LocalDateTime actual = this.converter.convert(milli, LocalDateTime.class, createConvertOptions(sourceZoneId, targetZoneId)); + assertThat(actual).isEqualTo(expected); + } + private static Stream localDateTimeConversion_params() { return Stream.of( - Arguments.of(1687622249729L, NEW_YORK, LDT_NY_1, TOKYO, LDT_TOKYO_1), - Arguments.of(1687622249729L, LOS_ANGELES, LDT_LA_1, PARIS, LDT_PARIS_1) + Arguments.of(1687622249729L, NEW_YORK, LDT_2023_NY, TOKYO, LDT_2023_TOKYO), + Arguments.of(1687622249729L, LOS_ANGELES, LDT_2023_LA, PARIS, LDT_2023_PARIS) ); } @@ -3651,5 +3867,5 @@ public ZoneId getSourceZoneIdForLocalDates() { }; } - private ConverterOptions chicagoZone() { return createConvertOptions(null, CHICAGO); } + private ConverterOptions chicagoZone() { return createConvertOptions(CHICAGO, CHICAGO); } }