7

我试图在几天内获得两个时间戳的差异,而 TimeUnit 为我返回了完全不正确的结果。

这是我的代码:

long ts = 1522242239952L;
long now = 1527274162820L;
long difference = now - ts;
int ageInDays = (int) TimeUnit.MILLISECONDS.convert(difference, TimeUnit.DAYS);
int ageInDays2 = (int) (difference/(1000 * 60 * 60 * 24));
System.out.println(ageInDays);
System.out.println(ageInDays2);

输出是:

-1756844032
58

为什么 TimeUnit 计算如此不正确?

4

3 回答 3

7

因为您正在使用 TimeUnit.convert 向后。尝试

TimeUnit.DAYS.convert(difference, TimeUnit.MILLISECONDS);

要不就

TimeUnit.MILLISECONDS.toDays(difference);

参考:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/TimeUnit.html#convert(long,%20java.util.concurrent.TimeUnit)

于 2018-05-25T19:18:48.613 回答
3

您误解了 TimeUnit 文档:

长转换(长 sourceDuration,TimeUnit sourceUnit)

将给定单位中的给定持续时间转换为此单位。

您的源单位是 MILLISECONDS,并且您需要 DAYS,因此该行应为

int ageInDays = (int) TimeUnit.DAYS.convert(difference, TimeUnit.MILLISECONDS);
于 2018-05-25T19:17:43.933 回答
0

为什么 TimeUnit 计算如此不正确?

其他答案是正确的:您正在执行与您想要的相反的转换。TimeUnit.MILLISECONDS.convert(difference, TimeUnit.DAYS);从天转换为毫秒。

但是你怎么得到一个负数?这是因为int溢出。相反的转换正确执行并产生 434 758 135 795 200 000。这比 32 位int可以容纳的要大得多。因此,当您转换为 时int,最高有效位被切掉。偶然地,最终成为符号位的位int是 1,表示负数。我通常会避免在没有先进行范围检查的情况下进行这种转换。你可能想养成同样的习惯。Math.toIntExact()在这里很有帮助。

如何修复:java.time

其他答案基本还是正确的。我仍然想提供一些建议。

如果您认为从一天 16:04 到第二天 16:04 的时间是 1 天,那么您需要考虑时区才能得到正确的结果。例如:

    ZoneId zone = ZoneId.of("Africa/Mogadishu");
    ZonedDateTime birth = Instant.ofEpochMilli(ts).atZone(zone);
    long ageInDays = ChronoUnit.DAYS.between(birth, ZonedDateTime.now(zone));
    System.out.println(ageInDays);

在这种情况下,刚才运行代码的结果也是你所期望的:

58

另一方面,如果您将 1 天定义为 24 小时,无论挂钟的读数如何,这是一种以亚秒精度获取年龄的方法。您可以随时转换为之后的几天。

    Instant birth = Instant.ofEpochMilli(ts);
    Duration age = Duration.between(birth, Instant.now());
    System.out.println(age);
    System.out.println(age.toDays());

输出:

PT1398H52M13.994841S
58

两行中的第一行表示年龄是 1398 小时 52 分 13.994841 秒。第二行与之前的结果一致。

这两个片段背后的想法也可以组合和混合。总而言之,我认为这java.time提供了一些更难获得的可能性TimeUnit,以及一些非常清晰且不言自明的代码,您不会轻易犯与问题中相同的错误。

链接:

于 2018-05-25T20:03:56.793 回答