BEANUTILS-515: Java 8 Time converters. (#13)
* BEANUTILS-515: Java 8 Time converters
* BEANUTILS-515: Java 8 Time converters
* BEANUTILS-515: Java 8 Time converters
* Update maven.yml
Display better error message for failing build.
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index fe2b2b7..5cfe7db 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -69,6 +69,9 @@
<action issue="BEANUTILS-528" dev="ggregory" type="update" due-to="Melloware, Matt Sicker, Gary Gregory">
New converters for UUID, URI, and Path #10.
</action>
+ <action issue="BEANUTILS-515" dev="ggregory" type="update" due-to="Melloware, Matt Sicker, Gary Gregory">
+ New converters for Java 8 Time classes LocalDate, LocaleDateTime, ZonedDateTime, OffsetDateTime #13.
+ </action>
</release>
<release version="1.9.4" date="2019-08-13" description="The primary reason for this release is a bugfix for
diff --git a/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java
index 0e16ce8..a3e362c 100644
--- a/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java
+++ b/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java
@@ -19,6 +19,12 @@
import java.text.DateFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
@@ -35,6 +41,10 @@
* <ul>
* <li><code>java.util.Date</code></li>
* <li><code>java.util.Calendar</code></li>
+ * <li><code>java.time.LocalDate</code></li>
+ * <li><code>java.time.LocalDateTime</code></li>
+ * <li><code>java.time.OffsetDateTime</code></li>
+ * <li><code>java.time.ZonedDateTime</code></li>
* <li><code>java.sql.Date</code></li>
* <li><code>java.sql.Time</code></li>
* <li><code>java.sql.Timestamp</code></li>
@@ -233,6 +243,14 @@
date = ((Calendar)value).getTime();
} else if (value instanceof Long) {
date = new Date(((Long)value).longValue());
+ } else if (value instanceof LocalDateTime) {
+ date = java.sql.Timestamp.valueOf(((LocalDateTime)value));
+ } else if (value instanceof LocalDate) {
+ date = java.sql.Date.valueOf(((LocalDate)value));
+ } else if (value instanceof ZonedDateTime) {
+ date = Date.from(((ZonedDateTime)value).toInstant());
+ } else if (value instanceof OffsetDateTime) {
+ date = Date.from(((OffsetDateTime)value).toInstant());
}
String result = null;
@@ -266,6 +284,10 @@
* <ul>
* <li><code>java.util.Date</code></li>
* <li><code>java.util.Calendar</code></li>
+ * <li><code>java.time.LocalDate</code></li>
+ * <li><code>java.time.LocalDateTime</code></li>
+ * <li><code>java.time.OffsetDateTime</code></li>
+ * <li><code>java.time.ZonedDateTime</code></li>
* <li><code>java.sql.Date</code></li>
* <li><code>java.sql.Time</code></li>
* <li><code>java.sql.Timestamp</code></li>
@@ -324,6 +346,30 @@
return toDate(targetType, longObj.longValue());
}
+ // Handle LocalDate
+ if (value instanceof LocalDate) {
+ final LocalDate date = (LocalDate)value;
+ return toDate(targetType, date.atStartOfDay(getZoneId()).toInstant().toEpochMilli());
+ }
+
+ // Handle LocalDateTime
+ if (value instanceof LocalDateTime) {
+ final LocalDateTime date = (LocalDateTime)value;
+ return toDate(targetType, date.atZone(getZoneId()).toInstant().toEpochMilli());
+ }
+
+ // Handle ZonedDateTime
+ if (value instanceof ZonedDateTime) {
+ final ZonedDateTime date = (ZonedDateTime)value;
+ return toDate(targetType, date.toInstant().toEpochMilli());
+ }
+
+ // Handle OffsetDateTime
+ if (value instanceof OffsetDateTime) {
+ final OffsetDateTime date = (OffsetDateTime)value;
+ return toDate(targetType, date.toInstant().toEpochMilli());
+ }
+
// Convert all other types to String & handle
final String stringValue = value.toString().trim();
if (stringValue.length() == 0) {
@@ -359,6 +405,9 @@
* <ul>
* <li><code>java.util.Date</code></li>
* <li><code>java.util.Calendar</code></li>
+ * <li><code>java.time.LocalDate</code></li>
+ * <li><code>java.time.LocalDateTime</code></li>
+ * <li><code>java.time.ZonedDateTime</code></li>
* <li><code>java.sql.Date</code></li>
* <li><code>java.sql.Time</code></li>
* <li><code>java.sql.Timestamp</code></li>
@@ -391,6 +440,30 @@
return type.cast(new java.sql.Timestamp(value));
}
+ // java.time.LocalDateTime
+ if (type.equals(LocalDate.class)) {
+ LocalDate localDate = Instant.ofEpochMilli(value).atZone(getZoneId()).toLocalDate();
+ return type.cast(localDate);
+ }
+
+ // java.time.LocalDateTime
+ if (type.equals(LocalDateTime.class)) {
+ LocalDateTime localDateTime = Instant.ofEpochMilli(value).atZone(getZoneId()).toLocalDateTime();
+ return type.cast(localDateTime);
+ }
+
+ // java.time.ZonedDateTime
+ if (type.equals(ZonedDateTime.class)) {
+ ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(value), getZoneId());
+ return type.cast(zonedDateTime);
+ }
+
+ // java.time.OffsetDateTime
+ if (type.equals(OffsetDateTime.class)) {
+ OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(Instant.ofEpochMilli(value), getZoneId());
+ return type.cast(offsetDateTime);
+ }
+
// java.util.Calendar
if (type.equals(Calendar.class)) {
Calendar calendar = null;
@@ -633,4 +706,13 @@
log().debug(buffer.toString());
}
}
+
+ /**
+ * Gets the <code>java.time.ZoneId</code> from the <code>java.util.Timezone</code>
+ * set or use the system default if no time zone is set.
+ * @return the <code>ZoneId</code>
+ */
+ private ZoneId getZoneId() {
+ return timeZone == null ? ZoneId.systemDefault() : timeZone.toZoneId();
+ }
}
diff --git a/src/main/java/org/apache/commons/beanutils2/converters/LocalDateConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/LocalDateConverter.java
new file mode 100644
index 0000000..4386f61
--- /dev/null
+++ b/src/main/java/org/apache/commons/beanutils2/converters/LocalDateConverter.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.beanutils2.converters;
+
+import java.time.LocalDate;
+
+/**
+ * {@link DateTimeConverter} implementation that handles conversion to
+ * and from <b>java.time.LocalDate</b> objects.
+ * <p>
+ * This implementation can be configured to handle conversion either
+ * by using a Locale's default format or by specifying a set of format
+ * patterns (note, there is no default String conversion for Calendar).
+ * See the {@link DateTimeConverter} documentation for further details.
+ * <p>
+ * Can be configured to either return a <i>default value</i> or throw a
+ * <code>ConversionException</code> if a conversion error occurs.
+ *
+ * @since 2.0
+ */
+public final class LocalDateConverter extends DateTimeConverter {
+
+ /**
+ * Construct a <b>java.time.LocalDate</b> <i>Converter</i> that throws
+ * a <code>ConversionException</code> if an error occurs.
+ */
+ public LocalDateConverter() {
+ super();
+ }
+
+ /**
+ * Construct a <b>java.time.LocalDate</b> <i>Converter</i> that returns
+ * a default value if an error occurs.
+ *
+ * @param defaultValue The default value to be returned
+ * if the value to be converted is missing or an error
+ * occurs converting the value.
+ */
+ public LocalDateConverter(final Object defaultValue) {
+ super(defaultValue);
+ }
+
+ /**
+ * Return the default type this <code>Converter</code> handles.
+ *
+ * @return The default type this <code>Converter</code> handles.
+ */
+ @Override
+ protected Class<?> getDefaultType() {
+ return LocalDate.class;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/beanutils2/converters/LocalDateTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/LocalDateTimeConverter.java
new file mode 100644
index 0000000..e151684
--- /dev/null
+++ b/src/main/java/org/apache/commons/beanutils2/converters/LocalDateTimeConverter.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.beanutils2.converters;
+
+import java.time.LocalDateTime;
+
+/**
+ * {@link DateTimeConverter} implementation that handles conversion to
+ * and from <b>java.time.LocalDateTime</b> objects.
+ * <p>
+ * This implementation can be configured to handle conversion either
+ * by using a Locale's default format or by specifying a set of format
+ * patterns (note, there is no default String conversion for Calendar).
+ * See the {@link DateTimeConverter} documentation for further details.
+ * <p>
+ * Can be configured to either return a <i>default value</i> or throw a
+ * <code>ConversionException</code> if a conversion error occurs.
+ *
+ * @since 2.0
+ */
+public final class LocalDateTimeConverter extends DateTimeConverter {
+
+ /**
+ * Construct a <b>java.time.LocalDateTime</b> <i>Converter</i> that throws
+ * a <code>ConversionException</code> if an error occurs.
+ */
+ public LocalDateTimeConverter() {
+ super();
+ }
+
+ /**
+ * Construct a <b>java.time.LocalDateTime</b> <i>Converter</i> that returns
+ * a default value if an error occurs.
+ *
+ * @param defaultValue The default value to be returned
+ * if the value to be converted is missing or an error
+ * occurs converting the value.
+ */
+ public LocalDateTimeConverter(final Object defaultValue) {
+ super(defaultValue);
+ }
+
+ /**
+ * Return the default type this <code>Converter</code> handles.
+ *
+ * @return The default type this <code>Converter</code> handles.
+ */
+ @Override
+ protected Class<?> getDefaultType() {
+ return LocalDateTime.class;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/beanutils2/converters/OffsetDateTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/OffsetDateTimeConverter.java
new file mode 100644
index 0000000..9713962
--- /dev/null
+++ b/src/main/java/org/apache/commons/beanutils2/converters/OffsetDateTimeConverter.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.beanutils2.converters;
+
+import java.time.OffsetDateTime;
+
+/**
+ * {@link DateTimeConverter} implementation that handles conversion to
+ * and from <b>java.time.OffsetDateTime</b> objects.
+ * <p>
+ * This implementation can be configured to handle conversion either
+ * by using a Locale's default format or by specifying a set of format
+ * patterns (note, there is no default String conversion for Calendar).
+ * See the {@link DateTimeConverter} documentation for further details.
+ * <p>
+ * Can be configured to either return a <i>default value</i> or throw a
+ * <code>ConversionException</code> if a conversion error occurs.
+ *
+ * @since 2.0
+ */
+public final class OffsetDateTimeConverter extends DateTimeConverter {
+
+ /**
+ * Construct a <b>java.time.OffsetDateTime</b> <i>Converter</i> that throws
+ * a <code>ConversionException</code> if an error occurs.
+ */
+ public OffsetDateTimeConverter() {
+ super();
+ }
+
+ /**
+ * Construct a <b>java.time.OffsetDateTime</b> <i>Converter</i> that returns
+ * a default value if an error occurs.
+ *
+ * @param defaultValue The default value to be returned
+ * if the value to be converted is missing or an error
+ * occurs converting the value.
+ */
+ public OffsetDateTimeConverter(final Object defaultValue) {
+ super(defaultValue);
+ }
+
+ /**
+ * Return the default type this <code>Converter</code> handles.
+ *
+ * @return The default type this <code>Converter</code> handles.
+ */
+ @Override
+ protected Class<?> getDefaultType() {
+ return OffsetDateTime.class;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/beanutils2/converters/ZonedDateTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/ZonedDateTimeConverter.java
new file mode 100644
index 0000000..c43c56d
--- /dev/null
+++ b/src/main/java/org/apache/commons/beanutils2/converters/ZonedDateTimeConverter.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.beanutils2.converters;
+
+import java.time.ZonedDateTime;
+
+/**
+ * {@link DateTimeConverter} implementation that handles conversion to
+ * and from <b>java.time.ZonedDateTime</b> objects.
+ * <p>
+ * This implementation can be configured to handle conversion either
+ * by using a Locale's default format or by specifying a set of format
+ * patterns (note, there is no default String conversion for Calendar).
+ * See the {@link DateTimeConverter} documentation for further details.
+ * <p>
+ * Can be configured to either return a <i>default value</i> or throw a
+ * <code>ConversionException</code> if a conversion error occurs.
+ *
+ * @since 2.0
+ */
+public final class ZonedDateTimeConverter extends DateTimeConverter {
+
+ /**
+ * Construct a <b>java.time.ZonedDateTime</b> <i>Converter</i> that throws
+ * a <code>ConversionException</code> if an error occurs.
+ */
+ public ZonedDateTimeConverter() {
+ super();
+ }
+
+ /**
+ * Construct a <b>java.time.ZonedDateTime</b> <i>Converter</i> that returns
+ * a default value if an error occurs.
+ *
+ * @param defaultValue The default value to be returned
+ * if the value to be converted is missing or an error
+ * occurs converting the value.
+ */
+ public ZonedDateTimeConverter(final Object defaultValue) {
+ super(defaultValue);
+ }
+
+ /**
+ * Return the default type this <code>Converter</code> handles.
+ *
+ * @return The default type this <code>Converter</code> handles.
+ */
+ @Override
+ protected Class<?> getDefaultType() {
+ return ZonedDateTime.class;
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTestBase.java b/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTestBase.java
index 8b764b1..1b75bea 100644
--- a/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTestBase.java
+++ b/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTestBase.java
@@ -19,6 +19,12 @@
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@@ -99,7 +105,11 @@
"from Calendar",
"from SQL Date",
"from SQL Time",
- "from SQL Timestamp"
+ "from SQL Timestamp",
+ "from LocalDate",
+ "from LocalDateTime",
+ "from ZonedDateTime",
+ "from OffsetDateTime"
};
final long now = System.currentTimeMillis();
@@ -109,7 +119,11 @@
new java.util.GregorianCalendar(),
new java.sql.Date(now),
new java.sql.Time(now),
- new java.sql.Timestamp(now)
+ new java.sql.Timestamp(now),
+ Instant.ofEpochMilli(now).atZone(ZoneId.systemDefault()).toLocalDate().atStartOfDay(ZoneId.systemDefault()).toLocalDate(),
+ Instant.ofEpochMilli(now).atZone(ZoneId.systemDefault()).toLocalDateTime(),
+ ZonedDateTime.ofInstant(Instant.ofEpochMilli(now), ZoneId.systemDefault()),
+ OffsetDateTime.ofInstant(Instant.ofEpochMilli(now), ZoneId.systemDefault())
};
// Initialize calendar also with same ms to avoid a failing test in a new time slice
@@ -120,8 +134,14 @@
assertNotNull("Convert " + message[i] + " should not be null", val);
assertTrue("Convert " + message[i] + " should return a " + getExpectedType().getName(),
getExpectedType().isInstance(val));
+
+ long test = now;
+ if (date[i] instanceof LocalDate || val instanceof LocalDate) {
+ test = Instant.ofEpochMilli(now).atZone(ZoneId.systemDefault()).toLocalDate().atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ }
+
assertEquals("Convert " + message[i] + " should return a " + date[0],
- now, getTimeInMillis(val));
+ test, getTimeInMillis(val));
}
}
@@ -202,6 +222,9 @@
// java.sql.Time --> String Conversion
stringConversion(converter, expected, toSqlTime(calendar));
+ // java.time.LocalDateTime --> String Conversion
+ stringConversion(converter, expected, toLocalDateTime(calendar));
+
stringConversion(converter, null, null);
stringConversion(converter, "", "");
@@ -509,6 +532,15 @@
java.sql.Timestamp toSqlTimestamp(final Calendar calendar) {
return new java.sql.Timestamp(getTimeInMillis(calendar));
}
+
+ /**
+ * Convert a Calendar to a java.time.LocalDateTime
+ * @param calendar The calendar object to convert
+ * @return The converted java.time.LocalDate
+ */
+ LocalDateTime toLocalDateTime(final Calendar calendar) {
+ return Instant.ofEpochMilli(calendar.getTimeInMillis()).atZone(ZoneId.systemDefault()).toLocalDateTime();
+ }
/**
* Convert a Date or Calendar objects to the time in millisconds
@@ -528,6 +560,22 @@
return timeInMillis;
}
+ if (date instanceof LocalDate) {
+ return ((LocalDate)date).atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ }
+
+ if (date instanceof LocalDateTime) {
+ return ((LocalDateTime)date).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ }
+
+ if (date instanceof ZonedDateTime) {
+ return ((ZonedDateTime)date).toInstant().toEpochMilli();
+ }
+
+ if (date instanceof OffsetDateTime) {
+ return ((OffsetDateTime)date).toInstant().toEpochMilli();
+ }
+
if (date instanceof Calendar) {
return ((Calendar)date).getTime().getTime();
}
diff --git a/src/test/java/org/apache/commons/beanutils2/converters/LocalDateConverterTestCase.java b/src/test/java/org/apache/commons/beanutils2/converters/LocalDateConverterTestCase.java
new file mode 100644
index 0000000..277e82f
--- /dev/null
+++ b/src/test/java/org/apache/commons/beanutils2/converters/LocalDateConverterTestCase.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.beanutils2.converters;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.Calendar;
+
+import junit.framework.TestSuite;
+
+/**
+ * Test Case for the LocalDateConverter class.
+ *
+ */
+public class LocalDateConverterTestCase extends DateConverterTestBase {
+
+ /**
+ * Construct a new Date test case.
+ * @param name Test Name
+ */
+ public LocalDateConverterTestCase(final String name) {
+ super(name);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create Test Suite
+ * @return test suite
+ */
+ public static TestSuite suite() {
+ return new TestSuite(LocalDateConverterTestCase.class);
+ }
+
+ /** Set Up */
+ @Override
+ public void setUp() throws Exception {
+ }
+
+ /** Tear Down */
+ @Override
+ public void tearDown() throws Exception {
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create the Converter with no default value.
+ * @return A new Converter
+ */
+ @Override
+ protected DateTimeConverter makeConverter() {
+ return new LocalDateConverter();
+ }
+
+ /**
+ * Create the Converter with a default value.
+ * @param defaultValue The default value
+ * @return A new Converter
+ */
+ @Override
+ protected DateTimeConverter makeConverter(final Object defaultValue) {
+ return new LocalDateConverter(defaultValue);
+ }
+
+ /**
+ * Return the expected type
+ * @return The expected type
+ */
+ @Override
+ protected Class<?> getExpectedType() {
+ return LocalDate.class;
+ }
+
+ /**
+ * Convert from a Calendar to the appropriate Date type
+ *
+ * @param value The Calendar value to convert
+ * @return The converted value
+ */
+ @Override
+ protected Object toType(final Calendar value) {
+ return Instant.ofEpochMilli(value.getTimeInMillis()).atZone(ZoneId.systemDefault()).toLocalDate();
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/beanutils2/converters/LocalDateTimeConverterTestCase.java b/src/test/java/org/apache/commons/beanutils2/converters/LocalDateTimeConverterTestCase.java
new file mode 100644
index 0000000..ad63e35
--- /dev/null
+++ b/src/test/java/org/apache/commons/beanutils2/converters/LocalDateTimeConverterTestCase.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.beanutils2.converters;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Calendar;
+
+import junit.framework.TestSuite;
+
+/**
+ * Test Case for the LocalDateTimeConverter class.
+ *
+ */
+public class LocalDateTimeConverterTestCase extends DateConverterTestBase {
+
+ /**
+ * Construct a new Date test case.
+ * @param name Test Name
+ */
+ public LocalDateTimeConverterTestCase(final String name) {
+ super(name);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create Test Suite
+ * @return test suite
+ */
+ public static TestSuite suite() {
+ return new TestSuite(LocalDateTimeConverterTestCase.class);
+ }
+
+ /** Set Up */
+ @Override
+ public void setUp() throws Exception {
+ }
+
+ /** Tear Down */
+ @Override
+ public void tearDown() throws Exception {
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create the Converter with no default value.
+ * @return A new Converter
+ */
+ @Override
+ protected DateTimeConverter makeConverter() {
+ return new LocalDateTimeConverter();
+ }
+
+ /**
+ * Create the Converter with a default value.
+ * @param defaultValue The default value
+ * @return A new Converter
+ */
+ @Override
+ protected DateTimeConverter makeConverter(final Object defaultValue) {
+ return new LocalDateTimeConverter(defaultValue);
+ }
+
+ /**
+ * Return the expected type
+ * @return The expected type
+ */
+ @Override
+ protected Class<?> getExpectedType() {
+ return LocalDateTime.class;
+ }
+
+ /**
+ * Convert from a Calendar to the appropriate Date type
+ *
+ * @param value The Calendar value to convert
+ * @return The converted value
+ */
+ @Override
+ protected Object toType(final Calendar value) {
+ return Instant.ofEpochMilli(value.getTimeInMillis()).atZone(ZoneId.systemDefault()).toLocalDateTime();
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/beanutils2/converters/OffsetDateTimeConverterTestCase.java b/src/test/java/org/apache/commons/beanutils2/converters/OffsetDateTimeConverterTestCase.java
new file mode 100644
index 0000000..c4d2f05
--- /dev/null
+++ b/src/test/java/org/apache/commons/beanutils2/converters/OffsetDateTimeConverterTestCase.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.beanutils2.converters;
+
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.util.Calendar;
+
+import junit.framework.TestSuite;
+
+/**
+ * Test Case for the OffsetDateTimeConverter class.
+ *
+ */
+public class OffsetDateTimeConverterTestCase extends DateConverterTestBase {
+
+ /**
+ * Construct a new Date test case.
+ * @param name Test Name
+ */
+ public OffsetDateTimeConverterTestCase(final String name) {
+ super(name);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create Test Suite
+ * @return test suite
+ */
+ public static TestSuite suite() {
+ return new TestSuite(OffsetDateTimeConverterTestCase.class);
+ }
+
+ /** Set Up */
+ @Override
+ public void setUp() throws Exception {
+ }
+
+ /** Tear Down */
+ @Override
+ public void tearDown() throws Exception {
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create the Converter with no default value.
+ * @return A new Converter
+ */
+ @Override
+ protected DateTimeConverter makeConverter() {
+ return new OffsetDateTimeConverter();
+ }
+
+ /**
+ * Create the Converter with a default value.
+ * @param defaultValue The default value
+ * @return A new Converter
+ */
+ @Override
+ protected DateTimeConverter makeConverter(final Object defaultValue) {
+ return new OffsetDateTimeConverter(defaultValue);
+ }
+
+ /**
+ * Return the expected type
+ * @return The expected type
+ */
+ @Override
+ protected Class<?> getExpectedType() {
+ return OffsetDateTime.class;
+ }
+
+ /**
+ * Convert from a Calendar to the appropriate Date type
+ *
+ * @param value The Calendar value to convert
+ * @return The converted value
+ */
+ @Override
+ protected Object toType(final Calendar value) {
+ return OffsetDateTime.ofInstant(Instant.ofEpochMilli(value.getTimeInMillis()), ZoneId.systemDefault());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/beanutils2/converters/ZonedDateTimeConverterTestCase.java b/src/test/java/org/apache/commons/beanutils2/converters/ZonedDateTimeConverterTestCase.java
new file mode 100644
index 0000000..25e8247
--- /dev/null
+++ b/src/test/java/org/apache/commons/beanutils2/converters/ZonedDateTimeConverterTestCase.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.beanutils2.converters;
+
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.ZoneId;
+import java.util.Calendar;
+
+import junit.framework.TestSuite;
+
+/**
+ * Test Case for the ZonedDateTimeConverter class.
+ *
+ */
+public class ZonedDateTimeConverterTestCase extends DateConverterTestBase {
+
+ /**
+ * Construct a new Date test case.
+ * @param name Test Name
+ */
+ public ZonedDateTimeConverterTestCase(final String name) {
+ super(name);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create Test Suite
+ * @return test suite
+ */
+ public static TestSuite suite() {
+ return new TestSuite(ZonedDateTimeConverterTestCase.class);
+ }
+
+ /** Set Up */
+ @Override
+ public void setUp() throws Exception {
+ }
+
+ /** Tear Down */
+ @Override
+ public void tearDown() throws Exception {
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create the Converter with no default value.
+ * @return A new Converter
+ */
+ @Override
+ protected DateTimeConverter makeConverter() {
+ return new ZonedDateTimeConverter();
+ }
+
+ /**
+ * Create the Converter with a default value.
+ * @param defaultValue The default value
+ * @return A new Converter
+ */
+ @Override
+ protected DateTimeConverter makeConverter(final Object defaultValue) {
+ return new ZonedDateTimeConverter(defaultValue);
+ }
+
+ /**
+ * Return the expected type
+ * @return The expected type
+ */
+ @Override
+ protected Class<?> getExpectedType() {
+ return ZonedDateTime.class;
+ }
+
+ /**
+ * Convert from a Calendar to the appropriate Date type
+ *
+ * @param value The Calendar value to convert
+ * @return The converted value
+ */
+ @Override
+ protected Object toType(final Calendar value) {
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(value.getTimeInMillis()), ZoneId.systemDefault());
+ }
+}
\ No newline at end of file