diff --git a/pom.xml b/pom.xml index 7fd1b4c07..94acb4dff 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,13 @@ true + + org.threeten + threetenbp + ${threetenbp} + true + + com.mysema.querydsl querydsl-core diff --git a/src/main/java/org/springframework/data/convert/Jsr310BackPortConverters.java b/src/main/java/org/springframework/data/convert/Jsr310BackPortConverters.java new file mode 100644 index 000000000..70f2a9058 --- /dev/null +++ b/src/main/java/org/springframework/data/convert/Jsr310BackPortConverters.java @@ -0,0 +1,152 @@ +/* + * Copyright 2015 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.threeten.bp.DateTimeUtils.*; +import static org.threeten.bp.Instant.*; +import static org.threeten.bp.LocalDateTime.*; +import static org.threeten.bp.ZoneId.*; + +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; +import org.threeten.bp.Instant; +import org.threeten.bp.LocalDate; +import org.threeten.bp.LocalDateTime; +import org.threeten.bp.LocalTime; + +/** + * Helper class to register {@link Converter} implementations for the ThreeTen Backport project in case it's present on + * the classpath. + * + * @author Oliver Gierke + * @see http://www.threeten.org/threetenbp + */ +public abstract class Jsr310BackPortConverters { + + private static final boolean THREE_TEN_BACK_PORT_IS_PRESENT = ClassUtils.isPresent("org.threeten.bp.LocalDateTime", + Jsr310BackPortConverters.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 (!THREE_TEN_BACK_PORT_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); + converters.add(DateToInstantConverter.INSTANCE); + converters.add(InstantToDateConverter.INSTANCE); + + return converters; + } + + public static enum DateToLocalDateTimeConverter implements Converter { + + INSTANCE; + + @Override + public LocalDateTime convert(Date source) { + + return source == null ? null : ofInstant(toInstant(source), systemDefault()); + } + } + + public static enum LocalDateTimeToDateConverter implements Converter { + + INSTANCE; + + @Override + public Date convert(LocalDateTime source) { + return source == null ? null : toDate(source.atZone(systemDefault()).toInstant()); + } + } + + public static enum DateToLocalDateConverter implements Converter { + + INSTANCE; + + @Override + public LocalDate convert(Date source) { + return source == null ? null : ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalDate(); + } + } + + public static enum LocalDateToDateConverter implements Converter { + + INSTANCE; + + @Override + public Date convert(LocalDate source) { + return source == null ? null : toDate(source.atStartOfDay(systemDefault()).toInstant()); + } + } + + public static enum DateToLocalTimeConverter implements Converter { + + INSTANCE; + + @Override + public LocalTime convert(Date source) { + return source == null ? null : ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalTime(); + } + } + + public static enum LocalTimeToDateConverter implements Converter { + + INSTANCE; + + @Override + public Date convert(LocalTime source) { + return source == null ? null : toDate(source.atDate(LocalDate.now()).atZone(systemDefault()).toInstant()); + } + } + + public static enum DateToInstantConverter implements Converter { + + INSTANCE; + + @Override + public Instant convert(Date source) { + return source == null ? null : toInstant(source); + } + } + + public static enum InstantToDateConverter implements Converter { + + INSTANCE; + + @Override + public Date convert(Instant source) { + return source == null ? null : toDate(source.atZone(systemDefault()).toInstant()); + } + } +} diff --git a/src/test/java/org/springframework/data/convert/Jsr310BackPortConvertersUnitTests.java b/src/test/java/org/springframework/data/convert/Jsr310BackPortConvertersUnitTests.java new file mode 100644 index 000000000..d12fd558e --- /dev/null +++ b/src/test/java/org/springframework/data/convert/Jsr310BackPortConvertersUnitTests.java @@ -0,0 +1,133 @@ +/* + * Copyright 2015 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 static org.threeten.bp.DateTimeUtils.*; + +import java.text.SimpleDateFormat; +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; +import org.threeten.bp.Instant; +import org.threeten.bp.LocalDate; +import org.threeten.bp.LocalDateTime; +import org.threeten.bp.LocalTime; + +/** + * Unit tests for {@link Jsr310BackPortConverters}. + * + * @author Oliver Gierke + */ +public class Jsr310BackPortConvertersUnitTests { + + static final Date NOW = new Date(); + static final ConversionService CONVERSION_SERVICE; + + static { + + GenericConversionService conversionService = new GenericConversionService(); + + for (Converter converter : Jsr310BackPortConverters.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())); + } + + /** + * @see DATACMNS-623 + */ + @Test + public void convertsDateToInstant() { + + Date now = new Date(); + assertThat(CONVERSION_SERVICE.convert(now, Instant.class), is(toInstant(now))); + } + + /** + * @see DATACMNS-623 + */ + @Test + public void convertsInstantToDate() { + + Date now = new Date(); + assertThat(CONVERSION_SERVICE.convert(toInstant(now), Date.class), is(now)); + } + + private static String format(Date date, String format) { + return new SimpleDateFormat(format).format(date); + } +} diff --git a/template.mf b/template.mf index abe2cad8f..cb5aa97f2 100644 --- a/template.mf +++ b/template.mf @@ -32,6 +32,7 @@ Import-Template: org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional, org.joda.time.*;version="${jodatime:[=.=.=,+1.0.0)}";resolution:=optional, org.slf4j.*;version="[1.6.0,2.0.0)", + org.threeten.bp.*;version="${threetenbp:[=.=.=,+1.0.0)}";resolution:=optional, javax.servlet.*;version="[2.5.0, 4.0.0)";resolution:=optional, org.w3c.dom.*;version="0" DynamicImport-Package: *