为什么这不长时间

今天我从这样的代码中得到了奇怪的java转换问题

new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24 * 31)

这应该是在31天之前给出日期,但是在16天之后返回日期。 很明显,因为1000 * 60 * 60 * 24 * 31被评估为整数并溢出。

new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 31)按预期工作

我认为java应该将整个表达式转换为Long,因为第一个操作数是Long System.currentTimeMillis()但是由于某些我不理解的原因它不会发生在这里。 关于硬编码常量是否有一些例外是int?

这一切都说了,但我认为它应该得到答案。 将ZonedDateTime类与ZoneId一起ZoneId

  ZonedDateTime aMonthAgo = ZonedDateTime.now(ZoneId.of("Indian/Comoro")).minusMonths(1); 

刚才(4月11日)在我的电脑上输出:

2018-03-11T19:57:47.517032 + 03:00 [印度/科摩罗]

我减去一个月,这意味着28,29,30或31天,具体取决于我所在的月份和上个月的天数。 如果你无条件地想要31天,你可以拥有它,当然:

  ZonedDateTime thirtyoneDaysAgo = ZonedDateTime.now(ZoneId.of("Indian/Comoro")).minusDays(31); 

由于3月份有31天,因此在这种情况下结果相同。 它并不总是如此。

我正在使用并推荐java.time ,即现代Java日期和时间API。 与过时的Date类相比,使用它更好,并且更不容易出错。

您的代码出了什么问题?

这是关于运营商优先权的 。 1000 * 60 * 60 * 24 * 31int值组成。 是的,整数文字的类型为int除非它们具有L后缀。 因为乘法是在减法之前执行的(正如您所预期的那样),结果也是一个int ,但它会溢出,因为结果将大于int可以容纳的最大数。 不幸的是,Java没有通知你溢出,它只是给你一个错误的结果,这里-1616567296 ,大约-19天。 减去这些时间后,您将获得约19天的日期和时间。

作为一种习惯,使用括号, L后缀和下划线分组以提高可读性。

 ( System.currentTimeMillis() - ( 1_000L * 60L * 60L * 24L * 31L ) ) 

如果您想了解溢出,可以使用Math.multiplyExact​()进行乘法(从Java 8开始)。 幸运的是,现代的库类可以完全避免繁殖。 并发出任何溢出信号。

链接

  • Oracle教程:Date Time解释了如何使用java.time
  • Math.multiplyExact​() documentation