From a5df8d92a7f945afeaa2b88ca615b79a0323437b Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 27 Nov 2014 14:54:41 +0100 Subject: [PATCH] DATACMNS-606 - Added converters for JSR-310 types. Added Jsr310Converters to contain converters to map between Date and JDK 8 date/time types. --- .../data/convert/Jsr310Converters.java | 125 ++++++++++++++++++ .../convert/Jsr310ConvertersUnitTests.java | 111 ++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 src/main/java/org/springframework/data/convert/Jsr310Converters.java create mode 100644 src/test/java/org/springframework/data/convert/Jsr310ConvertersUnitTests.java diff --git a/src/main/java/org/springframework/data/convert/Jsr310Converters.java b/src/main/java/org/springframework/data/convert/Jsr310Converters.java new file mode 100644 index 000000000..2e11915ba --- /dev/null +++ b/src/main/java/org/springframework/data/convert/Jsr310Converters.java @@ -0,0 +1,125 @@ +/* + * Copyright 2013-2014 the original author or authors. + * + * Licensed 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.springframework.data.convert; + +import static java.time.Instant.*; +import static java.time.LocalDateTime.*; +import static java.time.ZoneId.*; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.util.ClassUtils; + +/** + * Helper class to register JSR-310 specific {@link Converter} implementations in case the we're running on Java 8. + * + * @author Oliver Gierke + */ +public abstract class Jsr310Converters { + + private static final boolean JAVA_8_IS_PRESENT = ClassUtils.isPresent("java.time.LocalDateTime", + Jsr310Converters.class.getClassLoader()); + + /** + * Returns the converters to be registered. Will only return converters in case we're running on Java 8. + * + * @return + */ + public static Collection> getConvertersToRegister() { + + if (!JAVA_8_IS_PRESENT) { + return Collections.emptySet(); + } + + List> converters = new ArrayList>(); + converters.add(DateToLocalDateTimeConverter.INSTANCE); + converters.add(LocalDateTimeToDateConverter.INSTANCE); + converters.add(DateToLocalDateConverter.INSTANCE); + converters.add(LocalDateToDateConverter.INSTANCE); + converters.add(DateToLocalTimeConverter.INSTANCE); + converters.add(LocalTimeToDateConverter.INSTANCE); + + return converters; + } + + static enum DateToLocalDateTimeConverter implements Converter { + + INSTANCE; + + @Override + public LocalDateTime convert(Date source) { + return source == null ? null : ofInstant(source.toInstant(), systemDefault()); + } + } + + static enum LocalDateTimeToDateConverter implements Converter { + + INSTANCE; + + @Override + public Date convert(LocalDateTime source) { + return source == null ? null : Date.from(source.atZone(systemDefault()).toInstant()); + } + } + + static enum DateToLocalDateConverter implements Converter { + + INSTANCE; + + @Override + public LocalDate convert(Date source) { + return source == null ? null : ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalDate(); + } + } + + static enum LocalDateToDateConverter implements Converter { + + INSTANCE; + + @Override + public Date convert(LocalDate source) { + return source == null ? null : Date.from(source.atStartOfDay(systemDefault()).toInstant()); + } + } + + static enum DateToLocalTimeConverter implements Converter { + + INSTANCE; + + @Override + public LocalTime convert(Date source) { + return source == null ? null : ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalTime(); + } + } + + static enum LocalTimeToDateConverter implements Converter { + + INSTANCE; + + @Override + public Date convert(LocalTime source) { + return source == null ? null : Date.from(source.atDate(LocalDate.now()).atZone(systemDefault()).toInstant()); + } + } +} diff --git a/src/test/java/org/springframework/data/convert/Jsr310ConvertersUnitTests.java b/src/test/java/org/springframework/data/convert/Jsr310ConvertersUnitTests.java new file mode 100644 index 000000000..6d85a4269 --- /dev/null +++ b/src/test/java/org/springframework/data/convert/Jsr310ConvertersUnitTests.java @@ -0,0 +1,111 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed 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.springframework.data.convert; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Date; + +import org.junit.Test; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.support.GenericConversionService; + +/** + * Unit tests for {@link Jsr310Converters}. + * + * @author Oliver Gierke + */ +public class Jsr310ConvertersUnitTests { + + static final Date NOW = new Date(); + static final ConversionService CONVERSION_SERVICE; + + static { + + GenericConversionService conversionService = new GenericConversionService(); + + for (Converter converter : Jsr310Converters.getConvertersToRegister()) { + conversionService.addConverter(converter); + } + + CONVERSION_SERVICE = conversionService; + } + + /** + * @see DATACMNS-606 + */ + @Test + public void convertsDateToLocalDateTime() { + assertThat(CONVERSION_SERVICE.convert(NOW, LocalDateTime.class).toString(), + is(format(NOW, "yyyy-MM-dd'T'HH:mm:ss.SSS"))); + } + + /** + * @see DATACMNS-606 + */ + @Test + public void convertsLocalDateTimeToDate() { + + LocalDateTime now = LocalDateTime.now(); + assertThat(format(CONVERSION_SERVICE.convert(now, Date.class), "yyyy-MM-dd'T'HH:mm:ss.SSS"), is(now.toString())); + } + + /** + * @see DATACMNS-606 + */ + @Test + public void convertsDateToLocalDate() { + assertThat(CONVERSION_SERVICE.convert(NOW, LocalDate.class).toString(), is(format(NOW, "yyyy-MM-dd"))); + } + + /** + * @see DATACMNS-606 + */ + @Test + public void convertsLocalDateToDate() { + + LocalDate now = LocalDate.now(); + assertThat(format(CONVERSION_SERVICE.convert(now, Date.class), "yyyy-MM-dd"), is(now.toString())); + } + + /** + * @see DATACMNS-606 + */ + @Test + public void convertsDateToLocalTime() { + assertThat(CONVERSION_SERVICE.convert(NOW, LocalTime.class).toString(), is(format(NOW, "HH:mm:ss.SSS"))); + } + + /** + * @see DATACMNS-606 + */ + @Test + public void convertsLocalTimeToDate() { + + LocalTime now = LocalTime.now(); + assertThat(format(CONVERSION_SERVICE.convert(now, Date.class), "HH:mm:ss.SSS"), is(now.toString())); + } + + private static String format(Date date, String format) { + return new SimpleDateFormat(format).format(date); + } +}