Skip to content

Commit

Permalink
GH-773 - Add zoneId to [At]DateString.
Browse files Browse the repository at this point in the history
This attribute will be used for converting attributes to Strings that cannot be converted without the knowledge of a valid zone (for example `java.time.Instant`).

The zoneId defaults to `UTC` which is the same as the default patterns use. The default is required to not break existing users.

For the time being, the zoneId is only used in the `org.neo4j.ogm.typeconversion.InstantStringConverter`.

This closes #771.
  • Loading branch information
michael-simons authored Mar 6, 2020
1 parent 3d28721 commit 935f9f5
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,19 @@

String ISO_8601 = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";

String DEFAULT_ZONE_ID = "UTC";

String value() default ISO_8601;

/**
* Some temporals like {@link java.time.Instant}, representing an instantaneous point in time cannot be formatted
* with a given {@link java.time.ZoneId}. In case you want to format an instant or similar with a default pattern,
* we assume a zone with the given id and default to {@literal UTC} which is the same assumption that the predefined
* patterns in {@link java.time.format.DateTimeFormatter} take.
* @return The zone id to use when applying a custom pattern to an instant temporal.
*/
String zoneId() default DEFAULT_ZONE_ID;

/**
* Toggle lenient conversion mode by setting this flag to true (defaults to false).
* Has to be supported by the corresponding converter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Object getConverter(Class<?> fieldType) {
if (fieldType == Date.class) {
return new DateStringConverter(format, isLenientConversion(dateStringConverterInfo));
} else if (fieldType == Instant.class) {
return new InstantStringConverter(format, isLenientConversion(dateStringConverterInfo));
return new InstantStringConverter(format, dateStringConverterInfo.get("zoneId"), isLenientConversion(dateStringConverterInfo));
} else {
throw new MappingException("Cannot use @DateString with attribute of type " + fieldType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.neo4j.ogm.typeconversion;

import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

import org.apache.commons.lang3.StringUtils;
Expand All @@ -45,11 +46,11 @@ public InstantStringConverter() {
this.lenient = false;
}

public InstantStringConverter(String userDefinedFormat, boolean lenient) {
public InstantStringConverter(String userDefinedFormat, String zoneId, boolean lenient) {

this.formatter = DateString.ISO_8601.equals(userDefinedFormat) ?
DateTimeFormatter.ISO_INSTANT :
DateTimeFormatter.ofPattern(userDefinedFormat);
DateTimeFormatter.ofPattern(userDefinedFormat).withZone(ZoneId.of(zoneId));
this.lenient = lenient;
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ public class Memo {
@DateString
private Instant actionedAsInstant;

@DateString(value = "yyyy-MM-dd HH:mm:ss")
private Instant actionedAsInstantWithCustomFormat1;

@DateString(value = "yyyy-MM-dd HH:mm:ss", zoneId = "Europe/Berlin")
private Instant actionedAsInstantWithCustomFormat2;

// uses default ISO 8601 date format
private Date[] escalations;

Expand Down Expand Up @@ -154,4 +160,20 @@ public Set<Date> getImplementations() {
public void setImplementations(Set<Date> implementations) {
this.implementations = implementations;
}

public Instant getActionedAsInstantWithCustomFormat1() {
return actionedAsInstantWithCustomFormat1;
}

public void setActionedAsInstantWithCustomFormat1(Instant actionedAsInstantWithCustomFormat1) {
this.actionedAsInstantWithCustomFormat1 = actionedAsInstantWithCustomFormat1;
}

public Instant getActionedAsInstantWithCustomFormat2() {
return actionedAsInstantWithCustomFormat2;
}

public void setActionedAsInstantWithCustomFormat2(Instant actionedAsInstantWithCustomFormat2) {
this.actionedAsInstantWithCustomFormat2 = actionedAsInstantWithCustomFormat2;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void directoryShouldBeScanned() {
public void nestedDirectoryShouldBeScanned() {
final DomainInfo domainInfo = DomainInfo.create("org.neo4j.ogm.domain.convertible");

assertThat(domainInfo.getClassInfoMap()).hasSize(21);
assertThat(domainInfo.getClassInfoMap()).hasSize(20);

Set<String> classNames = domainInfo.getClassInfoMap().keySet();
assertThat(classNames.contains("org.neo4j.ogm.domain.convertible.bytes.Photo")).isTrue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
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.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

Expand All @@ -52,13 +53,14 @@

/**
* @author Luanne Misquitta
* @author Michael J. Simons
*/
public class ConvertibleIntegrationTest extends TestContainersTestBase {

private static Session session;

@BeforeClass
public static void init() throws IOException {
public static void init() {
session = new SessionFactory(getDriver(), "org.neo4j.ogm.domain.convertible").openSession();
}

Expand All @@ -67,10 +69,7 @@ public void teardown() {
session.purgeDatabase();
}

/**
* @see DATAGRAPH-550
*/
@Test
@Test // DATAGRAPH-550
public void shouldSaveAndRetrieveEnums() {
List<Education> completed = new ArrayList<>();
completed.add(Education.HIGHSCHOOL);
Expand Down Expand Up @@ -121,7 +120,7 @@ public void shouldSaveAndRetrieveEnumsAsResult() {
.equals(Education.PHD)).isTrue();
}

@Test // DATAGRAPH-550 GH-758
@Test // DATAGRAPH-550 GH-758 GH-771
public void shouldSaveAndRetrieveDates() {
SimpleDateFormat simpleDateISO8601format = new SimpleDateFormat(DateString.ISO_8601);
simpleDateISO8601format.setTimeZone(TimeZone.getTimeZone("UTC"));
Expand Down Expand Up @@ -186,6 +185,32 @@ public void shouldSaveAndRetrieveDates() {
assertThat(loadedCal.get(Calendar.YEAR)).isEqualTo(date100000.get(Calendar.YEAR));
}

@Test // GH-771
public void instantsWithCustomFormatAndTZShouldWork() {

Memo memo = new Memo();
memo.setMemo("theMemo");
Instant actionedInstant = ZonedDateTime.of(2020, 3, 6, 16, 6, 23, 0, ZoneId.of("Europe/Berlin")).toInstant();
memo.setActionedAsInstant(actionedInstant);
memo.setActionedAsInstantWithCustomFormat1(actionedInstant);
memo.setActionedAsInstantWithCustomFormat2(actionedInstant);
session.save(memo);

Memo loadedMemo = session.loadAll(Memo.class, new Filter("memo", ComparisonOperator.EQUALS, "theMemo"))
.iterator().next();

assertThat(loadedMemo.getActionedAsInstantWithCustomFormat1()).isEqualTo(actionedInstant);
assertThat(loadedMemo.getActionedAsInstantWithCustomFormat2()).isEqualTo(actionedInstant);

Iterable<Map<String, Object>> results = session.query(
"MATCH (m:Memo) WHERE id(m) = $id RETURN m.actionedAsInstantWithCustomFormat1 as a1, m.actionedAsInstantWithCustomFormat2 as a2",
Collections.singletonMap("id", loadedMemo.getId())).queryResults();
assertThat(results).hasSize(1);
Map<String, Object> result = results.iterator().next();
assertThat(result).containsEntry("a1", "2020-03-06 15:06:23");
assertThat(result).containsEntry("a2", "2020-03-06 16:06:23");
}

@Test
public void shouldSaveAndRetrieveJava8Dates() {

Expand All @@ -209,7 +234,7 @@ public void shouldSaveAndRetrieveJava8Dates() {
}

@Test
public void shouldSaveListOfLocalDate() throws Exception {
public void shouldSaveListOfLocalDate() {

Java8DatesMemo memo = new Java8DatesMemo();

Expand All @@ -231,7 +256,7 @@ public void shouldSaveListOfLocalDate() throws Exception {
}

@Test
public void shouldSaveLocalDateTime() throws Exception {
public void shouldSaveLocalDateTime() {

Java8DatesMemo memo = new Java8DatesMemo();

Expand Down Expand Up @@ -362,27 +387,4 @@ public void shouldSaveAndRetrieveIntegerFloats() {
loadedAccount = session.load(Account.class, account.getId());
assertThat(loadedAccount.getLimit()).isEqualTo(account.getLimit());
}

public void assertSameArray(Object[] as, Object[] bs) {

if (as == null || bs == null) {
fail("null arrays not allowed");
}
if (as.length != bs.length) {
fail("arrays are not same length");
}

for (Object a : as) {
boolean found = false;
for (Object b : bs) {
if (b.toString().equals(a.toString())) {
found = true;
break;
}
}
if (!found) {
fail("array contents are not the same: " + as + ", " + bs);
}
}
}
}

0 comments on commit 935f9f5

Please sign in to comment.