0

I have a json formatted response Date to the controller that is like this:

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "America/Chicago")
private Date date;

So when I make a post call, it would look like:

"date": "2021-08-20 14:17:43"

So the response string would look something like this {"date":"2021-05-21 14:23:44"}. In JUnit, I am manually creating a response object and setting the Date object so I can then use Gson to turn it into a string and then assert that the two are equal. I am trying to match this in my SpringMVC JUnit test case by trying to do:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
        
String formattedDate = sdf.format(new Date());
LocalDate localDate = LocalDate.parse(formattedDate);
        
Date date = Date.from(localDate.atStartOfDay(ZoneId.of("America/Chicago")).toInstant());

But it is having an error parsing it because of the space between yyyy-MM-dd and HH:mm:ss:

java.time.format.DateTimeParseException: Text '2021-08-20 14:23:44' could not be parsed, unparsed text found at index 10

I think I may be doing this inefficiently, so I was wondering if there was a more simple way to make a Date object that would match the format of @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "America/Chicago")

I'm trying to match the response body so it passes via mockito.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
stackerstack
  • 243
  • 4
  • 16
  • Instead of SimpleDateFormat, You can instead use java.time.format.DateTimeFormatter like so: `String formattedDate = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of("America/Chicago")).format(LocalDateTime.now());` – nmina Aug 20 '21 at 20:14
  • Is there a way to get this to be a ```Date``` object? Because I need to do use the setter to set the date object. – stackerstack Aug 20 '21 at 20:18
  • I recommend you don’t use `Date`. That class is poorly designed and long outdated. Instead use only `LocalDateTime` and/or `Instant` from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Aug 21 '21 at 02:53
  • Does this answer your question? [want current date and time in “dd/MM/yyyy HH:mm:ss.SS” format](https://stackoverflow.com/questions/8745297/want-current-date-and-time-in-dd-mm-yyyy-hhmmss-ss-format)? And/or [this](https://stackoverflow.com/questions/1812700/timezone-problem-in-java)? – Ole V.V. Aug 21 '21 at 16:13
  • Which scenario exactly do you want your unit test to test? Creation of a response object in Java? Converting a response object to JSON? Converitng JSON to a Java object? Sorry, it’s unclear. – Ole V.V. Aug 23 '21 at 05:08
  • So the response string would look something like this ```{"date":"2021-05-21 14:23:44"}```. In JUnit, I am manually creating a response object and setting the ```Date``` object so I can then use Gson to turn it into a string and then assert that the two are equal. – stackerstack Aug 23 '21 at 13:43
  • Thanks, now I understand better. Assuming the Gson respects the `@JsonFormat` annotation you’re set and need not care about the format of the `Date`. Gson cares about that for you. You will get the correct string, there is no way you could get anything else. Did you try it? – Ole V.V. Aug 23 '21 at 19:48
  • As an aside I might not want to assert that the strings are exactly equal. They should be allowed to have different spaces, for example, as long as they contain equal JSON. `{ "date" : "2021-05-21 14:23:44" }` means the same as `{"date":"2021-05-21 14:23:44"}`. You could even risk that the strings that are equal now would be different with the next version of Gson even though the contents would still be correct. – Ole V.V. Aug 24 '21 at 04:52

4 Answers4

1

Do not mix the modern and the legacy Date-Time API

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String strDate = "2021-08-20 14:17:43";
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("u-M-d H:m:s", Locale.ENGLISH);
        LocalDateTime ldt = LocalDateTime.parse(strDate, dtf);
        System.out.println(ldt);

        // Get the required Instant
        ZonedDateTime zdtUtc = ldt.atZone(ZoneOffset.UTC);
        ZonedDateTime zdtChicago = zdtUtc.withZoneSameInstant(ZoneId.of("America/Chicago"));
        Instant instant = zdtChicago.toInstant();
        System.out.println(instant);
    }
}

Output:

2021-08-20T14:17:43
2021-08-20T14:17:43Z

ONLINE DEMO

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*. However, for any reason, if you need to convert this object of Instant to an object of java.util.Date, you can do so as follows:

Date date = Date.from(instant);

Learn more about the modern Date-Time API from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • The problem with this, is that the Date object shows as ```Fri Aug 20 16:19:47 EDT 2021```. But my responsebody shows the date via ```@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "America/Chicago")``` as ```2021-08-20 14:17:43```, so the two formats will not line up – stackerstack Aug 20 '21 at 20:21
  • I pretty much just need a ```Date``` object that has the format ```yyyy-MM-dd HH:mm:ss``` with the timezone as ```America/Chicago```. – stackerstack Aug 20 '21 at 20:21
  • @stackerstack - I've updated it as per your requirement. Feel free to comment in case of any further issues/doubts. – Arvind Kumar Avinash Aug 20 '21 at 20:25
  • Is there a way to update the ```String strDate```, because it should be a ```new Date()``` that gets passed in, not a hard coded pre-formatted String date that is passed – stackerstack Aug 20 '21 at 20:29
  • @stackerstack - A `java.util.Date` object simply represents an instant on the timeline — a wrapper around the number of milliseconds since the UNIX epoch (January 1, 1970, 00:00:00 GMT); it does not have any timezone or **format**. – Arvind Kumar Avinash Aug 20 '21 at 20:31
  • So there is no way for it to match the responsebody's date format? – stackerstack Aug 20 '21 at 20:32
  • @stackerstack - Formats can be matched only when you convert a `Date` into a `String` using a formatter. – Arvind Kumar Avinash Aug 20 '21 at 20:35
  • Right, but my response model's setter is naturally a ```set(Date date)```, not a ```set(String string)```. The JUnit error is basically saying ```org.opentest4j.AssertionFailedError: expected: <{"date":"Aug 20, 2021, 4:33:59 PM"}> but was: <{"date":"2021-08-20 15:33:59",}>``` – stackerstack Aug 20 '21 at 20:36
  • Unless there is another way to go about this, i am quite lost on how to do it :/ – stackerstack Aug 20 '21 at 20:37
0

you could be missing date deserializer

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
vaibhavsahu
  • 612
  • 2
  • 7
  • 19
0

Posting this to only try and satisfy what you're trying to achieve. But you should follow @Arvind's answer:

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone;


SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
String formattedDate = sdf.format(new Date());

// Updated the lines below
LocalDateTime localDateTime = LocalDateTime.parse(formattedDate, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Date date = Date.from(localDateTime.atZone(ZoneId.of("America/Chicago")).toInstant());

nmina
  • 276
  • 3
  • 8
0

Best is if you can skip the Date class completely and in your response use Instant or ZonedDateTime from java.time, the modern Java date and time API.

If you cannot avoid using the outdated Date class

… I was wondering if there was a more simple way to make a Date object that would match the format of @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "America/Chicago")

Essential edit: It depends very much on what you mean by match the format. A Date can neither have a format nor a time zone. The string in your JSON has got the format mentioned. The Date has not since this would no be possible. The time zone, America/Chicago, is not present neither in JSON nor in the Date. It is only used for converting between the two. Two Date objects are equal if they denote the same point in time, there is nothing more to it. When you ask about formatting the Date to match the @JsonFormat, this necessarily means formatting into a string.

To convert a string like 2021-08-20 14:23:44 into an old-fashioned Date object I would first define the format and time zone statically:

private static final DateTimeFormatter FORMATTER
        = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss", Locale.ROOT);
private static final ZoneId ZONE = ZoneId.of("America/Chicago");

And then do:

    String responseDateString = "2021-08-20 14:23:44";

    Instant inst = LocalDateTime.parse(responseDateString, FORMATTER)
            .atZone(ZONE)
            .toInstant();
    Date oldfashionedDate = Date.from(inst);
    
    System.out.println(oldfashionedDate);

Output in my time zone is:

Fri Aug 20 21:23:44 CEST 2021

If I set my time zone to America/Chicago before running, it’s easier to see that the result is correct:

Fri Aug 20 14:23:44 CDT 2021

What went wrong in your code?

First you are correct that formatting a Date into a string only to parse it back is over-complicating things. Second you noticed that your exception came from this line:

LocalDate localDate = LocalDate.parse(formattedDate);

A LocalDate is a date without time of day. So its one-arg parse method expects only 2021-08-20 in the string, nothing more. It was complaining about the space, not because it was a space but just because there were more characters after the expected ones at all.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • So the format for the Date from the instant is showing up as ```Fri Aug 20 14:23:44 CDT 2021``` in JUnit, while the result from the response body will show up as ```2021-08-20 14:23:44``` because of the ```@JsonFormat```. Is there just no way that these two will match and equal out because I need to do assertEquals on the two. – stackerstack Aug 21 '21 at 15:21
  • That’s true, @stackerstack. But the one in JSON is a string, not a `Date`, so they wouldn’t be equal anyway (JSON is a textual format). – Ole V.V. Aug 21 '21 at 15:29
  • @stackerstack I was in doubt, there may be a misunderstanding in your question, I don’t know. I have edited near the beginning of my answer to clear it up just in case it ever was there. Read and learn. – Ole V.V. Aug 22 '21 at 04:53