Java。 GregorianCalendar的月份日期差无法正常工作

时间:2018-08-08 13:49:03

标签: java java-date

java 6.0

假设今天是: 2018年8月8日

此处方法返回的两个月的日期差。

    public static Integer getDiffMonths(Date dateFrom, Date dateTo) {
        Calendar cal1 = new GregorianCalendar();
        cal1.setTime(dateFrom);
        Calendar cal2 = new GregorianCalendar();
        cal2.setTime(dateTo);
        return differenceInMonths(cal1, cal2);
}

 private static Integer differenceInMonths(Calendar dateFrom, Calendar dateTo) {
        int month1 = dateFrom.get(Calendar.YEAR) * 12 + dateFrom.get(Calendar.MONTH);
        int month2 = dateTo.get(Calendar.YEAR) * 12 + dateTo.get(Calendar.MONTH);
        int diff = month2 - month1;
        return diff;
    }

这里有4个测试用例的结果:

1。

currentDate = Aug 08 2018
DateTo =      Aug 13 2018
diffMonth = 0

2。

currentDate = Aug 08 2018
DateTo =      Oct 14 2018
diffMonth = 2

3。

currentDate = Aug 08 2018
DateTo =      Jan 03 2019
diffMonth = 5

错误:相差必须为4个月

4。

currentDate = Aug 08 2018
DateTo =      Aug 03 2019
diffMonth = 12

错误:相差必须为11个月

您可以看到测试用例#1和#2是正确的,但是测试用例#3和#4是不正确的。

为什么?

4 个答案:

答案 0 :(得分:6)

计算差异时,您将完全忽略这一天。因此1月1日与1月31日相同。

前两个案例在第二个日期碰巧有较大的一天,后两个案例的碰头则较小。但是这些信息将被忽略。

答案 1 :(得分:1)

示例3和4按预期方式工作。 8月-> 1月是5个月,8月-> 8月是12个月。减去月份值时,它严格处理月份值,而不是月份中的天。如果要考虑该月的某天,请添加以下内容:

if(dateFrom.get(Calendar.DAY_OF_MONTH) > dateTo.get(Calendar.DAY_OF_MONTH) && diff != 0){
    diffMonth -=1;
}

答案 2 :(得分:1)

这是现代Java日期和时间API java.time表现出色的(很多)情况之一:

public static long differenceInMonths(LocalDate dateFrom, LocalDate dateTo) {
    return ChronoUnit.MONTHS.between(dateFrom, dateTo);
}

是的,我强烈建议您在旧的Java 6代码中使用此代码。当然,它需要更多的解释。在开始之前,请允许我证明上面的一种方法可以为您带来预期的结果:

    LocalDate currentDate = LocalDate.of(2018, Month.AUGUST, 8);
    System.out.println("1. Months until Aug 13 2018: " 
            + differenceInMonths(currentDate, LocalDate.of(2018, Month.AUGUST, 13)));
    System.out.println("2. Months until Oct 14 2018: " 
            + differenceInMonths(currentDate, LocalDate.of(2018, Month.OCTOBER, 14)));
    System.out.println("3. Months until Jan 03 2019: " 
            + differenceInMonths(currentDate, LocalDate.of(2019, Month.JANUARY, 3)));
    System.out.println("4. Months until Aug 03 2019: " 
            + differenceInMonths(currentDate, LocalDate.of(2019, Month.AUGUST, 3)));

输出:

1. Months until Aug 13 2018: 0
2. Months until Oct 14 2018: 2
3. Months until Jan 03 2019: 4
4. Months until Aug 03 2019: 11

问题:当我有Date类型的变量时,如何使用此方法?

据我了解,您有一个采用两个老式Date参数的旧方法,并且您在问为什么它给出错误的结果。因此,我想您也将希望对其进行修复,以提供用户认为正确的结果(如您自己的答案所建议)。因此,重写您的方法以使用我上面的方法:

public static Integer getDiffMonths(Date dateFrom, Date dateTo) {
    ZoneId zone = ZoneId.systemDefault();
    LocalDate localDateFrom = DateTimeUtils.toInstant(dateFrom)
            .atZone(zone)
            .toLocalDate();
    LocalDate localDateTo = DateTimeUtils.toInstant(dateTo)
            .atZone(zone)
            .toLocalDate();
    long diff = differenceInMonths(localDateFrom, localDateTo);
    if (diff < Integer.MIN_VALUE || diff > Integer.MAX_VALUE) {
        throw new IllegalArgumentException("Dates are too far apart");
    }
    return (int) diff;
}

即使在您自己的代码中,从DateLocalDate的转换至少与到Calendar的转换一样冗长,但您可能会认为差值计算本身是值得的简单明了。

您的旧代码出了什么问题,别人已经很好地解释了,我没有理由重复。

问题:如何在Java 6上使用java.time?

java.time在Java 6上运行良好。

  • 在Java 8和更高版本以及较新的Android设备上(我被告知,从API级别26开始),内置了现代API。
  • 在Java 6和7中,获得ThreeTen Backport,即新类的backport(JSR 310的ThreeTen;请参见底部的链接)。
  • 在(较旧的)Android上,使用Android版本的ThreeTen Backport。叫做ThreeTenABP。并确保您使用子包从org.threeten.bp导入日期和时间类。

上面的代码使用ThreeTen Backport并使用子包从org.threeten.bp导入日期和时间类:

import org.threeten.bp.DateTimeUtils;
import org.threeten.bp.LocalDate;
import org.threeten.bp.Month;
import org.threeten.bp.ZoneId;
import org.threeten.bp.temporal.ChronoUnit;

如果要在Java 8或更高版本上使用相同的代码,并使用内置的java.time,则转换会有所不同,例如:

    LocalDate localDateFrom = dateFrom.toInstant()
            .atZone(zone)
            .toLocalDate();

链接

答案 3 :(得分:-1)

这里是正确的代码:

   public static Integer getDiffMonths(Date dateFrom, Date dateTo) {
        Calendar cal1 = new GregorianCalendar();
        cal1.setTime(dateFrom);
        Calendar cal2 = new GregorianCalendar();
        cal2.setTime(dateTo);
        return differenceInMonths(cal1, cal2);
    }

   private static Integer differenceInMonths(Calendar dateFrom, Calendar dateTo) {
        if (dateFrom == null || dateTo == null) {
            return 0;
        }
        int month1 = dateFrom.get(Calendar.YEAR) * 12 + dateFrom.get(Calendar.MONTH);
        int month2 = dateTo.get(Calendar.YEAR) * 12 + dateTo.get(Calendar.MONTH);
        int diff = month2 - month1;
        if (diff > 0) {
            if (dateFrom.get(Calendar.DAY_OF_MONTH) > dateTo.get(Calendar.DAY_OF_MONTH)) {
                diff--;
            }
        } else if (diff < 0) {
            if (dateTo.get(Calendar.DAY_OF_MONTH) > dateFrom.get(Calendar.DAY_OF_MONTH)) {
                diff++;
            }
        }
        return diff;
    }