JSR 310 :: System.currentTimeMillis() vs Instant.toEpochMilli() :: TimeZone
Asked Answered
E

3

26

Could you please shed some light on how to obtain correct epoch time in milliseconds for a default system timezone and given timezone.

Given

1. TimeZone: GMT+3

2. The following code snippet:

import java.time.*;

public class Main {        
    public static void main(String[] args) {
        System.out.println(LocalDateTime
            .now()
            .atZone(ZoneOffset.UTC)
            .toInstant()
            .toEpochMilli()
        );
        System.out.println(LocalDateTime
            .now()
            .atZone(ZoneOffset.of("+3"))
            .toInstant()
            .toEpochMilli()
        );
        System.out.println(System.currentTimeMillis());
    }
}

3. Output:

1444158955508
1444148155508
1444148155508

4. JavaDoc for System.currentTimeMillis() that tells that returned value will be the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.

So, why

  1. the output of the LocalDateTime at GMT+3 is the same as of System.currentTimeMillis(), although the docs for the System.currentTimeMillis() mention UTC?
  2. the output of the LocalDateTime at UTC differs from System.currentTimeMillis(), although the docs for the System.currentTimeMillis() mention UTC?
Evert answered 6/10, 2015 at 16:51 Comment(0)
C
46

Both System.currentTimeMillis() and Instant.toEpochMilli() return the number of milliseconds since the Unix epoch. That isn't "in" any particular time zone, although the Unix epoch is normally expressed as "midnight on January 1st 1970, UTC". But an instant is just an instant in time, and is the same whichever time zone you're in - but it will reflect a different local time.

The output of LocalDateTime.atZone(UTC) differs because you're saying "Take the local date and time, and convert it to an instant as if it were in the UTC time zone" - even though when you created that LocalDateTime you did so implicitly in the UTC+3 time zone... that's why it's "wrong".

LocalDateTime.now() takes the local date and time in the system default time zone. So if your time zone is UTC+3, the current instant in time is 2015-10-06T16:57:00Z, then LocalDateTime.now() will return .2015-10-06T19:57:00. Let's call that localNow...

So localNow.atZone(ZoneOffset.of("+3")) will return a ZonedDateTime representing 2015-10-06T19:57:00+03 - in other words, the same local date/time, but "knowing" that it's 3 hours ahead of UTC... so toInstant() will return an Instant representing 2015-10-06T16:57:00Z. Great - we still have the current date/time.

But localNow.atZone(ZoneOffset.UTC) will return a ZonedDateTime representing 2015-10-06T19:57:00Z - in other words, the same local date/time, but "thinking" that it's already in UTC... so toInstant() will return an Instant representing 2015-10-06T19:57:00Z.. which isn't the current time at all (it's in three hours).

Cord answered 6/10, 2015 at 16:55 Comment(1)
I'd like to add a humble comment on top of excellent answer by Jon. LocalDateTime.now().atZone(ZoneOffset.UTC) is a conversion call to ZonedDateTime as LocalDateTime does not have a zone. The argument passed into atZone() method should be an original (default in this example) time zone used to derive LocalDateTime, not the "to" zone the author probably intended to get out because Instant does not have a zone. If the author didn't specify TimeZone: GMT+3, it'd be hard to resolve his question.Storz
G
1

Short version:

There is no way to compute LocalDateTime -> Instant, you need to specify a timezone. With a timezone you get a ZonedDateTime and can compute ZonedDateTime -> Instant

Instant == System.currentTimeMillis() if the timezone of the ZonedDateTime equals the system default time zone.

Long version:

LocalDateTime is the time on your clock(plus date information). Which is not enough if you don't tell us which timezone your are in. 13:00 o'clock in Tokyo is not the same Instant as 13:00 o'clock in Paris.

Once you add a timezone to your LocalDateTime you get a ZonedDateTime and we can know in which Instant of time you actually are. E.g. are you 13:00 o'clock in Tokyo or in Paris?

To get the correct Instant the timezone of the ZonedDateTime needs to be correct. If it is 13:00 o'clock in Tokyo but you claim that you are 13:00 o'clock in Paris you will get a wrong Instant.

LocalDateTime:

It cannot represent an instant on the time-line without additional information such as an offset or time-zone.

ZonedDateTime:

This class handles conversion from the local time-line of LocalDateTime to the instant time-line of Instant. The difference between the two time-lines is the offset from UTC/Greenwich, represented by a ZoneOffset.

To get an Instant you need to convert LocalDateTime to ZonedDateTime first. If you did this correctly(by stating the correct timezone) your Instant will agree with System.currentTimeMillis().

System.currentTimeMillis():

the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.

  1. the output of the LocalDateTime at GMT+3 is the same as of System.currentTimeMillis(), although the docs for the System.currentTimeMillis() mention UTC?

If your timezone is GMT+3 then ZonedDateTime.toInstant() will give you the correct Instant and therefore agree with System.currentTimeMillis()

  1. the output of the LocalDateTime at UTC differs from System.currentTimeMillis(), although the docs for the System.currentTimeMillis() mention UTC?

If your timezone is not UTC then ZonedDateTime.toInstant() will give you an incorrect Instant.

Grace answered 5/12, 2018 at 12:12 Comment(1)
Isn't ZonedDateTime.toInstant() supposed to convert said ZonedDateTime to UTC then create the Instant from it? Based on the documentation (and my experience, albeit using ThreetenBackport, not the official API) I'd suspect that's the case.Roofdeck
W
0
System.out.println("Output 1 : " + LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
System.out.println("Output 2 : " + System.currentTimeMillis());

Output 1 : 1576047664910

Output 2 : 1576047664911

Wideangle answered 11/12, 2019 at 7:4 Comment(5)
How and why does this code plus its output provide an answer to the question, which already has an accepted answer? Can you explain?Ulyssesumayyad
@Ulyssesumayyad The question was how to obtain correct epoch time in milliseconds, so I suppose StinkyMadness just wanted to supply a short and concise answer for those who did not want to read the long answers. I agree with you, though, the answer isn’t very useful without at least a minimal explanation. And output 1 can be obtained in a simpler way from Instant.now().toEpochMilli().Accountable
@OleV.V I don't think the answer is bad, but it was just two lines of code when I put that comment...Ulyssesumayyad
Mostly i add that 2 lines for the "ZoneId.systemDefault()", also i'm total new here sorry if i did something against the rules.Wideangle
No, @StinkyMadness, you certainly did nothing against the rules (which it takes a bit to learn, BTW), and thanks for your contribution. Please decide yourself whether you want to improve your answer further — you have got an edit link under it in case.Accountable

© 2022 - 2024 — McMap. All rights reserved.