6

我想要 2 之间java.util.Date的月数,而不计算月中的天数。所以我只想要比较中的年份和月份。

例子:

 monthsBetween(new Date(2012,01,28), new Date(2012,02,01)) ---> 1

 monthsBetween(new Date(2012,02,27), new Date(2012,02,28)) ---> 0

 monthsBetween(new Date(2012,03,28), new Date(2012,07,01)) ---> 4

我已经使用 Joda-time 尝试过这个(返回 0,预期为 1):

private static int monthsBetween(final Date fromDate, final Date toDate) {
    DateTime date1 = new DateTime().withDate(2012, 1, 20);
    DateTime date2 = new DateTime().withDate(2012, 2, 13);
    PeriodType monthDay = PeriodType.yearDayTime().withDaysRemoved();
    Period difference = new Period(date1, date2, monthDay);
    int months = difference.getMonths();
    
    return months;
 }

还有这个(相同的结果),使用 Joda-time:

private static int monthsBetween(final Date fromDate, final Date toDate) {
        return Months.monthsBetween(new DateTime(fromDate), new   DateTime(toDate).getMonths();
    }

我怎样才能做到这一点?

4

8 回答 8

10

你问的是整月的数量——这与说“忽略月份部分”不同。

首先,我建议使用LocalDate而不是DateTime计算。理想情况下,根本不要使用java.util.Date,并以您的输入作为LocalDate开始(例如,将文本直接解析为该文本,或者您的数据来自何处。)在两个日期中将月份中的日期设置为 1,然后获取差异以月为单位:

private static int monthsBetweenIgnoreDays(LocalDate start, LocalDate end) {
    start = start.withDayOfMonth(1);
    end = end.withDayOfMonth(1);
    return Months.monthsBetween(start, end).getMonths();
}
于 2012-11-26T14:19:08.827 回答
5

此版本基于 JDK 日历:

public static void main(String[] args) throws Exception {
    SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
    Date d1 = f.parse("2012-01-01");
    Date d2 = f.parse("2012-02-02");
    int n = differenceInMonths(d1, d2);
    System.out.println(n);
}

private static int differenceInMonths(Date d1, Date d2) {
    Calendar c1 = Calendar.getInstance();
    c1.setTime(d1);
    Calendar c2 = Calendar.getInstance();
    c2.setTime(d2);
    int diff = 0;
    if (c2.after(c1)) {
        while (c2.after(c1)) {
            c1.add(Calendar.MONTH, 1);
            if (c2.after(c1)) {
                diff++;
            }
        }
    } else if (c2.before(c1)) {
        while (c2.before(c1)) {
            c1.add(Calendar.MONTH, -1);
            if (c1.before(c2)) {
                diff--;
            }
        }
    }
    return diff;
}
于 2012-11-26T15:39:20.610 回答
3

如果您在 int 值中有年份和月份:

months = (year2-year1)*12 + month2 - month1;

一年有12个月。

于 2015-03-30T16:25:04.570 回答
2

java.time

java.util日期时间 API 及其格式化 API已SimpleDateFormat过时且容易出错。建议完全停止使用它们并切换到现代 Date-Time API *

使用java.time现代 API 的解决方案:由于您不需要时间和时区来考虑您的要求,因此LocalDate最适合您的要求。

演示:

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;

public class Main {
    public static void main(String[] args) {
        // Test
        System.out.println(monthsBetween(LocalDate.of(2012, 1, 28), LocalDate.of(2012, 2, 1)));
        System.out.println(monthsBetween(LocalDate.of(2012, 2, 27), LocalDate.of(2012, 2, 28)));
        System.out.println(monthsBetween(LocalDate.of(2012, 3, 28), LocalDate.of(2012, 7, 1)));
    }

    static int monthsBetween(final LocalDate fromDate, final LocalDate toDate) {
        return Math.toIntExact(
                    ChronoUnit.MONTHS.between(
                            fromDate.with(TemporalAdjusters.firstDayOfMonth()),
                            toDate.with(TemporalAdjusters.firstDayOfMonth())
                    )
                );
    }
}

输出:

1
0
4

ONLINE DEMO

如果您需要使用java.util.Date

出于任何原因,如果您需要对 的对象执行此操作java.util.Date,您可以将 的对象转换java.util.DateInstantusing Date#toInstant,根据需要可以转换为其他java.time类型。

演示:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) throws ParseException {
        // Test
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
        sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
        
        System.out.println(monthsBetween(sdf.parse("2012-01-28"), sdf.parse("2012-02-01")));
        System.out.println(monthsBetween(sdf.parse("2012-02-27"), sdf.parse("2012-02-28")));
        System.out.println(monthsBetween(sdf.parse("2012-03-28"), sdf.parse("2012-07-01")));
    }

    static int monthsBetween(final Date fromDate, final Date toDate) {
        ZonedDateTime zdtFrom = fromDate.toInstant().atZone(ZoneOffset.UTC);
        ZonedDateTime zdtTo = toDate.toInstant().atZone(ZoneOffset.UTC);
        
        return Math.toIntExact(
                    ChronoUnit.MONTHS.between(
                            zdtFrom.with(TemporalAdjusters.firstDayOfMonth()),
                            zdtTo.with(TemporalAdjusters.firstDayOfMonth())
                    )
                );
    }
}

输出:

1
0
4

ONLINE DEMO

Trail: Date Time了解更多关于java.time现代日期时间 API *的信息。

一些重要的注意事项:

  1. 构造函数java.util.Date(int, int, int)已被弃用。
  2. java.util.Date并且java.util.Calendar月份是基于 0 的,即一月是月份#0。但是,SimpleDateFormat将 1 月视为第 1 个月。
  3. 以 0 为前缀的int数字被认为是八进制数,只能支持 0-7 范围内的数字。您遇到编译错误并非偶然,因为您只使用了 07 之前的月份。例如,如果您使用 08 或 09,您就会遇到编译错误。

* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,则可以使用ThreeTen-Backport,它将大部分java.time功能向后移植到 Java 6 和 7。如果您正在为 Android 项目和 Android API 工作level 仍然不符合 Java-8,请检查Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

于 2021-06-05T16:49:50.483 回答
1
/**
 * Gets number of months between two dates. 
 * <p>Months are calculated as following:</p>
 * <p>After calculating number of months from years and months from two dates,
 * if there are still any extra days, it will be considered as one more month.
 * For ex, Months between 2012-01-01 and 2013-02-06 will be 14 as 
 * Total Months = Months from year difference are 12 + Difference between months in dates is 1  
 * + one month since day 06 in enddate is greater than day 01 in startDate.
 * </p>
 * @param startDate
 * @param endDate
 * @return
 */
public static int getMonthsBetweenDates(Date startDate, Date endDate)
{
    if(startDate.getTime() > endDate.getTime())
    {
        Date temp = startDate;
        startDate = endDate;
        endDate = temp;
    }
    Calendar startCalendar = Calendar.getInstance(); 
    startCalendar.setTime(startDate);
    Calendar endCalendar = Calendar.getInstance();
    endCalendar.setTime(endDate);

    int yearDiff = endCalendar.get(Calendar.YEAR)- startCalendar.get(Calendar.YEAR);
    int monthsBetween = endCalendar.get(Calendar.MONTH)-startCalendar.get(Calendar.MONTH) +12*yearDiff;

    if(endCalendar.get(Calendar.DAY_OF_MONTH) >= startCalendar.get(Calendar.DAY_OF_MONTH))
        monthsBetween = monthsBetween + 1;
    return monthsBetween;

}
于 2013-06-21T20:06:59.313 回答
1

我只会获取日历实例的年和月字段,将年转换为月并获得差异。

private static int monthsBetween(final Date s1, final Date s2) {
    final Calendar d1 = Calendar.getInstance();
    d1.setTime(s1);
    final Calendar d2 = Calendar.getInstance();
    d2.setTime(s2);
    int diff = (d2.get(Calendar.YEAR) - d1.get(Calendar.YEAR)) * 12 + d2.get(Calendar.MONTH) - d1.get(Calendar.MONTH);
    return diff;
}
于 2015-04-24T13:38:59.950 回答
0

它也适用于闰年

public static int getNumberOfMonths(Date fromDate, Date toDate) {
    int monthCount = 0;
    Calendar cal = Calendar.getInstance();
    cal.setTime(fromDate);
    int c1date = cal.get(Calendar.DATE);
    int c1month = cal.get(Calendar.MONTH);
    int c1year = cal.get(Calendar.YEAR);
    cal.setTime(toDate);
    int c2date = cal.get(Calendar.DATE);
    int c2month = cal.get(Calendar.MONTH);
    int c2year = cal.get(Calendar.YEAR);
    System.out.println(" c1date:"+c1date+" month:"+c1month+" year:"+c1year);
    System.out.println(" c2date:"+c2date+" month:"+c2month+" year:"+c2year);
    GregorianCalendar grCal = new GregorianCalendar();
    boolean isLeapYear1 = grCal.isLeapYear(c1year);
    boolean isLeapYear2 = grCal.isLeapYear(c2year);
    monthCount = ((c2year - c1year) * 12) + (c2month - c1month);
    if(isLeapYear2 && c2month == 1 && c2date == 29){
        monthCount = monthCount+ ((c1date == 28)?0:1);
    }else if(isLeapYear1 && c1month == 1 && c1date == 29){
        monthCount = monthCount+ ((c2date == 28)?0:1);
    }else{
        monthCount = monthCount+ ((c2date >= c1date)?0:1);
    }
    return monthCount;

}
于 2016-03-07T06:24:06.387 回答
0

这将在几个月内为您带来差异

(endCal.get(Calendar.YEAR)*12+endCal.get(Calendar.MONTH))-(startCal.get(Calendar.YEAR)*12+startCal.get(Calendar.MONTH))
于 2017-08-16T00:09:35.590 回答