3 回答

TA貢獻1862條經驗 獲得超6個贊
我在 41 年和負數的計算中哪里出錯了?
除了使用臭名昭著的麻煩和過時SimpleDateFormat
的類以及同樣過時的類之外Date
,您的代碼中還有以下錯誤:
您正在解析
08.0
為 8 秒 0 秒。在我的 JDK-11 上SimpleDateFormat
選擇 0 秒并丟棄我認為正確的 8 秒。SimpleDateFormat
無法解析秒上的一位小數(僅精確三位小數),因此該錯誤的解決方案是完全丟棄SimpleDateFormat
。正如其他人所說
int
,您的乘法溢出。例如,30 * 24 * 60 * 60 * 1000
應該給出 2 592 000 000,但由于 anint
不能保存這個數字,所以你得到-1 702 967 296
了。因為這是一個負數,所以下面的除法給你一個負數的月數。正如 Solomon Slow 在評論中指出的那樣,一個月可能是 28、29、30 或 31 天。將所有月份設置為 30 天時,您將面臨不正確的天數和月份數以及最終年份數的風險。當我今天運行你的代碼時,正確的答案是 1 年 4 個月 13 天,但我得到了 19 天,6 天太多了。
您沒有考慮夏令時 (DST) 和其他時間異常。這些可能會導致一天為例如 23 或 25 小時,從而產生錯誤。
或者總結一下:您的錯誤是您試圖“手動”進行計算。日期和時間數學太復雜且容易出錯,無法執行此操作。您應該始終將其留給經過充分驗證的庫類。
我有更好的方法嗎?我當前的設置不考慮閏年或 365 天一年,我需要將這些考慮在內。
是的,有一個更好的方法。最好的方法可能是使用PeriodDuration
ThreeTen Extra 項目中的類,請參見下面的鏈接。我現在不打算在我的計算機上安裝那個庫,所以我將只展示使用內置類的好的和現代的解決方案:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.S");
LocalDateTime currentDateTime = LocalDateTime.now(ZoneId.of("Australia/Sydney"));
String earliestRunTime = "2017-12-16 01:30:08.0";
LocalDateTime earliestDateTime = LocalDateTime.parse(earliestRunTime, dtf);
// We want to find a period (years, months, days) and a duration (hours, minutes, seconds).
// To do that we cut at the greatest possible whole number of days
// and then measure the period before the cut and the duration after it.
LocalDateTime cut = earliestDateTime.plusDays(
ChronoUnit.DAYS.between(earliestDateTime, currentDateTime));
Period p = Period.between(earliestDateTime.toLocalDate(), cut.toLocalDate());
Duration d = Duration.between(cut, currentDateTime);
String result = String.format("%s years, %s months, %s days, %s hours, %s minutes, %s seconds",
p.getYears(), p.getMonths(), p.getDays(),
d.toHours(), d.toMinutesPart(), d.toSecondsPart());
System.out.println(result);
當我剛剛運行代碼時,我得到了:
1年4個月13天19小時26分7秒
在現代 Java 日期和時間 API java.time 中,aPeriod
是年、月和日Duration
的數量,a 是小時、分鐘、秒和秒的小數部分(低至納秒)。既然你想要兩者,我正在使用這兩個類。
我使用的toXxxPart
方法Duration
是在 Java 9 中引入的。如果您使用的是 Java 8(或 ThreeTen Backport),打印分鐘和秒會稍微復雜一些。搜索java format duration
或類似內容以了解如何操作。
我仍然沒有考慮夏季時間。為此,我們需要知道最早運行時間字符串的時區,然后使用ZonedDateTime
而不是LocalDateTime
. 否則代碼將非常相似。

TA貢獻1946條經驗 獲得超3個贊
我在 41 年和負數的計算中哪里出錯了?
因為分母會溢出。您需要使用長:
long diffMonths = diff / (30 * 24 * 60 * 60 * 1000L) % 12; //Notice the L at the end long diffYears = diff / (12 * 30 * 24 * 60 * 60 * 1000L); //Notice the L at the end
另請注意,這12 * 30
是一年中天數的一個非常糟糕的近似值。
我有更好的方法嗎?
是的。使用 Java 8 中的 Duration api。https://www.mkyong.com/java8/java-8-period-and-duration-examples/
很難給出準確的答案,因為這個問題有點模糊。例如 - 如果某一年是閏年,并且您正在比較日期 2020/03/28 和 2021/03/28,結果應該是什么?1年還是1年1天?(2020 年是閏年,所以 03/28 之后還有 03/29)

TA貢獻1799條經驗 獲得超9個贊
使用與您相同的方法,您需要將分母明確標識為長值。目前,它假定它們是整數,這會導致數字溢出——這意味著計算的值對于整數來說太大了。這可以解釋為什么你會得到負值/任意值。修復很簡單:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.s");
String earliestRunTime = "2017-12-16 01:30:08.0";
Date currentDate = new Date();
log.info("Current Date: {}" + format.format(currentDate));
try {
Date earliestDate = format.parse(earliestRunTime);
long diff = currentDate.getTime() - earliestDate.getTime();
long diffSeconds = diff / 1000L % 60L;
long diffMinutes = diff / (60L * 1000L) % 60L;
long diffHours = diff / (60L * 60L * 1000L) % 24L;
long diffDays = diff / (24L * 60L * 60L * 1000L) % 30L;
long diffMonths = diff / (30L * 24L * 60L * 60L * 1000L) % 12L;
long diffYears = diff / (12L * 30L * 24L * 60L * 60L * 1000L);
return String.format("%s years, %s months, %s days, %s hours, %s minutes, %s seconds",
diffYears, diffMonths, diffDays, diffHours, diffMinutes, diffSeconds);
}
catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
添加回答
舉報