Java当前日期与过去日期之间的时差,以年,月,日,小时,分钟,秒为单位

时间:2019-04-24 16:27:28

标签: java date datetime difference date-difference

我知道以前在这里已经多次询问过这个问题,但是我无法找到与我的案子有关的任何东西。我正在尝试查找当前日期时间和上一个日期时间之间的差异,两者的格式均为yyyy-MM-dd HH:mm:ss.s。根据{{​​3}}给出的答案,我想出了以下代码:

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 / 1000 % 60;
    long diffMinutes = diff / (60 * 1000) % 60;
    long diffHours = diff / (60 * 60 * 1000) % 24;
    long diffDays = diff / (24 * 60 * 60 * 1000) % 30;
    long diffMonths = diff / (30 * 24 * 60 * 60 * 1000) % 12;
    long diffYears = diff / (12 * 30 * 24 * 60 * 60 * 1000);

    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();
}

运行代码时,JSON返回以下结果:

lifetime: "41 years, -1 months, 14 days, 9 hours, 42 minutes, 37 seconds"

我在这里有两个问题:

  1. 在我41年的计算中,哪里出错了?
  2. 我有更好的方法吗?我当前的设置未考虑leap年或365天,因此我需要将这些考虑在内。

3 个答案:

答案 0 :(得分:1)

在我41年的计算中哪里出错了,并且数字为负?

因为分母将溢出。您需要使用Long:

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/

很难给出准确的答案,因为这个问题有点含糊。例如-如果一年中的某个年份是a年,并且您正在比较日期2020/03/28和2021/03/28,结果应该是什么? 1年还是1年零1天? (2020年是a年,因此03/28之后也为03/29)

答案 1 :(得分:1)

  
      
  1. 41年的计算中我出哪里错了,并且数字为负?
  2.   

除了使用臭名昭著的麻烦而已久的SimpleDateFormat类和同样过时的Date类之外,您的代码中还存在以下错误:

  • 您正在将08.0解析为8秒0秒。在我的JDK-11上,SimpleDateFormat选择0秒,并丢弃我认为正确的8秒。 SimpleDateFormat无法在秒上解析一个十进制数(只能精确地为三个十进制数),因此解决此错误的方法是完全丢弃SimpleDateFormat
  • 正如其他人所说,您的乘法有int溢出。例如,30 * 24 * 60 * 60 * 1000应该给出2 592 000 000,但是由于int无法保存该数字,因此您会得到-1 702 967 296。由于这是一个负数,因此下面的除法会为您提供负数的月数。
  • 正如所罗门·斯洛(Solomon Slow)在评论中指出的,一个月可能是28、29、30或31天。将所有月份都设置为30天时,您可能会冒着错误的天数和月数,甚至最后几年的风险。今天,当我运行您的代码时,正确的答案应该是1年4个月13天,但是我得到19天而不是6天。
  • 您没有考虑夏令时间(DST)和其他时间异常。这些可能会导致一天(例如23或25小时)出现错误。

或者总结:您的错误是您尝试“手动”进行计算。日期和时间数学太复杂且容易出错。您应该始终将其留给经过充分验证的库类。

  
      
  1. 我有更好的方法吗?我目前的设定未考虑leap年或365天,因此我需要将这些纳入   帐户。
  2.   

是的,还有更好的方法。最好的方法可能是使用ThreeTen Extra项目中的PeriodDuration类,请参见下面的链接。我现在不打算在我的计算机上安装该库,因此我将使用内置的类来展示一种好的现代解决方案:

    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中,Period表示年,月和日,而Duration表示小时,分钟,秒和分数秒(低至纳秒)。既然您都想要,我将同时使用这两个类。

我正在使用的toXxxPart的{​​{1}}方法是在Java 9中引入的。如果您正在使用Java 8(或ThreeTen Backport),则打印分钟和秒会更加复杂。搜索Duration或类似内容以了解操作方法。

我仍然没有考虑夏季时间。为此,我们需要知道最早的运行时间字符串的时区,然后使用java format duration而不是ZonedDateTime。否则,代码将非常相似。

链接

答案 2 :(得分:0)

使用与您相同的方法,您需要将分母明确标识为长值。当前,它假定它们是整数,这会导致数字溢出-意味着计算出的值对于整数而言太大。这可以解释为什么您会得到负/任意值。修复很简单:

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();
}