From c0cfe3dbbcbcd2036636318fa00ed365b3b6ec31 Mon Sep 17 00:00:00 2001 From: digithuman Date: Sat, 22 Oct 2022 08:34:01 +0800 Subject: [PATCH 01/10] =?UTF-8?q?LocalDateTime=E3=80=81ZonedDateTime=20Con?= =?UTF-8?q?vert=20Seconds=20and=20Nanos=20Replace=20milliseconds=20Lifting?= =?UTF-8?q?=20accuracy.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converters/DateTimeConverter.java | 86 +++++++++++++++---- 1 file changed, 67 insertions(+), 19 deletions(-) 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 70611bf13..0c44f7176 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java @@ -309,10 +309,8 @@ protected T convertToType(final Class targetType, final Object value) thr // didn't include the milliseconds. The following code // ensures it works consistently across JDK versions final java.sql.Timestamp timestamp = (java.sql.Timestamp)value; - long timeInMillis = ((timestamp.getTime() / 1000) * 1000); - timeInMillis += timestamp.getNanos() / 1000000; - - return toDate(targetType, timeInMillis); + return toDate(targetType, timestamp.getTime() / 1000, + timestamp.getNanos()); } // Handle Date (includes java.sql.Date & java.sql.Time) @@ -336,25 +334,29 @@ protected T convertToType(final Class targetType, final Object value) thr // Handle LocalDate if (value instanceof LocalDate) { final LocalDate date = (LocalDate)value; - return toDate(targetType, date.atStartOfDay(getZoneId()).toInstant().toEpochMilli()); + final Instant temp = date.atStartOfDay(getZoneId()).toInstant(); + return toDate(targetType, temp.getEpochSecond(), temp.getNano()); } // Handle LocalDateTime if (value instanceof LocalDateTime) { final LocalDateTime date = (LocalDateTime)value; - return toDate(targetType, date.atZone(getZoneId()).toInstant().toEpochMilli()); + final Instant temp = date.atZone(getZoneId()).toInstant(); + return toDate(targetType, temp.getEpochSecond(), temp.getNano()); } // Handle ZonedDateTime if (value instanceof ZonedDateTime) { final ZonedDateTime date = (ZonedDateTime)value; - return toDate(targetType, date.toInstant().toEpochMilli()); + final Instant temp = date.toInstant(); + return toDate(targetType, temp.getEpochSecond(), temp.getNano()); } // Handle OffsetDateTime if (value instanceof OffsetDateTime) { final OffsetDateTime date = (OffsetDateTime)value; - return toDate(targetType, date.toInstant().toEpochMilli()); + final Instant temp = date.toInstant(); + return toDate(targetType, temp.getEpochSecond(), temp.getNano()); } // Convert all other types to String & handle @@ -383,7 +385,7 @@ protected T convertToType(final Class targetType, final Object value) thr } /** - * Convert a long value to the specified Date type for this + * Convert a milliseconds long value to the specified Date type for this * Converter. *

* @@ -401,10 +403,49 @@ protected T convertToType(final Class targetType, final Object value) thr * * @param The target type * @param type The Date type to convert to - * @param value The long value to convert. + * @param milliSeconds The milliseconds long value to convert. * @return The converted date value. */ - private T toDate(final Class type, final long value) { + private T toDate(final Class type, final long milliSeconds) { + return toDate(type, milliSeconds / 1000, + Long.valueOf(milliSeconds % 1000).intValue() * 1000000); + } + + /** + * Convert a seconds value and a nanos value to the specified + * Date type for this Converter. + *

+ * + * This method handles conversion to the following types: + *

    + *
  • {@code java.util.Date}
  • + *
  • {@code java.util.Calendar}
  • + *
  • {@code java.time.LocalDate}
  • + *
  • {@code java.time.LocalDateTime}
  • + *
  • {@code java.time.ZonedDateTime}
  • + *
  • {@code java.sql.Date}
  • + *
  • {@code java.sql.Time}
  • + *
  • {@code java.sql.Timestamp}
  • + *
+ * + * @param The target type + * @param type The Date type to convert to + * @param seconds Represents seconds of UTC time since Unix epoch + * 1970-01-01T00:00:00Z. Must be from + * 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z + * inclusive. + * @param nanos Non-negative fractions of a second at + * nanosecond resolution. Negative second values + * with fractions must still have non-negative + * nanos values that count forward in time. Must + * be from 0 to 999,999,999 inclusive. + * @return The converted date value. + */ + private T toDate(final Class type, final long seconds, + final int nanos) { + // milliseconds + long value = seconds * 1000 + nanos / 1000000; + // java.util.Date if (type.equals(Date.class)) { return type.cast(new Date(value)); @@ -422,30 +463,37 @@ private T toDate(final Class type, final long value) { // java.sql.Timestamp if (type.equals(java.sql.Timestamp.class)) { - return type.cast(new java.sql.Timestamp(value)); + java.sql.Timestamp stamp = new java.sql.Timestamp(value); + stamp.setNanos(nanos); + return type.cast(stamp); } // java.time.LocalDateTime if (type.equals(LocalDate.class)) { - final LocalDate localDate = Instant.ofEpochMilli(value).atZone(getZoneId()).toLocalDate(); + final LocalDate localDate = Instant.ofEpochSecond(seconds, nanos) + .atZone(getZoneId()).toLocalDate(); return type.cast(localDate); } // java.time.LocalDateTime if (type.equals(LocalDateTime.class)) { - final LocalDateTime localDateTime = Instant.ofEpochMilli(value).atZone(getZoneId()).toLocalDateTime(); + final LocalDateTime localDateTime = Instant + .ofEpochSecond(seconds, nanos).atZone(getZoneId()) + .toLocalDateTime(); return type.cast(localDateTime); } // java.time.ZonedDateTime if (type.equals(ZonedDateTime.class)) { - final ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(value), getZoneId()); + final ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant( + Instant.ofEpochSecond(seconds, nanos), getZoneId()); return type.cast(zonedDateTime); } // java.time.OffsetDateTime if (type.equals(OffsetDateTime.class)) { - final OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(Instant.ofEpochMilli(value), getZoneId()); + final OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant( + Instant.ofEpochSecond(seconds, nanos), getZoneId()); return type.cast(offsetDateTime); } @@ -466,14 +514,14 @@ private T toDate(final Class type, final long value) { return type.cast(calendar); } - final String msg = toString(getClass()) + " cannot handle conversion to '" - + toString(type) + "'"; + final String msg = toString(getClass()) + + " cannot handle conversion to '" + toString(type) + "'"; if (log().isWarnEnabled()) { log().warn(" " + msg); } throw new ConversionException(msg); } - + /** * Default String to Date conversion. *

From 8183c2a99c2c2074fa8d6045706290c80fadf170 Mon Sep 17 00:00:00 2001 From: digithuman Date: Sat, 22 Oct 2022 09:20:52 +0800 Subject: [PATCH 02/10] =?UTF-8?q?add=20java.time.Instant=E3=80=81InstantCo?= =?UTF-8?q?nverter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commons/beanutils2/ConvertUtilsBean.java | 8 +- .../converters/DateTimeConverter.java | 18 +++- .../converters/InstantConverter.java | 69 +++++++++++++++ .../converters/DateConverterTestBase.java | 7 ++ .../converters/InstantConverterTestCase.java | 83 +++++++++++++++++++ 5 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/apache/commons/beanutils2/converters/InstantConverter.java create mode 100644 src/test/java/org/apache/commons/beanutils2/converters/InstantConverterTestCase.java diff --git a/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java b/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java index 6dc463128..2ec73bd95 100644 --- a/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java +++ b/src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java @@ -26,6 +26,7 @@ import java.nio.file.Path; import java.sql.Timestamp; import java.time.Duration; +import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -53,10 +54,11 @@ import org.apache.commons.beanutils2.converters.ConverterFacade; import org.apache.commons.beanutils2.converters.DateConverter; import org.apache.commons.beanutils2.converters.DoubleConverter; -import org.apache.commons.beanutils2.converters.EnumConverter; import org.apache.commons.beanutils2.converters.DurationConverter; +import org.apache.commons.beanutils2.converters.EnumConverter; import org.apache.commons.beanutils2.converters.FileConverter; import org.apache.commons.beanutils2.converters.FloatConverter; +import org.apache.commons.beanutils2.converters.InstantConverter; import org.apache.commons.beanutils2.converters.IntegerConverter; import org.apache.commons.beanutils2.converters.LocalDateConverter; import org.apache.commons.beanutils2.converters.LocalDateTimeConverter; @@ -112,6 +114,7 @@ *

  • java.sql.Date (no default value)
  • *
  • java.sql.Time (no default value)
  • *
  • java.sql.Timestamp (no default value)
  • + *
  • java.time.Instant (no default value)
  • *
  • java.time.LocalDate (no default value)
  • *
  • java.time.LocalDateTime (no default value)
  • *
  • java.time.LocalTime (no default value)
  • @@ -484,6 +487,7 @@ private void registerStandard(final boolean throwException, final boolean defaul *
  • {@code URL.class} - {@link URLConverter}
  • *
  • {@code URI.class} - {@link URIConverter}
  • *
  • {@code UUID.class} - {@link UUIDConverter}
  • + *
  • {@code Instant.class} - {@link InstantConverter}
  • *
  • {@code LocalDate.class} - {@link LocalDateConverter}
  • *
  • {@code LocalDateTime.class} - {@link LocalDateTimeConverter}
  • *
  • {@code LocalTime.class} - {@link LocalTimeConverter}
  • @@ -516,6 +520,7 @@ private void registerOther(final boolean throwException) { register(URL.class, throwException ? new URLConverter() : new URLConverter(null)); register(URI.class, throwException ? new URIConverter() : new URIConverter(null)); register(UUID.class, throwException ? new UUIDConverter() : new UUIDConverter(null)); + register(Instant.class, throwException ? new InstantConverter() : new InstantConverter(null)); register(LocalDate.class, throwException ? new LocalDateConverter() : new LocalDateConverter(null)); register(LocalDateTime.class, throwException ? new LocalDateTimeConverter() : new LocalDateTimeConverter(null)); register(LocalTime.class, throwException ? new LocalTimeConverter() : new LocalTimeConverter(null)); @@ -582,6 +587,7 @@ private void registerArrays(final boolean throwException, final int defaultArray registerArrayConverter(URL.class, new URLConverter(), throwException, defaultArraySize); registerArrayConverter(URI.class, new URIConverter(), throwException, defaultArraySize); registerArrayConverter(UUID.class, new UUIDConverter(), throwException, defaultArraySize); + registerArrayConverter(Instant.class, new InstantConverter(), throwException, defaultArraySize); registerArrayConverter(LocalDate.class, new LocalDateConverter(), throwException, defaultArraySize); registerArrayConverter(LocalDateTime.class, new LocalDateTimeConverter(), throwException, defaultArraySize); registerArrayConverter(LocalTime.class, new LocalTimeConverter(), throwException, defaultArraySize); 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 0c44f7176..cf928524c 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java @@ -42,6 +42,7 @@ *
      *
    • {@code java.util.Date}
    • *
    • {@code java.util.Calendar}
    • + *
    • {@code java.time.Instant}
    • *
    • {@code java.time.LocalDate}
    • *
    • {@code java.time.LocalDateTime}
    • *
    • {@code java.time.OffsetDateTime}
    • @@ -227,7 +228,7 @@ protected String convertToString(final Object value) { } else if (value instanceof Calendar) { date = ((Calendar) value).getTime(); } else if (value instanceof Long) { - date = new Date(((Long) value).longValue()); + date = new Date(((Long) value).longValue()); } else if (value instanceof LocalDateTime) { date = java.sql.Timestamp.valueOf(((LocalDateTime) value)); } else if (value instanceof LocalDate) { @@ -276,6 +277,7 @@ protected String convertToString(final Object value) { *
    • {@code java.time.LocalDateTime}
    • *
    • {@code java.time.OffsetDateTime}
    • *
    • {@code java.time.ZonedDateTime}
    • + *
    • {@code java.time.Instant}
    • *
    • {@code java.sql.Date}
    • *
    • {@code java.sql.Time}
    • *
    • {@code java.sql.Timestamp}
    • @@ -358,6 +360,12 @@ protected T convertToType(final Class targetType, final Object value) thr final Instant temp = date.toInstant(); return toDate(targetType, temp.getEpochSecond(), temp.getNano()); } + + //Handle Instant and other TemporalAccessor implementations. + if (value instanceof TemporalAccessor) { + final Instant temp = Instant.from(((TemporalAccessor) value)); + return toDate(targetType, temp.getEpochSecond(), temp.getNano()); + } // Convert all other types to String & handle final String stringValue = toTrim(value); @@ -396,6 +404,7 @@ protected T convertToType(final Class targetType, final Object value) thr *
    • {@code java.time.LocalDate}
    • *
    • {@code java.time.LocalDateTime}
    • *
    • {@code java.time.ZonedDateTime}
    • + *
    • {@code java.time.Instant}
    • *
    • {@code java.sql.Date}
    • *
    • {@code java.sql.Time}
    • *
    • {@code java.sql.Timestamp}
    • @@ -423,6 +432,7 @@ private T toDate(final Class type, final long milliSeconds) { *
    • {@code java.time.LocalDate}
    • *
    • {@code java.time.LocalDateTime}
    • *
    • {@code java.time.ZonedDateTime}
    • + *
    • {@code java.time.Instant}
    • *
    • {@code java.sql.Date}
    • *
    • {@code java.sql.Time}
    • *
    • {@code java.sql.Timestamp}
    • @@ -496,6 +506,12 @@ private T toDate(final Class type, final long seconds, Instant.ofEpochSecond(seconds, nanos), getZoneId()); return type.cast(offsetDateTime); } + + // java.time.Instant + if (type.equals(Instant.class)) { + final Instant instant = Instant.ofEpochSecond(seconds, nanos); + return type.cast(instant); + } // java.util.Calendar if (type.equals(Calendar.class)) { diff --git a/src/main/java/org/apache/commons/beanutils2/converters/InstantConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/InstantConverter.java new file mode 100644 index 000000000..3690919de --- /dev/null +++ b/src/main/java/org/apache/commons/beanutils2/converters/InstantConverter.java @@ -0,0 +1,69 @@ +/* + * 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; + +/** + * {@link DateTimeConverter} implementation that handles conversion to + * and from java.time.Instant objects. + *

      + * 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. + *

      + *

      + * Can be configured to either return a default value or throw a + * {@code ConversionException} if a conversion error occurs. + *

      + * + * @since 2.0 + * @see java.time.Instant + */ +public final class InstantConverter extends DateTimeConverter { + + /** + * Constructs a java.time.Instant Converter that throws + * a {@code ConversionException} if an error occurs. + */ + public InstantConverter() { + } + + /** + * Constructs a java.time.Instant Converter 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 InstantConverter(final Instant defaultValue) { + super(defaultValue); + } + + /** + * Gets the default type this {@code Converter} handles. + * + * @return The default type this {@code Converter} handles. + */ + @Override + protected Class getDefaultType() { + return Instant.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 70ff839e4..428b2a807 100644 --- a/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTestBase.java +++ b/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTestBase.java @@ -25,6 +25,7 @@ import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -86,6 +87,12 @@ long getTimeInMillis(final Object date) { if (date instanceof OffsetDateTime) { return ((OffsetDateTime) date).toInstant().toEpochMilli(); } + + //Instant and other TemporalAccessor + if (date instanceof TemporalAccessor) { + final Instant temp = Instant.from(((TemporalAccessor) date)); + return temp.toEpochMilli(); + } if (date instanceof Calendar) { return ((Calendar) date).getTime().getTime(); diff --git a/src/test/java/org/apache/commons/beanutils2/converters/InstantConverterTestCase.java b/src/test/java/org/apache/commons/beanutils2/converters/InstantConverterTestCase.java new file mode 100644 index 000000000..098eb74ca --- /dev/null +++ b/src/test/java/org/apache/commons/beanutils2/converters/InstantConverterTestCase.java @@ -0,0 +1,83 @@ +/* + * 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.util.Calendar; + +import junit.framework.TestSuite; + +/** + * Test Case for the InstantConverterTestCase class. + */ +public class InstantConverterTestCase extends DateConverterTestBase { + + /** + * Create Test Suite + * @return test suite + */ + public static TestSuite suite() { + return new TestSuite(InstantConverterTestCase.class); + } + + /** + * Constructs a new Date test case. + * @param name Test Name + */ + public InstantConverterTestCase(final String name) { + super(name); + } + + /** + * Gets the expected type + * @return The expected type + */ + @Override + protected Class getExpectedType() { + return Instant.class; + } + + /** + * Create the Converter with no default value. + * @return A new Converter + */ + @Override + protected InstantConverter makeConverter() { + return new InstantConverter(); + } + + /** + * Create the Converter with a default value. + * @param defaultValue The default value + * @return A new Converter + */ + @Override + protected InstantConverter makeConverter(final Instant defaultValue) { + return new InstantConverter(defaultValue); + } + + /** + * Convert from a Calendar to the appropriate Date type + * + * @param value The Calendar value to convert + * @return The converted value + */ + @Override + protected Instant toType(final Calendar value) { + return Instant.ofEpochMilli(value.getTimeInMillis()); + } +} \ No newline at end of file From f5cba57ef350c6c041581d3ef3ea93e35dc56069 Mon Sep 17 00:00:00 2001 From: digithuman Date: Sun, 23 Oct 2022 13:44:39 +0800 Subject: [PATCH 03/10] DateTimeFormatter replace SimpleDateFormat,Locate.US AM PM UpperCase replace am pm LowerCase. --- .../converters/DateTimeConverter.java | 246 +++++++++++++----- .../converters/SqlTimeConverter.java | 26 +- .../converters/SqlTimestampConverter.java | 26 +- .../beanutils2/ConvertUtilsTestCase.java | 2 +- .../converters/SqlTimeConverterTestCase.java | 7 +- 5 files changed, 199 insertions(+), 108 deletions(-) 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 cf928524c..36c28490b 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java @@ -16,16 +16,25 @@ */ package org.apache.commons.beanutils2.converters; -import java.text.DateFormat; +import static java.time.temporal.ChronoField.INSTANT_SECONDS; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; + import java.text.ParsePosition; import java.text.SimpleDateFormat; +import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.chrono.IsoChronology; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.time.format.ResolverStyle; import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; import java.util.Calendar; import java.util.Date; import java.util.Locale; @@ -222,33 +231,28 @@ public void setPatterns(final String[] patterns) { */ @Override protected String convertToString(final Object value) { - Date date = null; - if (value instanceof Date) { - date = (Date) value; - } else if (value instanceof Calendar) { - 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()); - } else if (value instanceof TemporalAccessor) { - // Backstop for other TemporalAccessor implementations. - date = Date.from(Instant.from(((TemporalAccessor) value))); + TemporalAccessor date = null; + + if(value instanceof Instant) { + date = ((Instant)value).atZone(getZoneId()); + }else if (value instanceof TemporalAccessor) { + //LocalDateTime、LocalDate、ZonedDateTime、OffsetDateTime + date = (TemporalAccessor) value; + } else { + try { + date= convertToType(LocalDateTime.class,value); + }catch(Exception e) { + log().debug(value+" to LocalDateTime Error : ",e); + } } - + String result = null; if (useLocaleFormat && date != null) { - DateFormat format = null; + DateTimeFormatter format = null; if (patterns != null && patterns.length > 0) { format = getFormat(patterns[0]); } else { - format = getFormat(locale, timeZone); + format = getFormat(locale, getZoneId(),0); } logFormat("Formatting", format); result = format.format(date); @@ -339,6 +343,13 @@ protected T convertToType(final Class targetType, final Object value) thr final Instant temp = date.atStartOfDay(getZoneId()).toInstant(); return toDate(targetType, temp.getEpochSecond(), temp.getNano()); } + + // Handle LocalDate + if (value instanceof LocalTime) { + final LocalTime date = (LocalTime)value; + final Instant temp = date.atDate(LocalDate.of(1970, 1, 1)).atZone(getZoneId()).toInstant(); + return toDate(targetType, temp.getEpochSecond(), temp.getNano()); + } // Handle LocalDateTime if (value instanceof LocalDateTime) { @@ -375,17 +386,14 @@ protected T convertToType(final Class targetType, final Object value) thr // Parse the Date/Time if (useLocaleFormat) { - Calendar calendar = null; + TemporalAccessor temporalAccessor = null; if (patterns != null && patterns.length > 0) { - calendar = parse(sourceType, targetType, stringValue); + temporalAccessor = parse(sourceType, targetType, stringValue); } else { - final DateFormat format = getFormat(locale, timeZone); - calendar = parse(sourceType, targetType, stringValue, format); - } - if (Calendar.class.isAssignableFrom(targetType)) { - return targetType.cast(calendar); + final DateTimeFormatter format = getFormat(locale, getZoneId(),stringValue); + temporalAccessor = parse(sourceType, targetType, stringValue, format); } - return toDate(targetType, calendar.getTime().getTime()); + return convertToType(targetType, temporalAccessor); } // Default String conversion @@ -599,35 +607,57 @@ private T toDate(final Class type, final String value) { } /** - * Gets a {@code DateFormat} for the Locale. + * Gets a {@code DateTimeFormatter} for the Locale. * @param locale The Locale to create the Format with (may be null) - * @param timeZone The Time Zone create the Format with (may be null) + * @param zoneId The Time Zone create the Format with (may be null) + * @param value format Value * * @return A Date Format. */ - protected DateFormat getFormat(final Locale locale, final TimeZone timeZone) { - DateFormat format = null; - if (locale == null) { - format = DateFormat.getDateInstance(DateFormat.SHORT); - } else { - format = DateFormat.getDateInstance(DateFormat.SHORT, locale); - } - if (timeZone != null) { - format.setTimeZone(timeZone); + protected DateTimeFormatter getFormat(final Locale locale, final ZoneId zoneId,String value) { + if (value.length()>10) { + return getFormat(locale,zoneId,0); + }else { + return getFormat(locale,zoneId,1); } - return format; + } + + /** + * Gets a {@code DateTimeFormatter} for the Locale. + * @param locale The Locale to create the Format with (may be null) + * @param zoneId The Time Zone create the Format with (may be null) + * @param type 0:LocalizedDateTime、1:LocalizedDate、2:LocalizedTime + * @return + */ + protected DateTimeFormatter getFormat(final Locale locale, final ZoneId zoneId,final int type) { + DateTimeFormatter format = null; + //DateTime + if (type==0) { + format = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT); + }else if (type==1){ + format = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT); + }else if (type==2) { + format = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT); + } + if (locale != null) { + format = format.withLocale(locale); + } + if (zoneId != null ) { + format = format.withZone(zoneId); + } + return format; } /** * Create a date format for the specified pattern. * * @param pattern The date pattern - * @return The DateFormat + * @return The DateTimeFormatter */ - private DateFormat getFormat(final String pattern) { - final DateFormat format = new SimpleDateFormat(pattern); - if (timeZone != null) { - format.setTimeZone(timeZone); + private DateTimeFormatter getFormat(final String pattern) { + final DateTimeFormatter format = DateTimeFormatter.ofPattern(pattern); + if (timeZone != null ) { + return format.withZone(getZoneId()); } return format; } @@ -642,13 +672,13 @@ private DateFormat getFormat(final String pattern) { * @return The converted Date object. * @throws Exception if an error occurs parsing the date. */ - private Calendar parse(final Class sourceType, final Class targetType, final String value) throws Exception { + private TemporalAccessor parse(final Class sourceType, final Class targetType, final String value) throws Exception { Exception firstEx = null; for (final String pattern : patterns) { try { - final DateFormat format = getFormat(pattern); - final Calendar calendar = parse(sourceType, targetType, value, format); - return calendar; + final DateTimeFormatter format = getFormat(pattern); + final TemporalAccessor temporalAccessor = parse(sourceType, targetType, value, format); + return temporalAccessor; } catch (final Exception ex) { if (firstEx == null) { firstEx = ex; @@ -674,24 +704,115 @@ private Calendar parse(final Class sourceType, final Class targetType, fin * @return The converted Calendar object. * @throws ConversionException if the String cannot be converted. */ - private Calendar parse(final Class sourceType, final Class targetType, final String value, - final DateFormat format) { + private TemporalAccessor parse(final Class sourceType, final Class targetType, final String value, + DateTimeFormatter format) { logFormat("Parsing", format); - format.setLenient(false); final ParsePosition pos = new ParsePosition(0); - final Date parsedDate = format.parse(value, pos); // ignore the result (use the Calendar) + final TemporalAccessor parsedDate = format.parse(value, pos); // ignore the result (use the Calendar) if (pos.getErrorIndex() >= 0 || pos.getIndex() != value.length() || parsedDate == null) { String msg = "Error converting '" + toString(sourceType) + "' to '" + toString(targetType) + "'"; - if (format instanceof SimpleDateFormat) { - msg += " using pattern '" + ((SimpleDateFormat)format).toPattern() + "'"; + if (format instanceof DateTimeFormatter) { + //msg += " using pattern '" + ((SimpleDateFormat)format).toPattern() + "'"; + msg += " using pattern '" + ((DateTimeFormatter)format).toString() + "'"; } if (log().isDebugEnabled()) { log().debug(" " + msg); } throw new ConversionException(msg); } - final Calendar calendar = format.getCalendar(); - return calendar; + + if (checkZonedDateTime(parsedDate)) { + return ZonedDateTime.from(parsedDate); + } else if(checkOffsetDateTime(parsedDate)) { + return OffsetDateTime.from(parsedDate); + } else if (checkLocalDateTime(parsedDate)) { + return LocalDateTime.from(parsedDate); + } else if (checkLocalDate(parsedDate)) { + return LocalDate.from(parsedDate); + } else if (checkLocalTime(parsedDate)) { + return LocalTime.from(parsedDate); + } else { + return Instant.from(parsedDate); + } + } + + /** + * Check ZonedDateTime from TemporalAccessor. + */ + public boolean checkZonedDateTime(TemporalAccessor temporal) { + if (temporal instanceof ZonedDateTime) { + return true; + } + if (temporal.query(TemporalQueries.offset())!=null) { + return false; + } + if (temporal.query(TemporalQueries.zone())==null) { + return false; + } + if (temporal.isSupported(INSTANT_SECONDS)) { + return true; + } + return checkLocalDateTime(temporal); + } + /** + * Check LocalDateTime from TemporalAccessor. + */ + public boolean checkLocalDateTime(TemporalAccessor temporal) { + if (temporal instanceof LocalDateTime) { + return true; + } + if (temporal.query(TemporalQueries.localDate())==null) { + return false; + } + if (temporal.query(TemporalQueries.localTime())==null) { + return false; + } + return true; + } + /** + * Check LocalDate from TemporalAccessor. + */ + public boolean checkLocalDate(TemporalAccessor temporal) { + if (temporal instanceof LocalDate) { + return true; + } + if (temporal.query(TemporalQueries.localDate())==null) { + return false; + } + if (temporal.query(TemporalQueries.localTime())!=null) { + return false; + } + return true; + } + /** + * Check LocalTime from TemporalAccessor. + */ + public boolean checkLocalTime(TemporalAccessor temporal) { + if (temporal instanceof LocalTime) { + return true; + } + if (temporal.query(TemporalQueries.localDate())!=null) { + return false; + } + if (temporal.query(TemporalQueries.localTime())==null) { + return false; + } + return true; + } + /** + * Check OffsetDateTime from TemporalAccessor. + */ + public boolean checkOffsetDateTime(TemporalAccessor temporal) { + if (temporal instanceof OffsetDateTime) { + return true; + } + if (temporal.query(TemporalQueries.offset())==null) { + return false; + } + if (temporal.isSupported(INSTANT_SECONDS)) { + return true; + } + return checkLocalDateTime(temporal); } /** @@ -729,15 +850,16 @@ public String toString() { * @param action The action the format is being used for * @param format The Date format */ - private void logFormat(final String action, final DateFormat format) { + private void logFormat(final String action, final DateTimeFormatter format) { if (log().isDebugEnabled()) { final StringBuilder buffer = new StringBuilder(45); buffer.append(" "); buffer.append(action); buffer.append(" with Format"); - if (format instanceof SimpleDateFormat) { + if (format instanceof DateTimeFormatter) { buffer.append("["); - buffer.append(((SimpleDateFormat)format).toPattern()); + buffer.append(((DateTimeFormatter)format).toString()); + //buffer.append(((SimpleDateFormat)format).toPattern()); buffer.append("]"); } buffer.append(" for "); diff --git a/src/main/java/org/apache/commons/beanutils2/converters/SqlTimeConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/SqlTimeConverter.java index 93d7bbb40..59eb6331a 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/SqlTimeConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/SqlTimeConverter.java @@ -17,9 +17,9 @@ package org.apache.commons.beanutils2.converters; import java.sql.Time; -import java.text.DateFormat; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.Locale; -import java.util.TimeZone; /** * {@link DateTimeConverter} implementation that handles conversion to @@ -68,25 +68,17 @@ protected Class