611

Java 8 has a completely new API for date and time. One of the most useful classes in this API is LocalDateTime, for holding a timezone-independent date-with-time value.

There are probably millions of lines of code using the legacy class java.util.Date for this purpose. As such, when interfacing old and new code there will be a need for converting between the two. As there seems to be no direct methods for accomplishing this, how can it be done?

JodaStephen
  • 60,927
  • 15
  • 95
  • 117
Knut Arne Vedaa
  • 15,372
  • 11
  • 48
  • 59
  • 3
    See here [http://stackoverflow.com/questions/33066904/simpliest-java8-localdate-to-java-util-date-conversion-and-vice-versa](http://stackoverflow.com/questions/33066904/simpliest-java8-localdate-to-java-util-date-conversion-and-vice-versa) – George Oct 12 '15 at 06:00
  • 3
    https://docs.oracle.com/javase/tutorial/datetime/iso/legacy.html – Bax Mar 16 '16 at 02:55
  • Possible duplicate of [Convert java.util.Date to java.time.LocalDate](http://stackoverflow.com/questions/21242110/convert-java-util-date-to-java-time-localdate) – Peter Perháč Apr 25 '16 at 18:26

9 Answers9

892

Short answer:

Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

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 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 are convenient methods to provide the conversion to and fro:

Date input = new Date();
Instant instant = input.toInstant();
Date output = Date.from(instant);

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-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:

Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());

In reverse, the LocalDateTime the time-zone is specified by calling the atZone(ZoneId) method. The ZonedDateTime can then be converted directly to an Instant:

LocalDateTime ldt = ...
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
Date output = Date.from(zdt.toInstant());

Note that the conversion from LocalDateTime to ZonedDateTime 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 of atZone(ZoneId) for more the definition of what the conversion will do.

Summary, if you round-trip a java.util.Date to a LocalDateTime and back to a java.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.

Mike Fikes
  • 3,507
  • 14
  • 28
JodaStephen
  • 60,927
  • 15
  • 95
  • 117
  • 5
    Thanks a lot for clear explanation. Especially for why `java.util.Date` does not contains timezone, but print it during `toString()`. Event [official documentation](http://docs.oracle.com/javase/6/docs/api/java/util/Date.html#toString()) does not tell this clearly as you post. – Cherry May 31 '16 at 04:24
  • *Warning:* `LocalDateTime.ofInstant(date.toInstant()...` does not behave as one would naively expect. For example `new Date(1111-1900,11-1,11,0,0,0);` will become `1111-11-17 23:53:28` using this approach. Take a look at the implementation of `java.sql.Timestamp#toLocalDateTime()` if you needed the result to be `1111-11-11 00:00:00` in the previous example. – dog Dec 05 '16 at 12:09
  • 3
    I've added a section about very old dates (pre-1582). FWIW, your suggested fix may well be wrong, as 1111-11-11 in java.util.Date is the same actual day in history as 1111-11-18 in java.time due to the different calendar systems (the 6.5 minute difference occurs to many time-zones before 1900) – JodaStephen Dec 06 '16 at 12:51
  • 3
    Also worth to note that `java.sql.Date#toInstant` throws an `UnsupportedOperationException`. So don't use `toInstant` in a RowMapper on `java.sql.ResultSet#getDate`. – brass monkey Apr 12 '17 at 16:06
165

Here is what I came up with ( and like all Date Time conundrums it is probably going to be disproved based on some weird timezone-leapyear-daylight adjustment :D )

Round-tripping: Date <<->> LocalDateTime

Given: Date date = [some date]

(1) LocalDateTime << Instant<< Date

    Instant instant = Instant.ofEpochMilli(date.getTime());
    LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

(2) Date << Instant << LocalDateTime

    Instant instant = ldt.toInstant(ZoneOffset.UTC);
    Date date = Date.from(instant);

Example:

Given:

Date date = new Date();
System.out.println(date + " long: " + date.getTime());

(1) LocalDateTime << Instant<< Date:

Create Instant from Date:

Instant instant = Instant.ofEpochMilli(date.getTime());
System.out.println("Instant from Date:\n" + instant);

Create Date from Instant (not necessary,but for illustration):

date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());

Create LocalDateTime from Instant

LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
System.out.println("LocalDateTime from Instant:\n" + ldt);

(2) Date << Instant << LocalDateTime

Create Instant from LocalDateTime:

instant = ldt.toInstant(ZoneOffset.UTC);
System.out.println("Instant from LocalDateTime:\n" + instant);

Create Date from Instant:

date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());

The output is:

Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

Instant from Date:
2013-11-01T14:13:04.574Z

Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

LocalDateTime from Instant:
2013-11-01T14:13:04.574

Instant from LocalDateTime:
2013-11-01T14:13:04.574Z

Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574
ChrisF
  • 134,786
  • 31
  • 255
  • 325
The Coordinator
  • 13,007
  • 11
  • 44
  • 73
  • I don't think that this is as big a deal as you may be presuming. Legacy code that uses java.util.Date has never depended on the java.time library and presumably works quite well without it. The need to change to the new API is probably not a high priority for many shops. New code can employ the new API de novo. The edge case is new code using old internal or 3rd party libraries that produce results as `Date`s which would need to be interop'd with new code using java.time. I contend that this case will be uncommon. – scottb Nov 02 '13 at 03:07
  • 2
    @scottb are you addressing that to me or the person who posed the question? As for my thoughts, well, it would be **nice** to have the conversion stated explicitly in the jdk 8 API -- They could have at least done that. In any case, there are lots of libraries that will be re-factored to include the new Java-8 features and to know how to do it is important. – The Coordinator Nov 02 '13 at 03:11
  • 4
    @scottb Why would the 3rd party case be uncommon? Its damn common. One example: JDBC 4 and less (hopefully not 5). – Raman Apr 27 '14 at 19:11
  • 6
    As a general JSR-310 rule, there is no need to convert between types using epoch-millis. Better alternatives exist using objects, see my full answer below. The answer above is also only fully valid if using a zone-offset like UTC - some parts of the answer will not work for a full time-zone like America/New_York. – JodaStephen May 27 '14 at 09:39
  • 2
    Instead of `Instant.ofEpochMilli(date.getTime())` do `date.toInstant()` – goat Apr 14 '16 at 20:48
  • 1
    @goat `toInstant()` looks nice, except it fails for `java.sql.Date`, arggggh! So it's finally easier to use `Instant.ofEpochMilli(date.getTime())`. – vadipp Dec 09 '19 at 05:04
29

Much more convenient way if you are sure you need a default timezone :

Date d = java.sql.Timestamp.valueOf( myLocalDateTime );
bernard paulus
  • 1,644
  • 1
  • 21
  • 33
Petrychenko
  • 453
  • 4
  • 5
14

The fastest way for LocalDateTime -> Date is:

Date.from(ldt.toInstant(ZoneOffset.UTC))

Lukasz Czerwinski
  • 13,499
  • 10
  • 55
  • 65
8

Everything is here : http://blog.progs.be/542/date-to-java-time

The answer with "round-tripping" is not exact : when you do

LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

if your system timezone is not UTC/GMT, you change the time !

joe-mojo
  • 101
  • 1
  • 1
  • Only if you do it during 1 hour a year, in the fall, when two times from LocalDateTime overlap. The spring forward move does not cause an issue. Most times will convert both directions properly. – David Oct 23 '15 at 17:24
8

the following seems to work when converting from new API LocalDateTime into java.util.date:

Date.from(ZonedDateTime.of({time as LocalDateTime}, ZoneId.systemDefault()).toInstant());

the reverse conversion can be (hopefully) achieved similar way...

hope it helps...

dizzy
  • 89
  • 1
  • 1
6

If you are on android and using threetenbp you can use DateTimeUtils instead.

ex:

Date date = DateTimeUtils.toDate(localDateTime.atZone(ZoneId.systemDefault()).toInstant());

you can't use Date.from since it's only supported on api 26+

humazed
  • 74,687
  • 32
  • 99
  • 138
4

I'm not sure if this is the simplest or best way, or if there are any pitfalls, but it works:

static public LocalDateTime toLdt(Date date) {
    GregorianCalendar cal = new GregorianCalendar();
    cal.setTime(date);
    ZonedDateTime zdt = cal.toZonedDateTime();
    return zdt.toLocalDateTime();
}

static public Date fromLdt(LocalDateTime ldt) {
    ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault());
    GregorianCalendar cal = GregorianCalendar.from(zdt);
    return cal.getTime();
}
Knut Arne Vedaa
  • 15,372
  • 11
  • 48
  • 59
  • 3
    There's definitely a pitfall going from `LocalDateTime` to `Date`. At daylight saving transitions, a `LocalDateTime` can be non-existent, or occur twice. You need to work out what you want to happen in each case. – Jon Skeet Oct 17 '13 at 15:43
  • 1
    BTW, `GregorianCalendar` belongs old awkward API that new `java.time` API aims to replace – Vadzim Apr 17 '17 at 16:25
0

I think below approach will solve the conversion without taking time-zone into consideration. Please comment if it has any pitfalls.

    LocalDateTime datetime //input
    public static final DateTimeFormatter yyyyMMddHHmmss_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String formatDateTime = datetime.format(yyyyMMddHHmmss_DATE_FORMAT);
    Date outputDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(formatDateTime); //output
Akhil S Kamath
  • 1,012
  • 13
  • 23