You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by rm...@apache.org on 2018/03/30 14:49:31 UTC
[2/8] johnzon git commit: complete support for DateFormat
complete support for DateFormat
Project: http://git-wip-us.apache.org/repos/asf/johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/johnzon/commit/fb05f282
Tree: http://git-wip-us.apache.org/repos/asf/johnzon/tree/fb05f282
Diff: http://git-wip-us.apache.org/repos/asf/johnzon/diff/fb05f282
Branch: refs/heads/master
Commit: fb05f282c9d0ec0f03426cfe6be1688ff9aa078f
Parents: 8583655
Author: amoscatelli <al...@live.com>
Authored: Fri Mar 30 15:59:37 2018 +0200
Committer: amoscatelli <al...@live.com>
Committed: Fri Mar 30 15:59:37 2018 +0200
----------------------------------------------------------------------
.../apache/johnzon/jsonb/JohnzonBuilder.java | 245 ++++++++++++-------
.../apache/johnzon/jsonb/JsonbTypesTest.java | 134 +++++++++-
2 files changed, 287 insertions(+), 92 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/johnzon/blob/fb05f282/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
----------------------------------------------------------------------
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
index 1ae554a..3af5efc 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
@@ -90,8 +90,19 @@ import java.util.function.Supplier;
import java.util.stream.Stream;
import static java.time.format.DateTimeFormatter.ofPattern;
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.HOUR_OF_DAY;
+import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
+import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
+import static java.time.temporal.ChronoField.YEAR;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalQueries;
import static java.util.Collections.emptyMap;
+import java.util.Objects;
import static java.util.Optional.ofNullable;
+import java.util.concurrent.TimeUnit;
import static javax.json.bind.config.PropertyNamingStrategy.IDENTITY;
import static javax.json.bind.config.PropertyOrderStrategy.LEXICOGRAPHICAL;
@@ -141,52 +152,52 @@ public class JohnzonBuilder implements JsonbBuilder {
final String orderValue = config.getProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY).map(String::valueOf).orElse(LEXICOGRAPHICAL);
final PropertyVisibilityStrategy visibilityStrategy = config.getProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY)
.map(PropertyVisibilityStrategy.class::cast).orElse(new PropertyVisibilityStrategy() {
- private final ConcurrentMap<Class<?>, PropertyVisibilityStrategy> strategies = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Class<?>, PropertyVisibilityStrategy> strategies = new ConcurrentHashMap<>();
- @Override
- public boolean isVisible(final Field field) {
- if (field.getAnnotation(JsonbProperty.class) != null) {
- return true;
- }
- final PropertyVisibilityStrategy strategy = strategies.computeIfAbsent(field.getDeclaringClass(), this::visibilityStrategy);
- return strategy == this ? Modifier.isPublic(field.getModifiers()) : strategy.isVisible(field);
- }
+ @Override
+ public boolean isVisible(final Field field) {
+ if (field.getAnnotation(JsonbProperty.class) != null) {
+ return true;
+ }
+ final PropertyVisibilityStrategy strategy = strategies.computeIfAbsent(field.getDeclaringClass(), this::visibilityStrategy);
+ return strategy == this ? Modifier.isPublic(field.getModifiers()) : strategy.isVisible(field);
+ }
- @Override
- public boolean isVisible(final Method method) {
- final PropertyVisibilityStrategy strategy = strategies.computeIfAbsent(method.getDeclaringClass(), this::visibilityStrategy);
- return strategy == this ? Modifier.isPublic(method.getModifiers()) : strategy.isVisible(method);
- }
+ @Override
+ public boolean isVisible(final Method method) {
+ final PropertyVisibilityStrategy strategy = strategies.computeIfAbsent(method.getDeclaringClass(), this::visibilityStrategy);
+ return strategy == this ? Modifier.isPublic(method.getModifiers()) : strategy.isVisible(method);
+ }
- private PropertyVisibilityStrategy visibilityStrategy(final Class<?> type) { // can be cached
- JsonbVisibility visibility = type.getAnnotation(JsonbVisibility.class);
- if (visibility != null) {
- try {
- return visibility.value().newInstance();
- } catch (final InstantiationException | IllegalAccessException e) {
- throw new IllegalArgumentException(e);
- }
- }
- Package p = type.getPackage();
- while (p != null) {
- visibility = p.getAnnotation(JsonbVisibility.class);
- if (visibility != null) {
- try {
- return visibility.value().newInstance();
- } catch (final InstantiationException | IllegalAccessException e) {
- throw new IllegalArgumentException(e);
- }
- }
- final String name = p.getName();
- final int end = name.lastIndexOf('.');
- if (end < 0) {
- break;
- }
- p = Package.getPackage(name.substring(0, end));
+ private PropertyVisibilityStrategy visibilityStrategy(final Class<?> type) { // can be cached
+ JsonbVisibility visibility = type.getAnnotation(JsonbVisibility.class);
+ if (visibility != null) {
+ try {
+ return visibility.value().newInstance();
+ } catch (final InstantiationException | IllegalAccessException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ Package p = type.getPackage();
+ while (p != null) {
+ visibility = p.getAnnotation(JsonbVisibility.class);
+ if (visibility != null) {
+ try {
+ return visibility.value().newInstance();
+ } catch (final InstantiationException | IllegalAccessException e) {
+ throw new IllegalArgumentException(e);
}
- return this;
}
- });
+ final String name = p.getName();
+ final int end = name.lastIndexOf('.');
+ if (end < 0) {
+ break;
+ }
+ p = Package.getPackage(name.substring(0, end));
+ }
+ return this;
+ }
+ });
config.getProperty("johnzon.attributeOrder").ifPresent(comp -> builder.setAttributeOrder(Comparator.class.cast(comp)));
config.getProperty("johnzon.enforceQuoteString")
@@ -227,13 +238,13 @@ public class JohnzonBuilder implements JsonbBuilder {
final AccessMode accessMode = config.getProperty("johnzon.accessMode")
.map(this::toAccessMode)
.orElseGet(() -> new JsonbAccessMode(
- propertyNamingStrategy, orderValue, visibilityStrategy,
- !namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE),
- defaultConverters,
- factory, parserFactoryProvider,
- config.getProperty("johnzon.accessModeDelegate")
- .map(this::toAccessMode)
- .orElseGet(() -> new FieldAndMethodAccessMode(true, true, false))));
+ propertyNamingStrategy, orderValue, visibilityStrategy,
+ !namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE),
+ defaultConverters,
+ factory, parserFactoryProvider,
+ config.getProperty("johnzon.accessModeDelegate")
+ .map(this::toAccessMode)
+ .orElseGet(() -> new FieldAndMethodAccessMode(true, true, false))));
builder.setAccessMode(accessMode);
@@ -309,7 +320,7 @@ public class JohnzonBuilder implements JsonbBuilder {
final ParameterizedType pt = findPT(s, JsonbSerializer.class);
if (pt == null) {
throw new IllegalArgumentException(s + " doesn't implement JsonbSerializer");
- }
+ }
final Type[] args = pt.getActualTypeArguments();
// TODO: support PT in ObjectConverter (list)
if (args.length != 1 || !Class.class.isInstance(args[0])) {
@@ -325,7 +336,7 @@ public class JohnzonBuilder implements JsonbBuilder {
final ParameterizedType pt = findPT(d, JsonbDeserializer.class);
if (pt == null) {
throw new IllegalArgumentException(d + " doesn't implement JsonbDeserializer");
- }
+ }
final Type[] args = pt.getActualTypeArguments();
if (args.length != 1 || !Class.class.isInstance(args[0])) {
throw new IllegalArgumentException("We only support deserializer on Class for now");
@@ -619,7 +630,6 @@ public class JohnzonBuilder implements JsonbBuilder {
}));
addDateFormatConfigConverters(converters, zoneIDUTC);
-
converters.forEach((k, v) -> builder.addAdapter(k.getFrom(), k.getTo(), v));
return converters;
}
@@ -630,98 +640,151 @@ public class JohnzonBuilder implements JsonbBuilder {
final Optional<Locale> locale = config.getProperty(JsonbConfig.LOCALE).map(Locale.class::cast);
final DateTimeFormatter formatter = locale.isPresent() ? ofPattern(dateFormat, locale.get()) : ofPattern(dateFormat);
- // Note: we try and fallback in the parsing cause we don't know if the date format provided is
- // for date, datetime, time
-
converters.put(new AdapterKey(Date.class, String.class), new ConverterAdapter<>(new Converter<Date>() {
- private volatile boolean useFormatter = true;
@Override
public String toString(final Date instance) {
- return LocalDateTime.ofInstant(instance.toInstant(), zoneIDUTC).toString();
+ return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC));
}
@Override
public Date fromString(final String text) {
- if (useFormatter) {
- try {
- return Date.from(LocalDateTime.parse(text, formatter).toInstant(ZoneOffset.UTC));
- } catch (final DateTimeParseException dpe) {
- useFormatter = false;
- }
+ try {
+ return Date.from(parseZonedDateTime(text, formatter, zoneIDUTC).toInstant());
+ } catch (final DateTimeParseException dpe) {
+ return Date.from(LocalDateTime.parse(text).toInstant(ZoneOffset.UTC));
}
- return Date.from(LocalDateTime.parse(text).toInstant(ZoneOffset.UTC));
}
}));
converters.put(new AdapterKey(LocalDateTime.class, String.class), new ConverterAdapter<>(new Converter<LocalDateTime>() {
- private volatile boolean useFormatter = true;
@Override
public String toString(final LocalDateTime instance) {
- return instance.toString();
+ return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(ZoneOffset.UTC), zoneIDUTC));
}
@Override
public LocalDateTime fromString(final String text) {
- if (useFormatter) {
- try {
- return LocalDateTime.parse(text, formatter);
- } catch (final DateTimeParseException dpe) {
- useFormatter = false;
- }
+ try {
+ return parseZonedDateTime(text, formatter, zoneIDUTC).toLocalDateTime();
+ } catch (final DateTimeParseException dpe) {
+ return LocalDateTime.parse(text);
}
- return LocalDateTime.parse(text);
}
}));
converters.put(new AdapterKey(LocalDate.class, String.class), new ConverterAdapter<>(new Converter<LocalDate>() {
- private volatile boolean useFormatter = true;
@Override
public String toString(final LocalDate instance) {
- return instance.toString();
+ return formatter.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(TimeUnit.DAYS.toMillis(instance.toEpochDay())), zoneIDUTC));
}
@Override
public LocalDate fromString(final String text) {
- if (useFormatter) {
- try {
- return LocalDate.parse(text, formatter);
- } catch (final DateTimeParseException dpe) {
- useFormatter = false;
- }
+ try {
+ return parseZonedDateTime(text, formatter, zoneIDUTC).toLocalDate();
+ } catch (final DateTimeParseException dpe) {
+ return LocalDate.parse(text);
+ }
+ }
+ }));
+ converters.put(new AdapterKey(OffsetDateTime.class, String.class), new ConverterAdapter<>(new Converter<OffsetDateTime>() {
+
+ @Override
+ public String toString(final OffsetDateTime instance) {
+ return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC));
+ }
+
+ @Override
+ public OffsetDateTime fromString(final String text) {
+ try {
+ return parseZonedDateTime(text, formatter, zoneIDUTC).toOffsetDateTime();
+ } catch (final DateTimeParseException dpe) {
+ return OffsetDateTime.parse(text);
}
- return LocalDate.parse(text);
}
}));
converters.put(new AdapterKey(ZonedDateTime.class, String.class), new ConverterAdapter<>(new Converter<ZonedDateTime>() {
- private volatile boolean useFormatter = true;
@Override
public String toString(final ZonedDateTime instance) {
- return instance.toString();
+ return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC));
}
@Override
public ZonedDateTime fromString(final String text) {
- if (useFormatter) {
- try {
- return ZonedDateTime.parse(text, formatter);
- } catch (final DateTimeParseException dpe) {
- useFormatter = false;
- }
+ try {
+ return parseZonedDateTime(text, formatter, zoneIDUTC);
+ } catch (final DateTimeParseException dpe) {
+ return ZonedDateTime.parse(text);
}
- return ZonedDateTime.parse(text);
+ }
+ }));
+ converters.put(new AdapterKey(Calendar.class, String.class), new ConverterAdapter<>(new Converter<Calendar>() {
+
+ @Override
+ public String toString(final Calendar instance) {
+ return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC));
+ }
+
+ @Override
+ public Calendar fromString(final String text) {
+ Calendar instance = Calendar.getInstance();
+ instance.setTime(Date.from(parseZonedDateTime(text, formatter, zoneIDUTC).toInstant()));
+ return instance;
+ }
+ }));
+ converters.put(new AdapterKey(GregorianCalendar.class, String.class), new ConverterAdapter<>(new Converter<GregorianCalendar>() {
+
+ @Override
+ public String toString(final GregorianCalendar instance) {
+ return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC));
+ }
+
+ @Override
+ public GregorianCalendar fromString(final String text) {
+ Calendar instance = GregorianCalendar.getInstance();
+ instance.setTime(Date.from(parseZonedDateTime(text, formatter, zoneIDUTC).toInstant()));
+ return GregorianCalendar.class.cast(instance);
+ }
+ }));
+ converters.put(new AdapterKey(Instant.class, String.class), new ConverterAdapter<>(new Converter<Instant>() {
+
+ @Override
+ public String toString(final Instant instance) {
+ return formatter.format(ZonedDateTime.ofInstant(instance, zoneIDUTC));
+ }
+
+ @Override
+ public Instant fromString(final String text) {
+ return parseZonedDateTime(text, formatter, zoneIDUTC).toInstant();
}
}));
});
}
+
+ private static ZonedDateTime parseZonedDateTime(final String text, final DateTimeFormatter formatter, final ZoneId defaultZone){
+ TemporalAccessor parse = formatter.parse(text);
+ ZoneId zone = parse.query(TemporalQueries.zone());
+ if (Objects.isNull(zone)) {
+ zone = defaultZone;
+ }
+ int year = parse.isSupported(YEAR) ? parse.get(YEAR) : 0;
+ int month = parse.isSupported(MONTH_OF_YEAR) ? parse.get(MONTH_OF_YEAR) : 0;
+ int day = parse.isSupported(DAY_OF_MONTH) ? parse.get(DAY_OF_MONTH) : 0;
+ int hour = parse.isSupported(HOUR_OF_DAY) ? parse.get(HOUR_OF_DAY) : 0;
+ int minute = parse.isSupported(MINUTE_OF_HOUR) ? parse.get(MINUTE_OF_HOUR) : 0;
+ int second = parse.isSupported(SECOND_OF_MINUTE) ? parse.get(SECOND_OF_MINUTE) : 0;
+ int millisecond = parse.isSupported(MILLI_OF_SECOND) ? parse.get(MILLI_OF_SECOND) : 0;
+ return ZonedDateTime.of(year, month, day, hour, minute, second, millisecond, zone);
+ }
private static void logIfDeprecatedTimeZone(final String text) {
/* TODO: get the list, UTC is clearly not deprecated but uses 3 letters
if (text.length() == 3) { // don't fail but log it
Logger.getLogger(JohnzonBuilder.class.getName()).severe("Deprecated timezone: " + text);
}
- */
+ */
}
private Map<String, ?> generatorConfig() {
http://git-wip-us.apache.org/repos/asf/johnzon/blob/fb05f282/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java
----------------------------------------------------------------------
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java
index 0f792df..ad5b6d2 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java
@@ -36,11 +36,13 @@ import java.time.Period;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
+import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
@@ -49,6 +51,7 @@ import java.util.OptionalLong;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
+import org.apache.cxf.common.util.StringUtils;
import static org.junit.Assert.assertEquals;
@@ -109,8 +112,44 @@ public class JsonbTypesTest {
assertEquals(expected, jsonb.toJson(types));
}
+ @Test
+ public void readAndWriteWithDateFormats() {
+ readAndWriteWithDateFormat(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"), "yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+ readAndWriteWithDateFormat(DateTimeFormatter.ofPattern("yyyyMMdd+HHmmssZ"), "yyyyMMdd+HHmmssZ");
+ readAndWriteWithDateFormat(DateTimeFormatter.ofPattern("yyyy-MM-dd"), "yyyy-MM-dd");
+ }
+
+ private void readAndWriteWithDateFormat(DateTimeFormatter dateTimeFormatter, String dateFormat) {
+ final LocalDate localDate = LocalDate.of(2015, 1, 1);
+ final LocalDateTime localDateTime = LocalDateTime.of(2015, 1, 1, 1, 1);
+ final ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of("UTC"));
+ final String expected = "{" +
+ "\"calendar\":\"" + dateTimeFormatter.format(zonedDateTime) + "\"," +
+ "\"date\":\"" + dateTimeFormatter.format(ZonedDateTime.ofInstant(localDateTime.toInstant(ZoneOffset.UTC), ZoneId.of("UTC"))) + "\"," +
+ "\"gregorianCalendar\":\"" + dateTimeFormatter.format(zonedDateTime) + "\"," +
+ "\"instant\":\"" + dateTimeFormatter.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(TimeUnit.DAYS.toMillis(localDate.toEpochDay())), ZoneId.of("UTC"))) + "\"," +
+ "\"localDate\":\"" + dateTimeFormatter.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(TimeUnit.DAYS.toMillis(localDate.toEpochDay())), ZoneId.of("UTC"))) + "\"," +
+ "\"localDateTime\":\"" + dateTimeFormatter.format(ZonedDateTime.ofInstant(localDateTime.toInstant(ZoneOffset.UTC), ZoneId.of("UTC"))) + "\"," +
+ "\"offsetDateTime\":\"" + dateTimeFormatter.format(ZonedDateTime.ofInstant(OffsetDateTime.of(localDateTime, ZoneOffset.UTC).toInstant(), ZoneId.of("UTC"))) + "\"" +
+ "}";
+
+ final Jsonb jsonb = newJsonb(dateFormat);
+
+ final DateTypes types = jsonb.fromJson(new StringReader(expected), DateTypes.class);
+ assertEquals(localDate, types.localDate);
+ assertEquals(expected, jsonb.toJson(types));
+ }
+
private static Jsonb newJsonb() {
- return JsonbProvider.provider().create().withConfig(new JsonbConfig().setProperty("johnzon.attributeOrder", new Comparator<String>() {
+ return newJsonb(null);
+ }
+
+ private static Jsonb newJsonb(String dateFormat) {
+ JsonbConfig jsonbConfig = new JsonbConfig();
+ if (!StringUtils.isEmpty(dateFormat)){
+ jsonbConfig.withDateFormat(dateFormat, Locale.getDefault());
+ }
+ return JsonbProvider.provider().create().withConfig(jsonbConfig.setProperty("johnzon.attributeOrder", new Comparator<String>() {
@Override
public int compare(final String o1, final String o2) {
return o1.compareTo(o2);
@@ -339,4 +378,97 @@ public class JsonbTypesTest {
period, localDateTime, localDate, offsetDateTime, offsetTime);
}
}
+
+ public static class DateTypes {
+ private Date date;
+ private Calendar calendar;
+ private GregorianCalendar gregorianCalendar;
+ private Instant instant;
+ private LocalDateTime localDateTime;
+ private LocalDate localDate;
+ private OffsetDateTime offsetDateTime;
+
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public Calendar getCalendar() {
+ return calendar;
+ }
+
+ public void setCalendar(Calendar calendar) {
+ this.calendar = calendar;
+ }
+
+ public GregorianCalendar getGregorianCalendar() {
+ return gregorianCalendar;
+ }
+
+ public void setGregorianCalendar(GregorianCalendar gregorianCalendar) {
+ this.gregorianCalendar = gregorianCalendar;
+ }
+
+ public Instant getInstant() {
+ return instant;
+ }
+
+ public void setInstant(Instant instant) {
+ this.instant = instant;
+ }
+
+ public LocalDateTime getLocalDateTime() {
+ return localDateTime;
+ }
+
+ public void setLocalDateTime(LocalDateTime localDateTime) {
+ this.localDateTime = localDateTime;
+ }
+
+ public LocalDate getLocalDate() {
+ return localDate;
+ }
+
+ public void setLocalDate(LocalDate localDate) {
+ this.localDate = localDate;
+ }
+
+ public OffsetDateTime getOffsetDateTime() {
+ return offsetDateTime;
+ }
+
+ public void setOffsetDateTime(OffsetDateTime offsetDateTime) {
+ this.offsetDateTime = offsetDateTime;
+ }
+
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final Types types = Types.class.cast(o);
+ return
+ Objects.equals(date, types.date) &&
+ Objects.equals(calendar, types.calendar) &&
+ Objects.equals(gregorianCalendar, types.gregorianCalendar) &&
+ Objects.equals(instant, types.instant) &&
+ Objects.equals(localDateTime, types.localDateTime) &&
+ Objects.equals(localDate, types.localDate) &&
+ Objects.equals(offsetDateTime, types.offsetDateTime);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ date, calendar, gregorianCalendar, instant, localDateTime, localDate, offsetDateTime);
+ }
+ }
}