266

I have an external API that returns me dates as longs, represented as milliseconds since the beginning of the Epoch.

With the old style Java API, I would simply construct a Date from it with

Date myDate = new Date(startDateLong)

What is the equivalent in Java 8's LocalDate/LocalDateTime classes?

I am interested in converting the point in time represented by the long to a LocalDate in my current local timezone.

Sae1962
  • 1,020
  • 14
  • 29
Vihung
  • 11,505
  • 15
  • 52
  • 76
  • 6
    Well you have to start by working out what time zone you care about. A "milliseconds since epoch" value gives you an instant in time... that could refer to different dates in different time zones. Bear in mind that `java.util.Date` was never really a date in the way that `LocalDate` is - it was an instant in time as well. – Jon Skeet Feb 03 '16 at 17:01
  • 2
    Check this question: http://stackoverflow.com/questions/21242110/convert-java-util-date-to-java-time-localdate, which covers the conversion of `java.util.Date` into `LocalDate` – hotzst Feb 03 '16 at 17:04
  • 3
    Note: This Q&A is also valuable for those trying to convert `File.lastModified()` (epoch millis) to `LocalDate(Time)`. – kevinarpe Mar 27 '17 at 09:27
  • Does this answer your question? [How to get milliseconds from LocalDateTime in Java 8](https://stackoverflow.com/questions/23944370/how-to-get-milliseconds-from-localdatetime-in-java-8) – George Siggouroglou Feb 09 '21 at 08:41
  • 1
    @GeorgeSiggouroglou That questions asks the opposite of this question, so it is not a duplicate. – Mark Rotteveel Feb 09 '21 at 11:13
  • @MarkRotteveel you are correct, I hurried, I removed the vote to close. – George Siggouroglou Feb 09 '21 at 21:51

6 Answers6

469

If you have the milliseconds since the Epoch and want to convert them to a local date using the current local timezone, you can use

LocalDate date =
    Instant.ofEpochMilli(longValue).atZone(ZoneId.systemDefault()).toLocalDate();

but keep in mind that even the system’s default time zone may change, thus the same long value may produce different result in subsequent runs, even on the same machine.

Further, keep in mind that LocalDate, unlike java.util.Date, really represents a date, not a date and time.

Otherwise, you may use a LocalDateTime:

LocalDateTime date =
    LocalDateTime.ofInstant(Instant.ofEpochMilli(longValue), ZoneId.systemDefault());
Holger
  • 243,335
  • 30
  • 362
  • 661
  • 2
    +1 from me for more detailed explanation. By the way, even a non-system zone can change (by tzupdater-tool or by jdk-change) and hence produce different results before and after. – Meno Hochschild Feb 04 '16 at 08:20
  • 2
    @Meno Hochschild: I wasn’t focusing on hardcoded timezones but rather comparing with timezones specified by the user, read from a configuration file or environment variables, where the programmer naturally assumes that the may change. Hardcoded timezones are indeed much like the system default; the programmer is tempted into thinking they were never changing… – Holger Feb 04 '16 at 09:13
  • 1
    Can I use `LocalDateTime.ofEpochSecond()` with, for example `milliseconds / 1000` ? – Demigod Jul 20 '18 at 13:38
  • 1
    @Demigod you can, when you think specifying a `ZoneOffset` is better than specifying a `ZoneId` (you can not use `ZoneId.systemDefault()` anymore, or at least not without converting it for a particular `Instant`, but when you have an `Instant`, why not use `LocalDateTime.ofInstant`…). – Holger Jul 20 '18 at 15:21
  • @holger, I have a long (millis). Why ZoneId.systemDefault() cannot be used? – Demigod Jul 21 '18 at 15:54
  • 2
    @Demigod `LocalDateTime.ofEpochSecond(…)` requires an actual `ZoneOffset`, but `ZoneId.systemDefault()` returns a `ZoneId`. A `ZoneId` can map to different offsets, depending on the point of time you’re referring to. That’s what `LocalDateTime.ofInstant` does for you, converting the specified `ZoneId` according to the provided `Instant`. – Holger Jul 24 '18 at 16:37
  • 1
    I came hoping to find a more readable solution than this, what a downgrade from the old java.util approach.... – Mihai Morcov Nov 08 '18 at 13:37
  • 1
    @MihaiMorcov it’s exactly the problem of the “old java.util approach” that it didn’t handle the semantic difference between a *local* data-time and a long value representing an *instant*. – Holger Nov 08 '18 at 14:23
  • 6
    Epoch is defined as UTC and should be therefore be timezone independent, so the ZoneId should always be UTC. – PlexQ Jan 04 '19 at 22:51
  • 2
    @PlexQ The specified timezone is not relevant for the Epoch, which indeed is timezone independent, but for the semantics of the resulting `LocalDate` or `LocalDateTime`. You can specify any timezone you want, as long as it is consistent with the subsequent use of these result objects. Think of what happens when you deal with multiple objects created by different methods. The typical use cases for *local* date or datetimes incorporate the system default timezone, e.g. `LocalDateTime.now()` ≈ `LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), ZoneId.systemDefault())`… – Holger Jan 07 '19 at 09:11
  • Some ZoneID miss nanosecond and millisecond. – fjjiaboming Apr 25 '19 at 03:56
  • 1
    @fjjiaboming there are no nanoseconds in the epochmillis in the first place. For any other loss of precision, name an actual example. – Holger Apr 25 '19 at 09:18
  • @MihaiMorcov Agreed. What a nightmare. I am converting some really old code and I wanted a simple way to use the long value of a file last mod date and I have to do all this? A real shame. – Pryftan Jun 26 '20 at 16:55
  • 1
    @Pryftan “to use the long value of a file last mod date” is a very unspecific task. Are you sure you actually need a `LocalDate`? Maybe whatever you actually want to do with the long value, can be done with the `Instant` created via `Instant.ofEpochMilli(longValue)` too. – Holger Jun 26 '20 at 17:46
44

You can start with Instant.ofEpochMilli(long):

LocalDate date =
  Instant.ofEpochMilli(startDateLong)
  .atZone(ZoneId.systemDefault())
  .toLocalDate();
Meno Hochschild
  • 38,305
  • 7
  • 88
  • 115
  • 5
    +1 for being explicit about time zone. If omitted, the JVM’s current default time zone is implicitly applied in determining the date. For any given moment the date varies around the world by time zone as a new day dawns earlier in the east. – Basil Bourque Feb 03 '16 at 22:00
16

I think I have a better answer.

new Timestamp(longEpochTime).toLocalDateTime();
Abhijeet
  • 583
  • 1
  • 7
  • 17
  • new Timestamp(ts).toLocalDateTime().toLocalDate() – Stepan Yakovenko Dec 27 '18 at 21:59
  • 7
    I mean - if you don't mind importing javal.sql.Timestamp, which, given Java's monolithic nature I suppose is fine because it's just all part of the JVM... but feels a bit smelly, but I still like it better as it recognized epoch is fundamentally in UTC. – PlexQ Jan 04 '19 at 22:53
  • 1
    The `Timestamp` class is poorly designed and long outdated. Your code will use the JVM’s time zone setting, but since this setting can be changed by another part of your program or another program running in the same JVM, we cannot be quite sure what it is. – Ole V.V. Sep 10 '19 at 14:51
  • 1
    It's always better to be explicit about which timezone is used. Using the old java.sql.Timestamp has the drawback of having the system timezone applied implicitly which usually causes confusion among developers. – Ruslan Sep 26 '19 at 14:54
4

Timezones and stuff aside, a very simple alternative to new Date(startDateLong) could be LocalDate.ofEpochDay(startDateLong / 86400000L)

Michael Piefel
  • 14,444
  • 5
  • 65
  • 95
  • 9
    I think you should at least explain what the 86400000L stands for. – BAERUS May 31 '17 at 09:56
  • 4
    I thought it was very easy to spot that it’s the number of milliseconds in a day. – Michael Piefel Jun 01 '17 at 07:55
  • 10
    For some it is, and I figured that only that would make sense, but without recalculation how many ms a day really has, I wouldn't be sure. Just speaking for myself, I don't know this number so well that I automatically know what it stands for. – BAERUS Jun 01 '17 at 08:40
  • Note also that the accepted answer really is the best answer, it just looks overwhelming. My simple hack just my be enough in many case. It’s a pity that `java.time` does not include `DateTimeConstants` as Joda did. – Michael Piefel Jun 01 '17 at 12:42
  • 2
    `java.util.concurrent.TimeUnit.MILLISECONDS.toDays(startDateLong)` – Vadzim Mar 18 '18 at 13:50
2

replace now.getTime() with your long value.

//GET UTC time for current date
        Date now= new Date();
        //LocalDateTime utcDateTimeForCurrentDateTime = Instant.ofEpochMilli(now.getTime()).atZone(ZoneId.of("UTC")).toLocalDateTime();
        LocalDate localDate = Instant.ofEpochMilli(now.getTime()).atZone(ZoneId.of("UTC")).toLocalDate();
        DateTimeFormatter dTF2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        System.out.println(" formats as " + dTF2.format(utcDateTimeForCurrentDateTime));
-6

In a specific case where your epoch seconds timestamp comes from SQL or is related to SQL somehow, you can obtain it like this:

long startDateLong = <...>

LocalDate theDate = new java.sql.Date(startDateLong).toLocalDate();
M. Prokhorov
  • 3,517
  • 20
  • 34
  • 2
    This does not really relate much to the asked question – Ketan R Dec 20 '19 at 06:43
  • 1
    @KetanR, I disagree. The question is "how to obtain a `LocalDate` from `epoch-millis`", and I show how, using `java.sql.Date` for shorthand. This approach makes sense in code that's already dealing with JDBC in some capacity, and it works just fine. If you still not convinced, explain how is it not related to initial question. – M. Prokhorov Dec 20 '19 at 12:02
  • 2
    If you read the question, it says "external API that returns me dates as longs" and you are explaining how this conversion can be done if you are receiving long date from SQL. Your answer does explain a very specific case of the date conversion but its not really relevant to question that has been ask ed.Your explanation could be a valid answer to some other related quesiton though. – Ketan R Dec 23 '19 at 09:52
  • 1
    @KetanR, if one receives long date from SQL, I'd advise to change his schema so he no longer receives dates in such form. However, if one receives dates as millis-timestamps from elsewhere (the external API), and immediately uses these dates to make JDBC queries, then `java.sql.Date` approach is among the shortest available, code-wise, and I'd say it's not all that useful to go though `Instant` with all the intermediate temporal objects when the end result is the same. – M. Prokhorov Dec 23 '19 at 18:29
  • 2
    As i have already said and is evident from your latest explanation, your answer is correct but not for the question at hand Question says: "I am interested in converting the point in time represented by the long to a LocalDate in my current local timezone. " your answer tells: "receive dates as millis-timestamps, and immediately use these dates to make JDBC queries". I dont understand what is not clear here ? – Ketan R Dec 26 '19 at 13:39