Hot questions for Using Joda-Time in spring boot

Question:

I'm using Spring Boot 1.5.6 with Jackson 2.8.8. When deserializing the answer of a REST call, Jackson fails with the following exception:

JSON parse error: Can not construct instance of org.joda.time.DateTime: no String-argument constructor/factory method to deserialize from String value ('2018-03-19T12:05:21.885+01:00')

It's true there is no String constructor, only an Object constructor in the DateTime object.

I included the jackson-datatype-joda dependency in my build.gradle file. These are the respective lines from the build.gradle:

compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion
compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: jacksonVersion
compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5', version: jacksonVersion
compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-joda', version: jacksonVersion

Is there any additional configuration I need to do?

PS: If I put the date String into a new DateTime("2018-03-19T12:05:21.885+01:00") it works fine.

Any ideas? Cheers!


Answer:

Did you register the JodaModule module in your ObjectMapper?

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());

Question:

I'm trying to use Joda LocalDateTime with JPA in my Spring Boot configuration. To do so, I have read this tutorial and I am able to use DateTime with no problem at all. The problem comes when I try to use LocalDateTime. For some reason, when fetching entities from my database, an error is thrown:

java.lang.NoSuchMethodError: org.joda.time.DateTime.toLocalDateTime()Lorg/joda/time/LocalDateTime;

I have checked and the method toLocalDateTime does exist in Joda Time:

So, what the heck? D:

I have also tried setting the @Type explicity as the tutorial suggests but got the same result:

@Column(name = "myLocalDateTime")
@Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime localDateTime;

To retrieve the data from my database I'm simply using a spring-data repository.

Here are some info about my envinroment:

Gradle dependencies:

// ...
compile 'org.hibernate:hibernate-core:5.1.16.Final'
compile 'org.hibernate:hibernate-validator:5.2.3.Final'
compile 'org.hibernate:hibernate-entitymanager:5.1.0.Final'
compile 'joda-time:joda-time'
compile group: 'org.jadira.usertype', name: 'usertype.core', version: '5.0.0.GA'

application.properties:

# ...
spring.jpa.properties.jadira.usertype.autoRegisterUserTypes = true

I am running on Java 7 with Spring Boot 1.5.15.RELEASE

Any help is much appreciated.

EDIT

Funny enough, persisting a record with a LocalDateTime field in my database works just fine... :/

Here's the list of libs in my project:

Gradle: antlr:antlr:2.7.7
Gradle: aopalliance:aopalliance:1.0
Gradle: ch.qos.logback:logback-classic:1.1.11
Gradle: ch.qos.logback:logback-core:1.1.11
Gradle: com.fasterxml.jackson.core:jackson-annotations:2.8.0
Gradle: com.fasterxml.jackson.core:jackson-core:2.8.11
Gradle: com.fasterxml.jackson.core:jackson-databind:2.8.11.2
Gradle: com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.8.11
Gradle: com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.8.11
Gradle: com.fasterxml.woodstox:woodstox-core:5.0.3
Gradle: com.fasterxml:classmate:1.3.4
Gradle: com.github.virtuald:curvesapi:1.04
Gradle: com.google.code.findbugs:jsr305:1.3.9
Gradle: com.google.guava:guava:19.0
Gradle: com.infradna.tool:bridge-method-annotation:1.13
Gradle: com.jayway.jsonpath:json-path:2.2.0
Gradle: com.mysema.commons:mysema-commons-lang:0.2.4
Gradle: com.querydsl:querydsl-core:4.1.4
Gradle: com.querydsl:querydsl-jpa:4.1.4
Gradle: com.sun.istack:istack-commons-runtime:2.16
Gradle: com.sun.xml.bind:jaxb-core:2.2.7
Gradle: com.sun.xml.bind:jaxb-impl:2.2.7
Gradle: com.sun.xml.fastinfoset:FastInfoset:1.2.12
Gradle: com.vaadin.external.google:android-json:0.0.20131108.vaadin1
Gradle: commons-beanutils:commons-beanutils:1.9.3
Gradle: commons-codec:commons-codec:1.10
Gradle: commons-collections:commons-collections:3.2.2
Gradle: dom4j:dom4j:1.6.1
Gradle: io.netty:netty-all:4.1.5.Final
Gradle: javax.inject:javax.inject:1
Gradle: javax.servlet:javax.servlet-api:3.1.0
Gradle: javax.transaction:javax.transaction-api:1.2
Gradle: javax.validation:validation-api:1.1.0.Final
Gradle: javax.xml.bind:jaxb-api:2.2.7
Gradle: javax.xml.bind:jsr173_api:1.0
Gradle: javax.xml.stream:stax-api:1.0-2
Gradle: joda-time:joda-time:2.9.9
Gradle: junit:junit:4.12
Gradle: net.minidev:accessors-smart:1.1
Gradle: net.minidev:json-smart:2.2.1
Gradle: org.apache.activemq:artemis-commons:1.5.6
Gradle: org.apache.activemq:artemis-core-client:1.5.6
Gradle: org.apache.activemq:artemis-jms-client:1.5.6
Gradle: org.apache.activemq:artemis-selector:1.5.6
Gradle: org.apache.commons:commons-collections4:4.1
Gradle: org.apache.commons:commons-lang3:3.4
Gradle: org.apache.geronimo.specs:geronimo-jms_2.0_spec:1.0-alpha-2
Gradle: org.apache.geronimo.specs:geronimo-json_1.0_spec:1.0-alpha-1
Gradle: org.apache.geronimo.specs:geronimo-jta_1.1_spec:1.1.1
Gradle: org.apache.johnzon:johnzon-core:0.9.5
Gradle: org.apache.poi:poi-ooxml-schemas:3.17
Gradle: org.apache.poi:poi-ooxml:3.17
Gradle: org.apache.poi:poi:3.17
Gradle: org.apache.santuario:xmlsec:2.0.7
Gradle: org.apache.xmlbeans:xmlbeans:2.6.0
Gradle: org.aspectj:aspectjweaver:1.8.13
Gradle: org.assertj:assertj-core:2.6.0
Gradle: org.atteo:evo-inflector:1.2.2
Gradle: org.codehaus.woodstox:stax2-api:3.1.4
Gradle: org.codehaus.woodstox:woodstox-core-asl:4.4.1
Gradle: org.hamcrest:hamcrest-core:1.3
Gradle: org.hamcrest:hamcrest-library:1.3
Gradle: org.hibernate.common:hibernate-commons-annotations:5.0.1.Final
Gradle: org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final
Gradle: org.hibernate:hibernate-core:5.1.16.Final
Gradle: org.hibernate:hibernate-entitymanager:5.1.0.Final
Gradle: org.hibernate:hibernate-validator:5.2.3.Final
Gradle: org.jadira.usertype:usertype.core:5.0.0.GA
Gradle: org.jadira.usertype:usertype.spi:5.0.0.GA
Gradle: org.javassist:javassist:3.21.0-GA
Gradle: org.jboss.logging:jboss-logging:3.3.2.Final
Gradle: org.jboss:jandex:2.0.3.Final
Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.70
Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.70
Gradle: org.jetbrains:annotations:13.0
Gradle: org.jgroups:jgroups:3.6.9.Final
Gradle: org.mockito:mockito-core:1.10.19
Gradle: org.objenesis:objenesis:2.1
Gradle: org.ow2.asm:asm:5.0.3
Gradle: org.skyscreamer:jsonassert:1.4.0
Gradle: org.slf4j:jcl-over-slf4j:1.7.25
Gradle: org.slf4j:jul-to-slf4j:1.7.25
Gradle: org.slf4j:log4j-over-slf4j:1.7.25
Gradle: org.slf4j:slf4j-api:1.7.25
Gradle: org.springframework.boot:spring-boot-autoconfigure:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-devtools:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-starter-aop:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-starter-artemis:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-starter-data-jpa:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-starter-data-rest:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-starter-jdbc:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-starter-logging:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-starter-security:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-starter-test:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-starter-web-services:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-starter-web:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-starter:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-test-autoconfigure:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot-test:1.5.15.RELEASE
Gradle: org.springframework.boot:spring-boot:1.5.15.RELEASE
Gradle: org.springframework.data:spring-data-commons:1.13.14.RELEASE
Gradle: org.springframework.data:spring-data-jpa:1.11.14.RELEASE
Gradle: org.springframework.data:spring-data-rest-core:2.6.14.RELEASE
Gradle: org.springframework.data:spring-data-rest-webmvc:2.6.14.RELEASE
Gradle: org.springframework.hateoas:spring-hateoas:0.23.0.RELEASE
Gradle: org.springframework.plugin:spring-plugin-core:1.2.0.RELEASE
Gradle: org.springframework.security:spring-security-config:4.2.7.RELEASE
Gradle: org.springframework.security:spring-security-core:4.2.7.RELEASE
Gradle: org.springframework.security:spring-security-test:4.2.7.RELEASE
Gradle: org.springframework.security:spring-security-web:4.2.7.RELEASE
Gradle: org.springframework.ws:spring-ws-core:2.4.2.RELEASE
Gradle: org.springframework.ws:spring-xml:2.4.2.RELEASE
Gradle: org.springframework:spring-aop:4.3.18.RELEASE
Gradle: org.springframework:spring-aspects:4.3.18.RELEASE
Gradle: org.springframework:spring-beans:4.3.18.RELEASE
Gradle: org.springframework:spring-context:4.3.18.RELEASE
Gradle: org.springframework:spring-core:4.3.18.RELEASE
Gradle: org.springframework:spring-expression:4.3.18.RELEASE
Gradle: org.springframework:spring-jdbc:4.3.18.RELEASE
Gradle: org.springframework:spring-jms:4.3.18.RELEASE
Gradle: org.springframework:spring-messaging:4.3.18.RELEASE
Gradle: org.springframework:spring-orm:4.3.18.RELEASE
Gradle: org.springframework:spring-oxm:4.3.18.RELEASE
Gradle: org.springframework:spring-test:4.3.18.RELEASE
Gradle: org.springframework:spring-tx:4.3.18.RELEASE
Gradle: org.springframework:spring-web:4.3.18.RELEASE
Gradle: org.springframework:spring-webmvc:4.3.18.RELEASE
Gradle: org.yaml:snakeyaml:1.17
Gradle: stax:stax-api:1.0.1

Answer:

The problem was solved using the solution described here.

As the answer says, WebLogic includes its own joda.time on the classpath and uses it unless I do specific tell it not to.

Question:

There is a Date of birth property in my request POJO which is String. In my response POJO, date of birth property is joda's DateTime. I converted it into DateTime by using "new DateTime(requestModel.getDateOfBirth())". Though, it is only accepting the date format(which is in String) as "yyyy-mm-dd". When I change the request date format, it throws "500

Blockquote

". Here is my POJOs:

RequestModel.java

public class RequestModel {
@NotNull(message = "dateOfBirth cannot be null")
private String dateOfBirth;

public RequestModel(String dateOfBirth) {
    super();
    this.dateOfBirth = dateOfBirth;
}
public String getDateOfBirth() {
    return dateOfBirth;
}

}

ResponseModel.java

public class ResponseModel {
private DateTime dateOfBirth;
public ResponseModel( DateTime dateOfBirth) {
    super();
    this.dateOfBirth = dateOfBirth;
}
public DateTime getDateOfBirth() {
    return dateOfBirth;
}
}

MyController.java

    @PostMapping(consumes = {
                            MediaType.APPLICATION_JSON_VALUE,
                            MediaType.APPLICATION_XML_VALUE
                        },
             produces = {
                        MediaType.APPLICATION_JSON_VALUE,
                        MediaType.APPLICATION_XML_VALUE
                        })
public ResponseEntity<ResponseModel> create(@Valid @RequestBody RequestModel requestModel) {
    ResponseModel response = new ResponseModel(new DateTime(requestModel.getDateOfBirth()));
    return new ResponseEntity<ResponseModel>(response, HttpStatus.CREATED);
}

This is the post JSON request which works fine:

{
"dateOfBirth" : "2012-12-12"
}

The following request body throws error:

{
"dateOfBirth" : "12-12-2012",
}

The error is:

{
"timestamp": "2020-03-14T19:09:51.647+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Invalid format: \"12-12-2012\" is malformed at \"12\"",
"path": "/myPath/"
}

I want to know how can I improve the controller logic or the design to accept any Date format in the API? Thanks for helping out


Answer:

First, since you are getting a date without time of day, you should prefer Joda-Time’s LocalDate over DateTime.

Second, the format that the constructor accepts (and the LocalDate constructor as well) is ISO 8601, the international standard. I should say that requiring your client to use this format in their request would be nice and recommended.

But if you insist: Use a formatter for parsing the string from the request into a LocalDate.

    DateTimeFormatter dateFormatter = DateTimeFormat.forPattern("dd-MM-yyyy");

    String requestDateOfBirth = "12-12-2012";
    LocalDate dateOfBirth = LocalDate.parse(requestDateOfBirth, dateFormatter);

    System.out.println("Date of birth: " + dateOfBirth);

Output from this snippet is:

Date of birth: 2012-12-12

In 12-12-2012 I didn’t know which was day of month and which was month, but you can swap dd for day of month and MM for month in the format pattern string if required.

And finally, if this is new code, you will probably prefer to use java.time, the modern Java date and time API, over Joda-Time. The official recommendation from the Joda-Time project says to migrate.

Links

Question:

I'm working on Rest API in Spring Boot and we are using fantastic library Joda Time. Since my server is configured to work in UTC timezone there is no need to use in whole application DateTime which have DateTimeZone information in it. We prefer to use LocalDateTime for storing all dates in system.

Now the question is about printing LocalDateTime in IOS 8601 format. Take a look at the code below:

TimeZone.setDefault(TimeZone.getTimeZone("UTC")); // server timezone
DateTimeFormatter fmt = ISODateTimeFormat.dateTime(); // yyyy-MM-dd'T'HH:mm:ss.SSSZZ
LocalDateTime createdAtLocalDateTime =  LocalDateTime.now();
DateTime createdAtDateTime =  user.getCreatedAt().toDateTime(DateTimeZone.UTC);
logger.info("DT: {}", fmt.print(createdAtDateTime));
logger.info("LDT: {}", fmt.print(createdAtLocalDateTime));

This outputs such results:

DT: 2019-03-20T20:19:19.691Z
LDT: 2019-03-20T20:37:00.642

So there is no Z at the end when serializing LocalDateTime but there is a Z when we are serializing DateTime in UTC time zone.

Now the question: How I can configure formatter to output this TimeZone information at the end (this Z letter) during serializing LocalDateTime instances. I know it is always in UTC but one of our consuming libraries are expecting this timezone information and we can not change the code in it unfortunately.

Better question: How to configure Jackson ObjectMapper to serialize LocalDateTime into json with this Z information at the end?

Adding to the formatter fmt.withZoneUTC() does not work.


Answer:

You can append a text literal to your formatter:

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        .append(ISODateTimeFormat.dateTime())
        .appendLiteral('Z')
        .toFormatter();

Question:

I used to apply org.joda.time.Interval to represent a time interval with fixed start and end times (different from a duration which is independent from specific times) to exchange via REST and store energy schedules in a Spring Boot server application (2.2.2.RELEASE).

I tried different ways to store a org.joda.time.Interval field of an object via JPA/Hibernate:

  • jadira (7.0.0.CR1) with annotation above the field definition (@Type(type = "org.jadira.usertype.dateandtime.joda.PersistentInterval"))
  • jadira (7.0.0.CR1) with property spring.jpa.properties.jadira.usertype.autoRegisterUserTypes=true set

However, I always get

@OneToOne or @ManyToOne on de.iwes.enavi.cim.schedule51.Schedule_MarketDocument.matching_Time_Period_timeInterval references an unknown entity: org.joda.time.Interval

Questions:

  1. Is there a way to get hibernate working with org.joda.time.Interval?
  2. What is the preferred solution to migrate from org.joda.time.Interval as java.time does not have a similar interval class?

Answer:

I ended up writing a custom class:

@Entity
public class FInterval {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO) 
    private long id;

    @Column
    private long startMillis;

    @Column
    private long endMillis;

    public FInterval() {
    }

    public long getStartMillis() {
        return startMillis;
    }

    public void setStartMillis(long start) {
        this.startMillis = start;
    }

    public long getEndMillis() {
        return endMillis;
    }

    public void setEndMillis(long end) {
        this.endMillis = end;
    }

    public FInterval(Interval entity) {
            this.startMillis = entity.getStartMillis();
            this.endMillis = entity.getEndMillis();
        }

    public Interval getInterval() {
        return new Interval(this.startMillis,this.endMillis);
    }
}

and an attribute converter:

@Converter(autoApply = true)
public class IntervalAttributeConverter implements AttributeConverter<Interval, FInterval> {

    @Override
    public FInterval convertToDatabaseColumn(Interval attribute) {
        return new FInterval(attribute);
    }

    @Override
    public Interval convertToEntityAttribute(FInterval dbData) {
        return dbData.getInterval();
    }
}