Create Java DateTime Instant from microseconds
Asked Answered
S

1

7

There has been changes in Java Date & Time API Since Java 9. LocalDateTime now has microseconds precision.

Java 9 has a fresh implementation of java.time.Clock capable of capturing the current moment in resolution finer than milliseconds (three digits of decimal fraction).

We get the time in microseconds from our backend service.

System.currentTimeMillis  > 1565245051795    > 2019-08-08T06:17:31.795
Service.getTime           > 1565245051795306 > 2019-08-08T06:17:31.795306

In order to construct a LocalDateTime to be used in our application, we do

long timeMicros = service.getTime();
long timeMillis = timeMicros / 1000;
LocalDateTime ldt = Instant.ofEpochMilli(timeMillis).atZone(ZoneId.systemDefault()).toLocalDateTime();

For querying the service we need time microseconds again, then we do

long timeMillis = dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long timeMicros = timeMillis * 1000;

The problem is we do not get back the time microseconds precision.

Is it possible to create an Instant with microsecond precision?
We are now using Java 11. I noticed this change when one of our JUnit tests failed because of the increased microsecond precision.

For the JUnit test I found a workaround:

private static final LocalDateTime START = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS);


I'm not sure if this is a workaround or an actual solution, but adding the last three microseconds digits from the timestamp seems to work.

long micros = 306L; //TODO get the last three digits from the timeMicros
ldt.plus(micros, ChronoUnit.MICROS));
Sebi answered 8/8, 2019 at 11:47 Comment(4)
Instead of Instant.ofEpochMilli(), use Instant.ofEpochSecond(). The one with two arguments: the seconds since epoch, and the additional nanoseconds to that.Rubetta
A possibility, but how to get the epochSeconds and the nanoAdjustment from the timeMicros long?Sebi
Seconds : divide timeMicros by a million. Nanoseconds: get microseconds by modulo of timeMicros by a million. Then multiply the result by a thousand.Rubetta
By the way, using LocalDateTime in this manner is mostly like not what you want to do. Use ZonedDateTime to represent a moment, a specific point on the timeline, as LocalDateTime by definition cannot. I cannot think of any use-case where calling LocalDateTime.now() is the right thing to do. See: What's the difference between Instant and LocalDateTime?Distributor
C
11
    long timeMicros = 1_565_245_051_795_306L;
    Instant i = Instant.EPOCH.plus(timeMicros, ChronoUnit.MICROS);
    System.out.println(i);

Output is:

2019-08-08T06:17:31.795306Z

Edit: Rather than dividing and multiplying to convert microseconds to milliseconds and/or seconds I preferred to use the built-in support for microseconds. Also when explicitly adding them to the epoch feels a little hand-held.

You already know how to convert Instant to LocalDateTime, you’ve shown it in the question, so I am not repeating that.

Edit:

Do you have a solution to get the timeMicros back from the Instant?

There are a couple of options. This way the calculation is not so complicated, so I might do:

    long microsBack = TimeUnit.SECONDS.toMicros(i.getEpochSecond())
            + TimeUnit.NANOSECONDS.toMicros(i.getNano());
    System.out.println(microsBack);

1565245051795306

To be more in style with the first conversion you may prefer the slightly shorter:

    long microsBack = ChronoUnit.MICROS.between(Instant.EPOCH, i);

Edit: Possibly nit-picking, but also to avoid anyone misunderstanding: LocalDateTime has had nanosecond precision always. Only the now method had millisecond precision on Java 8. I read somewhere that from Java 9 the precision varies with the platform, but you are right, microsecond precision seems typical.

Cingulum answered 8/8, 2019 at 19:8 Comment(4)
A good solution. Why then aren't Instant using the same EPOCH constant to create an Instant with ofEpochMilli? It is using Math class to create an Instant with seconds and nanoOfSecond.Sebi
Do you have a solution to get the timeMicros back from the Instant?Sebi
I have added a couple of options for converting back to micros at the end of the answer. For why ofEpochMilli wasn’t implemented in a similar fashion, I don’t know. It could be an efficiency consideration, on the other hand I’m not necessarily convinced that the implementation is the most efficient possible, so I’m unsure.Cingulum
Or perhaps ofEpochMilli was coded early in the project, before the ChronoUnit enum, and then not changed later.Cingulum

© 2022 - 2024 — McMap. All rights reserved.