两个Date()比较时差1秒,差1天



我写了一个简单的TimeService方法getDateAfter(int days)和测试它:

@Test
@Order(7)
public void getDateAfterCorrect() throws InterruptedException {
waitIfNeeded();
LocalDateTime today = LocalDateTime.now();
LocalDateTime tomorrow = timeService.getDateAfter(1).toInstant()
.atZone(ZoneId.systemDefault()).toLocalDateTime();
long diff = ChronoUnit.SECONDS.between(today, tomorrow);
long secondsAtDay = 86400;
Assertions.assertEquals(secondsAtDay, diff);
}

应该是86400秒at day,但diff是86399。我试图通过实现waitIfNeeded()方法

来考虑代码的一部分可以在另一个时间执行。
private void waitIfNeeded() throws InterruptedException {
var currentMillis = Instant.now().get(ChronoField.MILLI_OF_SECOND);
if (currentMillis > 500) {
Thread.sleep(1000 - currentMillis);
}
}

你知道为什么我不能做这个测试和其他可能在这里出错的事情吗(我假设编程语言如何处理step year等)

我设法使测试简化和工作,现在它是OK的:

@Test
@Order(7)
public void getDateAfterCorrect() throws InterruptedException {
waitIfNeeded();
long today = timeService.getDate().toInstant().getEpochSecond();
long tommorow = timeService.getDateAfter(1).toInstant().getEpochSecond();
Assertions.assertEquals(86400, tommorow - today);
}

但是为什么用其他方法比较这两个日期会产生这样的结果,这仍然是有趣的,如果有深度知识的人能回答这个问题,可能很少有人会感兴趣。

java.utilDate-Time API过时且容易出错。建议完全停止使用它,切换到现代的Date-Time API*

除此之外,不是自己执行计算(减法),而是使用Instant#until,它在指定的ChronoUnit中返回持续时间。

import java.time.Instant;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
// This is a sample Instant. In your case, it will be returned by
// timeService.getDate().toInstant()
Instant today = Instant.now();
// This is a sample Instant after one day. In your case, it will be returned by
// timeService.getDateAfter(1).toInstant()
Instant tomorrow = today.plus(1, ChronoUnit.DAYS);
long seconds = today.until(tomorrow, ChronoUnit.SECONDS);
// In your case, you will use Assertions.assertEquals(86400, seconds);
System.out.println(seconds);
}
}

输出:

86400
<<p>

在线演示/kbd>从Trail: Date Time了解更多关于现代Date-Time API的信息.


*如果你正在为一个Android项目工作,你的Android API级别仍然不兼容Java-8,检查Java 8+可用的API。注意Android 8.0 Oreo已经提供了对java.time的支持。

解释:为什么是86399?缺乏精度

我假设timeService.getDateAfter(1)返回一个老式的Date对象。你不应该再在你的代码中使用Date了,它已经被java取代了。time,您在许多年前也在使用的现代Java日期和时间API。但是我们仍然很好奇,为什么你使用Date的代码没有给出一天86 400秒的预期结果。

Date的精度为毫秒级。java。time具有纳秒级的精度,自Java 9以来,许多类的now方法在大多数平台上具有微秒级的精度。例如,LocalDateTime.now()返回0.001234秒整秒。几微秒后,也许在0.001432过整秒时,您的时间服务返回值为0.001秒过整秒的Date。今天的0.001234和明天的0.001之间不是一整天,也不是整整24小时,所以秒差被截断为86 399。

当我在循环中运行你的代码时,第一次我得到86 400。这一定是因为我在两次调用之间间隔了整整一毫秒的时间。可能是因为JVM预热。接下来的几次我得到了86 399。

一个可能的修复

获得一致精度的一种方法是将所有内容截断为毫秒。甚至到秒。通过对代码的以下更改,我始终得到86 400。

LocalDateTime today = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS);

当使用ChronoUnit.SECONDS而不是.MILLIS时,我得到了相同的结果。我相信这也在一定程度上解释了为什么你自己的答案发生了变化。不过,我觉得你需要意识到,两次通话之间确实会间隔一段时间,而你无法控制间隔的时间。所以你可能会得到86 401甚至在极少数情况下更高的数字。即使我没有在我的几次运行中观察到它。

我曾经在一个地方工作过,那里有很多单元测试偶尔会失败。即使我养成了在单元测试中输入关于零星失败的注释的习惯,这也是代码中令人讨厌和不信任的来源。请不要把自己和同事置于同样的境地。

最新更新