184

In Java 8, how can I convert a Timestamp (in java.sql) to a LocalDate (in java.time)?

assylias
  • 321,522
  • 82
  • 660
  • 783
simonides
  • 3,040
  • 3
  • 19
  • 34
  • 1
    I had a somewhat similar issue here: http://stackoverflow.com/a/23197731/1856960. Short answer, not really. But that is not necessarily a bad thing. – jacobhyphenated Apr 24 '14 at 16:52

3 Answers3

286

You can do:

timeStamp.toLocalDateTime().toLocalDate();

Note that timestamp.toLocalDateTime() will use the Clock.systemDefaultZone() time zone to make the conversion. This may or may not be what you want.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • 1
    It's been added in Java 8. – assylias Apr 24 '14 at 09:13
  • 71
    Just a note, the `timestamp.toLocalDateTime()` method will use the systemDefault time zone to make the conversion. This may or may not be what you want. – jacobhyphenated Apr 24 '14 at 12:53
  • 1
    @jacobhyphenated please post answer using explicit time zone. Thanks! – user482745 Jun 19 '18 at 08:58
  • 1
    @jacobhyphenated's comment is extremely important here, hence the number of upvotes for it. See my answer for details https://stackoverflow.com/a/57101544/2032701 – Ruslan Jul 18 '19 at 19:40
  • @jacobhyphenated, `LocalDateTime` always uses system-default timezone. That's what "Local" means in its name. – M. Prokhorov Aug 26 '19 at 11:07
  • @M.Prokhorov This is not strictly true. LocalDateTime has no timezone, which is not the same as using the system-default timezone. Sometimes that doesn't matter, sometimes that can be a very important distinction. – jacobhyphenated Aug 28 '19 at 15:49
  • @jacobhyphenated, it not having time zone info manifests as inability for its instances to be converted into an instant, unless given a time zone explicitly. However, the `now()` of it is a "now" in default zone, hense the local part. It'd honestly be better to just name it `DateTime` and not give it a `now()`. – M. Prokhorov Aug 28 '19 at 16:54
53

The accepted answer is not ideal, so I decided to add my 2 cents

timeStamp.toLocalDateTime().toLocalDate();

is a bad solution in general, I'm not even sure why they added this method to the JDK as it makes things really confusing by doing an implicit conversion using the system timezone. Usually when using only java8 date classes the programmer is forced to specify a timezone which is a good thing.

The good solution is

timestamp.toInstant().atZone(zoneId).toLocalDate()

Where zoneId is the timezone you want to use which is typically either ZoneId.systemDefault() if you want to use your system timezone or some hardcoded timezone like ZoneOffset.UTC

The general approach should be

  1. Break free to the new java8 date classes using a class that is directly related, e.g. in our case java.time.Instant is directly related to java.sql.Timestamp, i.e. no timezone conversions are needed between them.
  2. Use the well-designed methods in this java8 class to do the right thing. In our case atZone(zoneId) made it explicit that we are doing a conversion and using a particular timezone for it.
Ruslan
  • 3,063
  • 1
  • 19
  • 28
  • 2
    I disagree. Timestamp (and also Date) do not have any zone information. LocalDate does not have any zone information so the two are equivalent. Conversion between them can be done independently of any particular zone. Converting to a ZonedDateTime before converting to LocalDate adds in a timezone and then takes it off again - you're much more likely to get a nasty -hard to find- error doing it that way. – AutomatedMike Aug 13 '19 at 11:23
  • 8
    @AutomatedMike Timestamp (and also Date) represent a point in time in UTC. LocalDate does not represent a point in time, but rather a time as seen on a wall clock. Please read their javadocs. "Conversion between them can be done independently of any particular zone" - **not true**, new Timestamp(0L).toLocalDateTime() returns "1970-01-01T03:00" for my Moscow timezone. The result may be different for you timezone. – Ruslan Aug 14 '19 at 12:14
  • 1
    @AutomatedMike Even if you strip the time by doing toLocalDate() it's easy to prove that the day can be wrong if Timestamp had a time around midnight, for example new Timestamp(1000 * 60 * 60 * 23).toLocalDateTime().toLocalDate() returns "1970-01-02" for my Moscow timezone when it's "1970-01-01" in UTC. – Ruslan Aug 14 '19 at 12:14
  • 1
    @AutomatedMike "adds in a timezone and then takes it off again" - the point is not adding and removing a timezone, but rather converting between different notions by using an **explicit** timezone. This is in contrast to the examples of my previous two comments where a timezone is applied **implicitly**. – Ruslan Aug 14 '19 at 12:16
  • 1
    When you get a TIMESTAMP (WITHOUT TIME ZONE) from the database it is effectively a LocalDateTime though. Then if you're mapping it to a java.sql.Timestamp your JDBC driver converts the local date/time data to a number of milliseconds using the system default TimeZone. `.toLocalDateTime()` uses the default timezone again to convert the milliseconds to a LocalDateTime. This seems relatively safe to me. – Yay295 Oct 01 '21 at 22:02
  • @Yay295 it is safe for sure if you keep track of what's going on implicitly. It's more about the approaches you take. My approach is to try to avoid conversions (or at least implicit ones) at all costs possible. The scenario you described has one problem - JDBC driver maps a "local date" to "timestamp" (posgtres?). Databases and libraries are not always good with dates and they mess up with different paradigms quite often. Instead of doing the "smart" .toLocalDateTime() I would convert explicitly using ZoneId.systemDefault() and put a comment in code explaining what's going on to my teammates. – Ruslan Oct 02 '21 at 23:23
  • @Yay295 thanks for the comment though. Here is more info on my approaches https://metaruslan.com/posts/how-to-handle-dates-in-java – Ruslan Oct 02 '21 at 23:26
8

I'll slightly expand @assylias answer to take time zone into account. There are at least two ways to get LocalDateTime for specific time zone.

You can use setDefault time zone for whole application. It should be called before any timestamp -> java.time conversion:

public static void main(String... args) {
    TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
    TimeZone.setDefault(utcTimeZone);
    ...
    timestamp.toLocalDateTime().toLocalDate();
}

Or you can use toInstant.atZone chain:

timestamp.toInstant()
        .atZone(ZoneId.of("UTC"))
        .toLocalDate();
Pavel
  • 2,557
  • 1
  • 23
  • 19