I'm currently in the process of upgrading a few projects from Java 8 to Java 11 where one of the unit tests for a converter failed. Basically the problem stems from the equality check failing due to a a date precision which previously passed with JDK 8.
Here's a section sample of the test, I've moved the contents of the converter for clarity:
@Test
public void testDateTime() {
LocalDateTime expected = LocalDateTime.now().plusDays(1L);
// converter contents
long epochMillis = expected.atZone(ZoneId.systemDefault())
.toInstant().toEpochMilli();
LocalDateTime actual = LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMillis),
TimeZone.getDefault().toZoneId());
assertThat(actual, equalTo(expected));
}
This results to an assetion error due to:
Expected :<2021-06-02T14:06:21.820299>
Actual :<2021-06-02T14:06:21.820>
I can trunkate the expected with assertThat(actual, equalTo(expected.truncatedTo(ChronoUnit.MILLIS)))
for them to be equal, however, this means each time a comparison is made (isAfter, isBefore, equals) to the converter class being tested, trunkating will have to be applied.
Is there a proper way to do conversions for between LocalDateTime
to Long
and vice versa for JDK 11 (or a documentation I might have missed perhaps :))?
Update:
As pointed out in the comments, the representation is not the same for Java 8 and 11 thus resulting to the failing test. To give more context on what is being asked by this post, here are the 2 methods being verified by the test (which i moved to the test itself to only capture what is being executed because the unit test that failed belongs to a class that uses the utility method)
public Long localDateTimeToEpochMillis(LocalDateTime ldt) {
Instant instant = ldt.atZone(ZoneId.systemDefault()).toInstant();
return ldt.atZone(ZoneId.systemDefault())
.toInstant().toEpochMilli();
}
and
public LocalDateTime epochMillisToLocalDateTime(long epochMillis) {
return LocalDateTime.ofInstant(
Instant.ofEpochMilli(epochMillis),
ZoneId.systemDefault());
}
What the existng tests seems to verify is that given a long value, i should get the same LocalDateTime equivalent and this was done by using the Given (LocalDateTime converted to Long value) then back to LocalDateTime for comparison.
If you take a look at the difference:
Expected :<2021-06-02T14:06:21.820299>
Actual :<2021-06-02T14:06:21.820>
You can see that it removes anything less than a millisecond.
This happens because you convert the LocalDateTime
to milliseconds:
.toInstant().toEpochMilli();
In order to avoid that, you can use Instant#getNano
:
Gets the number of nanoseconds, later along the time-line, from the start of the second. The nanosecond-of-second value measures the total number of nanoseconds from the second returned by getEpochSecond().
It could look like this:
Instant instant=expected.atZone(ZoneId.systemDefault())
.toInstant();
long epochMillis = instant.toEpochMilli();
long nanos=instant.getNano()%1000000;//get nanos of Millisecond
LocalDateTime actual = LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMillis).plusNanos(nanos),
TimeZone.getDefault().toZoneId());
As this post and JDK-8068730 Increase the precision of the implementation of java.time.Clock.systemUTC() describes, Java 8 did not capture time units smaller than a millisecond. Since Java 9, LocalDateTime.now
(and similar) get the time with microseconds.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With