我今天遇到了来自此类代码的奇怪的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?
答案 0 :(得分:3)
所有人都说过,但我认为它应该得到答案。将ZonedDateTime
课程与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日期和时间API java.time
。与过时的Date
类相比,使用起来更好,更不容易出错。
关于operator precedence。 1000 * 60 * 60 * 24 * 31
由int
个值组成。是的,整数文字的类型为int
,除非它们具有L
后缀。因为乘法是在减法之前执行的(正如您已经预期的那样),结果也是int
,但它会溢出,因为结果将大于int
可以容纳的最大数。不幸的是,Java没有通知你溢出,它只是给你一个错误的结果,在这里-1616567296
,大约-19天。减去这些时间后,您将获得约19天的日期和时间。
作为一种习惯,请使用括号,L
后缀和underscore-grouping以获取可读性。
( System.currentTimeMillis() - ( 1_000L * 60L * 60L * 24L * 31L ) )
如果您想了解溢出,可以使用Math.multiplyExact()
进行乘法(从Java 8开始)。幸运的是,现代的库类可以完全避免繁殖。并发出任何溢出信号。
java.time
。Math.multiplyExact() documentation