We have a method that gets a date from an alpha-numeric String. Say that the String is Y04051000547GF
the resultant date will be Tue May 04 00:00:00 IST 2010
.
public static Date getDateFromGITID(String gitId) {
Date date = null;
try {
String dateInGit = nid.substring(1, 7); // six characters from the second character
DateFormat format = new SimpleDateFormat("ddMMyy", Locale.ENGLISH);
date = format.parse(dateInGit);
} catch (ParseException e) {
}
return date;
}
Now, this date is assigned to an attribute of a class UserInfo
, when we try to convert an object of this class to a JSON the date gets decremented by 1 day!
Object to JSON string
public static String asJsonString(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
User Info Bean
public class UserInfo {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
Date date;
}
This is main method
public static void main(String[] args) {
UserInfo obj = new UserInfo();
obj.date = getDateFromGITID("Y04051000547GF");
System.out.println(obj.date);
System.out.println(asJsonString(obj));
}
This was the console output
Tue May 04 00:00:00 IST 2010
{"dob":"2010-05-03"}
You can see that the date is 4th of May 2010, but in JSON it gets decremented by 1 day. We were able to figure out what was causing the issue, the date is printed in IST which has a difference of 5 hours and 30 minutes with UST. So we added that time before the JSON conversion and the converted time didn't have a change.
public static void main(String[] args) {
UserInfo obj = new UserInfo();
obj.dob = getDOBFromNID("Y04051000547GF");
System.out.println(obj.dob);
obj.dob.setHours(5);
obj.dob.setMinutes(30);
obj.dob.setSeconds(00);
System.out.println(asJsonString(obj));
}
Output
Tue May 04 00:00:00 IST 2010
{"dob":"2010-05-04"}
Yes, we have used the deprecated method but it is just for testing. Even if we try to decrease the time by 1 second the issue will come.
Edit-1 (Found another fix by setting the time zone to the formatter)
We were able to fix this issue in another way when we set the time zone to GST (Java SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") gives timezone as IST) for the DateFormat
inside the getDateFromGITID
method.
format.setTimeZone(TimeZone.getTimeZone("GMT"));
So the questions are
You are using terribly flawed date-time classes that were years ago supplanted by the modern java.time classes defined in JSR 310.
Specifically, I expect your problem is due to using java.util.Date#toString
method to generate text. That method lies to you. A java.util.Date
value represents a date and time as seen with an an offset of zero hours-minutes-seconds from the temporal meridian of UTC. The toString
method applies the JVM’s current default time zone while generating its textual result.
While well-intentioned, this anti-feature causes endless confusion and pain to Java developers. Avoid this class. Avoid all the legacy date-time classes!
Do all your date-time work using the industry-leading java.time classes.
String original = "Y04051000547GF" ;
String input = original.substring( 1 , 7 ) ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "ddMMuu" ) ;
LocalDate ld = LocalDate.parse( input , f ) ;
If you must interoperate with old code not yet updated to java.time, convert using new methods added to the old classes.
Going from a date to a moment, a specific point on the timeline, requires a time of day, and requires a time zone. I will assume you want the first moment of that date as seen in UTC (an offset of zero).
ZonedDateTime zdt = ld.atStartOfDay( ZoneOffset.UTC ) ;
Instant instant = zdt.toInstant() ;
java.util.Date d = java.util.Date.from( instant ) ;
I must note how ill-advised is the use of a date-with-time-and-offset type like java.util.Date
to represent a date-only value. If possible, consider reworking your codebase to use correct types. For a date-only value, use java.time.LocalDate
class. LocalDate
is supported by Java 8+, JDBC 4.2+, Hibernate, Jackson, etc.
Or, as Commented by Ole V.V., perhaps you mean to get the first moment of the day as seen in a particular time zone.
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;
The best solution is to educate the publisher of your data to use standard ISO 8601 formats for data exchange of date-time values rather than invent their own.