Short answer
Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
Explanation
Despite its name, java.util.Date
represents an instant on the time-line, not a "date". The actual data stored within the object is a long
count of milliseconds since 1970-01-01T00:00Z (midnight at the start of 1970 GMT/UTC).
The equivalent class to java.util.Date
in JSR-310 is Instant
, thus there is a convenient method toInstant()
to provide the conversion:
Date input = new Date();
Instant instant = input.toInstant();
A java.util.Date
instance has no concept of time-zone. This might seem strange if you call toString()
on a java.util.Date
, because the toString
is relative to a time-zone. However that method actually uses Java's default time-zone on the fly to provide the string. The time-zone is not part of the actual state of java.util.Date
.
An Instant
also does not contain any information about the time-zone. Thus, to convert from an Instant
to a local date it is necessary to specify a time-zone. This might be the default zone - ZoneId.systemDefault()
- or it might be a time-zone that your application controls, such as a time-zone from user preferences. Use the atZone()
method to apply the time-zone:
Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
A ZonedDateTime
contains state consisting of the local date and time, time-zone and the offset from GMT/UTC. As such the date - LocalDate
- can be easily extracted using toLocalDate()
:
Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
LocalDate date = zdt.toLocalDate();
Java 9 answer
In Java SE 9, a new method has been added that slightly simplifies this task:
Date input = new Date();
LocalDate date = LocalDate.ofInstant(input.toInstant(), ZoneId.systemDefault());
This new alternative is more direct, creating less garbage, and thus should perform better.
Best Answer
Short answer:
Explanation: (based on this question about
LocalDate
)Despite its name,
java.util.Date
represents an instant on the time-line, not a "date". The actual data stored within the object is along
count of milliseconds since 1970-01-01T00:00Z (midnight at the start of 1970 GMT/UTC).The equivalent class to
java.util.Date
in JSR-310 isInstant
, thus there are convenient methods to provide the conversion to and fro:A
java.util.Date
instance has no concept of time-zone. This might seem strange if you calltoString()
on ajava.util.Date
, because thetoString
is relative to a time-zone. However that method actually uses Java's default time-zone on the fly to provide the string. The time-zone is not part of the actual state ofjava.util.Date
.An
Instant
also does not contain any information about the time-zone. Thus, to convert from anInstant
to a local date-time it is necessary to specify a time-zone. This might be the default zone -ZoneId.systemDefault()
- or it might be a time-zone that your application controls, such as a time-zone from user preferences.LocalDateTime
has a convenient factory method that takes both the instant and time-zone:In reverse, the
LocalDateTime
the time-zone is specified by calling theatZone(ZoneId)
method. TheZonedDateTime
can then be converted directly to anInstant
:Note that the conversion from
LocalDateTime
toZonedDateTime
has the potential to introduce unexpected behaviour. This is because not every local date-time exists due to Daylight Saving Time. In autumn/fall, there is an overlap in the local time-line where the same local date-time occurs twice. In spring, there is a gap, where an hour disappears. See the Javadoc ofatZone(ZoneId)
for more the definition of what the conversion will do.Summary, if you round-trip a
java.util.Date
to aLocalDateTime
and back to ajava.util.Date
you may end up with a different instant due to Daylight Saving Time.Additional info: There is another difference that will affect very old dates.
java.util.Date
uses a calendar that changes at October 15, 1582, with dates before that using the Julian calendar instead of the Gregorian one. By contrast,java.time.*
uses the ISO calendar system (equivalent to the Gregorian) for all time. In most use cases, the ISO calendar system is what you want, but you may see odd effects when comparing dates before year 1582.