Set Current TimeZone to #JsonFormat timezone value - java

#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy", timezone = "Asia/Kolkata")
private Date activationDate;
From the above java code, I want to set timezone value as Current System timezone using below:
TimeZone.getDefault().getID() - it returns value as "Asia/Kolkata"
But if i set this code to json format
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy", timezone = TimeZone.getDefault().getID())
I am getting error like "The value for annotation attribute JsonFormat.timezone must be a constant expression"
Pls help me to solve this issue.
Thanks in advance,
Vishnu

You can use JsonFormat.DEFAULT_TIMEZONE, after properly configuring the ObjectMapper:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy", timezone = JsonFormat.DEFAULT_TIMEZONE)
From the docs:
Value that indicates that default TimeZone (from deserialization or serialization context) should be used: annotation does not define value to use.
NOTE: default here does NOT mean JVM defaults but Jackson databindings default, usually UTC, but may be changed on ObjectMapper.
In order to configure the ObjectMapper:
#Configuration
public class MyApp {
#Autowired
public void configureJackson(ObjectMapper objectMapper) {
objectMapper.setTimeZone(TimeZone.getDefault());
}
}
To set the default TimeZone on your application use this JVM property:
-Duser.timezone=Asia/Kolkata

You cannot assign timezone value a dynamic or a runtime value. It should be constant or a compile time value and enums too accepted.
So you should assign a constant to timezone. like below.
private static final String MY_TIME_ZONE="Asia/Kolkata";
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy", timezone = MY_TIME_ZONE);

You can use enumeration in order to possibly enrich you time zones that you would use. A solution using enumeration is the following enumeration class implementation.
package <your package goes here>;
import java.util.TimeZone;
public enum TimeZoneEnum {
DEFAULT(TimeZone.getDefault()),
ASIA_KOLKATA = (TimeZone.getTimeZone("Africa/Abidjan")),
//other timezones you maybe need
...
private final TimeZone tz;
private TimeZoneEnum(final TimeZone tz)
{
this.tz = tz;
}
public final TimeZone getTimeZone()
{
return tz;
}
}
Then you can utilize you enumeration like below:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy", timezone = TimeZoneEnum.ASIA_KOLKATA )

Related

Formatting date retrieved from JSON file in Java

I import a JSON file and try to format date properly. But every time I try a different combination as mentioned on this or some similar threads, I always get "Cannot deserialize value of type java.time.LocalDate from String "09.09.1977": Failed to deserialize java.time.LocalDate" error.
Before fixing the problem, I think I need to be clarified about the following questions (I need to format Date in "dd.MM.yyy" format):
1) Does the date field have in the JSON file to be a specific format?
2) I have entity and dto classes where my date field is located. Should I apply related format annotations, etc. in these classes on the date field as shown below?
Here are some examples I tried:
#DateTimeFormat(pattern = "dd.MM.yyyy", iso = DateTimeFormat.ISO.DATE_TIME)
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd.MM.yyyy")
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
#JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDate startDate;
Or how can I format the read json file in "dd.MM.yyyy"? Here is the related code block where I retrieve the file and convert from EmployeeRequest request to Employee:
final List<Employee> employees = requests.stream()
.map(EmployeeRequestMapper::mapToEntity)
.toList();
[tl;dr] Use the same data type on class level and in annotations
You currently try to use an automatic de-/serialization of a String that represents a date (only year, month of year and day of month). For this to work automatically, you need to annotate the class member with the classes that are responsible for serialization and deserialization. The one you are currently using is a LocalDateTimeSerializer, though it should be a LocalDateSerializer, because the current one will expect a time-of-day part in the String to be (de-)serialized, which it hasn't and your target class is a private LocalDate startDate.
In my opinion (not tested), your code should look like this:
#DateTimeFormat(pattern = "dd.MM.yyyy", iso = DateTimeFormat.ISO.DATE)
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd.MM.yyyy")
#JsonDeserialize(using = LocalDateDeserializer.class)
#JsonSerialize(using = LocalDateSerializer.class)
private LocalDate startDate;

Spring Boot change date format of request param field as DTO

My controller has a GET endpoint which accept lots of query parameters. So, instead of having multiple #QueryParam I created a CriteriaDTO in order to perform dynamic queries to our Mongo database
My controller endpoint :
#GetMapping
public List<MyObject> getAll(#Valid CriteriaDTO criteriaDTO){
return myObjectService.findAll(criteriaDTO);
}
public class CriteriaDTO {
private int offset = 0
private int limit = 20
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate minDate
// getters, setters ...
}
And, I want to pass the minDate is the URL with the following format yyyy-MM-dd but I need to convert it to the following format yyyy-MM-dd'T'HH:mm:ss.SSS.
My question is : Is there any annotation or something else which accepts the first format yyyy-MM-dd and automatically convert it to another ?
To be clear if I make the following call :
http://localhost:8080/api/myobject?minDate=2020-01-01
And then criteriaDTO.getminDate() will return 2020-01-01'T'00:00:00.000
Thanks for your help :)
You can do it in a more simple way than searching an annotation-magic solution.
Just add to your CriteriaDTO an additional getter for LocalDateTime:
public LocalDateTime getMinDateTime() {
return this.minDate.atTime(0, 0, 0, 0);
}
and use it wherever you need time instead of date.
Define setter and parse with SimpleDateFormat
public void setMinDate() {
if(!minDate.empty()) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
this.minDate = formatter.parse(minDate)
}
}
I would recommend to use atStartOfDay instead of converting this 2020-01-01 to 2020-01-01'T'00:00:00.000 using custom deserializer. And also since you are manipulating the input data i would recommend to do it as separate operation
LocalDateTime date = criteriaDTO.getminDate().atStartOfDay() //2020-01-01'T'00:00
And you can also add formatter DateTimeFormatter to get required output
date.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) //2020-01-01'T'00:00:00
You have several options. Check what you exactly need,
LocalDate date = LocalDate.now();
LocalDateTime dateTime = LocalDateTime.of(date, LocalTime.MIDNIGHT);
System.out.println(dateTime); //2020-02-04T00:00:00
System.out.println(DateTimeFormatter.ISO_DATE_TIME.format(dateTime)); //2020-02-04T00:00:00
System.out.println(date.atStartOfDay()); ////2020-02-04T00:00
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
System.out.println(formatter.format(dateTime)); //2020-02-04T00:00:00.000
You need to modify the getter in dto to format it, for example:
class CriteriaDTO {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
private int offset = 0
private int limit = 20
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate minDate
public String getMinDate() {
return formatter.format(LocalDateTime.of(minDate, LocalTime.MIDNIGHT));
}
// getters, setters ...
}

Jackson: InvalidFormatException: Can not construct instance of java.util.Date from String value

Pretty straightforward, according to the docs.
My POJO field looks like this:
public class Message {
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS")
private Date _timestamp;
public Date getTimestamp() {
return _timestamp;
}
public void setTimestamp(Date timestamp) {
this._timestamp = timestamp;
}
}
I try to map it with Jackson like so:
ObjectMapper mapper = new ObjectMapper();
Message message = mapper.readValue(message, Message.class);
The incoming date string looks like this:
2018-10-30 12:44:34.270
I get the following error:
com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of java.util.Date from String value '2018-10-30 12:44:34.270': not a valid representation (error: Failed to parse Date value '2018-10-30 12:44:34.270': Can not parse date "2018-10-30 12:44:34.270": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))
I've tried the following but the error doesn't change:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSSZ")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
Not sure what else I can do. I can't change the incoming format so this is what I have to live with.
Thanks in advance.
The problem is not where you expected it to be:
The name timestampin your JSON input
{ "timestamp": "2018-10-30 12:44:34.270" }
simply did not match the name _timestamp in your Java class Message
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS")
private Date _timestamp;
There are several alternative ways how to make it matching:
In the JSON input change "timestamp" to "_timestamp"
In the Java code change Date _timestamp; to Date timestamp;
(and also change the code with your getter and setter accordingly)
In the Java code add the annotation #JsonProperty("timestamp")
to your Date _timestamp; definition

Jackson - Can't deserialize datetime with timezone offset 'unparsed text found at index 23'

My datetime has to come from frontend with timezone offset: 2017-07-04T06:00:00.000+01:00
I cannot deserialize it with Jackson.
The error is:
Text '2017-07-04T06:00:00.000+01:00' could not be parsed, unparsed
text found at index 23;
I was trying to Google the solution by all are about DateTime with Z at the end.
#NotNull
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS aZ")
private LocalDateTime time;
Is there any solution for that?
The pattern a is used to parse AM/PM, which is not in the input String, that's why you get a parse error.
The input format matches an OffsetDateTime, which can be parsed with the respective built-in formatter DateTimeFormatter.ISO_OFFSET_DATE_TIME, so you can use this formatter in a deserializer object and register it in the module. You must also remove the JsonFormat annotation from the field.
ObjectMapper om = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
LocalDateTimeDeserializer deserializer = new LocalDateTimeDeserializer(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
module.addDeserializer(LocalDateTime.class, deserializer);
om.registerModule(module);
This will parse the input and convert it to a LocalDateTime. In the test I've made, the value of LocalDateTime was set to 2017-07-04T06:00.
To control the output, you can either do:
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
Which will output the LocalDateTime as 2017-07-04T06:00:00, or you can use a custom formatter:
LocalDateTimeSerializer serializer = new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS a"));
module.addSerializer(LocalDateTime.class, serializer);
The serializer above will output the field as 2017-07-04T06:00:00.000 AM. Please note that the Z pattern will not work because a LocalDateTime has no timezone information and it can't resolve its offset - because when you deserialized to a LocalDateTime, the offset information in the input (+01:00) was lost.
Another alternative (without the need to configure the object mapper) is to use the correct pattern in the annotation:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS[xxx]")
private LocalDateTime time;
Note that I used the pattern [xxx] because the offset (+01:00) can be optional: when deserializing, this information is lost becase a LocalDateTime has no information about timezones and offsets, so when serializing this field won't be found - making the field optional (using [] delimiters) make it work for both deserialization and serialization.
This will deserialize the input 2017-07-04T06:00:00.000+01:00 and serialize to 2017-07-04T06:00:00.000 (note that the optional offset is not used in serialization, as the LocalDateTime has no such information).
If you want different formats for deserialization and serialization, you can also create custom classes and anotate them in the field:
public class CustomDeserializer extends LocalDateTimeDeserializer {
public CustomDeserializer() {
super(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}
}
public class CustomSerializer extends LocalDateTimeSerializer {
public CustomSerializer() {
super(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS a"));
}
}
// in this case, don't use #JsonFormat
#JsonSerialize(using = CustomSerializer.class)
#JsonDeserialize(using = CustomDeserializer.class)
private LocalDateTime time;
This will use the format 2017-07-04T06:00:00.000+01:00 for deserialize and the format 2017-07-04T06:00:00.000 AM to serialize.

Jackson date format with #JsonFormat?

I want to serialize certain Calendar fields of a POJO with a specific format.
with no annotations, fields like this:
private Calendar timestamp1;
private Calendar timestamp2;
produce JSON like this:
{ ..., timestamp1: 1402402106000, timestamp2: 1402488595000, ... }
I would to add a field formatted as a string as it actually represents a Day as a 24-hour unit, not a specific instant of time. But when I add a new field with an annotation:
#JsonFormat(pattern = "yyyy-MM-dd")
private Calendar oneDay;
I was hoping to get JSON like this:
{ ..., timestamp1: 1402402106000, timestamp2: 1402488595000, oneDay: "2014-06-12", ... }
Instead, I got a the following exception:
com.fasterxml.jackson.databind.JsonMappingException:
Cannot format given Object as a Date
(through reference chain: java.util.HashMap["data"]->java.util.ArrayList[0]-myPojo["oneDay"])
What am I doing wrong?
I'm using Jackson 2.2.0
Here's what I've used: #JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
That works for me.

Categories

Resources