#JsonFormat converts Date with incorrect timezone - java

I have a simple POJO with a Date field with initial value coming in:
1985-09-17T01:00:00.000+0400
then this Date value gets mapped to a DTO with the Date field annotated:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssX")
private Date dateOfBirth;
Then the result is shown:
1985-09-16T21:00:00Z
I have tried setting the timestamp property in #JsonFormat, but that didn't help and the date is still invalid.
How can I correctly convert the date?

The value within a java.util.Date is the number of milliseconds since the Unix epoch, which occurred at midnight January 1st 1970, UTC. As it's a number of milliseconds since a fixed epoch, the value within java.util.Date is the same around the world at any particular instant, regardless of local time zone.
So in your case it's better to use ZonedDateTime class if you use java 8 ZonedDateTime

Both dates represents the same instant:
1985-09-17T01:00:00.000+0400
1985-09-16T21:00:00Z
When you print dates in java it uses the current timezone of the VM, but internally the Date class stores that information in a long representing the time in milliseconds since the epoch.
If you like you can get the a String representation of the date using a custom timezone using the setTimeZone method of DateFormat:
Sets the time zone for the calendar of this DateFormat object.
Here a simple snippet of code:
Date date = ...
DateFormat formatter = ...
TimeZone timeZone = ...
// Set a custom timezone
formatter.setTimeZone(timeZone);
// Get a string representation of the daet with a custom timezone
String formattedDateWithCustomTimezone = formatter.format(date);

Related

How to print a date using DateTimeFormatter.ISO_LOCAL_DATE?

I want to use DateTimeFormatter.ISO_LOCAL_DATE to print and parse dates. This is what I'm doing for printing:
Date date;
String text = DateTimeFormatter.ISO_LOCAL_DATE.format(
date.toInstant()
);
This is what I'm getting:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: Year
at java.time.Instant.getLong(Instant.java:603)
at java.time.format.DateTimePrintContext$1.getLong(DateTimePrintContext.java:205)
at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2543)
at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2182)
at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1744)
at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1718)
This happens because the Instant class represents a point in the timeline: the number of nanoseconds since unix epoch (1970-01-01T00:00Z), without any concept of timezone - so it doesn't have a specific date/time (day/month/year, hours/minutes/seconds), as it can represent a different date and time in different timezones.
Setting a specific zone in the formatter, like you did, converts the Instant to that zone (so the count of nanoseconds since epoch can be translated to a specific date and time), making it possible to be formatted.
For this specific case, you want only the date part (day, month and year) in ISO8601 format, so one alternative is to convert the Instant to a LocalDate and call the toString() method. As you set UTC in the formatter, I'm using the same to convert it:
String text = date.toInstant()
// convert to UTC
.atZone(ZoneOffset.UTC)
// get the date part
.toLocalDate()
// toString() returns the date in ISO8601 format
.toString();
This return the same thing as your formatter. Of course for another formats, you should use the formatter, but specifically for ISO8601, you can use the toString() method.
You could also convert the Instant to the timezone you want (in this case, to UTC) and pass it directly to the formatter:
String text = DateTimeFormatter.ISO_LOCAL_DATE.format(
date.toInstant().atZone(ZoneOffset.UTC)
);
The only difference is that, when you set the zone in the formatter, the date is converted to that zone when formatting (when you don't set it, the date is not converted).
This is how it works:
String text = DateTimeFormatter.ISO_LOCAL_DATE
.withZone(ZoneId.of("UTC"))
.format(date.toInstant());

convert UTC timestamp to any other zone timestamp

I'm using MongoDB to store my data. Mongo stores timestamps in UTC as a default.
We process data in different time zones. I'm struggling to convert UTC timestamp to PDT or IST timestamps.
Trying to construct a method to pass timezone(into which my timestamp is to be converted) and timestamp(UTC). Method to return the timestamp of specified time zone.
public Date getDateBasedOnZone(Date date, "America/Los_Angeles") {
return dateInAmerica/Los_Angeles;
}
You could use something like the following to get the time in a particular zone:
date.toInstant().atZone( ZoneId.of( "America/Los_Angeles" ) )
A java.util.Date object does NOT contain timezone information so it's impossible to convert from one timezone to another in a java.util.Date (it doesn't make sense). It's simply a wrapper around long which is milliseconds since EPOCH.
You only start seeing timezone in java.util.Calendar or when a java.util.Date is converted to String.
There's also Joda-Time which has far better date API's than the core Java libraries.
You can use a dateformat with the required timezone and apply it to the date
public Date convertToZone(Date date, String tz) {
DateFormat TFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TFormat.setTimeZone(TimeZone.getTimeZone(tz));
return df.parse(currentTFormat.format(date));
}

Spring's #DateTimeFormat produces a Date object with the previous day

I've got a requestParam that takes in a date (ie. 2017-01-24T06:00:00.000Z).
I'm using DateTimeFormat to format it into a date to pass into my controller.
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date myDate
but when I print myDate to my console I get "Mon Jan 23, 18:00:00 CST 2017", but in my example above it should be Jan 24th. Why is it changing my date back 1 day?
There are several things at play here.
First, Spring's #DateTimeFormat, when annotating a java.util.Date field or parameter, uses a SimpleDateFormat with its timezone set to UTC.
Second, you've used DateTimeFormat.ISO.DATE which represents
The most common ISO Date Format yyyy-MM-dd, e.g. "2000-10-31".
In other words, it does not consider any timezone information in your date string (this doesn't really matter because your date string was rooted at Zulu anyway).
Third, you've provided a date string where everything but the iso pattern gets ignored. The SimpleDateFormat only cares about the 2017-01-24 part.
Since the timezone is set to UTC, it considers the 2017-01-24 date as being rooted at UTC, at midnight, zero'ed hours, minutes, and seconds.
Finally, since your system's default time zone is Central Standard Time, ie. UTC-6), when you call toString on the Date object, it'll return a String that is formatted with that time zone, ie. 6 hours before midnight.
Remember also that a Date has no concept of a timezone. It is a timestamp.
To "fix" this, construct your #DateTimeFormat with an appropriate pattern that interprets both time and time zone. I would use
#DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX") Date myDate
For me it works without #JsonFormat when json has the pattern 'yyyy-mm-dd'
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate startDate;
If you want to configure the time zone in general you can use the following:
spring.jackson.time-zone=EST
The documentation indicates: "Time zone used when formatting dates. For instance, 'America/Los_Angeles' or 'GMT+10'".
This will work. Tried and tested
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
#JsonFormat(pattern = "MM/dd/yyyy")
private LocalDate startDate;

In Java, Can the Date type just hold MM-dd-yyyy or just hh:mm:ss?

I am converting epoch format time to the normal format, but when I convert it to date I get, MM-dd-yyyy hh:mm:ss.
If I want to single out just the date or the time I have to use SimpleDateFormat. But this returns a String. I was wondering if there was a way to make this string a Date type.
The type java.util.Date is actually a timestamp, it is not much more than a wrapper for a number of milliseconds since 01-01-1970, 00:00:00 UTC. (The class name Date is unfortunately badly chosen).
It is not very well suited for holding just a date or just a time value.
If you are using Java 8, use the new date and time API (package java.time); use for example LocalDate if you need to store a year/month/day, or a LocalTime if you need to store just a time-of-day (hours, minutes, seconds, milliseconds).
If you are using Java 7 or older, consider using the equivalent classes in the Joda Time library.
You can format the date as MM-dd-yyyy HH:mm:ss not (MM-dd-yyyy hh:mm:ss)
https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
DateFormat dateformat= new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
Date date = dateformat.parse("01-25-1988 23:54:59");
System.out.println(date);
System.out.println(dateformat.format(date));

Storing java util date into mysql as a timestamp

I am using JPA and MySQl
In my domain object i have a date field as
#Temporal(TemporalType.TIMESTAMP)
private Date lastSeenDate;
From my UI the date goes as a String in format dd-mm-yyyy
I used
final DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
final Date date = format.parse(dateString);
but for String date
06-06-2013
the date stored in mysql is
0011-12-04 00:00:00.0
How do I store it into mysql to match the mysql format
The class Date represents a specific instant in time, with millisecond
precision.
From: http://docs.oracle.com/javase/6/docs/api/java/util/Date.html
The date class always has a time component.
All dates in Java are essentially timestamps as they represent a single millisecond in time.
There's no way I can think of to deal only with dates but you might want to have a look at java.util.Calendar. If you can set the hours, minutes, seconds and milliseconds all to zero (or other defined time) you can then work with days, months and years more naturally. It's not a very good offering but it works well enough.
According to JavaDocs: java.util.Date allocates a Date object and initializes it so that it represents the time at which it was allocated, measured to the nearest millisecond.
So, it works like a database Timestamp. It would always have a time component although it may be all zeroes that is representing a midnight. But, you could use the same SimpleDateFormat to print out the java.util.Date without its time component.
final DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
final Date utilDate = sdf.parse("2013-06-20");
System.out.println(sdf.format(utilDate)); // prints back 2013-06-20
Or, you may want to look into java.sql.Date which does not have a time component.
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());

Categories