I have Date field in my class marked with jaxb annotation.
#XmlElement(name = "startTime")
public Date getStartTime() {
return startTime;
}
But in the result i get startTime wtihout timezone even if it have it in zoneinfo.
So after setting it like this:
startTime = new Date(System.currentTimeMillis());
I get that kind of result in JSON
"startTime":"2016-05-25T17:22:23"
How can i get format like this : "2016-05-25T17:22:23.848+06:00" ?
If you are working with Java8, have a look to the java.time package, and use the class Instant instead of Date.
Related
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;
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 ...
}
#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 )
I have following JSON:
{
"id" : "1",
"birthday" : 401280850089
}
And POJO class:
public class FbProfile {
long id;
#JsonDeserialize(using = LocalDateDeserializer.class)
LocalDate birthday;
}
I am using Jackson to do deserialization:
public FbProfile loadFbProfile(File file) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
FbProfile profile = mapper.readValue(file, FbProfile.class);
return profile;
}
But it throws an exception:
com.fasterxml.jackson.databind.JsonMappingException: Unexpected token
(VALUE_NUMBER_INT), expected VALUE_STRING: Expected array or string.
How can I deserialize epoch to LocalDate? I would like to add that if I change the datatype from LocalDate to java.util.Date it works perfectly fine. So maybe it's better to deserialize to java.util.Date and create the getter and setter which will do the conversion to/from LocalDate.
I've managed to do it writing my own deserializer (thank you #Ole V.V. to point me to the post Java 8 LocalDate Jackson format):
public class LocalDateTimeFromEpochDeserializer extends StdDeserializer<LocalDateTime> {
private static final long serialVersionUID = 1L;
protected LocalDateTimeFromEpochDeserializer() {
super(LocalDate.class);
}
#Override
public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
return Instant.ofEpochMilli(jp.readValueAs(Long.class)).atZone(ZoneId.systemDefault()).toLocalDateTime();
}
}
Notice about timezone is also very useful. Thank you!
The still open question is if it can be done without writing own deserializer?
Another option that I went with if you have the ability to change the POJO, is to just declare your field as java.time.Instant.
public class FbProfile {
long id;
Instant birthday;
}
This will deserialize from a number of different formats including epoch. Then if you need to use it as a LocalDate or something else in your business logic, simply do what some of the converters above are doing:
LocalDate asDate = birthday.atZone(ZoneId.systemDefault()).toLocalDate()
or
LocalDateTime asDateTime = birthday.atZone(ZoneId.systemDefault()).toLocalDateTime()
I found a way to do it without writing a custom deserializer, but it'll require some modifications.
First, the LocalDateDeserializer accepts a custom DateTimeFormatter. So, we need to create a formatter that accepts an epoch millis. I did this by joining the INSTANT_SECONS and MILLI_OF_SECOND fields:
// formatter that accepts an epoch millis value
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// epoch seconds
.appendValue(ChronoField.INSTANT_SECONDS, 1, 19, SignStyle.NEVER)
// milliseconds
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
// create formatter, using UTC as timezone
.toFormatter().withZone(ZoneOffset.UTC);
I also set the formatter with UTC zone, so it won't be affected by timezones and DST changes.
Then, I've created the deserializer and registered in my ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
// add the LocalDateDeserializer with the custom formatter
module.addDeserializer(LocalDate.class, new LocalDateDeserializer(formatter));
mapper.registerModule(module);
I also had to remove the annotation from the birthday field (because the annotation seems to override the module configuration):
public class FbProfile {
long id;
// remove #JsonDeserialize annotation
LocalDate birthday;
}
And now the big issue: as the DateTimeFormatter accepts only String as input, and the JSON contains a number in birthday field, I had to change the JSON:
{
"id" : "1",
"birthday" : "401280850089"
}
Note that I changed birthday to a String (put the value between quotes).
With this, the LocalDate is read from JSON correctly:
FbProfile value = mapper.readValue(json, FbProfile.class);
System.out.println(value.getBirthday()); // 1982-09-19
Notes:
I couldn't find a way to pass the number directly to the formatter (as it takes only String as input), so I had to change the number to be a String. If you don't want to do that, then you'll have to write a custom converter anyway.
You can replace ZoneOffset.UTC with any timezone you want (even ZoneId.systemDefault()), it'll depend on what your application needs. But as told in #Ole V.V.'s comment, the timezone might cause the date to change.
I want to convert java.time.LocalDate into java.util.Date type. Because I want to set the date into JDateChooser. Or is there any date chooser that supports java.time dates?
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
That assumes your date chooser uses the system default timezone to transform dates into strings.
Here's a utility class I use to convert the newer java.time classes to java.util.Date objects and vice versa:
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
public class DateUtils {
public static Date asDate(LocalDate localDate) {
return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
}
public static Date asDate(LocalDateTime localDateTime) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
public static LocalDate asLocalDate(Date date) {
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
}
public static LocalDateTime asLocalDateTime(Date date) {
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
}
}
Edited based on #Oliv comment.
Disclaimer: For illustrating existing java apis only. Should not be used in production code.
You can use java.sql.Date.valueOf() method as:
Date date = java.sql.Date.valueOf(localDate);
No need to add time and time zone info here because they are taken implicitly.
See LocalDate to java.util.Date and vice versa simplest conversion?
java.time has the Temporal interface which you can use to create Instant objects from most of the the time classes. Instant represents milliseconds on the timeline in the Epoch - the base reference for all other dates and times.
We need to convert the Date into a ZonedDateTime, with a Time and a Zone, to do the conversion:
LocalDate ldate = ...;
Instant instant = Instant.from(ldate.atStartOfDay(ZoneId.of("GMT")));
Date date = Date.from(instant);
This works for me:
java.util.Date d = new SimpleDateFormat("yyyy-MM-dd").parse(localDate.toString());
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html#toString--
In order to create a java.util.Date from a java.time.LocalDate, you have to
add a time to the LocalDate
interpret the date and time within a time zone
get the number of seconds / milliseconds since epoch
create a java.util.Date
The code might look as follows:
LocalDate localDate = LocalDate.now();
Date date = new Date(localDate.atStartOfDay(ZoneId.of("America/New_York")).toEpochSecond() * 1000);
Kotlin Solution:
1) Paste this extension function somewhere.
fun LocalDate.toDate(): Date = Date.from(this.atStartOfDay(ZoneId.systemDefault()).toInstant())
2) Use it, and never google this again.
val myDate = myLocalDate.toDate()
public static Date convertToTimeZone(Date date, String tzFrom, String tzTo) {
return Date.from(LocalDateTime.ofInstant(date.toInstant(), ZoneId.of(tzTo)).atZone(ZoneId.of(tzFrom)).toInstant());
}
LocalDate date = LocalDate.now();
DateFormat formatter = new SimpleDateFormat("dd-mm-yyyy");
try {
Date utilDate= formatter.parse(date.toString());
} catch (ParseException e) {
// handle exception
}
localDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
Try this:
public Date convertFrom(LocalDate date) {
return Date.valueOf(date);
}
Simple
public Date convertFrom(LocalDate date) {
return java.sql.Timestamp.valueOf(date.atStartOfDay());
}
java.util.Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());