Java日期月差异

时间:2009-07-06 10:37:05

标签: java date

我有开始日期和结束日期。

我需要在Java中这两个日期之间的月数。

例如

  • 日期:2009-01-29
  • 迄今为止:2009-02-02

它有一个日期和一个二月日期。

它应该返回2.

21 个答案:

答案 0 :(得分:38)

正如其他人所说,如果有一个图书馆会在几个月内给你时差,你可以使用它,那么你也可以。

否则,如果y1m1是第一个日期的年份和月份,而y2m2是第二个日期的年份和月份,那么你想要的价值是:

(y2 - y1) * 12 + (m2 - m1) + 1;

请注意,即使第二个日期在第一个日期之后,中间项(m2 - m1)也可能是负数,但这没关系。

无论是月份是在1月= 0还是1月= 1,无论是年份是AD,1900年以来的年份,还是其他什么都没关系,只要这两个日期使用相同的基础。因此,例如,不要混合AD和BC日期,因为没有第0年,因此BC从AD偏移1。

如果以适当的形式提供给您,或者使用日历,您可以直接从日期获得y1等。

答案 1 :(得分:35)

除了使用Joda时间,这似乎是我最喜欢的建议,我会提供以下代码:

public static final int getMonthsDifference(Date date1, Date date2) {
    int m1 = date1.getYear() * 12 + date1.getMonth();
    int m2 = date2.getYear() * 12 + date2.getMonth();
    return m2 - m1 + 1;
}

编辑:从Java 8开始,有一种更标准的计算相同差异的方法。请参阅我的替代答案using JSR-310 api

答案 2 :(得分:30)

我会强烈为此推荐Joda-Time

  1. 这使得这种工作非常简单(请查看Periods
  2. 它不会受到困扰当前日期/时间对象的线程问题的影响(我正在考虑格式化程序,特别是)
  3. 这是Java 7附带的新Java日期/时间API的基础(所以你正在学习一些将成为标准的东西)
  4. 请注意尼克霍尔特的评论如下。夏令时的变化。

答案 3 :(得分:18)

现在{8}已包含在Java 8及更高版本的SDK中,这是获得两个日期值差异的更标准方法:

public static final long getMonthsDifference(Date date1, Date date2) {
    YearMonth m1 = YearMonth.from(date1.toInstant());
    YearMonth m2 = YearMonth.from(date2.toInstant());

    return m1.until(m2, ChronoUnit.MONTHS) + 1;
}

这有利于清楚地说明计算的精确度,并且很容易理解计算的目的是什么。

答案 4 :(得分:15)

Java 8解决方案:

@Test
public void monthBetween() {

    LocalDate d1 = LocalDate.of(2013, Month.APRIL, 1);
    LocalDate d2 = LocalDate.of(2014, Month.APRIL, 1);

    long monthBetween = ChronoUnit.MONTHS.between(d1, d2);

    assertEquals(12, monthBetween);

}

答案 5 :(得分:8)

使用joda time将是这样的(我比较了今天和2012年12月20日之间的几个月)

import org.joda.time.DateTime ;
import org.joda.time.Months;

DateTime x = new DateTime().withDate(2009,12,20); // doomsday lol

Months d = Months.monthsBetween( new DateTime(), x);
int monthsDiff = d.getMonths();

结果:41个月(2009年7月6日起)

应该很容易吗? :)

ps:您还可以使用SimpleDateFormat转换日期 像:

Date x = new SimpleDateFormat("dd/mm/yyyy").parse("20/12/2009");
DateTime z = new DateTime(x);

如果您不想使用Joda(无论出于何种原因),您可以将日期转换为TimeStamp,然后在两个日期之间进行毫秒的差异,然后计算回数月。但我仍然倾向于使用Joda时间来简化:)

答案 6 :(得分:8)

基于上面提到的答案,我自己编写了一些,我将其添加到现有的DateUtils类中:

    public static Integer differenceInMonths(Date beginningDate, Date endingDate) {
        if (beginningDate == null || endingDate == null) {
            return 0;
        }
        Calendar cal1 = new GregorianCalendar();
        cal1.setTime(beginningDate);
        Calendar cal2 = new GregorianCalendar();
        cal2.setTime(endingDate);
        return differenceInMonths(cal1, cal2);
    }

    private static Integer differenceInMonths(Calendar beginningDate, Calendar endingDate) {
        if (beginningDate == null || endingDate == null) {
            return 0;
        }
        int m1 = beginningDate.get(Calendar.YEAR) * 12 + beginningDate.get(Calendar.MONTH);
        int m2 = endingDate.get(Calendar.YEAR) * 12 + endingDate.get(Calendar.MONTH);
        return m2 - m1;
    }

关联单位测试:

    public void testDifferenceInMonths() throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        assertEquals(12, DateUtils.differenceInMonths(sdf.parse("2014/03/22"), sdf.parse("2015/03/22")).intValue());

        assertEquals(11, DateUtils.differenceInMonths(sdf.parse("2014/01/01"), sdf.parse("2014/12/25")).intValue());

        assertEquals(88, DateUtils.differenceInMonths(sdf.parse("2014/03/22"), sdf.parse("2021/07/05")).intValue());

        assertEquals(6, DateUtils.differenceInMonths(sdf.parse("2014/01/22"), sdf.parse("2014/07/22")).intValue());
    }

答案 7 :(得分:2)

Joda Time是一个非常酷的Java日期和时间库,可以帮助您使用Periods实现您想要的目标。

答案 8 :(得分:2)

这取决于您对一个月的定义,但这是我们使用的:

int iMonths = 0;

Calendar cal1 = GregorianCalendar.getInstance();
cal1.setTime(date1);

Calendar cal2 = GregorianCalendar.getInstance();
cal2.setTime(date2);

while (cal1.after(cal2)){
    cal2.add(Calendar.MONTH, 1);
    iMonths++;
}

if (cal2.get(Calendar.DAY_OF_MONTH) > cal1.get(Calendar.DAY_OF_MONTH)){
            iMonths--;
        }


return iMonths;

答案 9 :(得分:2)

TL;博士

ChronoUnit.MONTHS.between( 
    YearMonth.from( LocalDate.of( 2009 , 1 , 29 ) ) , 
    YearMonth.from( LocalDate.of( 2009 , 2 , 2 ) )
)

时区

Answer by Roland Tepp已接近但忽略了时区的关键问题。确定月份和日期需要一个时区,就像任何特定时刻日期在全球各地一样。

ZonedDateTime

因此,他将java.util.Date个对象转换为java.time.Instant个对象的示例隐式使用UTC。根据定义,这两个类中的值始终为UTC。因此,您需要将这些对象调整到所需/预期的时区,以便能够提取有意义的日期。

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtStart = myJavaUtilDate1.toInstant().atZone( z );
ZonedDateTime zdtStop = myJavaUtilDate2.toInstant().atZone( z );

YearMonth

由于您想知道日期范围触及了多少个日历月,而不是30天的块数,请转换为YearMonth个对象。

YearMonth start = YearMonth.from( zdtStart );
YearMonth stop = YearMonth.from( zdtStop );

ChronoUnit

通过调用ChronoUnit枚举来计算月数。

long monthsBetween = ChronoUnit.MONTHS.between( start , stop );
  

1

半开

你想要2的结果,但我们在这里得到1。原因是在日期时间工作中,最佳实践是通过半开放方法定义时间跨度。在半开放中,开头是包含,而结尾是独占。我建议你在整个日期工作中坚持这个定义,因为这样做最终有意义,消除了令人困惑的含糊之处,并使你的工作更容易在心理上解析,更不容易出错。但是如果你坚持你的定义,只需在结果中添加1,假设你有正数的结果(意味着你的时间跨度是时间而不是向后)。

LocalDate

原始问题不明确,但可能需要仅限日期值而不是日期时间值。如果是这样,请使用LocalDate类。 LocalDate类表示没有时间且没有时区的仅限日期的值。

LocalDate start = LocalDate.of( 2009 , 1 , 29 ) ; 
LocalDate stop = LocalDate.of( 2009 , 2 , 2 ) ;

long monthsBetween = ChronoUnit.MONTHS.between( start ,  stop );
  

1

关于 java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore

答案 10 :(得分:1)

这是使用java.util.Calendar对象的解决方案:

private static Integer getMonthsBetweenDates(Date d1, Date d2) {
    Calendar todayDate = getCalendar(d1);
    Calendar pastDate = getCalendar(d2);

    int yearDiff = todayDate.get(Calendar.YEAR) - pastDate.get(Calendar.YEAR);
    if (pastDate.get(Calendar.MONTH) < 11 && pastDate.get(Calendar.DAY_OF_MONTH) < 31){ //if pastDate is smaller than 31/12
        yearDiff++;
    }

    int monthCount = 0;
    for (int year = 0 ; year < yearDiff ; year++){
        if (year == 0) {
            monthCount += 12 - pastDate.get(Calendar.MONTH);
        } else if (year == yearDiff - 1){ //last year
            if (todayDate.get(Calendar.MONTH) < pastDate.get(Calendar.MONTH)){
                monthCount += todayDate.get(Calendar.MONTH) + 1;
            } else if (todayDate.get(Calendar.MONTH) >= pastDate.get(Calendar.MONTH) && todayDate.get(Calendar.DAY_OF_MONTH) < pastDate.get(Calendar.DAY_OF_MONTH)){
                monthCount += todayDate.get(Calendar.MONTH);
            } else if (todayDate.get(Calendar.MONTH) >= pastDate.get(Calendar.MONTH) && todayDate.get(Calendar.DAY_OF_MONTH) >= pastDate.get(Calendar.DAY_OF_MONTH)){
                monthCount += todayDate.get(Calendar.MONTH) + 1;
            }
        }
        for (int months = 0 ; months < 12 ; months++){
            if (year > 0 && year < yearDiff -1){
                monthCount++;
            }
        }
    }
    return monthCount;
}

答案 11 :(得分:1)

您可以使用日历或Joda time库。

在Joda时间,您可以使用Days.daysBetween()方法。然后,您可以计算月份差异。您还可以使用DateTime.getMonthOfYear()并进行减法(对于同一年的日期)。

答案 12 :(得分:1)

这是因为类Java日期和日历使用0-11中的月份索引

January = 0
December = 1

建议使用Joda Time

答案 13 :(得分:1)

它不是最好的答案,但你可以使用unixtimestamp 首先,您会找到日期的unixtime 然后互相推出

最后你应该将unixtime(sum)转换为String

答案 14 :(得分:1)

我必须写这个实现,becoz我有自定义的时间段,我必须在两个日期内寻找。 在这里,您可以定义自定义时间段并输入逻辑,以进行计算。

此处TimePeriod是一个POJO,它有开始,结束,期间开始,期末

公共课每月延长期{

public int getPeriodCount(String startDate, String endDate, int scalar) {
    int cnt = getPeriods(startDate, endDate, scalar).size();        
    return cnt;
}

public List getPeriods(String startDate, String endDate, int scalar) {

    ArrayList list = new ArrayList();

    Calendar startCal = CalendarUtil.getCalendar(startDate);
    Calendar endCal =  CalendarUtil.getCalendar(endDate);

    while (startCal.compareTo(endCal) <= 0) {
        TimePeriod period = new TimePeriod();
        period.setStartDate(startCal.getTime());
        period.setPeriodStartDate(getPeriodStartDate((Calendar) startCal.clone()).getTime());
        Calendar periodEndCal = getPeriodEndDate((Calendar) startCal.clone(), scalar);
        period.setEndDate(endCal.before(periodEndCal) ? endCal.getTime()    : periodEndCal.getTime());
        period.setPeriodEndDate(periodEndCal.getTime());

        periodEndCal.add(Calendar.DATE, 1);
        startCal = periodEndCal;
        list.add(period);
    }

    return list;
}


private Calendar getPeriodStartDate(Calendar cal) {     
    cal.set(Calendar.DATE, cal.getActualMinimum(Calendar.DATE));        
    return cal;
}

private Calendar getPeriodEndDate(Calendar cal, int scalar) {

    while (scalar-- > 0) {
        cal.set(Calendar.DATE, cal.getActualMaximum(Calendar.DATE));
        if (scalar > 0)
            cal.add(Calendar.DATE, 1);          
    }

    return cal;
}

}

答案 15 :(得分:0)

这里是java中monthDiff的完整实现,没有迭代。它返回两个日期之间的完整月的数量。如果要在结果中包括不完整的月份数(如第一个问题),则必须在调用该方法之前将两个日期的天,小时,分钟,秒和毫秒清零,否则可以更改不比较天,小时,分钟等的方法。

import java.util.Date;
import java.util.Calendar;
...
public static int monthDiff(Date d1, Date d2) {
    int monthDiff;

    Calendar c1, c2;
    int M1, M2, y1, y2, t1, t2, h1, h2, m1, m2, s1, s2, ms1, ms2;

    c1 = Calendar.getInstance();
    c1.setTime(d1);

    c2 = Calendar.getInstance();
    c2.setTime(d2);

    M1 = c1.get(Calendar.MONTH);
    M2 = c2.get(Calendar.MONTH);
    y1 = c1.get(Calendar.YEAR);
    y2 = c2.get(Calendar.YEAR);
    t1 = c1.get(Calendar.DAY_OF_MONTH);
    t2 = c2.get(Calendar.DAY_OF_MONTH);

    if(M2 < M1) { 
        M2 += 12;
        y2--;
    }

    monthDiff = 12*(y2 - y1) + M2 - M1;

    if(t2 < t1)
        monthDiff --; // not a full month
    else if(t2 == t1) { // perhaps a full month, we have to look into the details
        h1 = c1.get(Calendar.HOUR_OF_DAY);
        h2 = c2.get(Calendar.HOUR_OF_DAY);
        if(h2 < h1)
            monthDiff--; // not a full month
        else if(h2 == h1) { // go deeper
            m1 = c1.get(Calendar.MINUTE);
            m2 = c2.get(Calendar.MINUTE);
            if(m2 < m1) // not a full month
                monthDiff--;
            else if(m2 == m1) { // look deeper
                s1 = c1.get(Calendar.SECOND);
                s2 = c2.get(Calendar.SECOND);
                if(s2 < s1)
                    monthDiff--; // on enleve l'age de mon hamster
                else if(s2 == s1) { 
                    ms1 = c1.get(Calendar.MILLISECOND);
                    ms2 = c2.get(Calendar.MILLISECOND);
                    if(ms2 < ms1)
                        monthDiff--;
                    // else // it's a full month yeah
                }
            }
        }
    }
    return monthDiff;
}

答案 16 :(得分:0)

仅用1行和一些数学就可以完成很多长代码答案:

events.js:174
      throw er; // Unhandled 'error' event
      ^

Error: write EPIPE
    at WriteWrap.afterWrite [as oncomplete] (net.js:779:14)
Emitted 'error' event at:
    at Socket.onerror (_stream_readable.js:713:12)
    at Socket.emit (events.js:189:13)
    at Socket.EventEmitter.emit (domain.js:441:20)
    at onwriteError (_stream_writable.js:431:12)
    at onwrite (_stream_writable.js:456:5)
    at _destroy (internal/streams/destroy.js:40:7)
    at Socket._destroy (net.js:604:3)
    at Socket.destroy (internal/streams/destroy.js:32:8)
    at WriteWrap.afterWrite [as oncomplete] (net.js:781:10)

答案 17 :(得分:0)

long monthsBetween = ChronoUnit.MONTHS.between(LocalDate.parse("2016-01-29").minusMonths(1),
            LocalDate.parse("2016-02-02").plusMonths(1));
  1. 2016-01-29至2016-01-02 =个月1
  2. 2016-02-29至2016-02-02 =个月1
  3. 2016-03-29至2016-05-02 =第5个月

答案 18 :(得分:0)

低于逻辑将在几个月内获取差异

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

答案 19 :(得分:0)

用于查找两个日期之间的月份差异的完整代码段如下:

public String getContractMonth(String contractStart, String contractEnd) {
    SimpleDateFormat dfDate = new SimpleDateFormat("yyyy-MM-dd");
    String months = "0";
    try {
        Date startDate = dfDate.parse(contractStart);
        Date endDate = dfDate.parse(contractEnd);

        Calendar startCalendar = Calendar.getInstance();
        startCalendar.setTime(startDate);
        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(endDate);

        int diffYear = endCalendar.get(Calendar.YEAR) - startCalendar.get(Calendar.YEAR);
        int diffMonth = diffYear * 12 + endCalendar.get(Calendar.MONTH) - startCalendar.get(Calendar.MONTH);
        months = diffMonth + "";
    } catch (ParseException e) {
        e.printStackTrace();
    } catch (java.text.ParseException e) {
        e.printStackTrace();
    }
    return months;
}

答案 20 :(得分:0)

为什么不用完整的时间计算

 public static Integer calculateMonthDiff(Date begining, Date end) throws Exception {

        if (begining.compareTo(end) > 0) {
            throw new Exception("Beginning date is greater than the ending date");
        }

        if (begining.compareTo(end) == 0) {
            return 0;
        }

        Calendar cEndCheckDate = Calendar.getInstance();
        cEndCheckDate.setTime(begining);
        int add = 0;
        while (true) {
            cEndCheckDate.add(Calendar.MONTH, 1);
            add++;
            if (cEndCheckDate.getTime().compareTo(end) > 0) {
                return add - 1;
            }
        }
    }