From 3a7e5b02e6b69dc4660a6305da68d440e832d225 Mon Sep 17 00:00:00 2001 From: John DeRegnaucourt Date: Sun, 17 Mar 2024 19:46:58 -0400 Subject: [PATCH] Added more converter tests. For all Date & Time related classes, their conversion to Map will consistently have "date" and "time" keys, a "zone" or "offset" key, and "epochMillis" (and "epochNanos" when appropriate) --- pom.xml | 24 ++ .../com/cedarsoftware/util/MapUtilities.java | 10 + .../util/convert/CalendarConversions.java | 11 +- .../cedarsoftware/util/convert/Converter.java | 6 +- .../util/convert/DateConversions.java | 8 +- .../util/convert/LocalDateConversions.java | 10 +- .../convert/LocalDateTimeConversions.java | 11 + .../util/convert/LocalTimeConversions.java | 11 +- .../util/convert/MapConversions.java | 265 +++++------------- .../convert/OffsetDateTimeConversions.java | 12 +- .../util/convert/TimeZoneConversions.java | 5 + .../util/convert/ZoneIdConversions.java | 6 + .../util/convert/ConverterEverythingTest.java | 179 +++++++----- .../util/convert/ConverterTest.java | 102 ++++--- 14 files changed, 304 insertions(+), 356 deletions(-) diff --git a/pom.xml b/pom.xml index cf4f0ef3..a0c29961 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,7 @@ + yyyy-MM-dd'T'HH:mm:ss.SSSZ 1.8 1.8 @@ -40,6 +41,7 @@ 1.6.13 + 3.3.0 3.1.0 3.12.1 3.6.3 @@ -112,6 +114,28 @@ + + org.apache.maven.plugins + maven-jar-plugin + ${version.plugin.jar} + + + + java-util + ${project.version} + com.cedarsoftware + https://github.com/jdereg/java-util + ${user.name} + ${maven.build.timestamp} + ${java.version} (${java.vendor} ${java.vm.version}) + ${os.name} ${os.arch} ${os.version} + + + true + + + + org.apache.felix maven-scr-plugin diff --git a/src/main/java/com/cedarsoftware/util/MapUtilities.java b/src/main/java/com/cedarsoftware/util/MapUtilities.java index 30528288..f23891ea 100644 --- a/src/main/java/com/cedarsoftware/util/MapUtilities.java +++ b/src/main/java/com/cedarsoftware/util/MapUtilities.java @@ -182,4 +182,14 @@ public static Map mapOf(K k1, V v1, K k2, V v2, K k3, V v3) map.put(k3, v3); return Collections.unmodifiableMap(map); } + + public static Map mapOf(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) + { + Map map = new LinkedHashMap<>(); + map.put(k1, v1); + map.put(k2, v2); + map.put(k3, v3); + map.put(k4, v4); + return Collections.unmodifiableMap(map); + } } diff --git a/src/main/java/com/cedarsoftware/util/convert/CalendarConversions.java b/src/main/java/com/cedarsoftware/util/convert/CalendarConversions.java index dab5a3d9..e9fed0cd 100644 --- a/src/main/java/com/cedarsoftware/util/convert/CalendarConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/CalendarConversions.java @@ -115,14 +115,9 @@ static String toString(Object from, Converter converter) { static Map toMap(Object from, Converter converter) { Calendar cal = (Calendar) from; Map target = new CompactLinkedMap<>(); - target.put(MapConversions.YEAR, cal.get(Calendar.YEAR)); - target.put(MapConversions.MONTH, cal.get(Calendar.MONTH) + 1); - target.put(MapConversions.DAY, cal.get(Calendar.DAY_OF_MONTH)); - target.put(MapConversions.HOUR, cal.get(Calendar.HOUR_OF_DAY)); - target.put(MapConversions.MINUTE, cal.get(Calendar.MINUTE)); - target.put(MapConversions.SECOND, cal.get(Calendar.SECOND)); - target.put(MapConversions.MILLI_SECONDS, cal.get(Calendar.MILLISECOND)); - target.put(MapConversions.ZONE, cal.getTimeZone().getID()); + target.put(MapConversions.DATE, LocalDate.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH)).toString()); + target.put(MapConversions.TIME, LocalTime.of(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), cal.get(Calendar.MILLISECOND) * 1_000_000).toString()); + target.put(MapConversions.ZONE, cal.getTimeZone().toZoneId().toString()); return target; } } diff --git a/src/main/java/com/cedarsoftware/util/convert/Converter.java b/src/main/java/com/cedarsoftware/util/convert/Converter.java index 1749d565..23566ba8 100644 --- a/src/main/java/com/cedarsoftware/util/convert/Converter.java +++ b/src/main/java/com/cedarsoftware/util/convert/Converter.java @@ -732,6 +732,7 @@ private static void buildFactoryConversions() { CONVERSION_DB.put(pair(TimeZone.class, TimeZone.class), Converter::identity); CONVERSION_DB.put(pair(String.class, TimeZone.class), StringConversions::toTimeZone); CONVERSION_DB.put(pair(Map.class, TimeZone.class), MapConversions::toTimeZone); + CONVERSION_DB.put(pair(ZoneId.class, TimeZone.class), ZoneIdConversions::toTimeZone); // Duration conversions supported CONVERSION_DB.put(pair(Void.class, Duration.class), VoidConversions::toNull); @@ -774,7 +775,8 @@ private static void buildFactoryConversions() { CONVERSION_DB.put(pair(ZoneId.class, ZoneId.class), Converter::identity); CONVERSION_DB.put(pair(String.class, ZoneId.class), StringConversions::toZoneId); CONVERSION_DB.put(pair(Map.class, ZoneId.class), MapConversions::toZoneId); - + CONVERSION_DB.put(pair(TimeZone.class, ZoneId.class), TimeZoneConversions::toZoneId); + // ZoneOffset conversions supported CONVERSION_DB.put(pair(Void.class, ZoneOffset.class), VoidConversions::toNull); CONVERSION_DB.put(pair(ZoneOffset.class, ZoneOffset.class), Converter::identity); @@ -894,7 +896,7 @@ private static void buildFactoryConversions() { CONVERSION_DB.put(pair(java.sql.Date.class, Map.class), DateConversions::toMap); CONVERSION_DB.put(pair(Timestamp.class, Map.class), MapConversions::initMap); CONVERSION_DB.put(pair(LocalDate.class, Map.class), LocalDateConversions::toMap); - CONVERSION_DB.put(pair(LocalDateTime.class, Map.class), MapConversions::initMap); + CONVERSION_DB.put(pair(LocalDateTime.class, Map.class), LocalDateTimeConversions::toMap); CONVERSION_DB.put(pair(ZonedDateTime.class, Map.class), MapConversions::initMap); CONVERSION_DB.put(pair(Duration.class, Map.class), DurationConversions::toMap); CONVERSION_DB.put(pair(Instant.class, Map.class), InstantConversions::toMap); diff --git a/src/main/java/com/cedarsoftware/util/convert/DateConversions.java b/src/main/java/com/cedarsoftware/util/convert/DateConversions.java index 4fb1fdb4..006750d6 100644 --- a/src/main/java/com/cedarsoftware/util/convert/DateConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/DateConversions.java @@ -18,8 +18,6 @@ import com.cedarsoftware.util.CompactLinkedMap; -import static com.cedarsoftware.util.convert.Converter.VALUE; - /** * @author Kenny Partlow (kpartlow@gmail.com) *
@@ -147,7 +145,11 @@ static String toString(Object from, Converter converter) { static Map toMap(Object from, Converter converter) { Date date = (Date) from; Map map = new CompactLinkedMap<>(); - map.put(VALUE, date.getTime()); + ZonedDateTime zdt = toZonedDateTime(date, converter); + map.put(MapConversions.DATE, zdt.toLocalDate().toString()); + map.put(MapConversions.TIME, zdt.toLocalTime().toString()); + map.put(MapConversions.ZONE, converter.getOptions().getZoneId().toString()); + map.put(MapConversions.EPOCH_MILLIS, date.getTime()); return map; } } diff --git a/src/main/java/com/cedarsoftware/util/convert/LocalDateConversions.java b/src/main/java/com/cedarsoftware/util/convert/LocalDateConversions.java index 3d03ae13..29d5a9fa 100644 --- a/src/main/java/com/cedarsoftware/util/convert/LocalDateConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/LocalDateConversions.java @@ -6,7 +6,7 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.ZoneId; +import java.time.LocalTime; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Calendar; @@ -16,8 +16,6 @@ import com.cedarsoftware.util.CompactLinkedMap; -import static com.cedarsoftware.util.convert.Converter.VALUE; - /** * @author Kenny Partlow (kpartlow@gmail.com) *
@@ -52,8 +50,8 @@ static LocalDateTime toLocalDateTime(Object from, Converter converter) { } static ZonedDateTime toZonedDateTime(Object from, Converter converter) { - ZoneId zoneId = converter.getOptions().getZoneId(); - return ((LocalDate) from).atStartOfDay(zoneId); + LocalDate localDate = (LocalDate) from; + return ZonedDateTime.of(localDate, LocalTime.parse("00:00:00"), converter.getOptions().getZoneId()); } static double toDouble(Object from, Converter converter) { @@ -102,7 +100,7 @@ static String toString(Object from, Converter converter) { static Map toMap(Object from, Converter converter) { LocalDate localDate = (LocalDate) from; Map target = new CompactLinkedMap<>(); - target.put(VALUE, localDate.toString()); + target.put(MapConversions.DATE, localDate.toString()); return target; } } diff --git a/src/main/java/com/cedarsoftware/util/convert/LocalDateTimeConversions.java b/src/main/java/com/cedarsoftware/util/convert/LocalDateTimeConversions.java index a92596fa..599ab9f4 100644 --- a/src/main/java/com/cedarsoftware/util/convert/LocalDateTimeConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/LocalDateTimeConversions.java @@ -11,8 +11,11 @@ import java.time.format.DateTimeFormatter; import java.util.Calendar; import java.util.Date; +import java.util.Map; import java.util.concurrent.atomic.AtomicLong; +import com.cedarsoftware.util.CompactLinkedMap; + /** * @author Kenny Partlow (kpartlow@gmail.com) *
@@ -100,4 +103,12 @@ static String toString(Object from, Converter converter) { LocalDateTime localDateTime = (LocalDateTime) from; return localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); } + + static Map toMap(Object from, Converter converter) { + LocalDateTime localDateTime = (LocalDateTime) from; + Map target = new CompactLinkedMap<>(); + target.put(MapConversions.DATE, localDateTime.toLocalDate().toString()); + target.put(MapConversions.TIME, localDateTime.toLocalTime().toString()); + return target; + } } diff --git a/src/main/java/com/cedarsoftware/util/convert/LocalTimeConversions.java b/src/main/java/com/cedarsoftware/util/convert/LocalTimeConversions.java index ff2ec670..f4c2c734 100644 --- a/src/main/java/com/cedarsoftware/util/convert/LocalTimeConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/LocalTimeConversions.java @@ -37,16 +37,7 @@ private LocalTimeConversions() {} static Map toMap(Object from, Converter converter) { LocalTime localTime = (LocalTime) from; Map target = new CompactLinkedMap<>(); - target.put("hour", localTime.getHour()); - target.put("minute", localTime.getMinute()); - if (localTime.getNano() != 0) { // Only output 'nano' when not 0 (and then 'second' is required). - target.put("nano", localTime.getNano()); - target.put("second", localTime.getSecond()); - } else { // 0 nano, 'second' is optional if 0 - if (localTime.getSecond() != 0) { - target.put("second", localTime.getSecond()); - } - } + target.put(MapConversions.TIME, localTime.toString()); return target; } diff --git a/src/main/java/com/cedarsoftware/util/convert/MapConversions.java b/src/main/java/com/cedarsoftware/util/convert/MapConversions.java index e42d3688..181684b8 100644 --- a/src/main/java/com/cedarsoftware/util/convert/MapConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/MapConversions.java @@ -102,88 +102,84 @@ private MapConversions() {} public static final String KEY_VALUE_ERROR_MESSAGE = "To convert from Map to %s the map must include one of the following: %s[_v], or [value] with associated values."; - private static String[] UUID_PARAMS = new String[] { MOST_SIG_BITS, LEAST_SIG_BITS }; static Object toUUID(Object from, Converter converter) { Map map = (Map) from; - ConverterOptions options = converter.getOptions(); if (map.containsKey(MOST_SIG_BITS) && map.containsKey(LEAST_SIG_BITS)) { long most = converter.convert(map.get(MOST_SIG_BITS), long.class); long least = converter.convert(map.get(LEAST_SIG_BITS), long.class); return new UUID(most, least); } - return fromValueForMultiKey(from, converter, UUID.class, UUID_PARAMS); + return fromMap(from, converter, UUID.class, MOST_SIG_BITS, LEAST_SIG_BITS); } static Byte toByte(Object from, Converter converter) { - return fromValue(from, converter, Byte.class); + return fromMap(from, converter, Byte.class); } static Short toShort(Object from, Converter converter) { - return fromValue(from, converter, Short.class); + return fromMap(from, converter, Short.class); } static Integer toInt(Object from, Converter converter) { - return fromValue(from, converter, Integer.class); + return fromMap(from, converter, Integer.class); } static Long toLong(Object from, Converter converter) { - return fromValue(from, converter, Long.class); + return fromMap(from, converter, Long.class); } static Float toFloat(Object from, Converter converter) { - return fromValue(from, converter, Float.class); + return fromMap(from, converter, Float.class); } static Double toDouble(Object from, Converter converter) { - return fromValue(from, converter, Double.class); + return fromMap(from, converter, Double.class); } static Boolean toBoolean(Object from, Converter converter) { - return fromValue(from, converter, Boolean.class); + return fromMap(from, converter, Boolean.class); } static BigDecimal toBigDecimal(Object from, Converter converter) { - return fromValue(from, converter, BigDecimal.class); + return fromMap(from, converter, BigDecimal.class); } static BigInteger toBigInteger(Object from, Converter converter) { - return fromValue(from, converter, BigInteger.class); + return fromMap(from, converter, BigInteger.class); } static String toString(Object from, Converter converter) { - return fromValue(from, converter, String.class); + return fromMap(from, converter, String.class); } static Character toCharacter(Object from, Converter converter) { - return fromValue(from, converter, char.class); + return fromMap(from, converter, char.class); } static AtomicInteger toAtomicInteger(Object from, Converter converter) { - return fromValue(from, converter, AtomicInteger.class); + return fromMap(from, converter, AtomicInteger.class); } static AtomicLong toAtomicLong(Object from, Converter converter) { - return fromValue(from, converter, AtomicLong.class); + return fromMap(from, converter, AtomicLong.class); } static AtomicBoolean toAtomicBoolean(Object from, Converter converter) { - return fromValue(from, converter, AtomicBoolean.class); + return fromMap(from, converter, AtomicBoolean.class); } static java.sql.Date toSqlDate(Object from, Converter converter) { - return fromSingleKey(from, converter, TIME, java.sql.Date.class); + return fromMap(from, converter, java.sql.Date.class, EPOCH_MILLIS); } static Date toDate(Object from, Converter converter) { - return fromSingleKey(from, converter, EPOCH_MILLIS, Date.class); + return fromMap(from, converter, Date.class, EPOCH_MILLIS); } - private static final String[] TIMESTAMP_PARAMS = new String[] { TIME, NANOS }; static Timestamp toTimestamp(Object from, Converter converter) { Map map = (Map) from; - ConverterOptions options = converter.getOptions(); if (map.containsKey(TIME)) { long time = converter.convert(map.get(TIME), long.class); int ns = converter.convert(map.get(NANOS), int.class); @@ -192,79 +188,45 @@ static Timestamp toTimestamp(Object from, Converter converter) { return timeStamp; } - return fromValueForMultiKey(map, converter, Timestamp.class, TIMESTAMP_PARAMS); + return fromMap(map, converter, Timestamp.class, TIME, NANOS); } - private static final String[] TIMEZONE_PARAMS = new String[] { ZONE }; static TimeZone toTimeZone(Object from, Converter converter) { - Map map = (Map) from; - ConverterOptions options = converter.getOptions(); - - if (map.containsKey(ZONE)) { - return converter.convert(map.get(ZONE), TimeZone.class); - } else { - return fromValueForMultiKey(map, converter, TimeZone.class, TIMEZONE_PARAMS); - } + return fromMap(from, converter, TimeZone.class, ZONE); } - private static final String[] CALENDAR_PARAMS = new String[] { YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLI_SECONDS, ZONE }; static Calendar toCalendar(Object from, Converter converter) { Map map = (Map) from; - if (map.containsKey(TIME)) { - Object zoneRaw = map.get(ZONE); - TimeZone tz; - - if (zoneRaw instanceof String) { - String zone = (String) zoneRaw; - tz = TimeZone.getTimeZone(zone); - } else { - tz = TimeZone.getTimeZone(converter.getOptions().getZoneId()); - } - - Calendar cal = Calendar.getInstance(tz); - Date epochInMillis = converter.convert(map.get(TIME), Date.class); - cal.setTimeInMillis(epochInMillis.getTime()); - return cal; - } - else if (map.containsKey(YEAR)) { - int year = converter.convert(map.get(YEAR), int.class); - int month = converter.convert(map.get(MONTH), int.class); - int day = converter.convert(map.get(DAY), int.class); - int hour = converter.convert(map.get(HOUR), int.class); - int minute = converter.convert(map.get(MINUTE), int.class); - int second = converter.convert(map.get(SECOND), int.class); - int ms = converter.convert(map.get(MILLI_SECONDS), int.class); - Object zoneRaw = map.get(ZONE); - - TimeZone tz; - - if (zoneRaw instanceof String) { - String zone = (String) zoneRaw; - tz = TimeZone.getTimeZone(zone); + if (map.containsKey(DATE) && map.containsKey(TIME)) { + LocalDate localDate = converter.convert(map.get(DATE), LocalDate.class); + LocalTime localTime = converter.convert(map.get(TIME), LocalTime.class); + ZoneId zoneId; + if (map.containsKey(ZONE)) { + zoneId = converter.convert(map.get(ZONE), ZoneId.class); } else { - tz = TimeZone.getTimeZone(converter.getOptions().getZoneId()); + zoneId = converter.getOptions().getZoneId(); } - - Calendar cal = Calendar.getInstance(tz); - cal.set(Calendar.YEAR, year); - cal.set(Calendar.MONTH, month - 1); - cal.set(Calendar.DAY_OF_MONTH, day); - cal.set(Calendar.HOUR_OF_DAY, hour); - cal.set(Calendar.MINUTE, minute); - cal.set(Calendar.SECOND, second); - cal.set(Calendar.MILLISECOND, ms); + LocalDateTime ldt = LocalDateTime.of(localDate, localTime); + ZonedDateTime zdt = ldt.atZone(zoneId); + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(zoneId)); + cal.set(Calendar.YEAR, zdt.getYear()); + cal.set(Calendar.MONTH, zdt.getMonthValue() - 1); + cal.set(Calendar.DAY_OF_MONTH, zdt.getDayOfMonth()); + cal.set(Calendar.HOUR_OF_DAY, zdt.getHour()); + cal.set(Calendar.MINUTE, zdt.getMinute()); + cal.set(Calendar.SECOND, zdt.getSecond()); + cal.set(Calendar.MILLISECOND, zdt.getNano() / 1_000_000); + cal.getTime(); return cal; - } else { - return fromValueForMultiKey(map, converter, Calendar.class, CALENDAR_PARAMS); } + return fromMap(from, converter, Calendar.class, DATE, TIME, ZONE); } - private static final String[] LOCALE_PARAMS = new String[] { LANGUAGE }; static Locale toLocale(Object from, Converter converter) { Map map = (Map) from; if (map.containsKey(VALUE) || map.containsKey(V)) { - return fromValueForMultiKey(map, converter, Locale.class, LOCALE_PARAMS); + return fromMap(map, converter, Locale.class, LANGUAGE); } String language = converter.convert(map.get(LANGUAGE), String.class); @@ -284,41 +246,17 @@ static Locale toLocale(Object from, Converter converter) { return new Locale(language, country, variant); } - - private static final String[] LOCAL_DATE_PARAMS = new String[] { YEAR, MONTH, DAY }; static LocalDate toLocalDate(Object from, Converter converter) { - Map map = (Map) from; - if (map.containsKey(MONTH) && map.containsKey(DAY) && map.containsKey(YEAR)) { - ConverterOptions options = converter.getOptions(); - int month = converter.convert(map.get(MONTH), int.class); - int day = converter.convert(map.get(DAY), int.class); - int year = converter.convert(map.get(YEAR), int.class); - return LocalDate.of(year, month, day); - } else { - return fromValueForMultiKey(map, converter, LocalDate.class, LOCAL_DATE_PARAMS); - } + return fromMap(from, converter, LocalDate.class, DATE); } - private static final String[] LOCAL_TIME_PARAMS = new String[] { HOUR, MINUTE, SECOND, NANO }; static LocalTime toLocalTime(Object from, Converter converter) { - Map map = (Map) from; - if (map.containsKey(HOUR) && map.containsKey(MINUTE)) { - ConverterOptions options = converter.getOptions(); - int hour = converter.convert(map.get(HOUR), int.class); - int minute = converter.convert(map.get(MINUTE), int.class); - int second = converter.convert(map.get(SECOND), int.class); - int nano = converter.convert(map.get(NANO), int.class); - return LocalTime.of(hour, minute, second, nano); - } else { - return fromValueForMultiKey(map, converter, LocalTime.class, LOCAL_TIME_PARAMS); - } + return fromMap(from, converter, LocalTime.class, TIME); } - private static final String[] OFFSET_TIME_PARAMS = new String[] { HOUR, MINUTE, SECOND, NANO, OFFSET_HOUR, OFFSET_MINUTE }; static OffsetTime toOffsetTime(Object from, Converter converter) { Map map = (Map) from; if (map.containsKey(HOUR) && map.containsKey(MINUTE)) { - ConverterOptions options = converter.getOptions(); int hour = converter.convert(map.get(HOUR), int.class); int minute = converter.convert(map.get(MINUTE), int.class); int second = converter.convert(map.get(SECOND), int.class); @@ -327,38 +265,22 @@ static OffsetTime toOffsetTime(Object from, Converter converter) { int offsetMinute = converter.convert(map.get(OFFSET_MINUTE), int.class); ZoneOffset zoneOffset = ZoneOffset.ofHoursMinutes(offsetHour, offsetMinute); return OffsetTime.of(hour, minute, second, nano, zoneOffset); - } else { - return fromValueForMultiKey(map, converter, OffsetTime.class, OFFSET_TIME_PARAMS); } + return fromMap(from, converter, OffsetTime.class, HOUR, MINUTE, SECOND, NANO, OFFSET_HOUR, OFFSET_MINUTE); } - private static final String[] OFFSET_DATE_TIME_PARAMS = new String[] { YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, NANO, OFFSET_HOUR, OFFSET_MINUTE }; + private static final String[] OFFSET_DATE_TIME_PARAMS = new String[] { DATE, TIME, OFFSET }; static OffsetDateTime toOffsetDateTime(Object from, Converter converter) { Map map = (Map) from; - ConverterOptions options = converter.getOptions(); - if (map.containsKey(DATE_TIME) && map.containsKey(OFFSET)) { - LocalDateTime dateTime = converter.convert(map.get(DATE_TIME), LocalDateTime.class); + if (map.containsKey(DATE) && map.containsKey(TIME)) { + LocalDate date = converter.convert(map.get(DATE), LocalDate.class); + LocalTime time = converter.convert(map.get(TIME), LocalTime.class); ZoneOffset zoneOffset = converter.convert(map.get(OFFSET), ZoneOffset.class); - return OffsetDateTime.of(dateTime, zoneOffset); - } else if (map.containsKey(YEAR) && map.containsKey(OFFSET_HOUR)) { - int year = converter.convert(map.get(YEAR), int.class); - int month = converter.convert(map.get(MONTH), int.class); - int day = converter.convert(map.get(DAY), int.class); - int hour = converter.convert(map.get(HOUR), int.class); - int minute = converter.convert(map.get(MINUTE), int.class); - int second = converter.convert(map.get(SECOND), int.class); - int nano = converter.convert(map.get(NANO), int.class); - int offsetHour = converter.convert(map.get(OFFSET_HOUR), int.class); - int offsetMinute = converter.convert(map.get(OFFSET_MINUTE), int.class); - ZoneOffset zoneOffset = ZoneOffset.ofHoursMinutes(offsetHour, offsetMinute); - return OffsetDateTime.of(year, month, day, hour, minute, second, nano, zoneOffset); - } else { - return fromValueForMultiKey(map, converter, OffsetDateTime.class, OFFSET_DATE_TIME_PARAMS); + return OffsetDateTime.of(date, time, zoneOffset); } + return fromMap(from, converter, OffsetDateTime.class, OFFSET_DATE_TIME_PARAMS); } - private static final String[] LOCAL_DATE_TIME_PARAMS = new String[] { DATE, TIME }; - static LocalDateTime toLocalDateTime(Object from, Converter converter) { Map map = (Map) from; if (map.containsKey(DATE)) { @@ -366,86 +288,70 @@ static LocalDateTime toLocalDateTime(Object from, Converter converter) { LocalTime localTime = map.containsKey(TIME) ? converter.convert(map.get(TIME), LocalTime.class) : LocalTime.MIDNIGHT; // validate date isn't null? return LocalDateTime.of(localDate, localTime); - } else { - return fromValueForMultiKey(from, converter, LocalDateTime.class, LOCAL_DATE_TIME_PARAMS); } + return fromMap(from, converter, LocalDateTime.class, DATE, TIME); } - private static final String[] ZONED_DATE_TIME_PARAMS = new String[] { ZONE, DATE_TIME }; static ZonedDateTime toZonedDateTime(Object from, Converter converter) { Map map = (Map) from; if (map.containsKey(ZONE) && map.containsKey(DATE_TIME)) { ZoneId zoneId = converter.convert(map.get(ZONE), ZoneId.class); LocalDateTime localDateTime = converter.convert(map.get(DATE_TIME), LocalDateTime.class); return ZonedDateTime.of(localDateTime, zoneId); - } else { - return fromValueForMultiKey(from, converter, ZonedDateTime.class, ZONED_DATE_TIME_PARAMS); } + return fromMap(from, converter, ZonedDateTime.class, ZONE, DATE_TIME); } static Class toClass(Object from, Converter converter) { - return fromValue(from, converter, Class.class); + return fromMap(from, converter, Class.class); } - private static final String[] DURATION_PARAMS = new String[] { SECONDS, NANOS }; static Duration toDuration(Object from, Converter converter) { Map map = (Map) from; if (map.containsKey(SECONDS)) { - ConverterOptions options = converter.getOptions(); long sec = converter.convert(map.get(SECONDS), long.class); int nanos = converter.convert(map.get(NANOS), int.class); return Duration.ofSeconds(sec, nanos); - } else { - return fromValueForMultiKey(from, converter, Duration.class, DURATION_PARAMS); } + return fromMap(from, converter, Duration.class, SECONDS, NANOS); } - private static final String[] INSTANT_PARAMS = new String[] { SECONDS, NANOS }; static Instant toInstant(Object from, Converter converter) { Map map = (Map) from; if (map.containsKey(SECONDS)) { - ConverterOptions options = converter.getOptions(); long sec = converter.convert(map.get(SECONDS), long.class); long nanos = converter.convert(map.get(NANOS), long.class); return Instant.ofEpochSecond(sec, nanos); - } else { - return fromValueForMultiKey(from, converter, Instant.class, INSTANT_PARAMS); } + return fromMap(from, converter, Instant.class, SECONDS, NANOS); } - private static final String[] MONTH_DAY_PARAMS = new String[] { MONTH, DAY }; static MonthDay toMonthDay(Object from, Converter converter) { Map map = (Map) from; if (map.containsKey(MONTH) && map.containsKey(DAY)) { - ConverterOptions options = converter.getOptions(); int month = converter.convert(map.get(MONTH), int.class); int day = converter.convert(map.get(DAY), int.class); return MonthDay.of(month, day); - } else { - return fromValueForMultiKey(from, converter, MonthDay.class, MONTH_DAY_PARAMS); } + return fromMap(from, converter, MonthDay.class, MONTH, DAY); } - private static final String[] YEAR_MONTH_PARAMS = new String[] { YEAR, MONTH }; static YearMonth toYearMonth(Object from, Converter converter) { Map map = (Map) from; if (map.containsKey(YEAR) && map.containsKey(MONTH)) { - ConverterOptions options = converter.getOptions(); int year = converter.convert(map.get(YEAR), int.class); int month = converter.convert(map.get(MONTH), int.class); return YearMonth.of(year, month); - } else { - return fromValueForMultiKey(from, converter, YearMonth.class, YEAR_MONTH_PARAMS); } + return fromMap(from, converter, YearMonth.class, YEAR, MONTH); } - private static final String[] PERIOD_PARAMS = new String[] { YEARS, MONTHS, DAYS }; static Period toPeriod(Object from, Converter converter) { Map map = (Map) from; if (map.containsKey(VALUE) || map.containsKey(V)) { - return fromValueForMultiKey(from, converter, Period.class, PERIOD_PARAMS); + return fromMap(from, converter, Period.class, YEARS, MONTHS, DAYS); } Number years = converter.convert(map.getOrDefault(YEARS, 0), int.class); @@ -458,19 +364,15 @@ static Period toPeriod(Object from, Converter converter) { static ZoneId toZoneId(Object from, Converter converter) { Map map = (Map) from; if (map.containsKey(ZONE)) { - ConverterOptions options = converter.getOptions(); ZoneId zoneId = converter.convert(map.get(ZONE), ZoneId.class); return zoneId; } else if (map.containsKey(ID)) { - ConverterOptions options = converter.getOptions(); ZoneId zoneId = converter.convert(map.get(ID), ZoneId.class); return zoneId; - } else { - return fromSingleKey(from, converter, ZONE, ZoneId.class); } + return fromMap(from, converter, ZoneId.class, ZONE); } - private static final String[] ZONE_OFFSET_PARAMS = new String[] { HOURS, MINUTES, SECONDS }; static ZoneOffset toZoneOffset(Object from, Converter converter) { Map map = (Map) from; if (map.containsKey(HOURS)) { @@ -478,13 +380,12 @@ static ZoneOffset toZoneOffset(Object from, Converter converter) { int minutes = converter.convert(map.getOrDefault(MINUTES, 0), int.class); // optional int seconds = converter.convert(map.getOrDefault(SECONDS, 0), int.class); // optional return ZoneOffset.ofHoursMinutesSeconds(hours, minutes, seconds); - } else { - return fromValueForMultiKey(from, converter, ZoneOffset.class, ZONE_OFFSET_PARAMS); } + return fromMap(from, converter, ZoneOffset.class, HOURS, MINUTES, SECONDS); } static Year toYear(Object from, Converter converter) { - return fromSingleKey(from, converter, YEAR, Year.class); + return fromMap(from, converter, Year.class, YEAR); } static URL toURL(Object from, Converter converter) { @@ -493,7 +394,7 @@ static URL toURL(Object from, Converter converter) { try { if (map.containsKey(VALUE) || map.containsKey(V)) { - return fromValue(map, converter, URL.class); + return fromMap(map, converter, URL.class); } String protocol = (String) map.get(PROTOCOL); @@ -531,8 +432,7 @@ static URL toURL(Object from, Converter converter) { } static URI toURI(Object from, Converter converter) { - Map map = asMap(from); - return fromValue(map, converter, URI.class); + return fromMap(from, converter, URI.class); } static Map initMap(Object from, Converter converter) { @@ -541,38 +441,14 @@ static URI toURI(Object from, Converter converter) { return map; } - /** - * Allows you to check for a single named key and convert that to a type of it exists, otherwise falls back - * onto the value type V or VALUE. - * - * @param type of object to convert the value. - * @return type if it exists, else returns what is in V or VALUE - */ - private static T fromSingleKey(final Object from, final Converter converter, final String key, final Class type) { - validateParams(converter, type); - + private static T fromMap(Object from, Converter converter, Class type, String...keys) { Map map = asMap(from); - - if (map.containsKey(key)) { - return converter.convert(map.get(key), type); + if (keys.length == 1) { + String key = keys[0]; + if (map.containsKey(key)) { + return converter.convert(map.get(key), type); + } } - - return extractValue(map, converter, type, key); - } - - private static T fromValueForMultiKey(Object from, Converter converter, Class type, String[] keys) { - validateParams(converter, type); - - return extractValue(asMap(from), converter, type, keys); - } - - private static T fromValue(Object from, Converter converter, Class type) { - validateParams(converter, type); - - return extractValue(asMap(from), converter, type); - } - - private static T extractValue(Map map, Converter converter, Class type, String...keys) { if (map.containsKey(V)) { return converter.convert(map.get(V), type); } @@ -584,12 +460,7 @@ private static T extractValue(Map map, Converter converter, Class t String keyText = ArrayUtilities.isEmpty(keys) ? "" : "[" + String.join(", ", keys) + "], "; throw new IllegalArgumentException(String.format(KEY_VALUE_ERROR_MESSAGE, Converter.getShortName(type), keyText)); } - - private static void validateParams(Converter converter, Class type) { - Convention.throwIfNull(type, "type cannot be null"); - Convention.throwIfNull(converter, "converter cannot be null"); - } - + private static Map asMap(Object o) { Convention.throwIfFalse(o instanceof Map, "from must be an instance of map"); return (Map)o; diff --git a/src/main/java/com/cedarsoftware/util/convert/OffsetDateTimeConversions.java b/src/main/java/com/cedarsoftware/util/convert/OffsetDateTimeConversions.java index 30ff401d..bb301c3e 100644 --- a/src/main/java/com/cedarsoftware/util/convert/OffsetDateTimeConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/OffsetDateTimeConversions.java @@ -9,7 +9,6 @@ import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; -import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Calendar; @@ -110,14 +109,11 @@ static String toString(Object from, Converter converter) { } static Map toMap(Object from, Converter converter) { - OffsetDateTime offsetDateTime = (OffsetDateTime) from; - - LocalDateTime localDateTime = offsetDateTime.toLocalDateTime(); - ZoneOffset zoneOffset = offsetDateTime.getOffset(); - + ZonedDateTime zdt = toZonedDateTime(from, converter); Map target = new CompactLinkedMap<>(); - target.put(MapConversions.DATE_TIME, converter.convert(localDateTime, String.class)); - target.put(MapConversions.OFFSET, converter.convert(zoneOffset, String.class)); + target.put(MapConversions.DATE, zdt.toLocalDate().toString()); + target.put(MapConversions.TIME, zdt.toLocalTime().toString()); + target.put(MapConversions.OFFSET, zdt.getOffset().toString()); return target; } diff --git a/src/main/java/com/cedarsoftware/util/convert/TimeZoneConversions.java b/src/main/java/com/cedarsoftware/util/convert/TimeZoneConversions.java index 937fff2e..e950f59c 100644 --- a/src/main/java/com/cedarsoftware/util/convert/TimeZoneConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/TimeZoneConversions.java @@ -1,5 +1,6 @@ package com.cedarsoftware.util.convert; +import java.time.ZoneId; import java.util.TimeZone; public class TimeZoneConversions { @@ -8,4 +9,8 @@ static String toString(Object from, Converter converter) { return timezone.getID(); } + static ZoneId toZoneId(Object from, Converter converter) { + TimeZone tz = (TimeZone) from; + return tz.toZoneId(); + } } diff --git a/src/main/java/com/cedarsoftware/util/convert/ZoneIdConversions.java b/src/main/java/com/cedarsoftware/util/convert/ZoneIdConversions.java index 222756a7..eaad6489 100644 --- a/src/main/java/com/cedarsoftware/util/convert/ZoneIdConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/ZoneIdConversions.java @@ -2,6 +2,7 @@ import java.time.ZoneId; import java.util.Map; +import java.util.TimeZone; import com.cedarsoftware.util.CompactLinkedMap; @@ -32,4 +33,9 @@ static Map toMap(Object from, Converter converter) { target.put("zone", zoneID.toString()); return target; } + + static TimeZone toTimeZone(Object from, Converter converter) { + ZoneId zoneId = (ZoneId) from; + return TimeZone.getTimeZone(zoneId); + } } diff --git a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java index 799ddd4e..87cba12a 100644 --- a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java +++ b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java @@ -52,6 +52,9 @@ import static com.cedarsoftware.util.MapUtilities.mapOf; import static com.cedarsoftware.util.convert.Converter.VALUE; import static com.cedarsoftware.util.convert.Converter.pair; +import static com.cedarsoftware.util.convert.MapConversions.DATE; +import static com.cedarsoftware.util.convert.MapConversions.TIME; +import static com.cedarsoftware.util.convert.MapConversions.ZONE; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -221,39 +224,46 @@ private static void loadMapTests() { return cal; }, (Supplier>) () -> { Map map = new CompactLinkedMap<>(); - map.put(MapConversions.YEAR, 2024); - map.put(MapConversions.MONTH, 2); - map.put(MapConversions.DAY, 5); - map.put(MapConversions.HOUR, 22); - map.put(MapConversions.MINUTE, 31); - map.put(MapConversions.SECOND, 17); - map.put(MapConversions.MILLI_SECONDS, 409); - map.put(MapConversions.ZONE, TOKYO); + map.put(DATE, "2024-02-05"); + map.put(TIME, "22:31:17.409"); + map.put(ZONE, TOKYO); return map; }, true}, }); TEST_DB.put(pair(Date.class, Map.class), new Object[][] { - { new Date(-1L), mapOf(VALUE, -1L), true}, - { new Date(0L), mapOf(VALUE, 0L), true}, - { new Date(now), mapOf(VALUE, now), true}, - { new Date(1L), mapOf(VALUE, 1L), true}, - }); - TEST_DB.put(pair(LocalDate.class, Map.class), new Object[][] { - {LocalDate.parse("1969-12-31"), mapOf(VALUE, "1969-12-31"), true}, - {LocalDate.parse("1970-01-01"), mapOf(VALUE, "1970-01-01"), true}, - {LocalDate.parse("1970-01-02"), mapOf(VALUE, "1970-01-02"), true}, + { new Date(-1L), mapOf(MapConversions.EPOCH_MILLIS, -1L, DATE, "1970-01-01", TIME, "08:59:59.999", ZONE, TOKYO_Z.toString()), true}, + { new Date(0L), mapOf(MapConversions.EPOCH_MILLIS, 0L, DATE, "1970-01-01", TIME, "09:00", ZONE, TOKYO_Z.toString()), true}, + { new Date(1L), mapOf(MapConversions.EPOCH_MILLIS, 1L, DATE, "1970-01-01", TIME, "09:00:00.001", ZONE, TOKYO_Z.toString()), true}, + { new Date(1710714535152L), mapOf(MapConversions.EPOCH_MILLIS, 1710714535152L, DATE, "2024-03-18", TIME, "07:28:55.152", ZONE, TOKYO_Z.toString()), true}, }); TEST_DB.put(pair(java.sql.Date.class, Map.class), new Object[][] { - { new java.sql.Date(-1L), mapOf(VALUE, -1L), true}, - { new java.sql.Date(0L), mapOf(VALUE, 0L), true}, - { new java.sql.Date(now), mapOf(VALUE, now), true}, - { new java.sql.Date(1L), mapOf(VALUE, 1L), true}, + { new java.sql.Date(-1L), mapOf(MapConversions.EPOCH_MILLIS, -1L, DATE, "1970-01-01", TIME, "08:59:59.999", ZONE, TOKYO_Z.toString()), true}, + { new java.sql.Date(0L), mapOf(MapConversions.EPOCH_MILLIS, 0L, DATE, "1970-01-01", TIME, "09:00", ZONE, TOKYO_Z.toString()), true}, + { new java.sql.Date(1L), mapOf(MapConversions.EPOCH_MILLIS, 1L, DATE, "1970-01-01", TIME, "09:00:00.001", ZONE, TOKYO_Z.toString()), true}, + { new java.sql.Date(1710714535152L), mapOf(MapConversions.EPOCH_MILLIS, 1710714535152L, DATE, "2024-03-18", TIME, "07:28:55.152", ZONE, TOKYO_Z.toString()), true}, + }); + TEST_DB.put(pair(LocalDate.class, Map.class), new Object[][] { + {LocalDate.parse("1969-12-31"), mapOf(DATE, "1969-12-31"), true}, + {LocalDate.parse("1970-01-01"), mapOf(DATE, "1970-01-01"), true}, + {LocalDate.parse("1970-01-02"), mapOf(DATE, "1970-01-02"), true}, + }); + TEST_DB.put(pair(LocalDateTime.class, Map.class), new Object[][] { + { LocalDateTime.parse("1969-12-31T23:59:59.999999999"), mapOf(DATE, "1969-12-31", TIME, "23:59:59.999999999"), true}, + { LocalDateTime.parse("1970-01-01T00:00"), mapOf(DATE, "1970-01-01", TIME, "00:00"), true}, + { LocalDateTime.parse("1970-01-01T00:00:00.000000001"), mapOf(DATE, "1970-01-01", TIME, "00:00:00.000000001"), true}, + { LocalDateTime.parse("2024-03-10T11:07:00.123456789"), mapOf(DATE, "2024-03-10", TIME, "11:07:00.123456789"), true}, + }); + TEST_DB.put(pair(OffsetDateTime.class, Map.class), new Object[][] { + { OffsetDateTime.parse("1969-12-31T23:59:59.999999999+09:00"), mapOf(DATE, "1969-12-31", TIME, "23:59:59.999999999", "offset", "+09:00"), true}, + { OffsetDateTime.parse("1970-01-01T00:00+09:00"), mapOf(DATE, "1970-01-01", TIME, "00:00", "offset", "+09:00"), true}, + { OffsetDateTime.parse("1970-01-01T00:00:00.000000001+09:00"), mapOf(DATE, "1970-01-01", TIME, "00:00:00.000000001", "offset", "+09:00"), true}, + { OffsetDateTime.parse("2024-03-10T11:07:00.123456789+09:00"), mapOf(DATE, "2024-03-10", TIME, "11:07:00.123456789", "offset", "+09:00"), true}, }); TEST_DB.put(pair(Duration.class, Map.class), new Object[][] { - { Duration.ofMillis(-1), mapOf("seconds", -1L, "nanos", 999000000)}, + { Duration.ofMillis(-1), mapOf("seconds", -1L, "nanos", 999000000), true}, }); TEST_DB.put(pair(Instant.class, Map.class), new Object[][] { - { Instant.parse("2024-03-10T11:07:00.123456789Z"), mapOf("seconds", 1710068820L, "nanos", 123456789)}, + { Instant.parse("2024-03-10T11:07:00.123456789Z"), mapOf("seconds", 1710068820L, "nanos", 123456789), true}, }); TEST_DB.put(pair(Character.class, Map.class), new Object[][]{ {(char) 0, mapOf(VALUE, (char)0)}, @@ -277,11 +287,21 @@ private static void loadAtomicBooleanTests() { TEST_DB.put(pair(Void.class, AtomicBoolean.class), new Object[][]{ {null, null} }); + TEST_DB.put(pair(Short.class, AtomicBoolean.class), new Object[][]{ + {(short)-1, new AtomicBoolean(true)}, + {(short)0, new AtomicBoolean(false), true}, + {(short)1, new AtomicBoolean(true), true}, + }); TEST_DB.put(pair(Integer.class, AtomicBoolean.class), new Object[][]{ {-1, new AtomicBoolean(true)}, {0, new AtomicBoolean(false), true}, {1, new AtomicBoolean(true), true}, }); + TEST_DB.put(pair(Long.class, AtomicBoolean.class), new Object[][]{ + {-1L, new AtomicBoolean(true)}, + {0L, new AtomicBoolean(false), true}, + {1L, new AtomicBoolean(true), true}, + }); TEST_DB.put(pair(Float.class, AtomicBoolean.class), new Object[][]{ {1.9f, new AtomicBoolean(true)}, {1.0f, new AtomicBoolean(true), true}, @@ -354,6 +374,13 @@ private static void loadAtomicIntegerTests() { {Integer.MIN_VALUE, new AtomicInteger(-2147483648)}, {Integer.MAX_VALUE, new AtomicInteger(2147483647)}, }); + TEST_DB.put(pair(Long.class, AtomicInteger.class), new Object[][]{ + {-1L, new AtomicInteger(-1)}, + {0L, new AtomicInteger(0), true}, + {1L, new AtomicInteger(1), true}, + {(long)Integer.MIN_VALUE, new AtomicInteger(-2147483648)}, + {(long)Integer.MAX_VALUE, new AtomicInteger(2147483647)}, + }); TEST_DB.put(pair(AtomicInteger.class, AtomicInteger.class), new Object[][] { { new AtomicInteger(1), new AtomicInteger((byte)1), true} }); @@ -399,6 +426,13 @@ private static void loadAtomicLongTests() { TEST_DB.put(pair(AtomicLong.class, AtomicLong.class), new Object[][]{ {new AtomicLong(16), new AtomicLong(16)} }); + TEST_DB.put(pair(Long.class, AtomicLong.class), new Object[][]{ + {-1L, new AtomicLong(-1), true}, + {0L, new AtomicLong(0), true}, + {1L, new AtomicLong(1), true}, + {Long.MAX_VALUE, new AtomicLong(Long.MAX_VALUE), true}, + {Long.MIN_VALUE, new AtomicLong(Long.MIN_VALUE), true}, + }); TEST_DB.put(pair(Float.class, AtomicLong.class), new Object[][]{ {-1f, new AtomicLong(-1), true}, {0f, new AtomicLong(0), true}, @@ -855,6 +889,13 @@ private static void loadLocalTimeTests() { { new java.sql.Date(86399999L), LocalTime.parse("08:59:59.999")}, { new java.sql.Date(86400000L), LocalTime.parse("09:00:00")}, }); + TEST_DB.put(pair(LocalDateTime.class, LocalTime.class), new Object[][]{ // no reverse option (Time local to Tokyo) + { LocalDateTime.parse("0000-01-01T00:00:00"), LocalTime.parse("00:00:00")}, + { LocalDateTime.parse("0000-01-02T00:00:00"), LocalTime.parse("00:00:00")}, + { LocalDateTime.parse("1969-12-31T23:59:59.999999999"), LocalTime.parse("23:59:59.999999999")}, + { LocalDateTime.parse("1970-01-01T00:00:00"), LocalTime.parse("00:00:00")}, + { LocalDateTime.parse("1970-01-01T00:00:00.000000001"), LocalTime.parse("00:00:00.000000001")}, + }); TEST_DB.put(pair(Instant.class, LocalTime.class), new Object[][]{ // no reverse option (Time local to Tokyo) { Instant.parse("1969-12-31T23:59:59.999999999Z"), LocalTime.parse("08:59:59.999999999")}, { Instant.parse("1970-01-01T00:00:00Z"), LocalTime.parse("09:00:00")}, @@ -916,6 +957,13 @@ private static void loadLocalDateTests() { return cal; }, LocalDate.parse("2024-03-02"), true } }); + TEST_DB.put(pair(ZonedDateTime.class, LocalDate.class), new Object[][] { + {ZonedDateTime.parse("0000-01-01T00:00:00Z").withZoneSameLocal(TOKYO_Z), LocalDate.parse("0000-01-01"), true }, + {ZonedDateTime.parse("0000-01-02T00:00:00Z").withZoneSameLocal(TOKYO_Z), LocalDate.parse("0000-01-02"), true }, + {ZonedDateTime.parse("1969-12-31T00:00:00Z").withZoneSameLocal(TOKYO_Z), LocalDate.parse("1969-12-31"), true }, + {ZonedDateTime.parse("1970-01-01T00:00:00Z").withZoneSameLocal(TOKYO_Z), LocalDate.parse("1970-01-01"), true }, + {ZonedDateTime.parse("1970-01-02T00:00:00Z").withZoneSameLocal(TOKYO_Z), LocalDate.parse("1970-01-02"), true }, + }); } /** @@ -1027,6 +1075,12 @@ private static void loadZoneIdTests() { {"UTC", ZoneId.of("UTC"), true}, {"GMT", ZoneId.of("GMT"), true}, }); + TEST_DB.put(pair(TimeZone.class, ZoneId.class), new Object[][]{ + {TimeZone.getTimeZone("America/New_York"), ZoneId.of("America/New_York"),true}, + {TimeZone.getTimeZone("Asia/Tokyo"), ZoneId.of("Asia/Tokyo"),true}, + {TimeZone.getTimeZone("GMT"), ZoneId.of("GMT"), true}, + {TimeZone.getTimeZone("UTC"), ZoneId.of("UTC"), true}, + }); TEST_DB.put(pair(Map.class, ZoneId.class), new Object[][]{ {mapOf("_v", "America/New_York"), NY_Z}, {mapOf("_v", NY_Z), NY_Z}, @@ -2102,13 +2156,6 @@ private static void loadDoubleTests() { TEST_DB.put(pair(Void.class, Double.class), new Object[][]{ {null, null} }); - TEST_DB.put(pair(Short.class, Double.class), new Object[][]{ - {(short) -1, -1.0}, - {(short) 0, 0.0}, - {(short) 1, 1.0}, - {Short.MIN_VALUE, (double) Short.MIN_VALUE}, - {Short.MAX_VALUE, (double) Short.MAX_VALUE}, - }); TEST_DB.put(pair(Integer.class, Double.class), new Object[][]{ {-1, -1.0}, {0, 0.0}, @@ -2447,24 +2494,6 @@ private static void loadLongTests() { {-9223372036854775808.0, Long.MIN_VALUE}, {9223372036854775807.0, Long.MAX_VALUE}, }); - TEST_DB.put(pair(AtomicBoolean.class, Long.class), new Object[][]{ - {new AtomicBoolean(true), 1L}, - {new AtomicBoolean(false), 0L}, - }); - TEST_DB.put(pair(AtomicInteger.class, Long.class), new Object[][]{ - {new AtomicInteger(-1), -1L}, - {new AtomicInteger(0), 0L}, - {new AtomicInteger(1), 1L}, - {new AtomicInteger(-2147483648), (long) Integer.MIN_VALUE}, - {new AtomicInteger(2147483647), (long) Integer.MAX_VALUE}, - }); - TEST_DB.put(pair(AtomicLong.class, Long.class), new Object[][]{ - {new AtomicLong(-1), -1L}, - {new AtomicLong(0), 0L}, - {new AtomicLong(1), 1L}, - {new AtomicLong(-9223372036854775808L), Long.MIN_VALUE}, - {new AtomicLong(9223372036854775807L), Long.MAX_VALUE}, - }); TEST_DB.put(pair(BigInteger.class, Long.class), new Object[][]{ {new BigInteger("-1"), -1L, true}, {BigInteger.ZERO, 0L, true}, @@ -2475,15 +2504,15 @@ private static void loadLongTests() { {new BigInteger("9223372036854775808"), Long.MIN_VALUE}, // Test wrap around }); TEST_DB.put(pair(BigDecimal.class, Long.class), new Object[][]{ - {new BigDecimal("-1"), -1L}, + {new BigDecimal("-1"), -1L, true}, {new BigDecimal("-1.1"), -1L}, {new BigDecimal("-1.9"), -1L}, - {BigDecimal.ZERO, 0L}, - {new BigDecimal("1"), 1L}, + {BigDecimal.ZERO, 0L, true}, + {new BigDecimal("1"), 1L, true}, {new BigDecimal("1.1"), 1L}, {new BigDecimal("1.9"), 1L}, - {new BigDecimal("-9223372036854775808"), Long.MIN_VALUE}, - {new BigDecimal("9223372036854775807"), Long.MAX_VALUE}, + {new BigDecimal("-9223372036854775808"), Long.MIN_VALUE, true}, + {new BigDecimal("9223372036854775807"), Long.MAX_VALUE, true}, {new BigDecimal("-9223372036854775809"), Long.MAX_VALUE}, // wrap around {new BigDecimal("9223372036854775808"), Long.MIN_VALUE}, // wrap around }); @@ -2825,37 +2854,33 @@ private static void loadShortTests() { {32768f, Short.MIN_VALUE} // verify wrap around }); TEST_DB.put(pair(Double.class, Short.class), new Object[][]{ - {-1.0, (short) -1}, + {-1.0, (short) -1, true}, {-1.99, (short) -1}, {-1.1, (short) -1}, - {0.0, (short) 0}, - {1.0, (short) 1}, + {0.0, (short) 0, true}, + {1.0, (short) 1, true}, {1.1, (short) 1}, {1.999, (short) 1}, - {-32768.0, Short.MIN_VALUE}, - {32767.0, Short.MAX_VALUE}, + {-32768.0, Short.MIN_VALUE, true}, + {32767.0, Short.MAX_VALUE, true}, {-32769.0, Short.MAX_VALUE}, // verify wrap around {32768.0, Short.MIN_VALUE} // verify wrap around }); - TEST_DB.put(pair(AtomicBoolean.class, Short.class), new Object[][]{ - {new AtomicBoolean(true), (short) 1}, - {new AtomicBoolean(false), (short) 0}, - }); TEST_DB.put(pair(AtomicInteger.class, Short.class), new Object[][]{ - {new AtomicInteger(-1), (short) -1}, - {new AtomicInteger(0), (short) 0}, - {new AtomicInteger(1), (short) 1}, - {new AtomicInteger(-32768), Short.MIN_VALUE}, - {new AtomicInteger(32767), Short.MAX_VALUE}, + {new AtomicInteger(-1), (short) -1, true}, + {new AtomicInteger(0), (short) 0, true}, + {new AtomicInteger(1), (short) 1, true}, + {new AtomicInteger(-32768), Short.MIN_VALUE, true}, + {new AtomicInteger(32767), Short.MAX_VALUE, true}, {new AtomicInteger(-32769), Short.MAX_VALUE}, {new AtomicInteger(32768), Short.MIN_VALUE}, }); TEST_DB.put(pair(AtomicLong.class, Short.class), new Object[][]{ - {new AtomicLong(-1), (short) -1}, - {new AtomicLong(0), (short) 0}, - {new AtomicLong(1), (short) 1}, - {new AtomicLong(-32768), Short.MIN_VALUE}, - {new AtomicLong(32767), Short.MAX_VALUE}, + {new AtomicLong(-1), (short) -1, true}, + {new AtomicLong(0), (short) 0, true}, + {new AtomicLong(1), (short) 1, true}, + {new AtomicLong(-32768), Short.MIN_VALUE, true}, + {new AtomicLong(32767), Short.MAX_VALUE, true}, {new AtomicLong(-32769), Short.MAX_VALUE}, {new AtomicLong(32768), Short.MIN_VALUE}, }); @@ -2869,15 +2894,15 @@ private static void loadShortTests() { {new BigInteger("32768"), Short.MIN_VALUE}, }); TEST_DB.put(pair(BigDecimal.class, Short.class), new Object[][]{ - {new BigDecimal("-1"), (short) -1}, + {new BigDecimal("-1"), (short) -1, true}, {new BigDecimal("-1.1"), (short) -1}, {new BigDecimal("-1.9"), (short) -1}, - {BigDecimal.ZERO, (short) 0}, - {new BigDecimal("1"), (short) 1}, + {BigDecimal.ZERO, (short) 0, true}, + {new BigDecimal("1"), (short) 1, true}, {new BigDecimal("1.1"), (short) 1}, {new BigDecimal("1.9"), (short) 1}, - {new BigDecimal("-32768"), Short.MIN_VALUE}, - {new BigDecimal("32767"), Short.MAX_VALUE}, + {new BigDecimal("-32768"), Short.MIN_VALUE, true}, + {new BigDecimal("32767"), Short.MAX_VALUE, true}, {new BigDecimal("-32769"), Short.MAX_VALUE}, {new BigDecimal("32768"), Short.MIN_VALUE}, }); diff --git a/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java b/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java index 414a0d71..2f531079 100644 --- a/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java +++ b/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java @@ -46,6 +46,7 @@ import static com.cedarsoftware.util.convert.Converter.VALUE; import static com.cedarsoftware.util.convert.ConverterTest.fubar.bar; import static com.cedarsoftware.util.convert.ConverterTest.fubar.foo; +import static com.cedarsoftware.util.convert.MapConversions.DATE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -1041,7 +1042,7 @@ void testLocalDateSqlDate(long epochMilli, ZoneId zoneId, LocalDate expected) { void testLocalDateTimestamp(long epochMilli, ZoneId zoneId, LocalDate expected) { Converter converter = new Converter(createCustomZones(zoneId)); Timestamp intermediate = converter.convert(expected, Timestamp.class); - assertThat(intermediate.getTime()).isEqualTo(epochMilli); + assertTrue(intermediate.toInstant().toString().startsWith(expected.toString())); } @ParameterizedTest @@ -2436,23 +2437,24 @@ void testStringOnMapToLocalDate() void testStringKeysOnMapToLocalDate() { Map map = new HashMap<>(); - map.put("day", "23"); - map.put("month", "12"); - map.put("year", "2023"); - LocalDate ld = this.converter.convert(map, LocalDate.class); + map.put("date", "2023-12-23"); + LocalDate ld = converter.convert(map, LocalDate.class); assert ld.getYear() == 2023; assert ld.getMonthValue() == 12; assert ld.getDayOfMonth() == 23; - map.put("day", 23); - map.put("month", 12); - map.put("year", 2023); + map.put("value", "2023-12-23"); ld = this.converter.convert(map, LocalDate.class); assert ld.getYear() == 2023; assert ld.getMonthValue() == 12; assert ld.getDayOfMonth() == 23; - } + map.put("_v", "2023-12-23"); + ld = this.converter.convert(map, LocalDate.class); + assert ld.getYear() == 2023; + assert ld.getMonthValue() == 12; + assert ld.getDayOfMonth() == 23; + } private static Stream identityParams() { return Stream.of( @@ -2765,7 +2767,7 @@ void testAtomicBoolean() @Test void testMapToAtomicBoolean() { - final Map map = new HashMap(); + final Map map = new HashMap<>(); map.put("value", 57); AtomicBoolean ab = this.converter.convert(map, AtomicBoolean.class); assert ab.get(); @@ -2788,7 +2790,7 @@ void testMapToAtomicBoolean() @Test void testMapToAtomicInteger() { - final Map map = new HashMap(); + final Map map = new HashMap<>(); map.put("value", 58); AtomicInteger ai = this.converter.convert(map, AtomicInteger.class); assert 58 == ai.get(); @@ -2811,7 +2813,7 @@ void testMapToAtomicInteger() @Test void testMapToAtomicLong() { - final Map map = new HashMap(); + final Map map = new HashMap<>(); map.put("value", 58); AtomicLong al = this.converter.convert(map, AtomicLong.class); assert 58 == al.get(); @@ -2835,7 +2837,7 @@ void testMapToAtomicLong() @MethodSource("toCalendarParams") void testMapToCalendar(Object value) { - final Map map = new HashMap(); + final Map map = new HashMap<>(); map.put("value", value); Calendar cal = this.converter.convert(map, Calendar.class); @@ -2852,39 +2854,49 @@ void testMapToCalendar(Object value) map.clear(); assertThatThrownBy(() -> this.converter.convert(map, Calendar.class)) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Map to Calendar the map must include one of the following: [year, month, day, hour, minute, second, millis, zone], [_v], or [value]"); + .hasMessageContaining("Map to Calendar the map must include one of the following: [date, time, zone]"); } @Test void testMapToCalendarWithTimeZone() { long now = System.currentTimeMillis(); - Calendar cal = Calendar.getInstance(); + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo")); cal.clear(); - cal.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo")); cal.setTimeInMillis(now); +// System.out.println("cal = " + cal.getTime()); - final Map map = new HashMap(); - map.put("time", cal.getTimeInMillis()); - map.put("zone", cal.getTimeZone().getID()); + ZonedDateTime zdt = cal.toInstant().atZone(cal.getTimeZone().toZoneId()); +// System.out.println("zdt = " + zdt); + + final Map map = new HashMap<>(); + map.put("date", zdt.toLocalDate()); + map.put("time", zdt.toLocalTime()); + map.put("zone", cal.getTimeZone().toZoneId()); +// System.out.println("map = " + map); Calendar newCal = this.converter.convert(map, Calendar.class); - assert cal.equals(newCal); +// System.out.println("newCal = " + newCal.getTime()); + assertEquals(cal, newCal); assert DeepEquals.deepEquals(cal, newCal); } @Test void testMapToCalendarWithTimeNoZone() { + TimeZone tz = TimeZone.getDefault(); long now = System.currentTimeMillis(); Calendar cal = Calendar.getInstance(); cal.clear(); - cal.setTimeZone(TimeZone.getDefault()); + cal.setTimeZone(tz); cal.setTimeInMillis(now); - final Map map = new HashMap(); - map.put("time", cal.getTimeInMillis()); + Instant instant = Instant.ofEpochMilli(now); + ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, tz.toZoneId()); + final Map map = new HashMap<>(); + map.put("date", zdt.toLocalDate()); + map.put("time", zdt.toLocalTime()); Calendar newCal = this.converter.convert(map, Calendar.class); assert cal.equals(newCal); assert DeepEquals.deepEquals(cal, newCal); @@ -2894,7 +2906,7 @@ void testMapToCalendarWithTimeNoZone() void testMapToGregCalendar() { long now = System.currentTimeMillis(); - final Map map = new HashMap(); + final Map map = new HashMap<>(); map.put("value", new Date(now)); GregorianCalendar cal = this.converter.convert(map, GregorianCalendar.class); assert now == cal.getTimeInMillis(); @@ -2910,14 +2922,14 @@ void testMapToGregCalendar() map.clear(); assertThatThrownBy(() -> this.converter.convert(map, GregorianCalendar.class)) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Map to Calendar the map must include one of the following: [year, month, day, hour, minute, second, millis, zone], [_v], or [value]"); + .hasMessageContaining("Map to Calendar the map must include one of the following: [date, time, zone]"); } @Test void testMapToDate() { long now = System.currentTimeMillis(); - final Map map = new HashMap(); + final Map map = new HashMap<>(); map.put("value", now); Date date = this.converter.convert(map, Date.class); assert now == date.getTime(); @@ -2940,7 +2952,7 @@ void testMapToDate() { void testMapToSqlDate() { long now = System.currentTimeMillis(); - final Map map = new HashMap(); + final Map map = new HashMap<>(); map.put("value", now); java.sql.Date date = this.converter.convert(map, java.sql.Date.class); assert now == date.getTime(); @@ -2963,7 +2975,7 @@ void testMapToSqlDate() void testMapToTimestamp() { long now = System.currentTimeMillis(); - final Map map = new HashMap(); + final Map map = new HashMap<>(); map.put("value", now); Timestamp date = this.converter.convert(map, Timestamp.class); assert now == date.getTime(); @@ -2986,7 +2998,7 @@ void testMapToTimestamp() void testMapToLocalDate() { LocalDate today = LocalDate.now(); - final Map map = new HashMap(); + final Map map = new HashMap<>(); map.put("value", today); LocalDate date = this.converter.convert(map, LocalDate.class); assert date.equals(today); @@ -3002,14 +3014,14 @@ void testMapToLocalDate() map.clear(); assertThatThrownBy(() -> this.converter.convert(map, LocalDate.class)) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("To convert from Map to LocalDate the map must include one of the following: [year, month, day], [_v], or [value] with associated values"); + .hasMessageContaining("To convert from Map to LocalDate the map must include one of the following: [date], [_v], or [value] with associated values"); } @Test void testMapToLocalDateTime() { long now = System.currentTimeMillis(); - final Map map = new HashMap(); + final Map map = new HashMap<>(); map.put("value", now); LocalDateTime ld = this.converter.convert(map, LocalDateTime.class); assert ld.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() == now; @@ -3032,7 +3044,7 @@ void testMapToLocalDateTime() void testMapToZonedDateTime() { long now = System.currentTimeMillis(); - final Map map = new HashMap(); + final Map map = new HashMap<>(); map.put("value", now); ZonedDateTime zd = this.converter.convert(map, ZonedDateTime.class); assert zd.toInstant().toEpochMilli() == now; @@ -3703,7 +3715,7 @@ void testCalendarToMap() { Calendar cal = Calendar.getInstance(); Map map = this.converter.convert(cal, Map.class); - assert map.size() == 8; + assert map.size() == 3; // date, time, zone } @Test @@ -3711,9 +3723,9 @@ void testDateToMap() { Date now = new Date(); Map map = this.converter.convert(now, Map.class); - assert map.size() == 1; - assertEquals(map.get(VALUE), now.getTime()); - assert map.get(VALUE).getClass().equals(Long.class); + assert map.size() == 4; // date, time, zone, epochMillis + assertEquals(map.get(MapConversions.EPOCH_MILLIS), now.getTime()); + assert map.get(MapConversions.EPOCH_MILLIS).getClass().equals(Long.class); } @Test @@ -3721,9 +3733,9 @@ void testSqlDateToMap() { java.sql.Date now = new java.sql.Date(System.currentTimeMillis()); Map map = this.converter.convert(now, Map.class); - assert map.size() == 1; - assertEquals(map.get(VALUE), now); - assert map.get(VALUE).getClass().equals(java.sql.Date.class); + assert map.size() == 4; // date, time, zone, epochMillis + assertEquals(map.get(MapConversions.EPOCH_MILLIS), now.getTime()); + assert map.get(MapConversions.EPOCH_MILLIS).getClass().equals(Long.class); } @Test @@ -3742,18 +3754,18 @@ void testLocalDateToMap() LocalDate now = LocalDate.now(); Map map = this.converter.convert(now, Map.class); assert map.size() == 1; - assertEquals(map.get(VALUE), now); - assert map.get(VALUE).getClass().equals(LocalDate.class); + assertEquals(map.get(DATE), now.toString()); + assert map.get(DATE).getClass().equals(String.class); } @Test void testLocalDateTimeToMap() { LocalDateTime now = LocalDateTime.now(); - Map map = this.converter.convert(now, Map.class); - assert map.size() == 1; - assertEquals(map.get(VALUE), now); - assert map.get(VALUE).getClass().equals(LocalDateTime.class); + Map map = converter.convert(now, Map.class); + assert map.size() == 2; // date, time + LocalDateTime now2 = converter.convert(map, LocalDateTime.class); + assertEquals(now, now2); } @Test