【问题标题】:Java Difference between current date and past date in Years, Months, Days, Hours, Minutes, Seconds [duplicate]Java当前日期和过去日期之间的年,月,日,小时,分钟,秒之间的差异[重复]
【发布时间】:2019-04-24 16:27:27
【问题描述】:

我知道这个问题之前已经在这里被问过很多次了,但是我一直没能找到任何关于我的案例的具体信息。我试图找出当前日期时间和以前的日期时间之间的差异,每个日期时间的格式都为yyyy-MM-dd HH:mm:ss.s。根据here 给出的答案,我想出了以下代码:

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. 我有更好的方法吗?我当前的设置不考虑闰年或 365 天一年,我需要将这些考虑在内。

【问题讨论】:

  • 结果应该是怎样的?如果相差 1 年,是打印“1 年 12 个月 365 天”还是“1 年 0 个月 0 天”?
  • 想一想:如果报告精确到秒很重要,但时间跨度可能包括 28、29、30 或 31 天的月份,那么也许你可能需要重新考虑您的用例。
  • @NeplatnyUdaj 理想情况下,差异应该是 1 年 0 个月 0 天。
  • @SolomonSlow 好点,我什至没有想到。
  • 您正在使用多年前被 java.time 取代的糟糕类。永远不要使用Date

标签: java date datetime difference date-difference


【解决方案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/

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

【讨论】:

  • 闰年应按1年1天处理。
【解决方案2】:
  1. 41 年和负数的计算哪里出错了?

除了使用出了名的麻烦且过时的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. 我有更好的方法吗?我当前的设置不考虑闰年或 365 天一年,我需要将这些考虑在内 帐户。

是的,有一个更好的方法。最好的方法可能是使用 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 是小时、分钟、秒和秒的小数部分(向下到纳秒)。既然你都想要,我同时使用这两个类。

我正在使用的DurationtoXxxPart 方法是在Java 9 中引入的。如果您使用的是Java 8(或ThreeTen Backport),则打印分钟和秒会稍微复杂一些。搜索 java format duration 或类似名称以了解如何操作。

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

链接

【讨论】:

    【解决方案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 / 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();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-07
      • 2022-01-19
      相关资源
      最近更新 更多