JAVAGregorianCalendar的日期月差不正确

JAVAGregorianCalendar的日期月差不正确,java,date,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(); c

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个测试用例的结果:

一,

二,

三,

错误:差异必须为4个月

四,

错误:差异必须为11个月

正如您所看到的,测试用例1和2是正确的,但是测试用例3和4是错误的


为什么?

计算差额时,您完全忽略了日期。所以1月1日和1月31日是一样的


前两种情况恰好在第二天有更大的一天,后两种情况正好是更小的一天。但是这个信息被忽略了。

示例3和4按预期工作。八月->一月是5个月,八月->八月是12个月。当您减去月份值时,它严格地处理月份值,而不是月份的天数。如果要计算当月的日期,请添加以下内容:

if(dateFrom.get(Calendar.DAY_OF_MONTH) > dateTo.get(Calendar.DAY_OF_MONTH) && diff != 0){
    diffMonth -=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;
    }

这是现代java日期和时间API java.time的众多优势之一:

public static long differenceInMonths(LocalDate dateFrom, LocalDate dateTo) {
    return ChronoUnit.MONTHS.between(dateFrom, dateTo);
}
是的,我郑重建议您在旧的Java6代码中使用它。当然,这需要更多的解释。在我开始之前,请允许我演示一下,上面的一行代码为您提供了您期望的结果:

    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)));
输出:

问题:当我有日期类型的变量时,如何使用此方法? 据我所知,你有一个旧方法,用两个老式的日期参数,你会问为什么它会给出不正确的结果。因此,我猜您也会希望修复它,以提供用户认为正确的结果,正如您自己的答案所示。因此,请重写您的方法以使用上面的我的方法:

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;
}
如果希望在Java 8或更高版本上使用相同的代码,并使用内置的Java.time,则转换的方式会稍有不同,例如:

    LocalDate localDateFrom = dateFrom.toInstant()
            .atZone(zone)
            .toLocalDate();
链接 解释如何使用java.time。 ,其中首次描述了java.time。 ,java.time的后端口到JSR-310的Java6和Java7。 ,Android版Three Ten Backport ,解释得非常透彻。
好的,您可能会在2018年12月之前得到Java 6。这是一个旧项目的源代码,它是在Java 6.0FYI上编写的,麻烦的旧日期时间类,如Java.util.date、Java.util.Calendar和Java.text.SimpleDateFormat,现在是遗留的,被这些类所取代。大部分java.time功能都在项目中向后移植到Java6和Java7。进一步适应项目中早期的Android。看。但是如果他明确地获取了年份和月份,为什么与日期相关呢?所以1月1日是月=0,但1月31日也是月=0?@Korashen如果忽略天,最后两个例子是正确的。从明年1月到明年1月是12个月。@Korashen因为如果他从1月31日到1月1日,他希望它返回11个月,因为12个月还没有过去。啊,这是模糊逻辑!因为代码工作得很好,不工作。如果currentDate=2018年8月8日,DateTo=2018年8月13日,则diffMonth=-1。但必须是0@Alexei对不起,我把比较倒过来了。我确定了我的答案。如果currentDate大于DateTo,那么减去1。但是这个测试用例呢:currentDate=2018年8月8日,DateTo=2018年8月1日->diffMonth=-1。这是不对的。必须是0@Alexei你是对的。这是一个非常具体的测试用例,可以通过添加一个额外的条件来处理。仅供参考,像java.util.date、java.util.Calendar和java.text.SimpleDataFormat这样麻烦的旧日期时间类现在是遗留的,被这些类所取代。大部分java.time功能都在项目中向后移植到Java6和Java7。进一步适应项目中早期的Android。请参见.FYI,像java.util.date、java.util.Calendar和java.text.SimpleDateFormat这样麻烦的旧日期时间类现在是遗留的,被这些类所取代。大部分java.time功能都在项目中向后移植到Java6和Java7。进一步适应项目中早期的Android。看见
public static long differenceInMonths(LocalDate dateFrom, LocalDate dateTo) {
    return ChronoUnit.MONTHS.between(dateFrom, dateTo);
}
    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
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;
}
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;
    LocalDate localDateFrom = dateFrom.toInstant()
            .atZone(zone)
            .toLocalDate();