2个java.util.Date之间的月数,不包括日期

时间:2012-11-26 14:14:23

标签: java date jodatime

我想要在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

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

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

我该如何做到这一点?

8 个答案:

答案 0 :(得分:8)

您要求的是整月的数量 - 这与说“忽略月份的某一部分”不同。

首先,我建议使用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();
}

答案 1 :(得分:5)

此版本基于JDK Calendar:

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

答案 2 :(得分:2)

如果你有年数和月数的int值:

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

一年12个月。

答案 3 :(得分:2)

java.time

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

使用现代 API java.time 的解决方案:由于您不需要在您的要求中考虑时间和时区,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的对象执行此操作,您可以使用可转换的Date#toInstantjava.util.Date的对象转换为Instant根据需要更改为其他 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

modern Date-Time API 中详细了解 java.timeTrail: Date Time*

一些重要的注意事项:

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

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

答案 4 :(得分: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;

}

答案 5 :(得分:1)

我只想获取Calendar实例的年和月字段,将年份转换为月份并获得差异。

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

答案 6 :(得分: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;

}

答案 7 :(得分:0)

这会让你在几个月内获得差异

(endCal.get(Calendar.YEAR)*12+endCal.get(Calendar.MONTH))-(startCal.get(Calendar.YEAR)*12+startCal.get(Calendar.MONTH))