How can I create a Java 8 LocalDate from a long Epoch time in Milliseconds?
Asked Answered
T

8

333

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.

Teplitz answered 3/2, 2016 at 16:58 Comment(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.Carmina
Check this question: #21242610, which covers the conversion of java.util.Date into LocalDateBlaineblainey
Note: This Q&A is also valuable for those trying to convert File.lastModified() (epoch millis) to LocalDate(Time).Galore
Does this answer your question? How to get milliseconds from LocalDateTime in Java 8Kaylyn
@GeorgeSiggouroglou That questions asks the opposite of this question, so it is not a duplicate.Cepeda
@MarkRotteveel you are correct, I hurried, I removed the vote to close.Kaylyn
K
601

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 Instant.ofEpochMilli(long epochMilli)

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());
Knotted answered 3/2, 2016 at 20:27 Comment(14)
+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.Elidiaelie
@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…Knotted
Can I use LocalDateTime.ofEpochSecond() with, for example milliseconds / 1000 ?Cazares
@Cazares 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…).Knotted
@holger, I have a long (millis). Why ZoneId.systemDefault() cannot be used?Cazares
@Cazares 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.Knotted
I came hoping to find a more readable solution than this, what a downgrade from the old java.util approach....Indecency
@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.Knotted
Epoch is defined as UTC and should be therefore be timezone independent, so the ZoneId should always be UTC.Keystroke
@Keystroke 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())Knotted
Some ZoneID miss nanosecond and millisecond.Hatter
@Hatter there are no nanoseconds in the epochmillis in the first place. For any other loss of precision, name an actual example.Knotted
@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.Gracye
@Gracye “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.Knotted
E
64

You can start with Instant.ofEpochMilli(long):

LocalDate date =
  Instant.ofEpochMilli(startDateLong)
  .atZone(ZoneId.systemDefault())
  .toLocalDate();
Elidiaelie answered 3/2, 2016 at 20:24 Comment(1)
+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.Baseler
P
18

I think I have a better answer.

new Timestamp(longEpochTime).toLocalDateTime();
Preadamite answered 22/6, 2018 at 10:22 Comment(7)
new Timestamp(ts).toLocalDateTime().toLocalDate()Misvalue
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.Keystroke
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.Agna
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.Tend
@Keystroke Java is not monolithic anymore. To use java.sql.Timestamp when using modules, you have to declare a dependency to the module java.sql, which will in turn introduce dependencies to java.xml and java.logging.Knotted
@Knotted True, though I haven't seen anyone building on anything above Java 11 in the wild yet.Keystroke
@Keystroke well, the modularization has been introduced in Java 9.Knotted
G
8

A simple version based on @Michael Piefel answer:

LocalDate myDate = LocalDate.ofEpochDay(Duration.ofMillis(epochMillis).toDays());
Grosvenor answered 10/3, 2022 at 17:12 Comment(1)
This works perfectly well as it takes leap years into account.Biddick
D
7

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

Dart answered 10/4, 2017 at 8:46 Comment(5)
I think you should at least explain what the 86400000L stands for.Lurlene
I thought it was very easy to spot that it’s the number of milliseconds in a day.Dart
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.Lurlene
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.Dart
java.util.concurrent.TimeUnit.MILLISECONDS.toDays(startDateLong)Infuse
B
3

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));
Benis answered 28/2, 2020 at 1:30 Comment(0)
L
1

I have tested this alternative solution for LocalDateTime:

public static LocalDateTime increaseByMillis(final LocalDateTime ldt, final long millis)
    {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(ldt.toInstant(ZoneOffset.UTC).toEpochMilli()+millis), ZoneId.of(ZoneOffset.UTC.getId()));
    }

Test:

LocalDateTime test = LocalDateTime.now();
        LocalDateTime increased = MyUtilsAbc.increaseByMillis(test, 1000);

        Assert.assertEquals("Increase of LocalDateTime not working (anymore)!", test.toEpochSecond(ZoneOffset.UTC) +1, increased.toEpochSecond(ZoneOffset.UTC));
Leora answered 8/10, 2022 at 10:51 Comment(0)
G
-7

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();
Grapple answered 15/10, 2018 at 12:14 Comment(5)
This does not really relate much to the asked questionIncogitant
@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.Grapple
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.Incogitant
@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.Grapple
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 ?Incogitant

© 2022 - 2024 — McMap. All rights reserved.